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:
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 in der Toolbar. Es öffnet sich der folgende Wizard:
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.
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:
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 |
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 |
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:
Am oberen rechten Rand des Abschnitts finden sich, neben dem Button zum Anlegen neuer Zeilen, noch weitere Schaltflächen:
Neue Zeile anlegen. |
|
Markierte Zeilen löschen. |
|
Verschiebt markierte Zeilen um eine Position nach oben. |
|
Verschiebt markierte Zeilen um eine Position nach unten. |
|
Sperrt bzw. entsperrt die Spalte |
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.
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 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:
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 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.
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.
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.