EGL besitzt auf den ersten Blick weit weniger Schlüsselwörter und Optionen in der Sprache als RPG. Der zweite Blick offenbart, dass viele Funktionalität, z. B. die der RPG Built-In-Funktionen, auch im EGL existiert, allerdings ohne Schlüsselwort-„Frontend“, sondern nur als Funktion in einer Library, deren Gesamtheit quasi einen Anhang zur Sprache bildet. Das entlastet die Sprache und verleiht ihr Flexibilität für ihre Entwicklung in der Zukunft.

Da die Funktionen in den Libraries nicht mehr im direkten Rampenlicht stehen, sollten sie zusätzlich etwas beleuchtet werden, damit sie nicht übersehen werden oder man sogar versucht, sie nachzubilden. Dabei helfen die herausgearbeiteten Prinzipien und viele ausgewählte Beispiele in diesem Artikel.

In den vorigen beiden Artikeln zu EGL standen Programm- und Funktionsaufrufe im Mittelpunkt unseres Interesses. Diese bilden die programmtechnischen Voraussetzungen für die Modularität von Anwendungen, ohne die komplexe Anwendungen nicht mehr überschaubar wären. Ohne eine mit der Modularisierung meist einhergehende Schichtenarchitektur würde vieles redundant programmiert werden. Wurden erst einmal wiederverwendbare Anwendungsbausteine herausgefunden, ob nur im vorliegenden Projekt oder sogar projektübergreifend, müssen diese auch verwaltet werden. Der wiederverwendbare Kode existiert allgemein in Prozeduren, die bei EGL als Funktionen bezeichnet werden. Die Funktionen werden bei EGL meist in Libraries oder Services zusammengefasst, Die Libraries wurden seitens des Kodierens im vorigen TechKnowLetter behandelt, die detailliertere Behandlung der Services ist Gegenstand eines nachfolgenden.

Libraries und Services sind formal und inhaltlich einander ähnlich und die Grenze zwischen beiden ist oft fließend. Die Libraries, um die es in diesem Artikel geht, beinhalten mehr häufig wiederkehrende oder komplexere Kode-Sequenzen, die redundante bzw. Low-Level-Programmierung vermeiden helfen, während bei Services mehr Aspekte der Laufzeit und der Externalisierung eine Rolle spielen. In der „Grauzone“ entscheidet man sich gern für die Libraries, weil deren Aufruf und Deployment am einfachsten zu realisieren sind. Libraries finden bei EGL als Programmstruktur Verwendung, und auch die Sprache EGL selbst stellt „standardisierte“ komplexere Funktionalität in Libraries zur Verfügung.

Die Erweiterung des Projektes mit der letzten Übung

Nach Durchführung der letzten Übung ergibt sich die auf Abbildung 1 dargestellte Projektstruktur. Das Programm KundenAnzeige, das die Root der Anwendung bildet, finden Sie auf Abbildung 2 und die Record-Definition für den anzuzeigenden Kunden auf Abbildung 3. Die wiederverwendungsfähige Funktionalität der Anwendung sollten Sie schon in zwei Libraries zusammenfassen, die dem Inhalt der Abbildungen 4, 5 und 6 entsprechen.

Abb. 1: Das EGL Projekt EGLKunden

package eglkunden; 

import eglkunden.daten.KundeZumAnzeigen;

program KundenAnzeige type BasicProgram{}

    use eglkunden.daten.KundeZumAnzeigenBeispielDaten;
    use eglkunden.praes.Drucken;
    use eglKunden.logik.KundenStatistik;

    function main()
        writeStdout("Kundenanzeige");

        kunde KundeZumAnzeigen;
        kunden KundeZumAnzeigen[];
        anzahlSaetze int;
        summeUmsaetze decimal(10, 2);
        durchschnittUmsaetze decimal(7, 2);

        holeKunde(30, kunde);
        druckeKunde(kunde);

        holeKunden(kunden);
        druckeKunden(kunden);

        printStatistik(zaehleKunden(kunden),
              berechneUmsatzSumme(kunden),
              berechneUmsatzDurchschnitt(kunden));
    end

end

Abb. 2: Das EGL Programm KundenAnzeige

package eglkunden.daten; 

record KundeZumAnzeigen type BasicRecord
    kndnr int;
    name string;
    adresse string;
    umsatz decimal(7, 2);
    zeitPunkt timeStamp;
    bemerkung string;

end

Abb. 3: Die EGL Record-Definition KundeZumAnzeigen

package eglkunden.daten; 

library KundeZumAnzeigenBeispielDaten type BasicLibrary{}

    private beispielKunden KundeZumAnzeigen[0];

    private function erzeugeKundenSaetze(kunden KundeZumAnzeigen[] inout)
        kundeZumAnzeigen1 KundeZumAnzeigen{kndnr = 10, name = "Luzifer Teufel",
              adresse = "Auf dem Scheiterhaufen", umsatz = 20.00};
        kundeZumAnzeigen2 KundeZumAnzeigen{kndnr = 20, name = "Hans Wurst",
              adresse = "Schweinfurt, Darmstädter Str. 1", umsatz = 9.98};
        kundeZumAnzeigen3 KundeZumAnzeigen{kndnr = 30, name = "Max Moritz",
              adresse = "Boltenhagen, Am Busch", umsatz = 100.00};
        kunden.appendElement(kundeZumAnzeigen1);
        kunden.appendElement(kundeZumAnzeigen2);
        kunden.appendElement(kundeZumAnzeigen3);
    end

    function holeKunden(kunden KundeZumAnzeigen[] inout)
        if(beispielKunden.getSize() == 0)
            erzeugeKundenSaetze(beispielKunden);
        end
        kunden = beispielKunden;
    end

    function holeKunde(kndnr int in, kunde KundeZumAnzeigen out)

        if(beispielKunden.getSize() == 0)
            erzeugeKundenSaetze(beispielKunden);
        end

        defaultKunde KundeZumAnzeigen;
        kunde = defaultKunde;
        for(i int from 1 to beispielKunden.getSize() by 1)
            if(beispielKunden[i].kndnr == kndnr)
                kunde = beispielKunden[i];
                exit for;
            end
        end
    end
end

Abb. 4: Die EGL Library KundeZumAnzeigenBeispielDaten

package eglkunden.logik; 

import eglkunden.daten.KundeZumAnzeigen;

library KundenStatistik type BasicLibrary{}

    function berechneUmsatzSumme(kunden KundeZumAnzeigen[] in)
                returns(decimal(10, 2))
        summeUmsaetze decimal(10, 2) = 0;
        for(i int from 1 to kunden.getSize() by 1)
            summeUmsaetze += kunden[i].umsatz;
        end
        return(summeUmsaetze);
    end

    function zaehleKunden(kunden KundeZumAnzeigen[] in)
                returns(int)
        return(kunden.getSize());
    end

    function berechneUmsatzDurchschnitt(kunden KundeZumAnzeigen[] in)
                returns(decimal(7, 2))
        anzahl int = zaehleKunden(kunden);
        if(anzahl == 0)
            return(0);
        else
            return(round(berechneUmsatzSumme(kunden) / anzahl, -2));
        end
    end

end

Abb. 5: Die EGL Library KundenStatistik

package eglkunden.praes; 

import eglkunden.daten.KundeZumAnzeigen;

library Drucken type BasicLibrary{}

    function druckeKunden(kunden KundeZumAnzeigen[] in)
        for(i int from 1 to kunden.getSize())
            druckeKunde(kunden[i]);
        end
    end

    function druckeKunde(kunde KundeZumAnzeigen in)
        zeile string = "Kndnr=" :: kunde.kndnr :: ", " :: kunde.name :: ", " ::
                        kunde.adresse :: ", Umsatz =" :: kunde.umsatz ::
                        " , Zeitpunkt=" :: kunde.zeitPunkt :: ", " ::
                        kunde.bemerkung;
        writeStdout(zeile);
    end

    function printStatistik(anzahlSaetze int in, summeUmsaetze
            decimal(10, 2) in, durchschnittUmsaetze decimal(7, 2) in)
        writeStdout("Anzahl Sätze: " :: anzahlSaetze);
        writeStdout("Summe Umsätze: " :: summeUmsaetze);
        writeStdout("Durchschnitt Umsätze: " :: durchschnittUmsaetze);
    end

end

Abb. 6: Die EGL Library Drucken

Die Library KundeZumAnzeigenBeispielDaten fasst Funktionen zum Erzeugen von Testdaten zusammen, für die, um zusätzliche Probleme durch Aufruf und Deployment zu vermeiden, die Library dem Service vorzuziehen ist.

Die Library als Bündelung für die Funktion aus KundenStatistik ist konzeptionell besser als die Alternative Service. Wenn allerdings Funktionen CPU-intensiv sind und auf einem anderen Rechner ausgelagert werden sollen, sollte dann ein Service als entsprechende Programmstruktur gewählt werden.

Bei der Library Drucken liegen die Verhältnisse ähnlich wie bei KundenStatistik. Als lokales Hilfsmittel zum Test sind die Funktionen in einer Library gut aufgehoben, bei allgemeiner Nutzung als „Laufzeit-Funktionen“ wäre dann wohl mehr die Alternative Service angezeigt.

EGL Bibliotheken als Anhang zur Sprache

EGL hat sich das Ziel gestellt, mit allen Technologien zusammenarbeiten zu können. Wenn man bedenkt, wie viele Schlüsselwörter die Sprache RPG bei ihrem vergleichsweise schmalen Aufgabenspektrum hat und dass dies nicht ohne Nebenwirkungen wie verbotene Namen , hohe Komplexität und geringere Flexibilität möglich ist, so muss man RPG diesbezüglich EGL nicht unbedingt als Vorbild nehmen. Für solch ein Vorbild taugen dann schon eher die objektorientierten Sprachen, allen voran Java, zu dem sich Ähnlichkeiten wegen der Zugrundelegung als Generierungssprache geradezu gebieten.

Java hat einen sehr kleinen Kern der Sprache, zu dem die Schlüsselwörter mit ihrer Funktionalität zählen. Java stellt seine Funktionalität in unzähligen Methoden (~ ILE Prozeduren, ~EGL Funktionen) der verschiedenen Klassen (in EGL etwas differenziert in Programme, Libraries, Services oder Handler) zur Verfügung. Dieser Ansatz von Java führt zwar zu einer flexiblen, aber nicht immer einfachen Kodierweise, wenn man nur einmal das Beispiel der Addition zweier Dezimalzahlen betrachtet. Das Kodieren wird oft etwas einfacher, wenn man mit statischen Methoden arbeitet, die in Java teilweise den „normalen“ Klassen zugeordnet werden, aber auch teilweise zu speziellen Klassen, auch als Bibliotheken bezeichnet, gebündelt werden, wobei die „Bibliothek“ java.lang.Math weitgehend der EGL Bibliothek MathLib entspricht. Was liegt also näher, als für andere allgemeine Sachverhalte nach dem gleichen Prinzip Bibliotheken bereitzustellen.

EGL Bibliotheken sind in Sammlungen von EGL Funktionen, die nach Sachverhalten zu Libraries gebündelt werden. Diese müssen wie beim Java Sprach-Paket java.lang nicht importiert werden, sind also beim Kodieren allgegenwärtig und können sogar ohne Bibliotheks-Qualifizierung adressiert werden, wie zu sehen bei den Funktionen round() (Abbildung 5) aus der Library MathLib und der Funktion writeStdout() (Abbildung 6) der Library SysLib.

EGL besitzt folgende Libraries

  • ConsoleLib für Funktionen zur Unterstützung der Arbeit mit zeichenorientierten Benutzeroberflächen wie 5250- und 3270-Anzeigen
  • DateTimeLib Funktionen zur Datums- Zeit- und Kalenderrechnung, angefangen vom aktuellen Zeitpunkt als Timestamp, über Extraktion von Datum, Zeit usw., die Berechnung von Zeitdifferenzen bis zur Berechnung, z. B. des Wochentages
  • JavaLib für Funktionen zum Zugriff auf Java Klassen und Objekte
  • J2eeLib für Funktionen zum Zugriff aus EGL Webanwendungen auf den Web-Container im Application Server
  • LobLib für Funktionen zur Arbeit mit großen Binary Large Objects und Character Large Objects, z. B. zum Speichern und Laden von Variablen der Typen CLOB und BLOB zu und von persistenten Datenstromdateien, zur Migration mit anderen Typen u. A.
  • MathLib für Funktionen zur Trigonometrie, zur Rundung sowie zur Bildung von Potenzen und Logarithmen
  • ReportLib enthält Funktionen für Jasper Reports
  • ServiceLib enthält Funktionen zum Adressieren und Binden für lokale Services und Web Services
  • SqlLib enthält Funktionen zum Verbinden zu Datenbanken, für Queries sowie zum Hoch- und Herunterladen von CSV-Dateien
  • StrLib enthält eine große Kollektion von Zeichenketten-Funktionen, vor allem zur Migration zwischen charachter respektive string und denn anderen den EGL Datentypen
  • SysLib stellt Funktionen zur Arbeit mit der Kommandozeile des Systems, mit Standard-Datenströmen, mit der Systemumgebung sowie auch zur Internationalisierung, zur Journalisierung und zur Fehlerbehandlung bereit.
  • SysVar mit Systemvariablen für verschiedene Zwecke

Die Bibliotheken J2eeLib, ConsoleLib, ReportLib und SqlLib betreffen die Kommunikation mit der Umgebung und werden deswegen nicht zu den „Core Libraries“ gezählt. Sie werden dann später in Verbindung mit den entsprechenden Anschlusstechnologien behandelt, ebenso die Kernbibliotheken JavaLib und ServiceLib. Die Funktionen aus den Libraries MathLib und LobLib sind weitestgehend selbsterklärend, so dass ich auf Details zu diesen verzichte. In den noch übrig bleibenden Libraries DateTimeLib, StrLib, SysLib und SysVar steckt eine Menge Konzept, so dass ich deren wichtigste Funktionen, Variablen und Konstanten nachfolgend etwas näher beleuchten werde.

Für die Informationen im Detail ist die EGL Sprachreferenz zuständig, deren Fakten allein schon wegen ihres Umfanges hier nicht wiedergegeben werden können. Deshalb beschränke ich mich auf die Analyse der zu Grunde liegenden Ziele und Gesetzmäßigkeiten sowie ausgewählte signifikante Demos.

Datumsrechnen und die Library DateTimeLib

EGL verfolgt ein einzigartiges wie auch geniales Konzept zu Datumrechnungen, das erst durch das Generieren ermöglicht wird. EGL besitzt die Datentypen date, time und timeStamp sowie den auf die vorigen anpassbaren Datentyp interval für eine Zeitdauer. Letzterer gehört zur Datumrechnung mit Datums-Addition und -Subtraktion und fällt z. B. an, wenn eine Differenz zwischen zwei Datumsdaten berechnet werden soll.

Die Datentypen werden intern gespeichert gemäß einer auf dem gregorianischen Kalender basierenden internen Maske, die dann als Spezifikation bei der Definition einer Variablen anzugeben ist, wenn nicht der Default gewählt wird. Letzterer ist bei timeStamp ‚yyyyMMddHHmmss‘. Bei timeStamp kann z. B. aber auch durch die interne Maske ‚ddHHmmssffffff‘ mit Mikrosekunden innerhalb Tagen gerechnet werden. Rechte Stellen können weggelassen werden, wenn keine größere Genauigkeit erforderlich ist. Wenn man auf linke führende Stellen verzichtet, muss man einen Überlauf links ins Nichts oder u. U. eigenartige Rechenergebnisse billigend mit in Kauf nehmen. Somit ergibt sich, dass date und time nur zwei allgemein gebräuchliche Spezialfälle von timeStamp mit den internen Masken ‚yyyyMMdd‘ bzw. ‚HHmmss‘ sind. Die interne Maske für ein Intervall ist genauso aufgebaut und muss bei Rechnungen zu den Masken der anderen Operanden passen, wenn keine Informationen verlorengehen sollen und keine unnütze oder irreführende Genauigkeit auftreten soll.

In einem EGL Programm müssen Datums-Zeit-Daten vorgegeben werden können, sei es durch das Ermitteln der momentanen Zeit oder durch explizite Angabe in einer Konstanten oder eines Literals, d. h. die interne Darstellung muss von einem entsprechenden String bestimmt werden können wie auch ein String aus der internen Darstellung. . Weiterhin besteht auch die Aufgabe, die Darstellungen für abweichende interne Masken oder auch als Zahlen, z. B. für das Jahr oder den Wochentag zu bilden. Das leisten diverse Funktionen der Library DateTimeLib.

Nicht zur Aufgabe der DateTimeLib gehört die Migration von und in Zeichendarstellungen auf Benutzeroberflächen. Hierzu werden weitaus komplexere Masken zum Parsen oder Formatieren genommen. Einige Funktionen zur Formatierung werden durch die Library StrLib bereitgestellt.

Die Schreibweise der Namen der Libraries wird derzeit recht uneinheitlich gehandhabt. In den Handbüchern, die an vielen Stellen noch nicht auf der Höhe der Zeit sind, beginnen diese mit einem Kleinbuchstaben, währen der Rational Business Developer (schon) konsequent mit Großbuchstaben beginnende Bibliotheksnamen generiert, so wie ich es auch in meinen Artikeln mache.

Abb. 7: Demo zum Arbeiten mit Datums- und Zeitdaten sowie mit der Library DateTimeLib

In der Demo auf Abbildung 7 ist ein Ausschnitt der Funktionalität der Library DateTimeLib in Verbindung mit den Datums- und Zeit-Operatoren „+ “ und „-“ dargestellt:

  • In den Zeilen 5 und 6 werden gemäß den Variablennamen Zeitmarken ohne Jahresangabe mit entsprechenden gültigen Literalen deklariert und initialisiert.
  • Bei der expliziten Initialisierung der Variablen jetzt in Zeile 7 wird die Funktion DateTimeLib.currentTimeStamp() verwendet. Die gleiche Funktion wäre auch bei einer impliziten Initialisierung per Default verwendet worden. Die Qualifizierung mit der Library wird zwar durch die Inhaltshilfe generiert, kann aber auch weggelassen werden.
  • In den Zeilen 12 bis 16 inklusive der Ausgaben auf die Konsole stecken einige interessante Details:
    • Bei Datums-Additionen und -Subtraktionen kommen bei Operanden und Ergebnis zweimal ein Datums-/Zeitwert und einmal ein Intervall vor.
    • Variablenwerte vom Typ interval können negativ sein.
    • Variablenwerte vom Typ interval können Zusammensetzungen sein, von denen ggf. der erste Teil negativ ist.
    • Der Wert des Intervalls im Beispiel müsste eigentlich positiv sein. Doch durch das Weglassen des Jahres in den Masken der Zeitmarken ergibt sich der negative Wert durch ein zyklisches Rechnen.
    • In den Zeilen 18 und 19 werden durch dateTimeLib-Funktionen Teilinformationen als Integer-Zahlen extrahiert.
  • Die Qualifizierung mit der Library ist in Zeile 19 weggelassen.
  • Für die Migration einer Zahl vom Typ decimal in einen Datums-Zeit-Wert vom Typ timeStamp muss man wie in Zeile 21 über den „Zwischentyp" string gehen, was mit der Funktion formatNumber() aus der Library StrLib realisiert wird.
  • Für das Addieren einer Zeitdauer wie in Zeile 24 muss der Typ interval benutzt werden. Diesen erhält man aus dem Typ string durch die Funktion intervalvalueWithPattern(). Nach string muss ggf. migriert werden.
  • In der Zeile 29 wird eine Zeitmarke mit der Funktion timeStampFrom() aus einem Datum und einem Zeitwert zusammengesetzt.
  • Eine gegenüber den Definitionen abweichende Darstellung der Ausgabewerte auf der Konsole resultiert aus der impliziten Anwendung der Formatierungsfunktionen aus der Library StrLib.

Die Library StrLib für Funktionen rund um die Zeichenketten bzw. Strings

Die Funktionen der Library StrLib rund um die Zeichenketten, d. h. um die Datentypen string und char, lassen sich in folgende Kategorien einteilen:

  • Ermittlung von Längen
  • Migration zwischen Zeichenketten und anderen Typen
  • Zeichenketten-Manipulationen und -Analyse
  • Formatierung von Datums-, Zeit- und Zahlenwerten

Wenden wir uns zuerst den Formatierungen, d. h. einer Migration mit Komfort von Zahlen und Datumsdaten in Strings, zu! Bislang wird eine solche nur in der Richtung, der Ausgabe in Zeichenketten durch Sprache EGL unterstützt. Die Gegenrichtung, das Parsen, gibt es bis jetzt nur in Verbindung mit den Benutzeroberflächen, wo Textfelder entsprechend konfiguriert werden können. Dies ist bzgl. Anwendungsdesign meist die beste Stelle für ein- und ausgabeseitige Formatierungen. Aber auch im Kode kann eine Formatierung sinnvoll sein, womit sich die folgenden Ausführungen und die Demo auf Abbildung 8 beschäftigen.

Zu erwähnen wäre noch, dass natürlich auch das Formatieren und insbesondere das Parsen von Java zur Verfügung stehen durch Verwendung externer Java Typen und Nutzung von Funktionen der Library JavaLib.

Abb. 8: Ausgabeformatierung Datumsdaten und Zahlen

Zur Steuerung der Formatierung von Datumsdaten und numerischen Daten sowie Währungsdaten (Datentyp money) durch EGL werden Schablonen (Masken) benutzt. Diese können explizit angegeben werden, bzw. sie werden generiert aus der Lokalen der Java Umgebung. Dabei gilt die folgende Rangfolge:

  • Explizit angegebene Formatierungsschablone:
    Eine Angabe in einer Konstantendeklaration ist i. A. stilistisch besser als im laufenden Programmkode so wie auf Abbildung 8, wo der Zweck das Mittel heiligt. Für standardisierte Formate gibt es extra Masken-Konstanten für die Schablonen, z. B. isoDateFormat="yyyy-MM-dd" in der Library StrLib.
  • Default-Formatierungsschablonen aus dem wirksamen EGL Build-Deskriptor:
    Diese initialisieren die entsprechenden System-Formatierungsvariablen wie StrLib.defaultTimeStampFormat. Wird eine solche nicht angegeben, wird ein Default gemäß der Lokalen der Java Umgebung wirksam.
  • Default-Formatierung aus der Lokalen des Betriebssystems:
    Die Lokale wird aus dem Betriebssystem übernommen und kann beim Start der Java Virtual Machine sowie auch später noch explizit überschrieben werden. Die Lokale für Deutschland ergibt sich aus der Sprachkennzeichnung „de" und dem Länderkennzeichen „DE".

Nun zur Auswertung der Demo auf Abbildung 8:

  • In den Zeilen 3 und 4 werden eine Variable bzw. eine Konstante deklariert und initialisiert, die dann Grundlage der Formatierungen sind. Beachten Sie, dass auch Umlaute, sogar das „ß", in den Namen vorkommen können. Die Variable jetzt kann keine Konstante sein, da sie erst zur Laufzeit einen Wert bekommt. Der Dezimalpunkt muss als Punkt angegeben werden, da es keine solche Eingabeformatierung wie für Zeitdaten mit DateTimeLib gibt.
  • Die mit StrLib.defaultTimestampFormat vorgegebene Default-Formatierung hat als Wert eine Leerkette, da für sie kein Initialwert im Build-Deskriptor angegeben wurde. Trotzdem formatiert sie mit einem vernünftigen Default, wie am Ergebnis der Zeilen 7 bis 9 in der Konsole zu erkennen ist. Dort wird diese Variable implizit bzw. explizit zur Formatierung des Timestamps verwendet.
  • Eine Formatierung eines Timestamps, bei der fast alle Register gezogen werden, findet in den Zeilen 10 und 11 statt. Beachten Sie hierbei die unterschiedlich ausführlich gewählten Darstellungen für den Wochentag, den Monat und die Zeitzone, Letztere im Vergleich mit den Zeilen 25 und 26. Interessant ist auch die Masken-Syntax für das Einstreuen von Texten.
  • Zeile 12 zeigt, dass eine Zeitmarke durchaus auch auf ein Datum formatiert werden kann, der Typ date also nur eine kompatible Einschränkung des Typs timeStamp ist, was die Zeilen 21 und 22 bestätigen. Das Gleiche gilt auch für den Typ time, wie die Zeilen 23 und 24 zeigen.
  • In den Zeilen 24 bis 26 werden wieder einmal viele Formatierungsoptionen verwendet. Die Sekundenbruchteile können deshalb nicht belegt sein, da beim Bilden der Zeitmarke mit DateTimeLib.timestampValue() in Zeile 3 per Default die Maske "yyyyMMddHHmmss" ohne Millisekunden verwendet wird.
  • In Zeile 13 wird eine Formatierungskonstante aus der StrLib verwendet, die, noch mit der Angabe für den gregorianischen Kalender verkettet, ein explizites Formatieren gemäß diesem Kalender bestimmt, im Gegensatz zu den vorherigen Formatierungen, bei denen der gregorianische Kalender aus der Lokalen impliziert wurde.
  • Die Zeilen 13 bis 20 zeigen das Formatieren bzgl. der anderen wichtigen Kalender von menschlichen Kulturen auf unserer Mutter Erde.
  • Die Variable defaultNumericFormat in Zeile 28 ist aus dem gleichen Grunde mit einem Leerstring belegt wie oben defaultTimeStampFormat (Zeile 28). Jedoch formatiert sie im Unterschied zu Letzterer bei expliziter Angabe in einen Leerstring. Durch dieses inkonsistente Verhalten unterscheiden sich die Formatierungen in den Zeilen 29 bzw. 30 und 31 voneinander. Diese „Ecke" von EGL wird sicherlich noch abgeschliffen werden.
  • In Zeile 21 wird ausgabeseitig per Default in Abhängigkeit von der Lokalen formatiert, anders als bei der Literal-Eingabe in Zeile 4.
  • Die Lokalen-abhängige ausgabeseitige Formatierung in den Zeilen 32 bis 35 tauscht ggf. die Zeichen für den Dezimalpunkt und eventuelle Trennzeichen für Dezimal-Triaden aus.
  • Einen kleinen Ausschnitt für die möglichen Maskenzeichen der den Edit Words entsprechenden Formatierungsmasken sehen Sie in den Zeilen 33 und 35. Eine vollständige Übersicht mit Erläuterungen gibt es im Anhang der EGL Sprachreferenz.

Abb. 9: Demonstration der Arbeitsweise einiger Funktionen zur Zeichenkettenmanipulation

Wenden wir uns der Betrachtung der wichtigsten restlichen Funktionen aus StrLib, wie sie auf Abbildung 9 demonstriert werden, zu:

  • Die beiden Variablen vorname und zuname unterscheiden sich in der Art des Unicode zur Speicherung. vorname wird mit char UTF-8 verwendet (wie es auch string tut) und zuname mit dem expliziten unicode UTF-16. Dies sehen Sie an den Werten der unterschiedlichen Längenfunktionen in den Zeilen 11 bis 15.
  • Die durch die oben benutzten Längenfunktionen berechneten Werte sind vom Typ int. Dieser passt aber nicht zur Signatur der Funktion SysLib.writeStdout(), die einen String-Parameter erwartet. Einen solchen bekommt man durch Anwendung der Funktion StrLib.formatNumber() (Zeile 11) auf den Parameter oder auch durch den Leerstring als ersten Operanden eines Ausdrucks (Zeilen 12 ff.), der einen String-Kontext für den Ausdruck definiert. Dieser bewirkt, dass auf das Integer die Formatierungsfunktion in Anlehnung an Java implizit ausgeführt wird.
  • In den Zeilen 13 und 14 erkennt man, dass die interne Darstellung einer char-Variablen, abgesehen vom Abschneiden überschüssiger Zeichen, als String erfolgt und dass die angegebene Länge Bestandteil der Metadaten ist.
  • Die Funktion zum Trimmen heißt clip() und bewirkt je nach Option 0, 1 oder 2 beidseitiges, linkes oder rechtes Abschneiden von Leerzeichen bzw. Nullen bei Zahlen. In Angabe dieser Option als nichtssagende Zahl lebt die inzwischen überkommene „Verschlüsselungstechnik" wieder auf.
  • Mit der Funktion spaces() in Zeile 7 kann eine beliebig lange Leerzeichenkette erzeugt werden.
  • Bei den allbekannten Funktionen upperCase() und lowerCase() in Zeile 8 ist nur die Behandlung des „ß" besonders bemerkenswert.
  • Das Suchen von Teilstrings mit indexOf() in Zeile 9 arbeitet erwartungsgemäß.
  • Das Bilden von Substrings in Zeile 10 wird ausnahmsweise nicht durch eine Funktion aus StrLib, sondern durch die EGL Syntax realisiert.
  • In Zeile 16 erkennt man, dass der Operator für die logische Negation das Ausrufezeichen ist.
  • Wie auch in Zeile 16 werden auch in den Zeilen 17 bis 19 Variablenwerte in einen anderen Datentyp migriert.
  • Die Zeilen 20 bis 22 dienen zur Vorbereitung der Demo zu den beiden Zeichenkettenanalyse-Funktionen getTokenCount() und getNetToken(). in den Zeilen 23 bis 25. Dort wird ein String gemäß vorgegebener Trennzeichen in Stücke zerlegt, die dann zum Beispiel nahtlos wieder zusammengefügt werden, was dann wiederum in Zeile 26 auf der Konsole dokumentiert wird.

Die Funktionen und Systemvariablen in den Libraries SysLib und SysVar

Die Library SysLib stellt Funktionen zur Arbeit mit dem System zur Verfügung, z. B. für:

  • Prüfziffernberechnung und -kontrolle
  • Logging und andere Aufzeichnungen inklusive Commit-Steuerung (SQL und non-SQL)
  • Diverse Funktionen für COBOL mit CICS
  • Nachrichten- und Fehlerbehandlung
  • Schreiben in Ausgabedatenströme wie auf die Konsole
  • Starten von Systemkommandos
  • Vorübergehendes Aussetzen der Anwendung

Abb. 10: Arbeiten mit Systemvariablen

Ausblick auf den nächsten Artikel

Im nächsten Artikel stehen sowohl eine pragmatische Sicht auf Syntax und Grammatik der Sprache EGL als auch etwas detailliertere Betrachtungen zu Ausdrücken und Rechenoperationen sowie die Ablauflogik im Mittelpunkt des Interesses.