You are on page 1of 9

* Utilities for export

class ZCL_EHFND_EXP_UTILITIES definition


public
final
create private .

public section.

*"* public components of class ZCL_EHFND_EXP_UTILITIES


*"* do not include other source files here!!!
constants GC_TIMESTAMP_DOMAIN_NAME1 type STRING value '/BOBF/TIMESTAMP'. "#EC
NOTEXT
constants GC_TIMESTAMP_DOMAIN_NAME2 type STRING value '/BOPF/TIMESTAMP'. "#EC
NOTEXT

type-pools ABAP .
class-methods IS_TIME_STAMP_DOMAIN
importing
!IV_DOMAIN_NAME type REFNAME
returning
value(RV_IS_TIME_STAMP) type ABAP_BOOL .
class-methods GET_INSTANCE
returning
value(RO_INSTANCE) type ref to ZCL_EHFND_EXP_UTILITIES .
methods CONVERT_TO_STRING
importing
!IV_VALUE type ANY
!IS_META_DATA type EHFNDS_EXP_FIELD_DESCRIPTOR
returning
value(RV_STRING) type STRING .
methods COULD_BE_NEGATIVE
importing
!IS_META_DATA type EHFNDS_EXP_FIELD_DESCRIPTOR
returning
value(RV_COULD_BE_NEGATIVE) type ABAP_BOOL .
methods CREATE_META_DATA
importing
!IS_NODE_DESCR type EHFNDS_EXP_NODE_DESCRIPTOR
exporting
!ET_META_DATA type EHFNDT_EXP_TAB_META_DATA .
methods CREATE_TABULAR_STRUCTURE
importing
!IS_NODE_DESCR type EHFNDS_EXP_NODE_DESCRIPTOR
!IT_HIERARCHICAL_DATA type ANY TABLE optional
exporting
!ET_TABULAR_DATA type EHFNDT_EXP_TAB_DATA .
methods CREATE_UNIQUE_DESCRIPTIONS
changing
!CT_META_DATA type EHFNDT_EXP_TAB_META_DATA .
methods IS_TIMESTAMP
importing
!IS_META_DATA type EHFNDS_EXP_FIELD_DESCRIPTOR
returning
value(RV_IS_TIMESTAMP) type ABAP_BOOL .
methods IS_TIMESTAMP_BY_ABAP_DATA_DESC
importing
!IO_DATA_DESC type ref to CL_ABAP_DATADESCR
returning
value(RV_IS_TIMESTAMP) type ABAP_BOOL .

private section.

types:
*"* private components of class ZCL_EHFND_EXP_UTILITIES
*"* do not include other source files here!!!
BEGIN OF gty_s_descr,
string TYPE string,
serial_number TYPE i,
first_index TYPE i,
END OF gty_s_descr .
types:
gty_th_descr TYPE HASHED TABLE OF gty_s_descr WITH UNIQUE KEY string .

class-data GO_INSTANCE type ref to ZCL_EHFND_EXP_UTILITIES .


constants GC_TIMESTAMP_DOMAIN_NAME3 type STRING value 'TZNTSTMPL'. "#EC NOTEXT
constants GC_TIMESTAMP_DOMAIN_NAME4 type STRING value 'TZNTSTMPLL'. "#EC NOTEXT
constants GC_TIMESTAMP_DOMAIN_NAME5 type STRING value 'TZNTSTMPS'. "#EC NOTEXT
constants GC_TIMESTAMP_DOMAIN_NAME6 type STRING value 'TZNTSTMPSL'. "#EC NOTEXT

methods CREATE_META_DATA_FOR_NODE
importing
!IS_NODE_DESCR type EHFNDS_EXP_NODE_DESCRIPTOR
changing
!CT_META_DATA type EHFNDT_EXP_TAB_META_DATA .
methods DENORMALIZE_HIERARCHY
importing
!IS_NODE_DESCR type EHFNDS_EXP_NODE_DESCRIPTOR
!IT_HIERARCHICAL_DATA type ANY TABLE optional
!IV_FLG_ON_LEADING_PATH type ABAP_BOOL
changing
!CT_ROWS type EHFNDT_EXP_TAB_DATA .
methods DENORMALIZE_SINGLE_RECORD
importing
!IS_NODE_DESCR type EHFNDS_EXP_NODE_DESCRIPTOR
!IS_DATA type ANY optional
!IV_FLG_ON_LEADING_PATH type ABAP_BOOL
changing
!CT_ROWS type EHFNDT_EXP_TAB_DATA .

ENDCLASS.

class ZCL_EHFND_EXP_UTILITIES IMPLEMENTATION.


METHOD is_time_stamp_domain.
rv_is_time_stamp = boolc( iv_domain_name = gc_timestamp_domain_name1 OR
iv_domain_name = gc_timestamp_domain_name2 OR
iv_domain_name = gc_timestamp_domain_name3 OR
iv_domain_name = gc_timestamp_domain_name4 OR
iv_domain_name = gc_timestamp_domain_name5 OR
iv_domain_name = gc_timestamp_domain_name6 ).
ENDMETHOD.
METHOD GET_INSTANCE.

" If there is no shared instance of this class yet, create one


IF ( go_instance IS INITIAL ).
CREATE OBJECT go_instance.
ENDIF.
" Return the shared instance of this class
ro_instance = go_instance.

ENDMETHOD.
METHOD convert_to_string.

DATA lv_string TYPE string.

IF ( me->could_be_negative( is_meta_data ) = abap_true ).


IF ( iv_value < 0 ).
" we have a numberical value < 0
" make sure the string representation has the negative sign as first
character
" rather than last character
lv_string = |-{ - iv_value }|.
* Begin Correction 19.12.2012 1803019 *********************************
ELSEIF ( is_meta_data-field_type = cl_abap_typedescr=>typekind_float AND
iv_value = 0 ).
" For float values we need a special handling because for example Micorsoft
Excel
" does not recognize the value 0 in float format as 0.0000000000000000E+00
lv_string = '0'.
* End Correction 19.12.2012 1803019 ***********************************
ELSE.
lv_string = iv_value.
ENDIF.
" Remove any trailing blanks
rv_string = condense( val = lv_string from = '' ).
ELSE.
rv_string = iv_value.
ENDIF.

ENDMETHOD.
METHOD could_be_negative.

rv_could_be_negative = boolc( is_meta_data-field_type =


cl_abap_typedescr=>typekind_decfloat OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_decfloat16 OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_decfloat34 OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_float OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_numeric OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_packed OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_int OR
is_meta_data-field_type =
cl_abap_typedescr=>typekind_int2 ).

ENDMETHOD.
METHOD create_meta_data.

CLEAR et_meta_data.

me->create_meta_data_for_node( EXPORTING is_node_descr = is_node_descr


CHANGING ct_meta_data = et_meta_data ).
ENDMETHOD.
METHOD create_tabular_structure.

CLEAR et_tabular_data.

IF ( lines( it_hierarchical_data ) > 0 ).


" Start the recursion to denormalize the hierarchy
me->denormalize_hierarchy( EXPORTING is_node_descr = is_node_descr
it_hierarchical_data =
it_hierarchical_data
iv_flg_on_leading_path = abap_true " at
this level we always have the leading path
CHANGING ct_rows = et_tabular_data ).
ENDIF.

ENDMETHOD.
METHOD create_unique_descriptions.

DATA lth_unique_descr TYPE gty_th_descr.


DATA ls_unique_descr TYPE gty_s_descr.
DATA lr_s_unique_descr TYPE REF TO gty_s_descr.
DATA lr_s_field_descr TYPE REF TO ehfnds_exp_field_descriptor.
DATA lr_s_field_descr2 TYPE REF TO ehfnds_exp_field_descriptor.
DATA lv_index TYPE i.

LOOP AT ct_meta_data REFERENCE INTO lr_s_field_descr.


lv_index = sy-tabix.

" Check if the same description has been processed before


READ TABLE lth_unique_descr REFERENCE INTO lr_s_unique_descr
WITH TABLE KEY string = lr_s_field_descr->field_descr.
IF ( sy-subrc <> 0 ).
" This is the first time we handle this description
" Create an entry in the look-up table
ls_unique_descr-string = lr_s_field_descr->field_descr.
ls_unique_descr-serial_number = 1.
ls_unique_descr-first_index = lv_index.
INSERT ls_unique_descr INTO TABLE lth_unique_descr.
ELSE.
" We have found a duplicate description
IF ( lr_s_unique_descr->serial_number = 1 ).
" This is the second occurrence of the description so we have to update the
" first occurrence as well
" Read the first occurrence from the meta data
READ TABLE ct_meta_data REFERENCE INTO lr_s_field_descr2 INDEX
lr_s_unique_descr->first_index.
ASSERT sy-subrc = 0.
" Append the serial number 1 to the first occurrence of the description
lr_s_field_descr2->field_descr = |{ lr_s_field_descr2->field_descr } -
{ lr_s_unique_descr->serial_number }|.
ENDIF.

" Append the next serial number to the description


ADD 1 TO lr_s_unique_descr->serial_number.
lr_s_field_descr->field_descr = |{ lr_s_field_descr->field_descr } -
{ lr_s_unique_descr->serial_number }|.
ENDIF.
ENDLOOP.

ENDMETHOD.
METHOD is_timestamp.

DATA ls_ddic_header TYPE x030l.

rv_is_timestamp = abap_false.

IF ( is_meta_data-data_descr IS NOT INITIAL ).


rv_is_timestamp = is_timestamp_by_abap_data_desc( io_data_desc = is_meta_data-
data_descr ).
ENDIF.

ENDMETHOD.
METHOD is_timestamp_by_abap_data_desc.

DATA ls_ddic_header TYPE x030l.

ls_ddic_header = io_data_desc->get_ddic_header( ).
" In order to identify timestamp fields we check if the field's domain
" is one of the unual timestamp domains.
rv_is_timestamp = is_time_stamp_domain( ls_ddic_header-refname ).

ENDMETHOD.

*--
* PRIVATE
METHOD CREATE_META_DATA_FOR_NODE.

DATA lr_s_field_descr TYPE REF TO ehfnds_exp_field_descriptor_xt.


DATA lr_s_field_descr_on_lead_path TYPE REF TO ehfnds_exp_field_descriptor_xt.

FIELD-SYMBOLS <ls_node_descr> TYPE ehfnds_exp_node_descriptor.

" We want to get the fields of the leading path on the right-hand side of the
table.
" Thus we process the fields that are not on the leading path first and append
their
" respective meta-data to the result table. After that we process the fields on
the
" leading path and append their respective meta-data to the result. This way the
fields
" on the leading path are at the end of the result table.
" NOTE: This logic corresponds to the logic in method DENORMALIZE_SINGLE_RECORD
which
" inserts the non-leading path-fields before the leading path-fields into the de-
normalized
" table (see end of method).

" First process all fields that are not on the leading path
LOOP AT is_node_descr-field_descr_tab REFERENCE INTO lr_s_field_descr.

IF ( sy-tabix = is_node_descr-field_on_leading_path ).
" Save the field descriptor of the leading path for later
lr_s_field_descr_on_lead_path = lr_s_field_descr.
ELSE.

" Consistency check: here we can have only these kinds of fields
ASSERT lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_elem OR
lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_table.

" Append the meta data of elementary fields directly to the result
" In case the current field is actually an internal table
" recursively append the meta data of its fields to the result
IF ( lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_elem ).

APPEND lr_s_field_descr->descr TO ct_meta_data.

ELSEIF ( lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_table ).

" node_descr_ref is a ref to data because it is not possible to define


recursive data structures.
" Thus, here we have to do a type-cast via field-symbol.
ASSIGN lr_s_field_descr->node_descr_ref->* TO <ls_node_descr>.

me->create_meta_data_for_node( EXPORTING is_node_descr = <ls_node_descr>


CHANGING ct_meta_data = ct_meta_data ).
ENDIF.
ENDIF.

ENDLOOP.

" Now append the fields on the leading path


IF ( lr_s_field_descr_on_lead_path IS BOUND ).
" node_descr_ref is ref to data because it is not possible to define recursive
data structures.
" Thus, here we have to do a type-cast via field-symbol.
ASSIGN lr_s_field_descr_on_lead_path->node_descr_ref->* TO <ls_node_descr>.

me->create_meta_data_for_node( EXPORTING is_node_descr = <ls_node_descr>


CHANGING ct_meta_data = ct_meta_data ).
ENDIF.

ENDMETHOD.
METHOD denormalize_hierarchy.

DATA lr_s_data TYPE REF TO data.


DATA lt_rows TYPE ehfndt_exp_tab_data.

FIELD-SYMBOLS <lg_s_hier> TYPE any.

IF ( lines( it_hierarchical_data ) = 0 ).
" Make sure we have at least one record even if it is initial
CREATE DATA lr_s_data LIKE LINE OF it_hierarchical_data.
ASSERT sy-subrc = 0.

" Make sure the new record is initial


ASSIGN lr_s_data->* TO <lg_s_hier>.
CLEAR <lg_s_hier>.

me->denormalize_single_record( EXPORTING is_node_descr = is_node_descr


is_data = <lg_s_hier>
iv_flg_on_leading_path =
iv_flg_on_leading_path
CHANGING ct_rows = lt_rows ).

APPEND LINES OF lt_rows TO ct_rows.


CLEAR lt_rows.
ELSE.
LOOP AT it_hierarchical_data ASSIGNING <lg_s_hier>.

me->denormalize_single_record( EXPORTING is_node_descr = is_node_descr


is_data = <lg_s_hier>
iv_flg_on_leading_path =
iv_flg_on_leading_path
CHANGING ct_rows = lt_rows ).

APPEND LINES OF lt_rows TO ct_rows.


CLEAR lt_rows.

" If we are currently not processing the leading path we have to process
" just the first record of the hierarchy table. Thus we exit the loop here.
IF ( iv_flg_on_leading_path = abap_false ).
EXIT.
ENDIF.
ENDLOOP.
ENDIF.

ENDMETHOD.
METHOD denormalize_single_record.

DATA lv_field_index TYPE i.


DATA lt_rows TYPE ehfndt_exp_tab_data.
DATA lt_row TYPE ehfndt_exp_tab_row.
DATA lr_t_row TYPE REF TO ehfndt_exp_tab_row.
DATA lv_value LIKE LINE OF lt_row.
DATA lr_s_field_descr TYPE REF TO ehfnds_exp_field_descriptor_xt.
DATA lv_field_name_on_leading_path TYPE string.

FIELD-SYMBOLS <lg_t_leading_path> TYPE INDEX TABLE.


FIELD-SYMBOLS <lg_t_field> TYPE INDEX TABLE.
FIELD-SYMBOLS <ls_node_descr> TYPE ehfnds_exp_node_descriptor.
FIELD-SYMBOLS <lg_field> TYPE any.

" First process the leading path


IF ( is_node_descr-field_on_leading_path > 0 ).
READ TABLE is_node_descr-field_descr_tab REFERENCE INTO lr_s_field_descr INDEX
is_node_descr-field_on_leading_path.
ASSERT sy-subrc = 0.

" Save the field name for later use (see loop below)
lv_field_name_on_leading_path = lr_s_field_descr->field_name.
ASSIGN COMPONENT lr_s_field_descr->field_name OF STRUCTURE is_data TO
<lg_t_leading_path>.
ASSERT sy-subrc = 0.

" node_descr_ref is ref to data because it is not possible to define recursive


data structures.
" Thus, here we have to do a type-cast via field-symbol.
ASSIGN lr_s_field_descr->node_descr_ref->* TO <ls_node_descr>.
me->denormalize_hierarchy( EXPORTING is_node_descr = <ls_node_descr>
it_hierarchical_data = <lg_t_leading_path>
iv_flg_on_leading_path = abap_true "
here we are on the leading path
CHANGING ct_rows = ct_rows ).
ENDIF.

" Now process all fields that do not belong to the leading path
LOOP AT is_node_descr-field_descr_tab REFERENCE INTO lr_s_field_descr
WHERE field_name <> lv_field_name_on_leading_path.

lv_field_index = sy-tabix.

" Copy elementary fields directly to the result


" In case the current field is actually an internal table
" recursively copy the fields of its first record
IF ( lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_elem ).
if <lg_field> is assigned.
UNASSIGN <lg_field>.
endif.
ASSIGN COMPONENT lv_field_index OF STRUCTURE is_data TO <lg_field>.
ASSERT sy-subrc = 0.

" Here we convert the original value into a string


lv_value = me->convert_to_string( iv_value = <lg_field> is_meta_data =
lr_s_field_descr->descr ).
APPEND lv_value TO lt_row.

ELSEIF ( lr_s_field_descr->field_kind = cl_abap_typedescr=>kind_table ).


ASSIGN COMPONENT lv_field_index OF STRUCTURE is_data TO <lg_t_field>.
ASSERT sy-subrc = 0.

" node_descr_ref is ref to data because it is not possible to define


recursive data structures.
" Thus, here we have to do a type-cast via field-symbol.
ASSIGN lr_s_field_descr->node_descr_ref->* TO <ls_node_descr>.

me->denormalize_hierarchy( EXPORTING is_node_descr = <ls_node_descr>


it_hierarchical_data = <lg_t_field>
iv_flg_on_leading_path = abap_false "
here we are not on the leading path
CHANGING ct_rows = lt_rows ).

" Here we are not on the leading path. Thus for each nested table just one
record
" is being processed. So DENORMALIZE_HIERARCHY can return just one row in
ct_rows.
" In order to get this one row we read the first (and only) record from
lt_rows.
READ TABLE lt_rows REFERENCE INTO lr_t_row INDEX 1.
IF ( sy-subrc = 0 ).
APPEND LINES OF lr_t_row->* TO lt_row.
ENDIF.

" Free some memory


CLEAR lt_rows.
ENDIF.
ENDLOOP.

" Merge the data of the leading path with the remaining fields of the current
record.
" Here we duplicate the values that do not belong to the leading path

" We want to get the fields of the leading path on the right-hand side of the
table.
" Thus we insert the fields that are not on the leading path before those on the
leading path.
" This way the fields on the leading path are at the end of the rows.
" NOTE: This logic corresponds to the logic in method CREATE_META_DATA which
processes the
" non-leading path-fields first and appends the meta-data for the leading path-
fields at the
" end.
IF ( lines( ct_rows ) = 0 ).
APPEND lt_row TO ct_rows.
ELSE.
LOOP AT ct_rows REFERENCE INTO lr_t_row.
INSERT LINES OF lt_row INTO lr_t_row->* INDEX 1.
ENDLOOP.
ENDIF.

" Free some memory


CLEAR lt_row.

ENDMETHOD.
ENDCLASS.

You might also like