Skip to content

davmapo/WRAP-CLASS-RTTS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

lcl_dynamic_type

Fluent builder around RTTS (CL_ABAP_STRUCTDESCR, CL_ABAP_TABLEDESCR, CL_ABAP_ELEMDESCR) for creating dynamic ABAP types at runtime.

Removes the boilerplate of manually building a component table and calling the RTTS APIs directly, replacing it with a readable chain syntax.


Concept

The class does one thing only: describe a type. It does not allocate, read, or write data. The typical lifecycle is:

NEW lcl_dynamic_type( )
  ->add_field( ... )        " 0..n times
  ->add_field_like( ... )   " 0..n times
  ->build_struct / build_line / build_table

add_field* methods return me (method chaining). build_* methods are terminal: they return the result and break the chain.


Method reference

add_field

Adds a field whose type is resolved via CL_ABAP_ELEMDESCR.

Parameter Type Required Description
iv_name string yes Field name in the structure (e.g. 'MATNR')
iv_type string yes Primitive ABAP type (see table below)
iv_length i no Length in characters / bytes / digits (used by C, N, X, P)
iv_decimals i no Number of decimal places (used by P only)

Valid values for iv_type:

Value ABAP type iv_length iv_decimals Default length
'C' Character yes - 1
'N' Numeric text yes - 1
'X' Hexadecimal yes - 1
'I' Integer - - (fixed)
'INT8' 8-byte integer - - (fixed)
'F' Float - - (fixed)
'D' Date - - (fixed)
'T' Time - - (fixed)
'P' Packed yes yes 8 / 0
'STRING' String - - (dynamic)
'XSTRING' Byte string - - (dynamic)
other DDIC fallback - -

Returns REF TO lcl_dynamic_type (me) for chaining.


add_field_like

Adds a field whose type is read from the ABAP Dictionary via CL_ABAP_TYPEDESCR=>DESCRIBE_BY_NAME. Use this to inherit domain properties (length, conversion exit, search help) from standard DDIC types such as MATNR, WERKS_D, MENGE_D, etc.

Parameter Type Required Description
iv_name string yes Field name in the structure
iv_ddic_type string yes DDIC type name (e.g. 'MATNR', 'MENGE_D', 'WERKS_D')

Returns REF TO lcl_dynamic_type (me) for chaining.


build_struct

Terminal method. Calls CL_ABAP_STRUCTDESCR=>CREATE with the accumulated component table and returns the structure descriptor.

Parameter Type Description
ro_desc REF TO cl_abap_structdescr Descriptor of the built structure

Use this when you need to pass the type descriptor to another RTTS API or inspect the components before allocating memory.


build_line

Terminal method. Calls build_struct internally, then allocates a single empty row via CREATE DATA ... TYPE HANDLE.

Parameter Type Description
ro_data REF TO data Pointer to an empty structure row

To access individual fields use a field symbol with ASSIGN COMPONENT ... OF STRUCTURE.


build_table

Terminal method. Calls build_struct, wraps it in a CL_ABAP_TABLEDESCR (standard table, default key, non-unique), then allocates an empty internal table via CREATE DATA ... TYPE HANDLE.

Parameter Type Description
ro_table REF TO data Pointer to an empty internal table

To pass the result to CL_SALV_TABLE=>FACTORY use a field symbol first:

ASSIGN lr_table->* TO FIELD-SYMBOL(<lt_table>).

This is required because CHANGING t_table TYPE ANY TABLE needs a runtime-resolved type, not an inline-dereferenced REF TO DATA.


Usage examples

1. Table with primitive fields → ALV

DATA(lr_table) = NEW lcl_dynamic_type(
  )->add_field( iv_name = 'MATNR' iv_type = 'C' iv_length = 18
  )->add_field( iv_name = 'MENGE' iv_type = 'P' iv_length = 13 iv_decimals = 3
  )->add_field( iv_name = 'WERKS' iv_type = 'C' iv_length = 4
  )->add_field( iv_name = 'BUDAT' iv_type = 'D'
  )->build_table( ).

" Assign to a field symbol to satisfy CHANGING ANY TABLE
ASSIGN lr_table->* TO FIELD-SYMBOL(<lt_table>).

cl_salv_table=>factory(
  IMPORTING r_salv_table = DATA(lo_salv)
  CHANGING  t_table      = <lt_table> ).
lo_salv->display( ).

2. DDIC fields to inherit domain and conversion exit

DATA(lr_table) = NEW lcl_dynamic_type(
  )->add_field_like( iv_name = 'MATNR' iv_ddic_type = 'MATNR'
  )->add_field_like( iv_name = 'WERKS' iv_ddic_type = 'WERKS_D'
  )->add_field_like( iv_name = 'MENGE' iv_ddic_type = 'MENGE_D'
  )->add_field(      iv_name = 'NOTE'  iv_type = 'STRING'
  )->build_table( ).

3. Single row → field-symbol access

DATA(lr_line) = NEW lcl_dynamic_type(
  )->add_field( iv_name = 'ID'   iv_type = 'I'
  )->add_field( iv_name = 'NAME' iv_type = 'C' iv_length = 40
  )->build_line( ).

ASSIGN lr_line->* TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT 'NAME' OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_name>).
<lv_name> = 'John Doe'.

4. Descriptor only (no data allocation)

DATA(lo_struct_desc) = NEW lcl_dynamic_type(
  )->add_field( iv_name = 'KUNNR' iv_type = 'C' iv_length = 10
  )->add_field( iv_name = 'NETWR' iv_type = 'P' iv_length = 15 iv_decimals = 2
  )->build_struct( ).

" Inspect the generated components
LOOP AT lo_struct_desc->components ASSIGNING FIELD-SYMBOL(<ls_comp>).
  WRITE: / <ls_comp>-name.
ENDLOOP.

5. Mix of primitive and DDIC fields

DATA(lr_table) = NEW lcl_dynamic_type(
  )->add_field_like( iv_name = 'VBELN' iv_ddic_type = 'VBELN_VA'
  )->add_field_like( iv_name = 'POSNR' iv_ddic_type = 'POSNV'
  )->add_field(      iv_name = 'QTY'   iv_type = 'P' iv_length = 13 iv_decimals = 3
  )->add_field(      iv_name = 'FLAG'  iv_type = 'C' iv_length = 1
  )->build_table( ).

Notes

  • The class does not manage data: there are no methods to read or write field values. Use field symbols and ASSIGN COMPONENT for that.
  • Each instance keeps its own component list. To build two different types, use two separate instances.
  • add_field also accepts DDIC type names in iv_type (falls back to DESCRIBE_BY_NAME), but using add_field_like is clearer in that case.
  • The table produced by build_table is always standard with a default non-unique key. For sorted or hashed tables, call build_struct directly and pass the descriptor to CL_ABAP_TABLEDESCR=>CREATE with the desired parameters.

Complete program (paste into SE38)

The following program is self-contained: it includes the full class definition and a START-OF-SELECTION block that builds a dynamic table, populates it with two rows, and displays it via ALV.

REPORT z_dynamic_type_demo.

*----------------------------------------------------------------------*
* lcl_dynamic_type — fluent builder around RTTS
*----------------------------------------------------------------------*
CLASS lcl_dynamic_type DEFINITION.
  PUBLIC SECTION.
    METHODS add_field
      IMPORTING
        iv_name     TYPE string
        iv_type     TYPE string
        iv_length   TYPE i OPTIONAL
        iv_decimals TYPE i OPTIONAL
      RETURNING
        VALUE(ro_self) TYPE REF TO lcl_dynamic_type.

    METHODS add_field_like
      IMPORTING
        iv_name      TYPE string
        iv_ddic_type TYPE string
      RETURNING
        VALUE(ro_self) TYPE REF TO lcl_dynamic_type.

    METHODS build_struct
      RETURNING
        VALUE(ro_desc) TYPE REF TO cl_abap_structdescr.

    METHODS build_line
      RETURNING
        VALUE(ro_data) TYPE REF TO data.

    METHODS build_table
      RETURNING
        VALUE(ro_table) TYPE REF TO data.

  PRIVATE SECTION.
    DATA mt_components TYPE cl_abap_structdescr=>component_table.

    METHODS get_elem_descr
      IMPORTING
        iv_type     TYPE string
        iv_length   TYPE i OPTIONAL
        iv_decimals TYPE i OPTIONAL
      RETURNING
        VALUE(ro_elem) TYPE REF TO cl_abap_elemdescr.
ENDCLASS.

CLASS lcl_dynamic_type IMPLEMENTATION.
  METHOD add_field.
    DATA ls_comp TYPE cl_abap_structdescr=>component.
    ls_comp-name = iv_name.
    ls_comp-type = get_elem_descr(
                     iv_type     = iv_type
                     iv_length   = iv_length
                     iv_decimals = iv_decimals ).
    APPEND ls_comp TO mt_components.
    ro_self = me.
  ENDMETHOD.

  METHOD add_field_like.
    DATA ls_comp TYPE cl_abap_structdescr=>component.
    ls_comp-name = iv_name.
    ls_comp-type ?= cl_abap_typedescr=>describe_by_name( iv_ddic_type ).
    APPEND ls_comp TO mt_components.
    ro_self = me.
  ENDMETHOD.

  METHOD build_struct.
    ro_desc = cl_abap_structdescr=>create( mt_components ).
  ENDMETHOD.

  METHOD build_line.
    DATA(lo_struct) = build_struct( ).
    CREATE DATA ro_data TYPE HANDLE lo_struct.
  ENDMETHOD.

  METHOD build_table.
    DATA(lo_struct) = build_struct( ).
    DATA(lo_tabledesc) = cl_abap_tabledescr=>create(
                           p_line_type  = lo_struct
                           p_table_kind = cl_abap_tabledescr=>tablekind_std
                           p_unique     = abap_false ).
    CREATE DATA ro_table TYPE HANDLE lo_tabledesc.
  ENDMETHOD.

  METHOD get_elem_descr.
    CASE to_upper( iv_type ).
      WHEN 'C'.
        ro_elem = cl_abap_elemdescr=>get_c( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
      WHEN 'N'.
        ro_elem = cl_abap_elemdescr=>get_n( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
      WHEN 'X'.
        ro_elem = cl_abap_elemdescr=>get_x( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
      WHEN 'I'.
        ro_elem = cl_abap_elemdescr=>get_i( ).
      WHEN 'INT8'.
        ro_elem = cl_abap_elemdescr=>get_int8( ).
      WHEN 'F'.
        ro_elem = cl_abap_elemdescr=>get_f( ).
      WHEN 'D'.
        ro_elem = cl_abap_elemdescr=>get_d( ).
      WHEN 'T'.
        ro_elem = cl_abap_elemdescr=>get_t( ).
      WHEN 'P'.
        ro_elem = cl_abap_elemdescr=>get_p(
                    p_length   = COND #( WHEN iv_length > 0 THEN iv_length ELSE 8 )
                    p_decimals = COND #( WHEN iv_decimals > 0 THEN iv_decimals ELSE 0 ) ).
      WHEN 'STRING'.
        ro_elem = cl_abap_elemdescr=>get_string( ).
      WHEN 'XSTRING'.
        ro_elem = cl_abap_elemdescr=>get_xstring( ).
      WHEN OTHERS.
        ro_elem ?= cl_abap_typedescr=>describe_by_name( iv_type ).
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

*----------------------------------------------------------------------*
* Main program
*----------------------------------------------------------------------*
START-OF-SELECTION.

  " Declare all field symbols upfront with explicit types
  FIELD-SYMBOLS: <lt_table> TYPE STANDARD TABLE,
                 <ls_row>   TYPE ANY,
                 <lv_val>   TYPE ANY.

  " 1. Build the type and allocate the table
  " MATNR and WERKS come from the DDIC — column labels are automatic.
  " MENGE and BUDAT are primitive types — labels must be set manually.
  DATA(lr_table) = NEW lcl_dynamic_type(
    )->add_field_like( iv_name = 'MATNR' iv_ddic_type = 'MATNR'
    )->add_field_like( iv_name = 'WERKS' iv_ddic_type = 'WERKS_D'
    )->add_field( iv_name = 'MENGE' iv_type = 'P' iv_length = 13 iv_decimals = 3
    )->add_field( iv_name = 'BUDAT' iv_type = 'D'
    )->build_table( ).

  ASSIGN lr_table->* TO <lt_table>.

  " 2. Populate the table row by row
  DO 2 TIMES.
    APPEND INITIAL LINE TO <lt_table> ASSIGNING <ls_row>.

    ASSIGN COMPONENT 'MATNR' OF STRUCTURE <ls_row> TO <lv_val>.
    <lv_val> = COND #( WHEN sy-index = 1 THEN 'MATERIAL-001'
                                         ELSE 'MATERIAL-002' ).

    ASSIGN COMPONENT 'WERKS' OF STRUCTURE <ls_row> TO <lv_val>.
    <lv_val> = '1000'.

    ASSIGN COMPONENT 'MENGE' OF STRUCTURE <ls_row> TO <lv_val>.
    <lv_val> = COND #( WHEN sy-index = 1 THEN '42.500' ELSE '10.000' ).

    ASSIGN COMPONENT 'BUDAT' OF STRUCTURE <ls_row> TO <lv_val>.
    <lv_val> = sy-datum.
  ENDDO.

  " 3. Display with ALV
  DATA lo_salv TYPE REF TO cl_salv_table.
  cl_salv_table=>factory(
    IMPORTING r_salv_table = lo_salv
    CHANGING  t_table      = <lt_table> ).

  " Only primitive-type fields need manual labels.
  DATA(lo_cols) = lo_salv->get_columns( ).
  lo_cols->get_column( 'MENGE' )->set_long_text( 'Quantity'     ).
  lo_cols->get_column( 'BUDAT' )->set_long_text( 'Posting Date' ).
  lo_cols->set_optimize( abap_true ).

  lo_salv->display( ).

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages