Datentypen & Aufzählungen in Faktor-IPS

Definition von Aufzählungen

Bei der Definition von Aufzählungen in Faktor-IPS unterscheiden wir explizit zwischen

  • Aufzählungen, bei denen die Werte Teil des Modells bzw. des Programms sind und

  • Aufzählungen, bei denen die Werte Teil der Konfiguration sind und das Modell/Programm unabhängig von den einzelnen Werten ist.

  • Aufzählungen, die beides können. Also Werte die Teil des Modells sind und zusätzlich in der Konfiguration erweitert werden können.

Umgesetzt ist dies durch die Trennung in Aufzählungstypen (Enumeration Types) und Aufzählungsinhalten (Enumeration Contents). Im Aufzählungstyp werden die Attribute festgelegt. Optional kann der Aufzählungstyp auch die Werte enthalten. In diesem Fall sind die Werte Teil des Modells und können im Programmcode referenziert werden (s. Aufzählungen). Der Codegenerator generiert hierfür dann einen entsprechenden Java-Enum mit einem Literal pro Wert der Aufzählung. Die konkreten Werte der Aufzählung können aber auch separat in einem eigenen Aufzählungsinhalt verwaltet werden. In diesem Fall generiert Faktor-IPS lediglich eine Klasse mit den entsprechenden Zugriffsmethoden für die Attribute. Zur Laufzeit können natürlich die Werte eines Aufzählungstyps abgefragt werden, es gibt aber keine generierten Literale (bzw. Konstanten). Im Programmcode sollte es keine Vergleiche auf konkrete einzelne Werte geben.

Definition von Aufzählungstypen mit Werten

Aufzählungen in Faktor-IPS können beliebig viele Attribute haben. Eines der Attribute muss als (default) ID gekennzeichnet werden. Die ID verwendet Faktor-IPS intern zur Identifikation verwendet. Darüber hinaus kann sie an anderen Stellen zur Identifikation verwendet werden, zum Beispiel bei der Persistierung in Datenbanken. Da hierfür prinzipiell auch ein anderes Attribut als Identifier verwendet werden kann, bezeichnen wir das von Faktor-IPS verwendete Attribut als „Default Identifier“.

Darüber hinaus muss eines der Attribute als Name (Bezeichnung) gekennzeichnet werden. Dieses Attribut wird in Faktor-IPS zur Anzeige der Werte verwendet, z. B. bei der Festlegung von Defaultwerten von Attributen oder im Editor für Produktbausteine.

Als ersten Aufzählungstyp werden wir die Zahlweise wie folgt definieren. Der Java Literal wird dabei automatisch aus der Bezeichnung generiert:

Table 1. Zahlweisen
ID (Fachliche) Bezeichnung Anzahl Zahlungen Pro Jahr Java Literal

m

Monatlich

12

MONATLICH

q

Quartalsweise

4

QUARTALSWEISE

h

Halbjährlich

2

HALBJAEHRLICH

j

Jährlich

1

JAEHRLICH

e

Einmalzahlung

EINMALZAHLUNG

Wählen Sie nun im Package-Explorer den Modellordner des Projektes und klicken Sie auf new enum type button in der Toolbar. Es öffnet sich der folgende Wizard:

Wizard zum Anlegen eines neuen Aufzählungstyps
Figure 1. Wizard zum Anlegen eines neuen Aufzählungstyps

Geben Sie als Namen des Typs Zahlweise ein. Die Felder Supertype und Abstract betrachten wir später. Die beiden Checkboxen Create ID-Attribute und Create Name-Attribute sind standardmäßig angehakt, so dass gleich die beiden benötigten Attribute (s.o.) erzeugt werden. Die einzelnen Zahlweisen sollen im ersten Schritt direkt im Aufzählungstyp definiert werden.

Aus diesem Grund bleibt die Checkbox Erweiterbar durch separaten Inhalt abgehakt. Klicken Sie nun auf Fertigstellen, um den Aufzählungstyp zu erzeugen. Es öffnet sich der Editor zur Bearbeitung des Aufzählungstyps.

Editor für Aufzählungstypen
Figure 2. Editor für Aufzählungstypen

Im Abschnitt Attributes werden die Attribute des Aufzählungstyps angezeigt. Der Wizard hat hier die beiden Attribute id und name, sowie ein zusätzliches Attribut namens LITERAL_NAME angelegt. Die konkreten Werte für das Attribut LITERAL_NAME verwendet der Faktor-IPS Codegenerator als Namen für die generierten Literale bzw. Konstanten im generierten Sourcecode, es kann daher auch nicht gelöscht (sehr wohl aber verschoben) werden. Als erstes fügen wir nun das Attribut anzahlZahlungenProJahr hinzu. Nach Klicken auf New…​ öffnet sich der folgende Dialog:

Dialog zum Bearbeiten eines Attributes
Figure 3. Dialog zum Bearbeiten eines Attributes

Geben Sie den Namen und den Datentyp des Attributs gemäß der Abbildung ein. Die folgende Tabelle beschreibt die Bedeutung der einzelnen Checkboxen:

Mehrsprachige Werte

Kennzeichnet ein Attribut als Mehrsprachiger String der in der entsprechenden Spalte des Werts in den Sprachen des Projekts übersetzt werden kann.

Zur Identifizierung verwenden (default)

Das Attribut wird als Identifier intern in Faktor-IPS verwendet [6].

Als Faktor-IPS Anzeigenamen verwenden

Das Attribut wird zur Anzeige der Werte in Faktor-IPS verwendet.

Eindeutiger Kennzeichner

Die Werte dieses Attributes sind eindeutig über alle Werte der Aufzählung. Der Wert kann hierdurch also identifiziert werden. Der Codegenerator generiert für solche Attribute eine entsprechende Zugriffsmethode getValueByAttributeName(…​).

Aus Supertyp-Hierarchie geerbt

Kennzeichnet das Attribut als vom Supertype geerbt. Dazu später mehr.

Für das neu angelegte Attribut anzahlZahlungenProJahr bleiben die Checkboxen alle abgehakt. Standardmäßig hat der Wizard das Attribut id als Default Identifier markiert und das Attribute name als Display Name.

Nach dem Schließen des Dialogs mit OK sind nun im Abschnitt Enumeration Values vier Spalten mit den Namen der Attribute zu sehen. Durch Drücken auf wird eine Zeile für den ersten Aufzählungswert angelegt. Die weiteren Zeilen werden beim Tabben automatisch erzeugt. Bevor Sie mit der Eingabe der Werte beginnen, schieben Sie das Attribut LITERAL_NAME durch Klicken auf Down noch eine Position nach unten. Geben Sie nun die Zahlweisen gemäß Tabelle 1 ein.

Die Spalte LITERAL_NAME wird automatisch belegt, sobald Sie die Spalte name verlassen. Das Resultat sollte wie folgt aussehen:

Aufzählungswerte für Zahlweise
Figure 4. Aufzählungswerte für Zahlweise

Am oberen rechten Rand des Abschnitts finden sich, neben dem Button zum Anlegen neuer Zeilen, noch weitere Schaltflächen:

enum new row

Neue Zeile anlegen.

enum delte row

Markierte Zeilen löschen.

enum row up

Verschiebt markierte Zeilen um eine Position nach oben.

enum row down

Verschiebt markierte Zeilen um eine Position nach unten.

enum sync literal

Sperrt bzw. entsperrt die Spalte LITERAL_NAME. Die Werte können dann nicht mehr manuell geändert werden, sondern werden immer von einer anderen Spalte [7]. bezogen und mit dieser auch synchron gehalten. Diese Option ist standardmäßig deaktiviert.

Nach dem Speichern hat Faktor-IPS den folgenden Sourcecode generiert. Der Übersichtlichkeit halber sind in dem folgenden Abschnitt die Javadocs entfernt und lediglich der Code für die Werte Jährlich und Einmalzahlung abgebildet.

@IpsDocumented(bundleName = "org.faktorips.beispiel.modell.modell-label-and-descriptions", defaultLocale = "de")
@IpsEnumType(name = "Zahlweise", attributeNames = { "id", "name", "anzahlZahlungenProJahr" })
public enum Zahlweise implements EnumValue {

    JAEHRLICH("j", "J\u00E4hrlich", Integer.valueOf(1)),
    EINMALZAHLUNG("e", "Einmalzahlung", null)

    private final String id;
    private final String name;
    private final Integer anzahlZahlungenProJahr;

    @IpsGenerated
    private Zahlweise(String id, String name, Integer anzahlZahlungenProJahr) {
        this.id = id;
        this.name = name;
        this.anzahlZahlungenProJahr = anzahlZahlungenProJahr;
    }

    @IpsEnumAttribute(name = "id", identifier = true, unique = true)
    @IpsGenerated
    public String getId() {
        return id;
    }

    @IpsEnumAttribute(name = "name", unique = true, displayName = true)
    @IpsGenerated
    public String getName() {
        return name;
    }

    @IpsEnumAttribute(name = "anzahlZahlungenProJahr")
    @IpsGenerated
    public Integer getAnzahlZahlungenProJahr() {
        return anzahlZahlungenProJahr;
    }

    @Override
    @IpsGenerated
    public String toString() {
        return "Zahlweise: " + id + '(' + name + ')';
    }
}

Für jedes Attribut wurde eine entsprechende Membervariable und Gettermethode generiert. Für jeden Aufzählungswert ein entsprechendes Literal.

Darüber hinaus wurden für jedes als eindeutig gekennzeichnetes Attribut zwei Methoden generiert. Die erste ermittelt den Aufzählungswert zu einer ID und die zweite Methode prüft, ob eine gegebene ID einen Aufzählungswert identifiziert. Der folgende Codeabschnitt zeigt dies für das Attribut id and name.

static {
    ID_MAP = new HashMap<>();
    for (Zahlweise value : values()) {
        ID_MAP.put(value.id, value);
    }
}

@IpsGenerated
public static final Zahlweise getValueById(String id) {
    return ID_MAP.get(id);
}

@IpsGenerated
public static final boolean isValueById(String id) {
    return getValueById(id) != null;
}

@IpsGenerated
public static final Zahlweise getValueByName(String name) {
    for (Zahlweise currentValue : values()) {
        if (currentValue.name.equals(name)) {
            return currentValue;
        }
    }
    return null;
}

@IpsGenerated
public static final boolean isValueByName(String name) {
    return getValueByName(name) != null;
}

Separate Definition der Aufzählungswerte

Als Beispiel verwenden wir die in der KFZ-Versicherung häufig als Risikomerkmal verwendete Art des in der Nacht verwendeten Parkplatzes. Die folgende Tabelle enthält die zulässigen Werte für die Parkplatzart.

Table 2. Parkplatzarten
ID Name

1

Straße

2

Stellplatz

3

Garage

4

Parkhaus

Da es nicht erforderlich ist, im Programmcode auf einzelne Parkplatzarten abzufragen und wir der Fachabteilung ermöglichen wollen, die Liste der Parkplatzarten zu pflegen, definieren wir dieses mal, dass die Werte des Aufzählungstyps Parkplatzart in einem separaten Aufzählungsinhalt verwaltet werden.

Markieren Sie zunächst wieder den Modellordner im Package-Explorer und starten Sie über new enum type button den Wizard, um einen neuen Aufzählungstyp anzulegen. In dem Dialog haken Sie diesmal die Checkbox Erweiterbar durch separaten Inhalt an und vergeben noch einen Namen für die Aufzählungsinhalt. I.d.R. bietet es sich an ihn identisch zum Aufzählungstypen zu nennen. Die folgende Abbildung zeigt den ausgefüllten Dialog:

Ausgefüllter Wizard zum Anlegen der Parkplatzart
Figure 5. Ausgefüllter Wizard zum Anlegen der Parkplatzart

Nach dem Klicken von Fertigstellen öffnet sich wieder der Editor zur Definition des Aufzählungstyps. Der Aufzählungstyp Parkplatzart hat keine weiteren Attribute, daher sind wir mit der Definition des Typs auch schon fertig.

Der folgende Abschnitt zeigt den generierten Sourcecode, wieder ohne Javadoc. Da die Werte im Modell nicht bekannt sind, wird statt einem Java Enum eine Klasse generiert. Für jedes Attribut wird wieder eine Membervariable und eine Gettermethode generiert. Der protected Konstruktor wird von der Faktor-IPS Runtime (per Reflection) verwendet, der public Konstruktor kann zum Beispiel in Testfällen verwendet werden.

@IpsEnumType(name = "Parkplatzart", attributeNames = { "id", "name" })
@IpsExtensibleEnum(enumContentName = "Parkplatzart")
public final class Parkplatzart implements Serializable, Comparable<Parkplatzart> {

    public static final long serialVersionUID = 1L;

    private final int index;
    private final String id;
    private final String name;
    private final IRuntimeRepository productRepository;

    @IpsGenerated
    protected Parkplatzart(int index, String idString, String nameString, IRuntimeRepository productRepository) {
        this.index = index;
        this.id = idString;
        this.name = nameString;
        this.productRepository = productRepository;
    }

    @IpsGenerated
    public Parkplatzart(int index, String id, String name) {
        this.index = index;
        this.id = id;
        this.name = name;
        this.productRepository = null;
    }

    @IpsEnumAttribute(name = "id", identifier = true, unique = true)
    @IpsGenerated
    public String getId() {
        return id;
    }

    @IpsEnumAttribute(name = "name", unique = true, displayName = true)
    @IpsGenerated
    public String getName() {
        return name;
    }

    @Override
    @IpsGenerated
    public String toString() {
        return "Parkplatzart: " + id + '(' + name + ')';
    }

    @IpsGenerated
    Object getEnumValueId() {
        return id;
    }
}

Bevor wir die Frage klären, wie wir programmatisch an die Werte der Aufzählung kommen, definieren wir erst einmal die konkreten Parkplatzarten. In diesem Artikel legen wir den Aufzählungsinhalt im gleichen Projekt an wie den Aufzählungstyp. Damit eine Fachabteilung die Werte unabhängig vom Modell bearbeiten kann, würde man den Aufzählungsinhalt in der Regel in einem Projekt anlegen, in dem ausschließlich die Produktdaten verwaltet werden, und nicht direkt im Modellprojekt. Details können dem Einführungstutorial und dem Tutorial zur Modellpartitionierung entnommen werden.

Den Aufzählungsinhalt legen wir durch Klicken auf new enum content button in der Toolbar an. Falls der Sourcefolder nicht vorbelegt ist, wählen Sie den Sourcefolder (i.d.R. Projektname/model) aus. Ansonsten brauchen Sie lediglich noch den Aufzählungstyp (hier: Parkplatzart) auszuwählen. Der Name des Aufzählungsinhalts und das Package, in dem er liegt, werden aus dem Aufzählungstyp entnommen und können auch nicht geändert werden.

Wizard zum Anlegen eines neuen Aufzählungsinhalts
Figure 6. Wizard zum Anlegen eines neuen Aufzählungsinhalts

Nach dem Klicken von Fertigstellen öffnet sich der Editor zum Bearbeiten der Aufzählungswerte. Dies funktioniert genauso wie im Editor für Aufzählungstypen (mit der Ausnahme, dass es für Aufzählungsinhalte nie eine Literalnamen-Spalte gibt). Geben Sie nun die Parkplatzarten gemäß Tabelle 2 ein und speichern Sie. Danach sollte der Editor so aussehen, wie es in der folgenden Abbildung dargestellt ist.

Editor für Aufzählungswerte
Figure 7. Editor für Aufzählungswerte

Für die Parkplatzarten wird nun – natürlich - kein Java Sourcecode generiert, da der Programmcode unabhängig von den konkreten Werten sein soll. Damit auf die Aufzählungswerte zur Laufzeit zugegriffen werden kann, hat der Generator statt dessen ein XML-File mit den Aufzählungswerten im Java Sourcefolder derived angelegt.

Die Werte eines Aufzählungstyps kann man über das Faktor-IPS RuntimeRepository über die Methode getEnumValues(Class<T> enumType) abfragen. Der folgende Sourcecode gibt die Parkplatzarten auf der Konsole aus.

public static void main(String[] args) {
    // Repository erzeugen
    IRuntimeRepository repository = ClassloaderRuntimeRepository.create(
            "org/faktorips/sample/model/internal/faktorips-repository-toc.xml");
    // Parkplatzarten aus dem Repository abfragen ...
    List<Parkplatzart> arten = repository.getEnumValues(Parkplatzart.class);

    // ... und auf der Konsole ausgeben
    for (Parkplatzart parkplatzart : arten) {
        System.out.println(parkplatzart.toString());
    }
}

Die Ausgabe des Programm sieht wie folgt aus:

Parkplatzart: 1(Straße)
Parkplatzart: 2(Stellplatz)
Parkplatzart: 3(Garage)
Parkplatzart: 4(Parkhaus)

Intuitiver wäre die Ermittlung der Werte eines Aufzählungstyps über eine statische Methode an der generierten Klasse, die dann an das Repository delegiert, wie folgt:

public final static List<Parkplatzart>getAllValues(IRuntimeRepository rp) {
    return rp.getEnumValues(Parkplatzart.class);
}

Wenn diese Aufzählungsklassen aber zur Remote-Kommunikation über Data Transfer Objects verwendet werden, müssen diese dazu auch auf der Clientseite bekannt sein. Das RuntimeRepository steht dagegen im Client nicht zur Verfügung. Aus diesem Grund darf es keine Compileabhängigkeit der Aufzählungsklassen zum RuntimeRepository geben.


6 Verwendet man die Option zur Generierung von JAXB Annotationen, so werden bei Aufzählungstypen mit separatem Aufzählungsinhalt die Werte dieses Attributes im XML abgelegt.
7 Welche Spalte zur Belegung des Literalnamens verwendet wird, kann eingestellt werden. Klicken Sie hierzu auf das Literalnamen-Attribut im Abschnitt Attributes und dann auf Bearbeiten…​. Es erscheint ein Dialog mit der Option Beziehe Vorbelegungswerte von. Hiermit kann eingestellt werden, von welcher Spalte die Literalnamen bezogen werden.