This is a test message to test the length of the message box.
Login
ABAP in der Praxis String Verarbeitung
Erstellt von Software-Heroes

ABAP in Praxis - String Verarbeitung

233

In diesem praktischen Beispiel schauen wir uns die String Verarbeitung zur Ermittlung der CDS Namen in CamelCase an und wie du das mit ABAP umsetzen kannst.

Werbung


In den folgenden Abschnitten werden wir auf die einfache und effiziente Verarbeitung von Strings eingehen. Wir nutzen dabei Modern ABAP, um an die Namen der Core Data Services zu gelangen.

 

Einleitung

In manchen Situationen fehlen uns die richtigen Schnittstellen, um an direkte Informationen zu kommen. So zum Beispiel hatten wir keine Klasse und keinen Funktionsbaustein gefunden, um an die CamelCase Namen der Core Data Services zu gelangen. Diese werden auf klassischem Weg in Tabellen gespeichert und die Informationen in Großbuchstaben konvertiert. Damit sind sie leider nicht so leicht für uns nutzbar.

In diesem Blog zerlegen wir den Quellcode der Core Data Services in verarbeitbare Bestandteile und ermitteln für uns den Namen mit Groß- und Kleinschreibung, wie er im Quellcode definiert wurde.

 

Vorbereitung

Bevor du mit der eigentlichen Aufgabe beginnen kannst, benötigen wir eine Grundlage, die du weiterentwickeln kannst. Dazu legen wir im System eine ausführbare Klasse an. Wir arbeiten dabei außerhalb von ABAP Cloud, da wir auf eine Tabelle zugreifen wollen, die nicht freigegeben ist. Dazu legen wir uns drei Typen an, die wir für die Verarbeitung benötigen, eine Range für die Selektion, eine Struktur für das Mapping und einen Tabellen typ für die Rückgabe der Daten.

TYPES tt_r_name TYPE RANGE OF ddddlsrc-ddlname.

TYPES: BEGIN OF ts_mapping,
         ddlname  TYPE ddddlsrc-ddlname,
         cds_name TYPE string,
       END OF ts_mapping.
TYPES tt_mapping TYPE SORTED TABLE OF ts_mapping WITH UNIQUE KEY ddlname.

 

Als nächsten Schritt legen wir eine Methode an, die die Ermittlung durchführen soll. Dabei wollen wir sie wiederverwendbar gestalten und übergeben die CDS Views, für die wir Namen ermitteln wollen, um nicht alle Objekte zu lesen.

METHODS extract_cds_name
  IMPORTING it_r_name        TYPE tt_r_name
  RETURNING VALUE(rt_result) TYPE tt_mapping.

 

Die Methode befüllen wir nun mit einigen Daten, um schnell ein Ergebnis aus der Logik zu erhalten. Bei der Befüllung wählen wir eine Inline-Deklaration und befüllen die Range Tabelle. Da SIGN und OPTION nur einmal gesetzt werden müssen, lassen wir sie außerhalb der Datensätze und sparen uns die weitere Aufzählung.

DATA(lt_r_names) = VALUE tt_r_name( sign   = 'I'
                                    option = 'EQ'
                                    ( low = 'I_COMPANYCODE' )
                                    ( low = '/1BS/SADL_CDS_EXP' )
                                    ( low = 'A_CHANGEMASTEROBJECTTYPETEXT' )
                                    ( low = 'C_BUDGETPERIODCHILDGROUP' )
                                    ( low = 'I_ABOPCHECKINGRULE' )
                                    ( low = 'I_JOBSTATUS' )
                                    ( low = 'SADL_CDS_RS_SO_ROOT_W_DB_HINT' )
                                    ( low = 'SADL_GW_V_AUNIT_V2_VH_WRONG_AN' )
                                    ( low = 'SEPM_SDDL_EXTENSIONS' ) ).

 

Zum Abschluss lesen wir auf der Tabelle DDDDLSRC die gewählten Core Data Services und den abgelegten Quellcode.

SELECT FROM ddddlsrc
  FIELDS ddlname, source
  WHERE ddlname IN @it_r_name
  INTO TABLE @DATA(lt_views).

 

Damit kannst du die folgende Klasse für die Implementierung verwenden. Möchtest du noch weiter gehen, dann kannst du die Umsetzung im Test Driven Development machen und dir vor der Implementierung noch die Testklasse aufbauen.

CLASS zcl_bs_demo_cds_names DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    TYPES tt_r_name TYPE RANGE OF ddddlsrc-ddlname.

    TYPES: BEGIN OF ts_mapping,
             ddlname  TYPE ddddlsrc-ddlname,
             cds_name TYPE string,
           END OF ts_mapping.
    TYPES tt_mapping TYPE SORTED TABLE OF ts_mapping WITH UNIQUE KEY ddlname.

    METHODS extract_cds_name
      IMPORTING it_r_name        TYPE tt_r_name
      RETURNING VALUE(rt_result) TYPE tt_mapping.
ENDCLASS.


CLASS zcl_bs_demo_cds_names IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DATA(lt_r_names) = VALUE tt_r_name( sign   = 'I'
                                        option = 'EQ'
                                        ( low = 'I_COMPANYCODE' )
                                        ( low = '/1BS/SADL_CDS_EXP' )
                                        ( low = 'A_CHANGEMASTEROBJECTTYPETEXT' )
                                        ( low = 'C_BUDGETPERIODCHILDGROUP' )
                                        ( low = 'I_ABOPCHECKINGRULE' )
                                        ( low = 'I_JOBSTATUS' )
                                        ( low = 'SADL_CDS_RS_SO_ROOT_W_DB_HINT' )
                                        ( low = 'SADL_GW_V_AUNIT_V2_VH_WRONG_AN' )
                                        ( low = 'SEPM_SDDL_EXTENSIONS' ) ).

    out->write( extract_cds_name( lt_r_names ) ).
  ENDMETHOD.


  METHOD extract_cds_name.
    SELECT FROM ddddlsrc
      FIELDS ddlname, source
      WHERE ddlname IN @it_r_name
      INTO TABLE @DATA(lt_views).

    " Implement here
  ENDMETHOD.
ENDCLASS.

 

Aufgabe

Die Aufgabe besteht nun darin, die Rückgabetabelle zu befüllen und die echten CamelCase Namen aus dem Code abzuleiten. In der internen Tabelle LT_VIEWS findest du den Namen des Views und das Coding:

 

Hinweis: Im nächsten Abschnitt werden wir auf die Lösung eingehen, wenn du die Aufgabe erst einmal selbstständig machen möchtest, solltest du hier pausieren.

 

Lösung

Schauen wir uns in diesem Abschnitt die verschiedenen Schritte zum Aufbau der Tabelle an.

 

Schleife

Im ersten Schritt wollen wir die verschiedenen Views verarbeiten, dazu legen wir eine Schleife an und arbeiten dabei mit einer Referenz. Mehr Informationen zur Arbeit damit, findest du in einem älteren Artikel von uns. Im zweiten Schritt fügen wir eine neue Zeile an unsere Ergebnistabelle und befüllen den Viewnamen. Die erzeugte Zeile weisen wir einer neuen Referenz zu, um später den Namen zu ergänzen.

LOOP AT lt_views REFERENCE INTO DATA(lr_view).
  INSERT VALUE #( ddlname = lr_view->ddlname ) INTO TABLE rt_result REFERENCE INTO DATA(lr_result).

ENDLOOP.

 

Wir übernehmen direkt den Namen des Views, da es sich um eine sortierte Tabelle handelt und wir den Schlüssel befüllen müssen, da wir sonst einen Fehler erhalten. Da wir keine Ausschlusskriterien haben, die einzelne Zeilen ausschließt, sondern wir alles ins Ergebnis übernehmen, macht dieser Schritt auch Sinn.

 

Aufteilung

Als nächstes wollen wir die einzelnen Statements der Quelle teilen, dazu verwenden wir einen klassischen SPLIT Befehl und erhalten eine String Tabelle mit den einzelnen Elementen aus dem Quellcode.

SPLIT lr_view->source AT ` ` INTO TABLE DATA(lt_split).

 

Suche

Wir können nun also mit der Verarbeitung der einzelnen Statements beginnen. Die Tabelle würde im Moment nach dem Split so aussehen:

 

Nun müssen wir nur die Tabelle mit Elementen durchsuchen und nach dem gesuchten CDS View filtern. Haben wir den Views gefunden, übernehmen wir diesen und verlassen die Routine. Damit die Werte miteinander vergleichbar sind, wandeln wir den CamelCase Namen per TO_UPPER um und vergleichen das Ergebnis. Da wir hier mit einer Referenz arbeiten, müssen wir für den Zugriff  "->*" verwenden.

LOOP AT lt_split REFERENCE INTO DATA(lr_split).
  IF to_upper( lr_split->* ) = lr_view->ddlname.
    lr_result->cds_name = lr_split->*.
    EXIT.
  ENDIF.
ENDLOOP.

 

Zum Abschluss verlassen wir die Logik mit EXIT und gehen zum nächsten Core Data Service.

 

Performance

Um die Performance etwas zu verbessern, müssen wir nicht immer bei der ersten Zeile beginnen, vor allem wenn viele Annotationen am Anfang sind. Im Grunde können wir nach dem Schlüsselwort DEFINE suchen, da dieses den View einleitet und definiert. Dazu suchen wir in der Tabelle nach "define", da diese aber keine Felder besitzt, verwenden wir den Zugriff über TABLE_LINE. Mit der Funktion LINE_INDEX lassen wir uns die aktuelle Zeile zurückgeben. Die Schleife ergänzen wir dann noch um den Zusatz FROM, um ab dieser Position zu starten. 

DATA(ld_start) = line_index( lt_split[ table_line = `define` ] ).

LOOP AT lt_split REFERENCE INTO DATA(lr_split) FROM ld_start.
ENDLOOP.

 

Sollte LINE_INDEX kein Statement finden, dann wird die Position auf 0 gesetzt und kann dann weiterhin für den LOOP verwendet werden, um mit der ersten Position zu beginnen.

 

Zeilensprünge

In einigen Fällen kann es vorkommen, dass "define" nicht gefunden werden kann. Schauen wir uns diese Fälle genauer an, werden wir erkennen, dass das Statement nicht sauber getrennt wurde.

 

Bei Backslash & "n" handelt es sich um einen Zeilenumbruch, der ebenfalls in der Tabelle gespeichert wird. In so einem Fall sollten wir alle Zeilenumbrüche im Quellcode vor dem SPLIT ersetzen, damit wir ein sauberes Ergebnis erhalten. Dafür verwenden wir die Replace Funktion und ersetzen alle Vorkommen des Zeilenumbruchs durch ein Leerzeichen.

lr_view->source = replace( val  = lr_view->source
                           sub  = cl_abap_char_utilities=>cr_lf
                           with = ` `
                           occ  = 0 ).

 

Schauen wir uns nun die Tabelle nach dem SPLIT an, dann sind die Statements sauber getrennt, um im Anschluss nach dem Anfang suchen zu können.

 

Namen

In unseren Beispielen gibt es auch einen Fall, der einen leeren Core Data Service zurückliefert. Schauen wir uns hier das Mapping im Detail an, werden wir feststellen, dass der Name im Quellcode nicht gleich dem Namen des Objekts ist.

 

Im einfachsten Fall übernehmen wir dann den Namen des gesuchten Core Data Services als Ergebnis. Hier gibt es dann keinen CamelCase Namen für das Objekt.

IF lr_result->cds_name IS INITIAL.
  lr_result->cds_name = lr_view->ddlname.
ENDIF.

 

Vollständiges Beispiel

In diesem Abschnitt findest du die vollständige Klasse mit der kompletten Implementierung, um das Beispiel nachstellen zu können. Grundsätzlich kannst du die Eingrenzung der einzelnen Views auch entfernen, um alle Views als Ergebnis zu erhalten. Dabei wird aber die Laufzeit sehr stark ansteigen. Die Ressource findest du bei uns im GitHub Repository für die Extraktion der CDS Informationen.

 

Fazit

In diesem praktischen Beispiel wollten wir dir die Arbeit mit Strings etwas näherbringen und wie du die verschiedenen Techniken nutzen kannst, um an das Ergebnis zu kommen. Hast du eine interessante Lösung für das Problem, gern in die Kommentare.


Enthaltene Themen:
TippABAP in der PraxisStringsCDS Namen
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP in der Praxis - Test Driven Development

Kategorie - ABAP

Wie funktioniert eigentlich TDD in der Praxis und gibt es einfache Beispiele zum Lernen in ABAP? In dieser Übung gehen wir auf den praktischen Teil ein.

24.09.2024

ABAP in der Praxis - Datenmenge zusammenführen

Kategorie - ABAP

Wir führen wir zwei unterschiedliche Datenmengen in ABAP zusammen, vor allem im Hinblick auf das Moderne ABAP? Eine praktische Aufgabe zum Thema.

17.09.2024

ABAP in der Praxis - Modern ABAP

Kategorie - ABAP

In dieser kleinen Aufgabe schauen wir uns bestehenden klassischen ABAP Quellcode an und versuchen diesen nach Modern ABAP zu optimieren.

27.08.2024

ABAP Tipp - Performance Datenfilterung

Kategorie - ABAP

Welche Anweisung verwendest du in ABAP zur Filterung von internen Tabellen und ist diese performant? In diesem Artikel mehr dazu.

13.08.2024

ABAP in der Praxis - Typkonvertierung

Kategorie - ABAP

Wie würdest du diese Typkonvertierung in ABAP durchführen? Ein Beispiel aus der Praxis und ein Lösungsvorschlag.

16.07.2024