Thema dieser Dokumentation / Das FirstSpirit 5 Modul- / Komponenten-Modell / Beispiel: Implementierung einer Eingabekomponente / ValueEngineer - Werte eines SwingGadgets behandeln
ValueEngineer - Werte eines SwingGadgets behandeln
Für die Behandlung der Werte, die innerhalb der neuen Eingabekomponente (CustomTextareaSwingGadget) gespeichert und bearbeitet werden können, muss anschließend eine Klasse bereitgestellt werden, die das Interface ValueEngineer<T> implementiert, wobei T immer der Daten-Container-Typ des zugehörigen EditorValue<T> (hier String) ist.
Listing: Beispiel ValueEngineer(Implementierung)
public class TextareaValueEngineer implements ValueEngineer<String> {
...
}
Die Implementierung TextareaValueEngineer dient dazu, Methoden für das Schreiben und Lesen des Persistenzobjekts der Eingabekomponente bereitzustellen. Dazu müssen die entsprechenden Methoden aus dem Interface ValueEngineer<T> implementiert werden (siehe Das Interface ValueEngineer<T>).
Das Laden der Werte (XML to Object) wird über die Methode T read(@NotNull List<Node> nodes) ausgeführt. Dazu wird vom FirstSpirit-Framework automatisch eine Liste von Knoten (Nodes) übergeben. Ein Objekt vom Typ Node ist ein Daten-Container, der einen Namen, Attribute (optional) und entweder einen textuellen Wert oder weitere Objekte vom Typ Node (Kind-Knoten) enthält. Die Implementierung der Methode für die Beispiel-Komponente ist denkbar einfach, da sie lediglich einen textuellen Wert berücksichtigen muss. Es muss also nur der Wert des ersten (und einzigen) Knotens als String zurückgeliefert werden. Über die Methode void setValue(@Nullable T value) aus dem Interface ValueHolder<T> (vgl. Aspekt: ValueHolder<T>) wird dieser String an das SwingGadget geliefert (vgl. Listing 31: Beispiel SwingGadget - Implementierung des Aspekts ValueHolder).
Listing: Beispiel ValueEngineer-Impl. – Methode read(…) – XML to Object
/**
* See implementation of {@link #write(String)} - we just
* return the text content of the first (and only) node
* in the provided list.
*/
@Nullable
public String read(@NotNull final List<Node> nodes) {
return nodes.get(0).getText();
}
Das Lesen der Werte aus dem SwingGadget (Object to XML) wird über die Methode T getValue() aus dem Interface ValueHolder<T> (vgl. Aspekt: ValueHolder<T>) ausgeführt (vgl. Listing 31: Beispiel SwingGadget - Implementierung des Aspekts ValueHolder). Für die Beispiel-Komponente liefert diese Methode einen String zurück, der an die Methode List<Node> write(@NotNull T value) weitergereicht wird. Die Methode write(…) muss den übergebenen String in ein Objekt vom Typ Node konvertieren und ihn innerhalb einer Liste von Knoten (Nodes) zurückliefern (Object to XML). Dazu wird zunächst ein neues Objekt vom Typ Node, mit einem spezifizierten Namen (hier: „txt“) und dem übergebenen String als textuellem Wert (value) erzeugt. Dieser Knoten wird anschließend innerhalb einer Liste von Knoten zurückgeliefert. Die Methode write(…) wird bei jedem Speichervorgang im SiteArchitect aufgerufen. Die Implementierung der read- und der write-Methode müssen zueinander passen. Das bedeutet, der Wert, der in write(…) geschrieben wird, muss in read(…) geladen werden können und umgekehrt.
Listing: Beispiel ValueEngineer-Impl. – Methode write(…) –Object to XML
/**
* We create a list with just one node with name {@code txt}
* and the text value as content.
*/
@NotNull
public List<Node> write(@NotNull final String value) {
return Collections.singletonList(Node.create("txt", value));
}
Für die Behandlung komplexer Datentypen werden die Methoden T getEmpty() und boolean isEmpty(@NotNull T value) benötigt. Komplexe Werte sind nie null, möglicherweise jedoch leer (siehe auch Wertetypen). Die Methode T getEmpty() erzeugt eine neue, leere Instanz eines Persistenztyps (Leerwert), beispielsweise eine leere Liste. Die Methode isEmpty(@NotNull T value) prüft, ob die Instanz eines komplexen Persistenztyps, beispielsweise eine Liste, leer ist oder nicht. Da die Methoden Bestandteil des Interfaces ValueEngineer<T> sind, müssen sie auch für einfache Persistenztypen implementiert werden. Analog zur Beispiel-Implementierung kann an dieser Stelle jedoch einfach null zurückgeliefert werden.
Listing: Beispiel ValueEngineer-Impl. – Leerwert-Behandlung
/**
* No special empty value, return just {@link null}.
*/
publicString getEmpty() {
return null;
}
/**
* No special empty value -> provided value cannot be empty ->
* return {@code false}.
*/
public boolean isEmpty(@NotNull final String value) {
return false;
}
Desweiteren muss die Methode T copy(@NotNull T original) implementiert werden, die eine neue Instanz des übergebenen Persistenztyps mit identischen Inhalten (Kopie) erzeugen soll. Da es sich beim Datentyp String der Beispiel-Implementierung um einen einfachen, unveränderlichen Datentypen handelt, kann an dieser Stelle auch die Original-Instanz zurückgeliefert werden. Für komplexe Wertetypen sollte aber immer eine Kopie der Original-Instanz zurückgeliefert und darauf geachtet werden, dass eine Änderung der Original-Instanz keine Auswirkung auf die Kopie hat:
Listing: Beispiel ValueEngineer-Impl. – Kopieren des Persistenztyps
/**
* {@link String} instances are immutable, so we can return the
* original value.
*/
@NotNull
public String copy(@NotNull final String original) {
return original;
}
Abhängig von der Implementierung des SwingGadgets, können Werte sprachabhängig gespeichert werden. In diesem Fall muss bei der weiteren Implementierung auch die gewünschte Zielsprache berücksichtigt werden. Die Sprachinformationen (und weitere Informationen zum Status eines Wertes) können über den ValueEngineerContext ermittelt werden (siehe Das Interface ValueEngineerContext<F extends GomFormElement>). Für die Ermittlung der Zielsprache ist das beispielsweise der Aufruf ValueEngineerContext.getLanguage(). |
Um die Stabilität des Interfaces ValueEngineer<T> zu gewährleisten, wird alle Funktionalität, die über die bisher implementierte Basisfunktionalität hinausgeht, über Aspekte realisiert (siehe Aspekte (ValueEngineer)). Die unterschiedlichen Aspekt-Typen stehen als Interface zur Verfügung. Die ValueEngineer-Implementierung muss die gewünschten Aspekte lediglich in der Methode <T> T getAspect(@NotNull AspectType<T> aspect) bekanntgeben und falls erforderlich, die entsprechenden Methoden des Interfaces implementieren. Das umliegende FirstSpirit-Gadget-Framework behandelt dann automatisch alle weiteren Funktionen.
Die Beispiel-Implementierung soll eine Suchfunktion mit Treffermarkierung unterstützen (siehe Aspekt Highlightable - Treffermarkierung für die Suche). Dazu müssen die im ValueEngineer enthaltenen Werte indiziert und Treffer, die dem definierten Suchmuster entsprechen, als Liste zurückgeliefert werde. Diese Funktionalität wird über den Aspekt MatchSupporting bereitgestellt (siehe Aspekt: MatchSupporting).
Durch Implementierung der Methode getAspect(…) muss zunächst eine eine aspektorientierte Instanz des Persistenztyps zurückgeliefert werden:
Listing: Beispiel ValueEngineer-Impl. – Erzeugen einer aspektorientierten Instanz
/**
* Supported aspects:
* <ul>
* <li>{@link MatchSupporting}</li>
* </ul>
*/
public <T> T getAspect(@NotNull final AspectType<T> aspect) {
if (aspect == MatchSupporting.TYPE) {
return aspect.cast(this);
}
return null;
}
Zusätzlich müssen die Methoden getStringForIndex(T value) und getMatches(Request request, T value) aus dem Interface MatchSupporting implementiert werden. Die erste Methode muss den übergebenen Wert in einen String transformieren, damit die Inhalte für die Suche indiziert werden können. Da die Beispiel-Implementierung bereits mit diesem Datentyp arbeitet, ist es hier ausreichend, den übergebenen Wert zurückzuliefern. Die zweite Methode soll eine Liste von Treffern liefern, die dem übergebenen Suchmuster entsprechen. Die Liste wird zur Visualisierung der Suchtreffer innerhalb des SwingGadgets (siehe Aspekt Highlightable - Treffermarkierung für die Suche) an die Methode highlight aus dem Interface Highlightable weitergereicht (Beispiel siehe Listing 47: Beispiel SwingGadget – der Aspekt Highlightable).
Listing: Beispiel ValueEngineer-Impl. – Value-Aspekt MatchSupporting
// MatchSupporting<String> aspect
@NotNull
public String getStringForIndex(@NotNull final String value) {
return value;
}
@NotNull
public List<? extends Match> getMatches(@NotNull final Request request,
@NotNull final String value) {
final List<Match> matches = new ArrayList<Match>();
for (final PatternClause clause : request.getClauses()) {
final Pattern pattern = request.toPattern(clause);
final Matcher matcher = pattern.matcher(value);
final boolean match = matcher.find();
if (match) {
matches.add(new Match(clause, matcher.start(), matcher.end()));
while (matcher.find()) {
matches.add(new Match(clause, matcher.start(), matcher.end()));
}
} else if (request.getJunction() == Junction.AND) {
return Collections.emptyList();
}
}
return matches;
}