Gyan Factory

Gyan Factory
SAP Technical Project Support

Wednesday, February 17, 2016

SALV Table 22 – Get Data Directly after SUBMIT



ABAP SALV model has some cool features. Getting the data table from the model, is one of them. Let’s explore the solution.

Problems analyzing LIST returned by LIST_FROM_MEMORY

SUBMIT is a great feature of ABAP. You can generate the report output, send LIST to the memory usingSUBMIT .. EXPORTING LIST TO MEMORY. You can get this list from the memory using the FM LIST_FROM_MEMORY and use that to write the list using WRITE_LIST .
Everything is good so far, but when you need to read the values from the list, things get messy. You would need to parse the data, remove the lines, formatting etc. This would be painful and not much fun.

Workaround

You can overcome this issues by a workaround:
  1. Export the list to the unique Memory ID from the submitted Program
  2. Import the list from the memory ID in the caller program
To be able to implement this workaround, you would need to modify the Submitted program in order to pass the data to the memory ID. Once you do that, caller program would be able to get it back after control is back in the main program.

Use SALV Model class CL_SALV_BS_RUNTIME_INFO

To overcome this limitation of the workaround, SALV model would be helpful. If the output of the submitted report is generated by the SALV Fullscreen model, the data can be easily retrieved back using the SALV model class CL_SALV_BS_RUNTIME_INFO. This class CL_SALV_BS_RUNTIME_INFO has few methods which can be really useful in this situation.
Let the model know, that you don’t want to generate the output, but interested in the data.

Instruct Model to get the data

 
cl_salv_bs_runtime_info=>set(
  EXPORTING
    display  = SPACE
    metadata = SPACE
    DATA     = ‘X’ 
).
 
Once the report is submitted, get the data from the runtime information

Retrieve Data

 
DATA lo_data TYPE REF TO DATA.
TRY.
    " get data from the SALV model
    cl_salv_bs_runtime_info=>get_data_ref(
          IMPORTING
            r_data = lo_data
    ).
  CATCH cx_salv_bs_sc_runtime_info.
    MESSAGE 'Unable to get data from SALV' TYPE 'I'.
ENDTRY.
 

Example

To show it in action, I’m submitting the report SALV_DEMO_TABLE_SIMPLE.

Demo Program

 
DATA: lt_outtab TYPE STANDARD TABLE OF alv_t_t2.
FIELD-SYMBOLS: <lt_outtab> LIKE lt_outtab.
DATA lo_data TYPE REF TO DATA.
 
" Let know the model
 cl_salv_bs_runtime_info=>set(
  EXPORTING
    display  = abap_false
    metadata = abap_false
    DATA     = abap_true
).
 
 
SUBMIT salv_demo_table_simple
  AND RETURN.
 
TRY.
    " get data from SALV model
    cl_salv_bs_runtime_info=>get_data_ref(
          IMPORTING
            r_data = lo_data
    ).
    ASSIGN lo_data->* to <lt_outtab>.
    BREAK-POINT.    
 
  CATCH cx_salv_bs_sc_runtime_info.
ENDTRY.
Note that,
  • Not sending the list to memory
  • No need to modify the submitted program to export the data to memory
  • No need to Import the data
These method does the same export and import but they are hidden from the caller.
Debugger shows that the data has been successfully retrieved from the model.
ABAP_SALV_GET_TABLE_FROM_SUBMIT

ABAP Performance for DELETE on ITAB



DELETE on ITAB is widely used when you need to filter out the entries. Lets see the performance for different Delete statements.

DELETE on ITAB

Filtering entries from an internal table would be done using the DELETE itab WHERE condition.
There would be huge difference in performance is you don’t use proper type for the ITAB on which delete is being performed. For comparison, I have used different type of tables with DELETE.
This example contains the two internal table – Header and Item. For every 3rd record on header, all items would be deleted. This code lines is for setting up the data:

 
DATA: lv_flag TYPE flag,
      lv_sta_time TYPE timestampl,
      lv_end_time TYPE timestampl,
      lv_diff_w   TYPE p DECIMALS 5,
      lv_diff_f   LIKE lv_diff_w,
      lv_save     LIKE lv_diff_w.
 
TYPES:
  BEGIN OF ty_header,
    vbeln   TYPE char10,
    field1  TYPE char50,
  END   OF ty_header.
TYPES: tt_header TYPE STANDARD TABLE OF ty_header.
 
TYPES:
  BEGIN OF ty_item,
    vbeln   TYPE char10,
    posnr   TYPE char6,
    field2  TYPE char50,
  END   OF ty_item.
TYPES: tt_item TYPE STANDARD TABLE OF ty_item.
 
DATA: t_header TYPE tt_header,
      t_item   TYPE tt_item.
 
PARAMETERS:  p_num TYPE i DEFAULT 1000.
 
START-OF-SELECTION.
  DATA: ls_header LIKE LINE OF t_header,
        ls_item   LIKE LINE OF t_item.
 
  DO p_num TIMES.
    ls_header-vbeln = sy-INDEX.
    ls_header-field1 = sy-abcde.
    APPEND ls_header TO t_header.
    DO 10 TIMES.
      ls_item-vbeln = ls_header-vbeln.
      ls_item-posnr = sy-INDEX.
      APPEND ls_item TO t_item.
    ENDDO.
 
  ENDDO.
 

Using DELETE on standard table using WHERE

Now, lets delete the items using the “classic” DELETE using WHERE.

  DATA: lv_mod_3 TYPE i.
  GET TIME STAMP FIELD lv_sta_time.
  SORT t_item BY vbeln.
  LOOP AT t_header INTO ls_header.
    lv_mod_3 = sy-tabix MOD 3.
    IF lv_mod_3 IS INITIAL.
      DELETE t_item WHERE vbeln = ls_header-vbeln.
    ENDIF.
  ENDLOOP.
  GET TIME STAMP FIELD lv_end_time.
  lv_diff_w = lv_end_time - lv_sta_time.
  WRITE: /(15) 'DELETE', lv_diff_w.

Processing time for DELETE

I ran the same code for multiple times, and timings are like these:
DELETE_Performance_1

Using Parallel Cursor

Now, lets use some parallel cursor on this DELETE. This is similar to Parallel Cursor for the Nested Loop. First read the index of the required record, DELETE from that index on wards, till it finds a different key.

  DATA: lv_mod_3 TYPE i.
  GET TIME STAMP FIELD lv_sta_time.
  SORT t_item BY vbeln.
  LOOP AT t_header INTO ls_header.
    lv_mod_3 = sy-tabix MOD 3.
    IF lv_mod_3 IS INITIAL.
 
      READ TABLE t_item TRANSPORTING NO FIELDS
        BINARY SEARCH
        WITH KEY vbeln = ls_header-vbeln.
      IF sy-subrc EQ 0.
        LOOP AT t_item INTO ls_item FROM sy-tabix.
          IF ls_item-vbeln NE ls_header-vbeln.
            EXIT.
          ENDIF.
          DELETE t_item INDEX sy-tabix.
        ENDLOOP.
      ENDIF.
 
    ENDIF.
  ENDLOOP.
  GET TIME STAMP FIELD lv_end_time.
  lv_diff_w = lv_end_time - lv_sta_time.
  WRITE: /(15) 'DELETE', lv_diff_w.
 

Processing time for DELETE

Obviously, the performance has to improve as parallel cursor is in play. The saving is surprisingly in range of ~95%.
DELETE_Performance_parallel

Using SORTED table

Next test is using the SORTED table with Unique Key. Since this item table has unique key fields, I can easily define.

 
* Change type to sorted
"types: tt_item type STANDARD TABLE OF ty_item.
TYPES: tt_item TYPE SORTED TABLE OF ty_item WITH UNIQUE KEY vbeln posnr.
 
*
  DATA: lv_mod_3 TYPE i.
  GET TIME STAMP FIELD lv_sta_time.
  SORT t_item BY vbeln.
  LOOP AT t_header INTO ls_header.
    lv_mod_3 = sy-tabix MOD 3.
    IF lv_mod_3 IS INITIAL.
      DELETE t_item WHERE vbeln = ls_header-vbeln.
    ENDIF.
  ENDLOOP.
  GET TIME STAMP FIELD lv_end_time.
  lv_diff_w = lv_end_time - lv_sta_time.
  WRITE: /(15) 'DELETE', lv_diff_w.
 

Processing time for DELETE on SORTED Unique Key

There is more performance improvement when using the SORTED table with unique key. Sorted tables are much more performance efficient as what we have seen in ABAP Internal Table Performance for STANDARD, SORTED and HASHED Table.
DELETE_Performance_Sorted

Using Secondary Key non-unique key

Since ABAP 731, Secondary key on the ITAB can be declared. This would be also helpful and increase the performance if you don’t have clear key.

 
"types: tt_item type STANDARD TABLE OF ty_item.
"types: tt_item type SORTED TABLE OF ty_item with UNIQUE key vbeln posnr.
TYPES: tt_item TYPE SORTED TABLE OF ty_item
                     WITH UNIQUE KEY vbeln posnr
                     WITH NON-UNIQUE SORTED KEY key2nd COMPONENTS field2.
 
  DATA: lv_mod_3 TYPE i.
  GET TIME STAMP FIELD lv_sta_time.
  LOOP AT t_header INTO ls_header.
    lv_mod_3 = sy-tabix MOD 3.
    IF lv_mod_3 IS INITIAL.
 
      DELETE t_item USING KEY key2nd WHERE field2 = ls_header-vbeln.
 
    ENDIF.
  ENDLOOP.
  GET TIME STAMP FIELD lv_end_time.
  lv_diff_w = lv_end_time - lv_sta_time.
  WRITE: /(15) 'DELETE', lv_diff_w.
 

Processing time for DELETE with Secondary Key

The numbers are almost same as Parallel cursor approach.
DELETE_Performance_Secondary

On Graph

Putting all together. Needed to change the scale to log10 in order to display the difference.
DELETE_Performance_graph_1
If DELETE with standard table is considered 100%, the other two options finishes the work in less than 0.15% time of that 100%.
DELETE_Performance_graph_2

Tuesday, February 16, 2016

Download EXCEL using CALL TRANSFORMATION

 
DATA: t_t100 TYPE STANDARD TABLE OF t100.
DATA: lv_xml  TYPE STRING.
DATA: lo_xml_doc TYPE REF TO cl_xml_document.
 
*
SELECT *
  FROM t100
  INTO TABLE t_t100
  UP TO 100 ROWS
  WHERE SPRSL EQ sy-langu.
 
*
CALL TRANSFORMATION ID
   SOURCE data_node = t_t100
   RESULT XML lv_xml.
 
*
CREATE OBJECT lo_xml_doc.
lo_xml_doc->parse_string( lv_xml ).
lo_xml_doc->export_to_file( 'c:tempt100.xls' ).
 
Opening up the file in excel:
Excel_Open_XML_popup
Excel_Open_XML_popup_2
Excel_generated_by_XML_output
File as XML
Excel_as_XML

Remove VERSION from the File

 
DATA: t_t100 TYPE STANDARD TABLE OF t100.
DATA: lv_xml  TYPE STRING.
DATA: lo_xml_doc TYPE REF TO cl_xml_document.
 
*
SELECT *
  FROM t100
  INTO TABLE t_t100
  UP TO 100 ROWS
  WHERE SPRSL EQ sy-langu.
 
*
CALL TRANSFORMATION ID
   SOURCE data_node = t_t100
   RESULT XML lv_xml.
 
*
REPLACE FIRST OCCURRENCE OF
    '<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">'
  IN lv_xml
  WITH '<asx:abap xmlns:asx="http://www.sap.com/abapxml">'.
 
*
CREATE OBJECT lo_xml_doc.
lo_xml_doc->parse_string( lv_xml ).
lo_xml_doc->export_to_file( 'c:tempt100.xls' ).
 
No more version column:
Excel_generated_by_XML_output_no_version
Do you have a Code Snippet which you want to share, Submit Code Snippet here

ABAP Factory Method Using SWITCH

SWITCH usage with returning the object reference. This would be great to reduce lot of clutter from the design pattern implementation i.e. Factory design pattern.

Preface

In the last article ABAP 740 SWITCH – Conditional Operator, I mentioned – the returned value is the object reference, the up-casting should be possible. This means that the Switch is capable of handling the object reference. Let see how.

Factory Design pattern using Switch

A while back, I demonstrated the implementation of the Factory Design patterns in ABAP objects. This was using the simple IF, ELSE. To recap, the Factory method design pattern hides all the complexity of object creation from the consumer of the object. Its good idea to read the article Factory Design patterns in ABAP objects before moving further.
ABAP_740_Switch_Usage_Factory_Method
Now, with ABAP 740 – we can use the SWITCH instead of the IF .. ELSE .. ENDIF

Factory Design pattern Using SWITCH

 
*=====
CLASS ophandler DEFINITION ABSTRACT.
  PUBLIC SECTION.
    CLASS-METHODS: factory
      IMPORTING iv_output_type TYPE kschl
      RETURNING VALUE(ro_obj)  TYPE REF TO ophandler.
    METHODS: process_output ABSTRACT.
ENDCLASS.                    "ophandler DEFINITION
 
*=====
CLASS ophandler_zabc DEFINITION INHERITING FROM ophandler.
  PUBLIC SECTION.
    METHODS: process_output REDEFINITION.
ENDCLASS.                    "ophandler_zabc DEFINITION
*
CLASS ophandler_zabc IMPLEMENTATION.
  METHOD process_output.
    WRITE: / 'Processing ZABC'.
  ENDMETHOD.                    "process_output
ENDCLASS.                    "ophandler_zabc IMPLEMENTATION
 
*=====
CLASS ophandler_zxyz DEFINITION INHERITING FROM ophandler.
  PUBLIC SECTION.
    METHODS: process_output REDEFINITION.
ENDCLASS.
*
CLASS ophandler_zxyz IMPLEMENTATION.
  METHOD process_output.
    WRITE: / 'Processing ZXYZ'.
  ENDMETHOD.                    "process_output
ENDCLASS.
 
*
CLASS ophandler IMPLEMENTATION.
  METHOD factory.
    ro_obj =
              SWITCH #(
                        iv_output_type
                        WHEN 'ZABC' THEN NEW ophandler_zabc( )
                        WHEN 'ZXYZ' THEN NEW ophandler_zxyz( )
                        ELSE THROW cx_sy_no_handler( )
                      ).
*
*    CASE iv_output_type.
*      WHEN 'ZABC'.
*        CREATE OBJECT ro_obj TYPE ophandler_zabc.
*      WHEN 'ZXYZ'.
*        "create another object
*      WHEN OTHERS.
*        " raise exception
*    ENDCASE.
  ENDMETHOD.                    "factory
ENDCLASS.                    "ophandler IMPLEMENTATION
 
*=====
CLASS lcl_main_app DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: run.
ENDCLASS.                    "lcl_main_app DEFINITION
*
CLASS lcl_main_app IMPLEMENTATION.
  METHOD run.
    DATA: lo_output TYPE REF TO ophandler.
    lo_output = ophandler=>factory( 'ZABC' ).
    lo_output->process_output( ).
  ENDMETHOD.                    "run
ENDCLASS.                    "lcl_main_app IMPLEMENTATION
 
 
START-OF-SELECTION.
  lcl_main_app=>run( ).
 

OO ABAP – Selection Criteria Object using RS_REFRESH_FROM_SELECT OPTIONS

Selection criteria object for the reports where you can implement the MVC for decoupling. This approach is using the FM RS_REFRESH_FROM_SELECTOPTIONS.

Introduction

If you love the object oriented design in ABAP Objects, you must have read the article – ABAP Object Oriented Approach for Reports – Redesign. In that article, I have shown you how to use the Selection criteria object. This object is nothing but carry around the selection screen values.
Typically class definition would be:

Using the FM RS_REFRESH_FROM_SELECTOPTIONS

Got a hang of the select object, now? If not, I suggest you read ABAP Object Oriented Approach for Reports – Redesign.
So far, I have been using the pattern – manually setting up the values from the select options into the attributes of the selection object. Keshav and Jani80k has suggested that we also can use the FM RS_REFRESH_FROM_SELECTOPTIONS. Jani80K provided the implementation as well in the article Object Oriented Approach for Reports with multiple Datasource. Thank you much!
This code lines are modified from the code lines provided in this comment, with few changes. These are the components of the class
  • Attributes – for selection criteria from screen
  • CONSTRUCTOR – calls default method
  • REFRESH – sets up the values again
Two small methods are added compared to the original code lines to reduce the code lines. I have left the commented lines as is, if you want to refer to it

Utility Selection class using FM RS_REFRESH_FROM_SELECTOPTIONS

 
*
CLASS lcx_exception DEFINITION
  INHERITING FROM CX_STATIC_CHECK
  FINAL.
  PUBLIC SECTION.
    DATA im_message TYPE char50.
    METHODS constructor
      IMPORTING
        textid LIKE textid OPTIONAL
        previous LIKE previous OPTIONAL
        im_message TYPE STRING OPTIONAL.
ENDCLASS.                    "lcx_exception DEFINITION
 
*
CLASS lcl_selopts DEFINITION FINAL.
 
  PUBLIC SECTION.
    DATA gt_selopts TYPE STANDARD TABLE OF rsparams WITH KEY selname READ-ONLY.
 
    DATA gt_banfn TYPE RANGE OF banfn read-ONLY.
    DATA gt_ernam TYPE RANGE OF ernam read-ONLY.
    DATA gt_badat TYPE RANGE OF badat read-ONLY.
    DATA gt_erdat TYPE RANGE OF erdat read-ONLY.
    DATA gt_afnam TYPE RANGE OF afnam read-ONLY.
 
    DATA gv_frrel TYPE mmpur_bool READ-ONLY.
    DATA gv_frpoc TYPE mmpur_bool READ-ONLY.
    DATA gv_frrej TYPE mmpur_bool READ-ONLY.
 
    METHODS constructor RAISING lcx_exception.
    METHODS REFRESH RAISING lcx_exception.
 
  PRIVATE SECTION.
    METHODS get_selopts RAISING lcx_exception.
    METHODS set_defaults.
    "
    METHODS get_value_range
      IMPORTING iv_name  TYPE char30
      CHANGING  ct_range TYPE STANDARD TABLE.
    "
    METHODS get_value_param
      IMPORTING       iv_name   TYPE char30
      RETURNING VALUE(rv_value) TYPE char45.
 
ENDCLASS.                    "lcl_selopts DEFINITION
*
CLASS lcl_selopts IMPLEMENTATION.
 
  METHOD constructor.
    "me->get_selopts( ).
    me->set_defaults( ).
  ENDMETHOD.                    "constructor
 
  METHOD get_selopts.
    CALL FUNCTION 'RS_REFRESH_FROM_SELECTOPTIONS'
      EXPORTING
        curr_report     = sy-repid
      TABLES
        selection_table = gt_selopts
      EXCEPTIONS
        not_found       = 1
        no_report       = 2
        OTHERS          = 3.
    IF sy-subrc NE  0.
      RAISE EXCEPTION TYPE lcx_exception
        EXPORTING
          im_message = 'No Select Options.'.
    ENDIF.
 
 
    me->get_value_range( EXPORTING iv_name = :
       'S_BANFN' CHANGING ct_range = gt_banfn ),
       'S_ERNAM' CHANGING ct_range = gt_ernam ),
       'S_BADAT' CHANGING ct_range = gt_badat ),
       'S_ERDAT' CHANGING ct_range = gt_erdat ),
       'S_AFNAM' CHANGING ct_range = gt_afnam ).
 
    gv_frrel = me->get_value_param( 'P_FRREL' ).
    gv_frrej = me->get_value_param( 'P_FRREJ' ).
    gv_frpoc = me->get_value_param( 'P_FRPOC' ).
 
*    DATA lr_selopt TYPE REF TO rsparams.
*    FIELD-SYMBOLS <banfn> LIKE LINE OF gt_banfn.
*    FIELD-SYMBOLS <ernam> LIKE LINE OF gt_ernam.
*    FIELD-SYMBOLS <badat> LIKE LINE OF gt_badat.
*    FIELD-SYMBOLS <erdat> LIKE LINE OF gt_erdat.
*    FIELD-SYMBOLS <afnam> LIKE LINE OF gt_afnam.
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'S_BANFN' REFERENCE INTO lr_selopt.
*     IF sy-subrc EQ 0 AND lr_selopt->option IS NOT INITIAL.
*      APPEND INITIAL LINE TO gt_banfn ASSIGNING <banfn>.
*      MOVE-CORRESPONDING lr_selopt->* TO <banfn>.
*    ENDIF.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'S_ERNAM' REFERENCE INTO lr_selopt.
*    IF sy-subrc EQ 0 AND lr_selopt->option IS NOT INITIAL.
*      APPEND INITIAL LINE TO gt_ernam ASSIGNING <ernam>.
*      MOVE-CORRESPONDING lr_selopt->* TO <ernam>.
*    ENDIF.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'S_BADAT' REFERENCE INTO lr_selopt.
*    IF sy-subrc EQ 0 AND lr_selopt->option IS NOT INITIAL.
*      APPEND INITIAL LINE TO gt_badat ASSIGNING <badat>.
*      MOVE-CORRESPONDING lr_selopt->* TO <badat>.
*    ENDIF.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'S_ERDAT' REFERENCE INTO lr_selopt.
*    IF sy-subrc EQ 0 AND lr_selopt->option IS NOT INITIAL.
*      APPEND INITIAL LINE TO gt_erdat ASSIGNING <erdat>.
*      MOVE-CORRESPONDING lr_selopt->* TO <erdat>.
*    ENDIF.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'S_AFNAM' REFERENCE INTO lr_selopt.
*    IF sy-subrc EQ 0 AND lr_selopt->option IS NOT INITIAL.
*      APPEND INITIAL LINE TO gt_afnam ASSIGNING <afnam>.
*      MOVE-CORRESPONDING lr_selopt->* TO <afnam>.
*    ENDIF.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'P_FRREL' REFERENCE INTO lr_selopt.
*    gv_frrel = lr_selopt->low.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'P_FRREJ' REFERENCE INTO lr_selopt.
*    gv_frrej = lr_selopt->low.
*
*    READ TABLE gt_selopts WITH TABLE KEY selname = 'P_FRPOC' REFERENCE INTO lr_selopt.
*    gv_frpoc = lr_selopt->low.
  ENDMETHOD.                    "get_selopts
 
  METHOD get_value_range.
 
    DATA lr_selopt TYPE REF TO rsparams.
    FIELD-SYMBOLS: <fs> TYPE ANY.
 
    LOOP AT gt_selopts   REFERENCE INTO lr_selopt
                         WHERE selname = iv_name.
      IF lr_selopt->option IS NOT INITIAL.
        APPEND INITIAL LINE TO ct_range ASSIGNING <fs>.
        MOVE-CORRESPONDING lr_selopt->* TO <fs>.
      ENDIF.
    ENDLOOP.
 
  ENDMETHOD.                    "get_Value_range
 
  METHOD get_value_param.
 
    DATA lr_selopt TYPE REF TO rsparams.
    FIELD-SYMBOLS: <fs> TYPE ANY.
 
    READ TABLE gt_selopts WITH TABLE KEY selname = iv_name REFERENCE INTO lr_selopt.
    CHECK sy-subrc EQ 0.
    rv_value = lr_selopt->low.
 
  ENDMETHOD.                    "get_Value_param
 
  METHOD set_defaults.
    FIELD-SYMBOLS <ernam> LIKE LINE OF gt_ernam.
    APPEND INITIAL LINE TO gt_ernam ASSIGNING <ernam>.
    <ernam>-SIGN   = 'I'.
    <ernam>-OPTION = 'EQ'.
    <ernam>-LOW = sy-uname.
 
    FIELD-SYMBOLS <erdat> LIKE LINE OF gt_erdat.
    APPEND INITIAL LINE TO gt_erdat ASSIGNING <erdat>.
    <erdat>-LOW    = sy-datum - 7.
    <erdat>-HIGH   = sy-datum.
    <erdat>-SIGN   = 'I'.
    <erdat>-OPTION = 'BT'.
  ENDMETHOD.                    "set_defaults
 
  METHOD REFRESH.
    CLEAR: gt_selopts, gt_banfn, gt_ernam, gt_badat,gt_erdat, gt_afnam, gv_frrel,gv_frpoc, gv_frrej.
    get_selopts( ).
  ENDMETHOD.                    "refresh
ENDCLASS.                    "lcl_selopts IMPLEMENTATION
*
CLASS lcx_exception IMPLEMENTATION.
  METHOD constructor.
    CALL METHOD super->constructor
      EXPORTING
        textid   = textid
        previous = previous.
    me->im_message = im_message.
  ENDMETHOD.                    "CONSTRUCTOR
ENDCLASS.                    "lcx_exception IMPLEMENTATION
 
To test it:

 
DATA: lo_sel TYPE REF TO lcl_selopts.
 
SELECT-OPTIONS: s_erdat FOR sy-datum OBLIGATORY.
 
INITIALIZATION.
  TRY .
      CREATE OBJECT lo_sel.
      s_erdat[] = lo_sel->gt_erdat.
    CATCH lcx_exception.
 
  ENDTRY.
 
 
START-OF-SELECTION.
  lo_sel->refresh( ).
 
Let me know your thoughts.