Wednesday 4 October 2017

Dynamic Programming in ABAP: Part 3 – An Example – ABAP RTTS

In my last blog I explained about the significance of field symbol and data references in dynamic programming.

https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-1-introduction-to-field-symbols.html

https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-2-introduction-to-data-reference.html

SAP ABAP RTTS, SAP Guides, SAP ABAP Learning, SAP Certifications

Now here we will see one example of dynamic programming approach and also a brief introduction to ABAP RTTS.

ABAP Runtime Type Services (RTTS) consists of two components:

◉ Runtime Type Identification (RTTI) – Provides the methods to get the type definition of data objects at runtime.

◉ Runtime Type Creation (RTTC) – Provides the methods to create the data objects at runtime with any type definition.

Basically, ABAP RTTS provides a set of classes, whose methods can be used for runtime type identification and runtime type creation. To know more about ABAP RTTS you can follow below link:

https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=42965


An example of dynamic programming:


Requirement: As an ABAP developer, very often we get the situation where we need to write data from an internal table to a file on application server.

Solution: We will build one class having a method which will take any internal table as input and write its content in a file on application server.

Class Definition:

CLASS cl_appserver_writer DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: write IMPORTING
                           iv_filename  TYPE string
                           it_data      TYPE ANY TABLE
                           write_header TYPE abap_bool DEFAULT space
                         EXPORTING
                           ev_message   TYPE string.
ENDCLASS.

Here importing parameter it_data is of TYPE ANY TABLE so that it can receive any internal table.

Class Implementation:

CLASS cl_appserver_writer IMPLEMENTATION.
  METHOD write.
    TYPES: BEGIN OF ty_comp_detail,
             name  TYPE abap_compname,
             descr TYPE scrtext_m,
           END OF ty_comp_detail.
    DATA: lo_type_def    TYPE REF TO cl_abap_typedescr.
    DATA: lo_struct_def  TYPE REF TO cl_abap_structdescr.
    DATA: lo_table_def   TYPE REF TO cl_abap_tabledescr.
    DATA: lo_data_def    TYPE REF TO cl_abap_datadescr.
    DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
    DATA: lt_components  TYPE abap_compdescr_tab.
    DATA: wa_components  LIKE LINE OF lt_components.
    DATA: lv_str         TYPE string.
    DATA: lv_filerow     TYPE string.
    DATA: lv_counter     TYPE i VALUE 0.
    DATA: lw_field_info  TYPE dfies.
    DATA: ls_comp_detail TYPE ty_comp_detail.
    DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.

    FIELD-SYMBOLS: <row> TYPE any.
    FIELD-SYMBOLS: <field_value> TYPE any.

* Using RTTS to get the runtime type information of the internal table
    lo_type_def  = cl_abap_tabledescr=>describe_by_data( it_data ).
    lo_table_def ?= lo_type_def.
    lo_data_def = lo_table_def->get_table_line_type( ).
    lo_struct_def ?= lo_data_def.

* Get the components of the structure
    lt_components = lo_struct_def->components.

    CLEAR: lo_data_def.

* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
    IF write_header EQ abap_true.
      LOOP AT lt_components INTO wa_components.
        lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
        lo_element_def ?= lo_data_def.
        lw_field_info = lo_element_def->get_ddic_field( ).
        ls_comp_detail-name = lw_field_info-rollname.  "Get the data element name

* Calling FM to get data element text
        CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
          EXPORTING
            i_data_element = lw_field_info-rollname
            i_language     = sy-langu
          IMPORTING
            e_scrtext_m    = ls_comp_detail-descr
          EXCEPTIONS
            error          = 1.
        IF ls_comp_detail-descr IS INITIAL.
          ls_comp_detail-descr = wa_components-name.
        ENDIF.
        APPEND ls_comp_detail TO lt_comp_detail.
        CLEAR: ls_comp_detail.
      ENDLOOP.
    ENDIF.

    OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
    IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
      IF write_header EQ abap_true.
        LOOP AT lt_comp_detail INTO ls_comp_detail.
          lv_counter = lv_counter + 1.
          IF lv_counter EQ 1.
            lv_filerow = ls_comp_detail-descr.
          ELSE.
            CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
          ENDIF.
        ENDLOOP.
        TRANSFER lv_filerow TO iv_filename.
        CLEAR: lv_filerow, lv_counter.
      ENDIF.

* Writing internal table content separated by comma
      LOOP AT it_data ASSIGNING <row>.
        LOOP AT lt_components INTO wa_components.
          lv_counter = lv_counter + 1.
          ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
          IF <field_value> IS ASSIGNED.
            lv_str = <field_value>.
            IF lv_counter EQ 1.
              lv_filerow = lv_str.
            ELSE.
              CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
            ENDIF.
            UNASSIGN <field_value>.
          ENDIF.
        ENDLOOP.
        TRANSFER lv_filerow TO iv_filename.
        CLEAR: lv_filerow, lv_counter.
      ENDLOOP.
      CLOSE DATASET iv_filename.
      ev_message = 'Success'.
    ELSE.
      ev_message = 'Failure'.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

Here the classes CL_ABAP_*DESCR are provided by the ABAP RTTS and used to get the type definition of data objects at runtime. Also we have extracted the data element name of each component of line type structure of internal table it_data using RTTS classes. Then we fetched the data element label using the FM WCGW_DATA_ELEMENT_TEXT_GET. This label is used to write the header for each column of internal table it_data if WRITE_HEADER parameter of class is provided with ABAP_TRUE.

Using the Class – The above designed class can be used as:

DATA: lt_data  TYPE STANDARD TABLE OF mara.
  DATA: lv_filename TYPE string.
  DATA: lv_message  TYPE string.

  SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.

  cl_appserver_writer=>write(
    EXPORTING
      iv_filename  = 'D:\usr\sap\testdata.csv'
      it_data      = lt_data
      write_header = abap_true
    IMPORTING
      ev_message   = lv_message
  ).

  WRITE: / lv_message.

Here we are passing one internal table of structure MARA to the class, and subsequently its content will be written on application server as comma separated values. However, we can pass internal table of any structure. This file can also be downloaded from application server to an excel spreadsheet.

So this is how field symbol, data reference, generic data type, RTTS helps in dynamic programming approach.

The complete code:


App Server Writer.txt

REPORT zwrite_appserver.

CLASS cl_appserver_writer DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: write IMPORTING
                           iv_filename  TYPE string
                           it_data      TYPE ANY TABLE
                           write_header TYPE abap_bool DEFAULT space
                         EXPORTING
                           ev_message   TYPE string.
ENDCLASS.

CLASS cl_appserver_writer IMPLEMENTATION.
  METHOD write.
    TYPES: BEGIN OF ty_comp_detail,
             name  TYPE abap_compname,
             descr TYPE scrtext_m,
           END OF ty_comp_detail.
    DATA: lo_type_def    TYPE REF TO cl_abap_typedescr.
    DATA: lo_struct_def  TYPE REF TO cl_abap_structdescr.
    DATA: lo_table_def   TYPE REF TO cl_abap_tabledescr.
    DATA: lo_data_def    TYPE REF TO cl_abap_datadescr.
    DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
    DATA: lt_components  TYPE abap_compdescr_tab.
    DATA: wa_components  LIKE LINE OF lt_components.
    DATA: lv_str         TYPE string.
    DATA: lv_filerow     TYPE string.
    DATA: lv_counter     TYPE i VALUE 0.
    DATA: lw_field_info  TYPE dfies.
    DATA: ls_comp_detail TYPE ty_comp_detail.
    DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.

    FIELD-SYMBOLS: <row> TYPE any.
    FIELD-SYMBOLS: <field_value> TYPE any.

* Using RTTS to get the runtime type information of the internal table
    lo_type_def  = cl_abap_tabledescr=>describe_by_data( it_data ).
    lo_table_def ?= lo_type_def.
    lo_data_def = lo_table_def->get_table_line_type( ).
    lo_struct_def ?= lo_data_def.
    lt_components = lo_struct_def->components.

    CLEAR: lo_data_def.

* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
    IF write_header EQ abap_true.
      LOOP AT lt_components INTO wa_components.
        lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
        lo_element_def ?= lo_data_def.
        lw_field_info = lo_element_def->get_ddic_field( ).
        ls_comp_detail-name = lw_field_info-rollname.

* Calling FM to get data element text
        CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
          EXPORTING
            i_data_element = lw_field_info-rollname
            i_language     = sy-langu
          IMPORTING
            e_scrtext_m    = ls_comp_detail-descr
          EXCEPTIONS
            error          = 1.
        IF ls_comp_detail-descr IS INITIAL.
          ls_comp_detail-descr = wa_components-name.
        ENDIF.
        APPEND ls_comp_detail TO lt_comp_detail.
        CLEAR: ls_comp_detail.
      ENDLOOP.
    ENDIF.


    OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
    IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
      IF write_header EQ abap_true.
        LOOP AT lt_comp_detail INTO ls_comp_detail.
          lv_counter = lv_counter + 1.
          IF lv_counter EQ 1.
            lv_filerow = ls_comp_detail-descr.
          ELSE.
            CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
          ENDIF.
        ENDLOOP.
        TRANSFER lv_filerow TO iv_filename.
        CLEAR: lv_filerow, lv_counter.
      ENDIF.

* Writing internal table content separated by comma
      LOOP AT it_data ASSIGNING <row>.
        LOOP AT lt_components INTO wa_components.
          lv_counter = lv_counter + 1.
          ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
          IF <field_value> IS ASSIGNED.
            lv_str = <field_value>.
            IF lv_counter EQ 1.
              lv_filerow = lv_str.
            ELSE.
              CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
            ENDIF.
            UNASSIGN <field_value>.
          ENDIF.
        ENDLOOP.
        TRANSFER lv_filerow TO iv_filename.
        CLEAR: lv_filerow, lv_counter.
      ENDLOOP.
      CLOSE DATASET iv_filename.
      ev_message = 'Success'.
    ELSE.
      ev_message = 'Failure'.
    ENDIF.
  ENDMETHOD.
ENDCLASS.


START-OF-SELECTION.
  DATA: lt_data  TYPE STANDARD TABLE OF mara.
  DATA: lv_filename TYPE string.
  DATA: lv_message  TYPE string.

  SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.

  cl_appserver_writer=>write(
    EXPORTING
      iv_filename  = 'D:\usr\sap\testdata.csv'
      it_data      = lt_data
      write_header = abap_true
    IMPORTING
      ev_message   = lv_message
  ).

  WRITE: / lv_message.
SAP Online Guides, Tutorials, Materials and Certifications.

Related Posts

3 comments:

  1. This post is extremely radiant. I extremely like this post. It is outstanding amongst other posts that I’ve read in quite a while. Much obliged for this better than average post. I truly value it! sap fico course fees in hyderabad

    ReplyDelete
  2. Just a request, whenever you write a blog with a code snippet, please show the output as well. Many readers (like me) may not have an SAP system standby to execute the code and see the output.

    ReplyDelete