[ABAP] OO: Events auslösen, Eventhandling

* Demo-Objekt, welches ein Event definiert und auslöst
CLASS lcl_demo DEFINITION.
  PUBLIC SECTION.
* der Event, der getriggert werden soll
    EVENTS:
      my_event
        EXPORTING
          VALUE(txt) TYPE string
          VALUE(val) TYPE i.

* Instanzkonstruktor mit Übergabeparameter
    METHODS:
      constructor
        IMPORTING
          iv_name TYPE string.

* die Methode, die den Event auslöst
    METHODS: write.

* gibt Namen zurück
    METHODS: get_name
      RETURNING VALUE(rv_name) TYPE string.

* Variable zum Speichern des Namens
  PRIVATE SECTION.
    DATA: gv_name TYPE string.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.
* Instanzkonstruktor mit Übergabeparameter
  METHOD constructor.
    gv_name = iv_name.
  ENDMETHOD.
* die Methode, die den Event auslöst
  METHOD write.
    WRITE: / '1. lcl_demo triggert Event "my_event"'.
* Event auslösen
    RAISE EVENT my_event
      EXPORTING
        txt = 'Übergabetext'
        val = 1.
  ENDMETHOD.
* gibt Namen zurück
  METHOD get_name.
    rv_name = gv_name.
  ENDMETHOD.
ENDCLASS.

* Eventhandlerklasse, die Methoden zur Ereignisbehalndlung abbildet
CLASS lcl_eventhandler DEFINITION.
  PUBLIC SECTION.
* die Ereignisbehandlungsroutine, die mit dem Event über SET HANDLER verknüpft wird
    CLASS-METHODS: on_my_event FOR EVENT my_event OF lcl_demo
      IMPORTING
          txt
          val
          sender.
ENDCLASS.

CLASS lcl_eventhandler IMPLEMENTATION.
* die Ereignisbehandlungsroutine, die mit dem Event verknüpft wird
  METHOD on_my_event.
    WRITE: / '2. lcl_eventhandler=>on_my_event:'.
    WRITE: / '   Name:', sender->get_name( ).
    WRITE: / '    txt:', txt.
    WRITE: / '    val:', val.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
* Objekt erzeugen
  DATA(o_evt) = NEW lcl_demo( 'Aufrufer-Objekt' ).

* Handler registrieren: Zuordnung von Eventhandler "on_my_event" zu Objekt der Demo-Klasse mit Eventtrigger für my_event
  SET HANDLER lcl_eventhandler=>on_my_event FOR o_evt.

* Funktionsaufruf, welcher den Event triggert
  o_evt->write( ).

Weiterführende Infos: Link und Link

[ABAP] vordefinierte Ausnahmeklassen (Exception-Klassen) im System

Eine schöne Übersicht zu System-Exception-Klassen findet sich hier: Link

* häufig verwendete System-Ausnahmeklassen:

CX_ROOT
CX_SY_ARITHMETIC_ERROR
CX_SY_ZERODIVIDE
CX_SY_CREATE_OBJECT_ERROR
CX_SY_FILE_ACCESS_ERROR
CX_SY_ITAB_LINE_NOT_FOUND
CX_SY_SQL_ERROR

[ABAP] Beispiel für Implementation einer lokalen Klasse

CLASS cl_one DEFINITION.
* Public-Deklarationen, global inner- und außerhalb des Objektes sichtbar
  PUBLIC SECTION.
    DATA: pu_val3 TYPE i.        " dyn. Variable, nur zur Obj-Laufzeit verfügbar

    CLASS-DATA: txt TYPE string. " statische Public-Variable
    CLASS-DATA: int TYPE i.      " statische Public-Variable

* statische Methode, hat keinen Zugriff auf pu_val3
    CLASS-METHODS: function_one
      IMPORTING
                i_txt          TYPE string
      RETURNING VALUE(ret_val) TYPE string. " wenn RETURNING VALUE, dann kein EXPORTING oder CHANGING möglich

* statische Methode, hat keinen Zugriff auf pu_val3
    CLASS-METHODS: function_two
      IMPORTING
                i_txt          TYPE string
                i_val          TYPE i
      RETURNING VALUE(ret_val) TYPE string. " wenn RETURNING VALUE, dann kein EXPORTING oder CHANGING möglich

* dynamische Methode, hat Zugriff auf pu_val3
    METHODS: function_three
      IMPORTING
                i_val1         TYPE i DEFAULT 10 " Default-Wert, falls Parameter nicht gesetzt
                i_val2         TYPE i DEFAULT 20 " Default-Wert, falls Parameter nicht gesetzt
      RETURNING VALUE(ret_val) TYPE i.           " wenn RETURNING VALUE, dann kein EXPORTING oder CHANGING möglich

* dynamische Methode, einfaches Exceptionhandling
    METHODS: function_four
      IMPORTING
                i_val1         TYPE i
      RETURNING VALUE(ret_val) TYPE i
      RAISING   cx_sy_arithmetic_error.

    METHODS: constructor.
* Private-Deklarationen, nur innerhalb der Klasse sichtbar
  PRIVATE SECTION.
    DATA: pr_val3 TYPE i.
ENDCLASS.

CLASS cl_one IMPLEMENTATION.
* Constructor der Klasse
  METHOD constructor.
    me->pr_val3 = 100.
  ENDMETHOD.
* Funktion mit einem Übergabeparameter
  method function_one.
    DATA: s TYPE string VALUE 'Return: '.
    ret_val = |{ s } { i_txt }|.
  ENDMETHOD.

* Funktion mit zwei Übergabeparametern
  METHOD function_two.
    ret_val = |{ i_txt } { i_val }|.
  ENDMETHOD.

* Funktion mit zwei Übergabeparametern und DEFAULT-Werten
* Verwendung des me-> Operators für den Zugriff auf Attribute der Klasse
  METHOD function_three.
    ret_val = ( me->pu_val3 + me->pr_val3 ) * ( i_val1 + i_val2 ).
  ENDMETHOD.

* Funktion mit einem Übergabeparameter
* löst bei DIV0 -> cx_sy_arithmetic_error aus
  METHOD function_four.
    ret_val = 100 / i_val1.
  ENDMETHOD.
ENDCLASS.

INITIALIZATION.
  cl_one=>int = 1.
  cl_one=>txt = |Test.|.

START-OF-SELECTION.
* statische Objektaufrufe
  WRITE: / cl_one=>int.
  WRITE: / cl_one=>txt.

  WRITE: / cl_one=>function_one( |Input.| ).

  WRITE: / cl_one=>function_two( i_txt = |Input2:|
                                 i_val = 1 ).

* dynamische Objektaufrufe
  DATA(o_cl_one) = NEW cl_one( ).
  o_cl_one->pu_val3 = 10.
  WRITE: / o_cl_one->function_three( ).
  WRITE: / o_cl_one->function_three( i_val1 = 1
                                     i_val2 = 2 ).

* Exception abfangen
  TRY.
      WRITE: / o_cl_one->function_four( 0 ). " Division durch 0 auslösen
    CATCH cx_root INTO DATA(e_text).
      WRITE: / e_text->get_text( ).
  ENDTRY.

[ABAP] ALV-Grid mit Header-Zeile

* Bausteine REUSE_ALV_GRID_DISPLAY und REUSE_ALV_COMMENTARY_WRITE sind obsolet
* stattdessen sollten die Klassen cl_gui_alv_grid bzw. cl_salv_table verwendet werden
DATA: it_header TYPE slis_t_listheader.
DATA: lv_comment LIKE LINE OF it_header.
DATA: it_flight LIKE TABLE OF sflight.
DATA: it_event TYPE slis_t_event.
DATA: lv_event TYPE LINE OF slis_t_event.

START-OF-SELECTION.
  lv_comment-typ = 'H'. " H = Header, S = Selection, A = Action
  lv_comment-key = 'COM_KEY1'.
  lv_comment-info = 'Überschrift'.
  APPEND lv_comment TO it_header.

  lv_comment-typ = 'S'. " H = Header, S = Selection, A = Action
  lv_comment-key = 'COM_KEY2'.
  lv_comment-info = 'Infotext'.
  APPEND lv_comment TO it_header.

  lv_comment-typ = 'A'. " H = Header, S = Selection, A = Action
  lv_comment-key = 'COM_KEY2'.
  lv_comment-info = 'Infotext2'.
  APPEND lv_comment TO it_header.

  lv_event-name = 'TOP_OF_PAGE'.
  lv_event-form = 'TOP_LINE'.
  APPEND lv_event TO it_event.

  CLEAR lv_event.

  SELECT * FROM sflight INTO TABLE it_flight UP TO 100 ROWS.

  CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
    EXPORTING
      i_structure_name   = 'SFLIGHT'
      i_callback_program = sy-repid
      it_events          = it_event
    TABLES
      t_outtab           = it_flight
    EXCEPTIONS
      program_error      = 1
      OTHERS             = 2.

  IF sy-subrc NE 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  ENDIF.

FORM top_line.
  CALL FUNCTION 'REUSE_ALV_COMMENTARY_WRITE'
    EXPORTING
      it_list_commentary = it_header
      i_logo             = 'ENJOYSAP_LOGO'
      i_end_of_list_grid = it_flight.
ENDFORM.

[ABAP] SALV-Table als F4-Suchhilfe-PopUp verwenden

CLASS lcl_salv_f4 DEFINITION.
  PUBLIC SECTION.
    CLASS-METHODS: get_selected_value
      IMPORTING
                i_table_name      TYPE string
                i_display_columns TYPE string DEFAULT '*'
                i_sel_column      TYPE string
                i_max_datacnt     TYPE i OPTIONAL
                i_top             TYPE i DEFAULT 3
                i_bottom          TYPE i DEFAULT 25
                i_left            TYPE i DEFAULT 25
                i_right           TYPE i DEFAULT 160
      RETURNING VALUE(ret_val)    TYPE string.
ENDCLASS.

CLASS lcl_salv_f4 IMPLEMENTATION.
  METHOD get_selected_value.

    ret_val = ''.

    TRY.
        IF i_display_columns NE ''.
* Datenreferenzen auf Table und Row
          DATA: o_table TYPE REF TO data.
          DATA: o_row TYPE REF TO data.

          FIELD-SYMBOLS: <table> TYPE STANDARD TABLE.
          FIELD-SYMBOLS: <row> TYPE any.

          CREATE DATA o_table TYPE STANDARD TABLE OF (i_table_name).
          ASSIGN o_table->* TO <table>.

          CREATE DATA o_row LIKE LINE OF <table>.
          ASSIGN o_row->* TO <row>.

* SELECT-Statement anpassen
          IF i_max_datacnt IS NOT INITIAL.
            SELECT (i_display_columns) FROM (i_table_name) INTO CORRESPONDING FIELDS OF TABLE @<table> UP TO @i_max_datacnt ROWS.
          ELSE.
            SELECT (i_display_columns) FROM (i_table_name) INTO CORRESPONDING FIELDS OF TABLE @<table>.
          ENDIF.

* SALV-Grid erzeugen
          DATA: o_salv TYPE REF TO cl_salv_table.

          cl_salv_table=>factory( IMPORTING
                                    r_salv_table = o_salv
                                  CHANGING
                                    t_table      = <table> ).

          o_salv->get_functions( )->set_default( abap_true ).
          o_salv->get_columns( )->set_optimize( abap_true ).
          o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
          o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>single ).
          o_salv->get_display_settings( )->set_list_header( 'Auswahl' ).

* Strukturbeschreibung einer Tabellenzeile
          DATA: o_struct TYPE REF TO cl_abap_structdescr.
          o_struct ?= cl_abap_typedescr=>describe_by_data( <row> ).

* Komponenten der Struktur
*          DATA: it_comp_tab TYPE abap_component_tab.
          DATA(it_comp_tab) = o_struct->get_components( ).

          DATA(it_columns) = o_salv->get_columns( ).
          DATA: lv_col TYPE REF TO cl_salv_column.

          LOOP AT it_comp_tab INTO DATA(lv_comp).
* Spalte anhand des Komponentennamens holen
            lv_col ?= it_columns->get_column( |{ lv_comp-name }| ).

* wenn Anzeigespalte als Komponente der Struktur verfügbar, dann Spalte anzeigen
            IF ( i_display_columns CS lv_comp-name ) OR ( i_display_columns = '*' ).
              lv_col->set_visible( abap_true ).
            ELSE.
              lv_col->set_visible( abap_false ).
            ENDIF.
          ENDLOOP.

* Popup-SALV-Grid anzeigen
          o_salv->set_screen_popup( start_column = i_left
                                    end_column   = i_right
                                    start_line   = i_top
                                    end_line     = i_bottom ).

* Popup anzeigen und auf Klick warten
          o_salv->display( ).

* Auswahl auswerten
          DATA(it_sel_rows) = o_salv->get_selections( )->get_selected_rows( ).

* Nummer der ersten gewählten Tabellenzeile holen -> it_sel_rows[ 1 ]
* Tabellenzeile ermitteln -> <table>[ ... ]
* und Feldwert anhand der Spaltennummer auslesen -> COMPONENT i_sel_column
          ASSIGN COMPONENT i_sel_column OF STRUCTURE <table>[ it_sel_rows[ 1 ] ] TO FIELD-SYMBOL(<cell>).

* Rückgabewert
          ret_val = <cell>.

        ENDIF.

      CATCH cx_root.

    ENDTRY.

  ENDMETHOD.
ENDCLASS.

PARAMETERS: p_par TYPE string OBLIGATORY.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_par.
  p_par = lcl_salv_f4=>get_selected_value( i_table_name = 'spfli'
                                           i_display_columns = 'mandt, carrid, connid, cityto, airpfrom'
                                           i_sel_column = 'cityto' ).

[ABAP] Dynamische Tabelle anhand eines Tabellennamens erstellen, Daten ausgeben

DATA: table_name TYPE string VALUE 'spfli'. " Tabellenname
DATA: o_it TYPE REF TO data.                " Referenz auf Tabelle
DATA: o_row TYPE REF TO data.               " Referenz auf Tabellenzeile

FIELD-SYMBOLS: <fs_table> TYPE ANY TABLE.   " Feldsymbol für Arbeit mit Tabelle
FIELD-SYMBOLS: <fs_row> TYPE any.           " Feldsymbol für Arbeit mit Zeile

* dynamische Tabelle vom Typ table_name erzeugen
CREATE DATA o_it TYPE TABLE OF (table_name).
* Feldsymbol auf die dynamische Tabelle anlegen
ASSIGN o_it->* TO <fs_table>.

IF <fs_table> IS ASSIGNED.
* dynamische Workarea vom Typ der Tabellenzeile erzeugen
  CREATE DATA o_row LIKE LINE OF <fs_table>.
* Feldsymbol auf die Workarea anlegen
  ASSIGN o_row->* TO <fs_row>.

  IF <fs_row> IS ASSIGNED.
* Daten holen
    SELECT * FROM (table_name) INTO TABLE <fs_table>.

* Daten ausgeben
    DATA: go_struct TYPE REF TO cl_abap_structdescr.

* herausfinden, wieviele Spalten die Tabelle hat
    go_struct ?= cl_abap_typedescr=>describe_by_data( <fs_row> ).
    DATA(gt_comp) = go_struct->get_components( ).

* alle Datensätze der Tabelle durchgehen
    LOOP AT <fs_table> INTO <fs_row>.

* spaltenweise jeden Datensatz durchgehen
      LOOP AT gt_comp ASSIGNING FIELD-SYMBOL(<fs_col>).
* den Inhalt der Strukturkomponente (Zeile) ausgeben
        ASSIGN COMPONENT <fs_col>-name OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fs_cell>).

        IF <fs_cell> IS ASSIGNED.
          WRITE: <fs_cell>.
        ENDIF.
      ENDLOOP.

      NEW-LINE.

    ENDLOOP.
  ENDIF.
ENDIF.

[ABAP] dynamische Tabelle zur Laufzeit aus einem Feldkatalog erstellen und in SALV-Table anzeigen

DATA : it_fcat TYPE lvc_t_fcat.

CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
  EXPORTING
    i_structure_name = 'SPFLI'  " Dictionary-Struktur
  CHANGING
    ct_fieldcat      = it_fcat. " Feldkatalog

DATA: o_tab TYPE REF TO data.
* dynamische Tabelle aus dem Feldkatalog erstellen
cl_alv_table_create=>create_dynamic_table( EXPORTING
                                             it_fieldcatalog = it_fcat
                                           IMPORTING
                                             ep_table        = o_tab ).

* dynamische Tabelle auf Feldsymbol abbilden
FIELD-SYMBOLS: <tab> TYPE ANY TABLE.
ASSIGN o_tab->* TO <tab>.

* Daten holen
SELECT * FROM spfli INTO TABLE <tab> UP TO 10 ROWS.

* Daten anzeigen
DATA: o_alv TYPE REF TO cl_salv_table.
TRY.
    cl_salv_table=>factory( IMPORTING
                              r_salv_table = o_alv
                            CHANGING
                              t_table      = <tab> ).

    o_alv->display( ).

  CATCH cx_root.
ENDTRY.

Links

[ABAP] Beliebige Dateien zu einer Zipdatei hinzufügen

TYPES: BEGIN OF bin_file,                          " Typ für Binärdatei mit Meta-Infos für das Zipfile
         name TYPE string,
         size TYPE i,
         data TYPE solix_tab,
       END OF bin_file.

DATA: lv_filename TYPE string.                     " Dateiname für FileOpen/FileSave
DATA: wa_file TYPE bin_file.                       " Binärdatei mit Meta-Infos für das Zipfile
DATA: it_binfiles TYPE STANDARD TABLE OF bin_file. " unkomprimierter Stream (Tabelle mit Dateien zum Zippen)
DATA: lv_path TYPE string.

START-OF-SELECTION.
* ZIP-Objekt erzeugen
  DATA(o_zip) = NEW cl_abap_zip( ).

  DATA: it_sel_filetab TYPE filetable.
  DATA: ret_code TYPE i.
  DATA: lv_action TYPE i.
* FileOpen-Dialog für Dateiauswahl anzeigen
* Mehrfachselektion möglich
  cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                window_title   = 'Dateien zum Komprimieren auswählen'
                                                multiselection = abap_true
                                              CHANGING
                                                file_table     = it_sel_filetab
                                                rc             = ret_code    " Anzahl ausgewählte Dateien, -1 bei Fehler
                                                user_action    = lv_action ).

  IF lv_action = cl_gui_frontend_services=>action_ok.

* Ausgewählte Dateien durchgehen
    LOOP AT it_sel_filetab INTO DATA(wa_sel_file).

      WRITE: / |Datei hinzugefügt: { wa_sel_file-filename }|.

* Dateien auf den Appl.-Server hochladen
      cl_gui_frontend_services=>gui_upload( EXPORTING
                                              filename   = |{ wa_sel_file-filename }|
                                              filetype   = 'BIN'
                                            IMPORTING
                                              filelength = wa_file-size
                                            CHANGING
                                              data_tab   = wa_file-data ).

* Pfad + Dateinamen aufsplitten
      CALL FUNCTION 'SO_SPLIT_FILE_AND_PATH'
        EXPORTING
          full_name     = wa_sel_file-filename
        IMPORTING
          file_path     = lv_path
          stripped_name = wa_file-name
        EXCEPTIONS
          x_error       = 1
          OTHERS        = 2.

* Datei zum Stream hinzufügen
      APPEND wa_file TO it_binfiles.

    ENDLOOP.

    ULINE.

    DATA: lv_xstring TYPE xstring.
* unkomprimierte Daten zum Zip-File hinzufügen
    LOOP AT it_binfiles INTO wa_file.

* jeden Datei-Stream binär zu xstring wandeln
      CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
        EXPORTING
          input_length = wa_file-size
        IMPORTING
          buffer       = lv_xstring
        TABLES
          binary_tab   = wa_file-data.

      o_zip->add( name    = wa_file-name
                  content = lv_xstring ).

    ENDLOOP.

* Daten komprimieren
    DATA(lv_zip) = o_zip->save( ).

    DATA: lv_zip_size TYPE i.
    DATA: it_zip_bin_data TYPE STANDARD TABLE OF raw255.

* xstring mit Zip-Daten zu binär rückwandeln
    CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
        buffer        = lv_zip
      IMPORTING
        output_length = lv_zip_size
      TABLES
        binary_tab    = it_zip_bin_data.

    DATA: lv_dest_filepath TYPE string.

* SaveFile-Dialog aufrufen
    cl_gui_frontend_services=>file_save_dialog( EXPORTING
                                                  window_title         = 'Zipdatei speichern'
                                                  file_filter          = '(*.zip)|*.zip|'
                                                CHANGING
                                                  filename             = lv_filename
                                                  path                 = lv_path
                                                  fullpath             = lv_dest_filepath ).

* Zipdatei vom Appl-Server auf den lokalen Pfad speichern
    cl_gui_frontend_services=>gui_download( EXPORTING
                                              filename                = lv_dest_filepath
                                              filetype                = 'BIN'
                                              bin_filesize            = lv_zip_size
                                            CHANGING
                                              data_tab                = it_zip_bin_data ).

    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
    ELSE.
      WRITE: / |Zipdatei erfolgreich unter { lv_dest_filepath } gespeichert.|.
    ENDIF.
  ENDIF.