operation.dsl

Bei der operation.dsl handelt es sich um eine domänenspezifische Sprache (DSL : Domain Specific Language). Ziel der operations.dsl ist es

  • den Zugriff auf Schnittstellen zu kapseln
  • eine Trennung zwischen fachlichem und technischem Code zu ermöglichen
  • die Gesamtkomplexität so zu verringern
  • die Wartbarkeit der Schnittstellen zu verbessern
  • die Lesbarkeit und Verständlichkeit des Schnittstellencodes zu verbessern
  • die Entwicklungsgeschwindigkeit zu erhöhen

 

 

Die operation.dsl setzt auf eine externe DSL unter Einbeziehung eines entsprechenden ausführbaren Models. D.h. die operation.dsl nutzt eine externe und von der Implementierungssprache unabhängige Möglichkeit zur Definition der fachlichen Operationen, welche zur Ausführungszeit in ein Model überführt und dieses Model ausgeführt wird.

foreach Cell  C 
     in Sheet 'Sheet 1' 
     in File  '.\TestExcelsheet.xlsx' 
execute 
        ${resultList}.add( [Cell].getStringCellValue() );

 

 

Dabei unterstützt die operation.dsl derzeit zwei Sprachen (Englisch und Deutsch), um die intuitive Lesbarkeit und Verständlichkeit (für den Fachbereich oder Kunden) zu erhöhen.

Für jede Zelle  C 
      in Blatt 'Sheet 1'
      in Datei '.\TestExcelsheet.xlsx' 
führe aus 
        ${resultList}.add( [Zelle].Text );

 

 

Der Plugin-Mechanismus der operation.dsl ermöglicht ferner einen Kontextwechsel der DSL. So kann bereits mit der Basisimplementierung wahlweise der Excel-, XML- oder Datei-Kontext genutzt werden (vgl. Beispielcode).

foreach Line with linenumber equals 2 
     in File   '.\CSV Testfile 1.2.1.csv' 
execute
       [Line].split('\t'); 
        [Line].mapping( 'first', 'second', 'third', 'fourth', 'fifth' ); 

        ${valueObjectWithCorrespondingAttributes}.* = [Line].*;

Für jede Zeile bei der text ähnlich '.* - Line 4.*' 
      in Datei   '.\Testfile 1.txt' 
führe aus 
        ${resultList}.add( [Zeile] ); 

 

 

Die operation.dsl bietet darüber hinaus eine i18n-Unterstützung, mittels derer konfigurativ die Namen und Attributnamen der DSL-Objekte des jeweiligen Kontext zur besseren Lesbarkeit mit sprachspezifischen Aliasnamen versehen werden können.

EN Line.linenumber = DE Zeile.Zeilennummer

 

 

In Kombination mit der mitgelieferten Expression Language können fachliche Bedingungen lesbar und verständlich auf einem fachlichen Niveau definiert werden.

foreach Element 'firstlevel' with name equals 'second one'
                              and firstAttribute like '.ir.*'
     in XML  ${xmlstring} 
execute 
        [Element].firstAttribute = 'first'; 
        [Element].secondAttribute = 10; 
        [Element].thirdAttribute = false; 
        ${resultDto} = [Element]; 

Für jedes Element 'firstlevel'
        in jedem  Element
        in Datei  '.\somefile.xml' 

führe aus
        ${resultDto}.* = [Element].*;

 

 

Die enthaltene Expression Language bietet eine Vielzahl an verschiedenen Operationen und kann auch für sich - also ohne das oben dargestellte Schleifenkonstrukt - genutzt werden.

 

Beschreibung   Operation   Beschreibung   Operation
Gleichheit  EN 1 equals ${A}.a.b.c   Ungleicheit  EN 1 not equals ${A}.a.b.c
DE 1 gleich ${A}.a.b.c   DE 1 nicht gleich ${A}.a.b.c
             
Ähnlichkeit  EN "HablaHabla" like ".*bla.*"   keine Ähnlichkeit  EN "HablaHabla" not like ".*urz.*"
DE "HablaHabla" ähnlich ".*bla.*"   DE "HablaHabla" nicht ähnlich ".*urz.*"
             
Kleiner  EN 1 less than 2   Größer  EN 2 greater than 1
DE 1 kleiner als 2   DE 2 größer als 1
             
Leer  EN "     " is empty   nicht Leer  EN "  xyz   " is not empty
DE "     " ist leer   DE "  xyz   " ist nicht leer
             
in Aufzählung enthalten  EN 1 in ( 1, 2, 3, 4, 5 )   nicht in Aufzählung enthalten  EN 6 not in ( 1, 2, 3, 4, 5 )
DE 1 in ( 1, 2, 3, 4, 5 )    DE 6 nicht in ( 1, 2, 3, 4, 5 ) 
             
Und  EN Ausdruck1 and Ausdruck2   Oder  EN Ausdruck1 or Ausdruck2
DE Ausdruck1 und Ausdruck2   DE Ausdruck1 oder Ausdruck2
             
Attribut-Selektionen  EN

${selection} = 
    select ( attribute1 as firstone,
             attribute2 as secondone ) 

      from ${someObject}
     where something equals "Habla";

  automatisches Attribut-Mapping (via Name)    ${leftparameter}.* = ${rightparameter}.*;
DE

${selektion} = 
   selektiere ( attribut1 as erstesAttribut,
                attribut2 as zweitesAttribut) 
          von ${einemObjekt}
      bei dem einAttribut gleich "Habla";

     
             
Wildcards bei Collections und Maps   ${collectionOfSomething}.*.somethingsAttribute 
     = "newValue";
   null save

 

${parameter}.someAttribute.someNullAttribute.someOtherAttribute

 (ergibt null / keine NullPointerException)

             
explizite Typkonvertierungen   ${someObject}.integerAttribute 
     = <java.lang.Integer> "1234";
  implizite Standard Typkonvertierungen   ${someObject}.integerAttribute 
     = "1234";
             
explizite Typkonvertierung String zu Datum   ${parameter}.dateAttribute = 
    <java.util.Date 'dd.MM.yyyy'> "01.06.2005";
  implizite Typkonvertierung String zu Datum   ${parameter}.dateAttribute = 
    <'dd.MM.yyyy'> "01.06.2005";

 

Dieser Showcase soll den lesenden Zugriff auf ein Excel-Workbook darstellen. Die darin enthaltenen Daten sollen als Objekte dem umgebenden System bereitgestellt werden. Das betreffende Workbook besteht hierbei aus den folgenden 4 einzelnen Blättern:

  • Stammdaten

Stammdaten

  • Bestellpositionen

  • Rechnungsadresse

  • Lieferadresse

Um die Daten adäquat abzubilden, sind zunächst die benötigten Transportobjekte zu definieren: 

  • Customer.java

public class Customer {
        private String kundennummer;
        private String anrede;
        private String vorname;
        private String nachname;
        private String strasse;
        private String hausnummer;
        private String postleitzahl;
        private String ort;

// + getter und setter
}

  • Orderitem.java

public class Orderitem {
        private Long lfdNr;
        private String artikelnummer;
        private String artikelbezeichnung;
        private Double einzelpreis;
        private Long menge;
        private Double gesamtpreis;

// + getter und setter
}

  • Address.java

public class Address {
        private String vorname;
        private String nachname;
        private String strasse;
        private String hausnummer;
        private String postleitzahl;
        private String ort;

// + getter und setter 
}

  • BillingAddress.java

public class BillingAddress extends Address {}

  • ShippingAddress.java

public class ShippingAddress extends Address {}

Für die Erzeugung von Orderitems nutzen wir eine Factory-Klasse:

  • OrderitemFactory.java

public class OrderitemFactory {
        public Orderitem createNew() {
               return new Orderitem();
        }
}

Danach kann in einer separaten Textdatei (Showcase.odsl) die DSL-Operationen beschrieben werden:

Stammdaten:
Für jedes Blatt  'Stammdaten' 
       in Datei  ${dateiname}  
führe aus 
        ${customer}.Kundennummer = G6; 
        ${customer}.Anrede = G8; 
        ${customer}.Vorname = G9; 
        ${customer}.Nachname = G10; 
        ${customer}.Strasse = G12; 
        ${customer}.Hausnummer = G13; 
        ${customer}.Postleitzahl = G14; 
        ${customer}.Ort = G15; 


Bestellpositionen:
Für jede Zeile bei der Zeilennummer größer als 5 
                   und E ist nicht leer 
                   und F ist nicht leer 
                   und H ist nicht leer 
                   und I ist nicht leer 
      in Blatt  'Bestellung' 
      in Datei  ${dateiname} 
führe aus 
        ${bestellposition} = ${orderitem}.createNew(); 
        ${bestellposition}.LfdNr = E; 
        ${bestellposition}.Artikelnummer = F; 
        ${bestellposition}.Artikelbezeichnung = G; 
        ${bestellposition}.Einzelpreis = H; 
        ${bestellposition}.Menge = I; 
        ${bestellposition}.Gesamtpreis = J; 
        ${orderitems}.add( ${bestellposition} ); 


Rechnungsadresse:
Für jedes Blatt  'Rechnungsadresse' 
       in Datei  ${dateiname} 
führe aus 
        ${billingaddress}.Vorname = G5; 
        ${billingaddress}.Nachname = G6; 
        ${billingaddress}.Strasse = G8; 
        ${billingaddress}.Hausnummer = G9; 
        ${billingaddress}.Postleitzahl = G10; 
        ${billingaddress}.Ort = G11; 


Lieferadresse:
Für jedes Blatt  'Lieferadresse' 
       in Datei  ${dateiname} 
führe aus 
        ${shippingaddress}.Vorname = G5; 
        ${shippingaddress}.Nachname = G6; 
        ${shippingaddress}.Strasse = G8; 
        ${shippingaddress}.Hausnummer = G9; 
        ${shippingaddress}.Postleitzahl = G10; 
        ${shippingaddress}.Ort = G11; 

 

Nun kann der Zugriff auf das Excel-Workbook erfolgen.

String filename = ".\\Excel Showcase.xlsx";

OperationDsl dsl = new OperationDsl();

ExcelPlugin excel = new ExcelPlugin();
FilePlugin file = new FilePlugin();

dsl.addPlugin(excel);
dsl.addPlugin(file);

Customer customer = new Customer();
List<Orderitem> orderitems = new LinkedList<Orderitem>();
BillingAddress billingAddress = new BillingAddress();
ShippingAddress shippingAddress = new ShippingAddress();

DslContext context = new DslContext();
context.addExternalParameter("dateiname", filename);
context.addExternalParameter("customer", customer);
context.addExternalParameter("orderitems", orderitems);
context.addExternalParameter("orderitem", new OrderitemFactory());
context.addExternalParameter("shippingaddress", shippingAddress);
context.addExternalParameter("billingaddress", billingAddress);

DslExpressions readOrder = dsl.parseFile("ShowCase.odsl", Locale.GERMAN);
readOrder.execute(context);

// Ab hier stehen die Daten in den Transportobjekten zur Verfügung

 

 

 

 

... 

Dieser Showcase soll den lesenden Zugriff auf ein XML Dokument darstellen. Die darin enthaltenen Daten sollen als Objekte dem umgebenden System bereitgestellt werden. Das XML Dokument ist hierbei wie folgt strukturiert:

<?xml version="1.0" encoding="UTF-8"?>
<Bestellung>
<Stammdaten 
        kundennummer = "HZ-3428.2623.1243"
        anrede = "Herr"
        vorname = "Max"
        nachname = "Mustermann"
        strasse = "Road to Hell"
        hausnummer = "666"
        postleitzahl = "66666"
        ort = "Helsinki"
/>
<Bestellpositionen>
<Position 
        lfdNr = "1"
        artikelnummer = "12430520"
        artikelbezeichnung = "iBirne Notebook"
        einzelpreis = "950,00"
        menge = "1"
        gesamtpreis = "950,00"
/>
<Position 
        lfdNr = "2"
        artikelnummer = "23498522"
        artikelbezeichnung = "Friedhof der Kuscheltiere"
        einzelpreis = "12,95"
        menge = "4"
        gesamtpreis = "51,80"
/>
<Position 
        lfdNr = "3"
        artikelnummer = "56930366"
        artikelbezeichnung = "Michaels Scheuermilch"
        einzelpreis = ""
        menge = ""
        gesamtpreis = ""
/>
<Position 
        lfdNr = "4"
        artikelnummer = "28582956"
        artikelbezeichnung = 'Badewanne "Gaby"'
        menge = "0"
/>
<Position 
        lfdNr = "5"
        artikelnummer = "45636262"
        artikelbezeichnung = "Fliegende Untertasse"
        einzelpreis = "10000000,00"
        menge = "-1"
        gesamtpreis = ""
/>
<Position 
        lfdNr = "6"
        artikelnummer = "87295296"
        einzelpreis = "0,55"
        menge = "1"
        gesamtpreis = "0,55"
/>
</Bestellpositionen>

<Lieferadresse>
        <vorname>J.</vorname>
        <nachname>Su&#233;z</nachname>
        <strasse>Stairway to Heaven</strasse>
        <hausnummer>7</hausnummer>
        <postleitzahl>77777</postleitzahl>
        <ort>Rom</ort>
</Lieferadresse>

<Rechnungsadresse></Rechnungsadresse>

</Bestellung>

Um die Daten adäquat abzubilden, sind zunächst die benötigten Transportobjekte zu definieren: 

  • Customer.java

public class Customer {
        private String kundennummer;
        private String anrede;
        private String vorname;
        private String nachname;
        private String strasse;
        private String hausnummer;
        private String postleitzahl;
        private String ort;

// + getter und setter
}

  • Orderitem.java

public class Orderitem {
        private Long lfdNr;
        private String artikelnummer;
        private String artikelbezeichnung;
        private Double einzelpreis;
        private Long menge;
        private Double gesamtpreis;

// + getter und setter
}

  • Address.java

public class Address {
        private String vorname;
        private String nachname;
        private String strasse;
        private String hausnummer;
        private String postleitzahl;
        private String ort;

// + getter und setter 
}

  • BillingAddress.java

public class BillingAddress extends Address {}

  • ShippingAddress.java

public class ShippingAddress extends Address {}

Für die Erzeugung von Orderitems nutzen wir eine Factory-Klasse:

  • OrderitemFactory.java

public class OrderitemFactory {
        public Orderitem createNew() {
               return new Orderitem();
        }
}

Danach kann in einer separaten Textdatei (Showcase.odsl) die DSL-Operationen beschrieben werden:

Stammdaten:
Für jedes Element  'Stammdaten' 
       in Datei    ${dateiname}  
führe aus 
        ${customer}.Kundennummer = [Element].kundennummer; 
        ${customer}.Anrede       = [Element].anrede; 
        ${customer}.Vorname      = [Element].vorname; 
        ${customer}.Nachname     = [Element].nachname; 
        ${customer}.Strasse      = [Element].strasse; 
        ${customer}.Hausnummer   = [Element].hausnummer; 
        ${customer}.Postleitzahl = [Element].postleitzahl; 
        ${customer}.Ort          = [Element].ort; 

Bestellpositionen:
Für jedes Element  'Position' 
               bei dem lfdNr         ist nicht leer 
                   und artikelnummer ist nicht leer 
                   und einzelpreis   ist nicht leer 
                   und menge         größer als 0 
      in Element  'Bestellpositionen' 
      in Datei    ${dateiname} 
führe aus 
        ${bestellposition}                    = ${orderitem}.createNew(); 
        ${bestellposition}.LfdNr              = [Element].lfdNr; 
        ${bestellposition}.Artikelnummer      = [Element].artikelnummer; 
        ${bestellposition}.Artikelbezeichnung = [Element].artikelbezeichnung; 
        ${bestellposition}.Einzelpreis        = [Element].einzelpreis; 
        ${bestellposition}.Menge              = [Element].menge; 
        ${bestellposition}.Gesamtpreis        = [Element].gesamtpreis; 
        ${orderitems}.add( ${bestellposition} ); 

Rechnungsadresse:
Für jedes Element  bei dem Elementtyp in ( 'vorname', 
                                           'nachname', 
                                           'strasse', 
                                           'hausnummer', 
                                           'postleitzahl', 
                                           'ort' )
       in Element  'Rechnungsadresse' 
       in Datei    ${dateiname} 
führe aus 
        ${attributname}                   = [Element].Elementtyp;
        ${billingaddress}.${attributname} = [Element].textContent; 

Lieferadresse:
Für jedes Element  bei dem Elementtyp in ( 'vorname', 
                                           'nachname', 
                                           'strasse', 
                                           'hausnummer', 
                                           'postleitzahl', 
                                           'ort' )
       in Element  'Lieferadresse' 
       in Datei    ${dateiname} 
führe aus 
        ${attributname}                    = [Element].Elementtyp;
        ${shippingaddress}.${attributname} = [Element].textContent;          

Nun kann der Zugriff auf die XML Datei erfolgen.

String filename = ".\\XML Showcase.xml";

OperationDsl dsl = new OperationDsl();

XmlPlugin xml = new XmlPlugin();
FilePlugin file = new FilePlugin();

dsl.addPlugin(xml);
dsl.addPlugin(file);

Customer customer = new Customer();
List<Orderitem> orderitems = new LinkedList<Orderitem>();
BillingAddress billingAddress = new BillingAddress();
ShippingAddress shippingAddress = new ShippingAddress();

DslContext context = new DslContext();
context.addExternalParameter("dateiname", filename);
context.addExternalParameter("customer", customer);
context.addExternalParameter("orderitems", orderitems);
context.addExternalParameter("orderitem", new OrderitemFactory());
context.addExternalParameter("shippingaddress", shippingAddress);
context.addExternalParameter("billingaddress", billingAddress);

DslExpressions readOrder = dsl.parseFile("ShowCase.odsl", Locale.GERMAN);
readOrder.execute(context);

// Ab hier stehen die Daten in den Transportobjekten zur Verfügung

 

 

 

 

...