|
Eigene Smart Elements

Grundsätzliches zur Plug-In-Entwicklung
Javanti erlaubt die Erstellung von Kursen. Ein Kurs besteht aus einer geordneten Sammlung von Folien. Eine Folie besteht nun wiederum aus einzelnen Elementen. Ein Element in Javanti ist einer Unterklasse von TAPElement. Die Menge der unterschiedlichen Elementtypen kann durch neue Subklassenbildung von TAPElment erweitert werden. Auf diese Weise lassen sich also neue Elemente implementieren und als Plug-In dynamisch zu Javanti hinzufügen. Plug-Ins sind demnach Unterklassen von TAPElement.
TAPElement ist eine abstrakte Klasse. Sie definiert eine Schnittstelle, die es der Javanti-Laufzeitumgebung erlaubt, alle Elemente gleich zu behandeln. Das spezialisierte Verhalten betrifft die Laufzeitumgebung von Javanti nicht, sondern ist eine Angelegenheit des Plug-In-Entwicklers.
Das Aussehen und Verhalten eines Elementes zur Laufzeit wird über die Eigenschaften eines Elementes festgelet. Eigenschaften beschreiben z.B. die Position, Farbe oder Größe eines Elementes. Andere Eigenschaften können z.B. festlegen, was geschieht, wenn das Element auf einer Javanti-Folie zur Laufzeit angeklickt wird.
Für TAP-Elemente können wir zwischen internen und veröffentlichten Eigenschaften unterscheiden. Die veröffentlichten Eigenschaften können vom Anwendungsbenutzer frei mit Werten belegt werden. Sie erscheinen zur Edit-Zeit in einem Objektinspektor, so dass die Werte geändert werden können. Interne Eigenschaften lassen sich dagegen nicht explizit durch den Anwendungsbenutzer setzen. Sie können und dürfen nur Plug-in-Klasse selbst verändert werden, z.B. wenn bestimmte Ereignisse eintreten.
Veröffentlichte Eigenschaften werden nicht direkt in Datenfeldern der Plug-In-Klasse gespeichert. Stattdessen benutzen wir Objekte vom Typ TAPPropertyGroup, um veröffentlichte Eigenschaften zu verwalten. Dabei werden jeweils mehrere semantisch zusammenhängende Eigenschaften in einer Eigenschaftsgruppe zusammengefasst. Diese speziellen Eigenschaftsgruppen werden durch Ableitung der abstrakten Mutterklasse TAPPropertyGroup angelegt.
Warum der ganze Aufwand? Die Auslagerung der veröffentlichten Eigenschaften in seperate Eigenschaftsklassen hat mehrere Vorteile. Erstens ist es dadurch möglich, Exemplare einer Eigenschaftsgruppe gemeinsam von mehreren Element-Exemplaren gemeinsam zu nutzen. So könnten z.B. mehrere Elemente auf der Tafel die gleiche Farbe zugewiesen bekommen. Ändert man nun für ein Element die Farbe, so ändert sich für alle anderen Elemente, die auf die gleiche Eigenschaftsgruppe zurückgreifen, ebenfalls die Farbe.
Ein weiterer Vorteil der Eigenschaftsgruppen liegt darin, dass Sie für Eigenschaften, die in mehreren Elementtypen auftauchen, keine Codeverdopplung erzeugen. So müssen Sie z.B. das Speichern, Setzen und Auslesen einer Eigenschaft nur einmalig in der Eigenschaftsgruppe implementieren - und nicht in allen Elementsubklassen.
Sie können für Ihr Plug-In aus einem Pool bereits vorhandener Eigenschaftsgruppen die geeigneten auswählen, oder Sie definieren zusätzlich eigene Eigenschaftsgruppen.
Sowohl die internen als auch die veröffentlichten Eigenschaften beschreiben den Zustand eines Elementes. Ein Element ist aber zunächst nur eine logische Dateneinheit auf einer Javanti-Folie. Zur grafischen Visualisierung des Elementes wird eine Swing-Komponente benötigt. Jedes Element braucht mindestens eine Swing-Komponente für seine graphische Repräsentation. Swing-Komponenten sind Unterklassen von JComponent. Sie können zur graphischen Repräsentation entweder bereits existierende Swing-Komponenten (JButton, JLabel, JTable usw.) verwenden oder eine eigene Komponente entwickeln. Die Swing-Komponente darf beliebig komplex sein und sogar eigene Unterkomponenten enthalten.
Eine Swing-Komponente übernimmt nicht nur die graphische Visualisierung sondern reagiert auch auf GUI-Ereignisse wie Mausklicks oder Tastatureingaben. Sie können das Verhalten einer Swing-Komponente verändern, indem Sie die entsprechenden Event-Listener austauschen. Wie sich die Swing-Komponente, die zu Ihrem Element gehört, verhalten soll, hängt zum einem davon ab, ob sich Javanti gerade in der Edit-Zeit oder in der Laufzeit befindet. Zum anderen könnten bestimmte Eigenschaftswerte das Verhalten verändern. Dies hängt von den Eigenschaften ab, die Sie für ein Plug-In festlegen möchten.
Vorgehensweise beim Plug-In-Entwickeln
- Stellen Sie zunächst eine Liste aller Eigenschaften und Datenobjekte auf, die ihr Element benutzen wird - egal ob intern oder öffentlich.
- Kristallisieren Sie die Eigenschaften heraus, die veröffentlicht werden sollen. Diese Eigenschaften lassen sich später im ObjecetInspector von Javanti oder über TCL-Befehle verändern.
- Überprüfen Sie, ob es für die öffentlichen Eigenschaften bereits TAPPropertyGroups im TAP-Paket gibt, die Sie zur Datenhaltung verwenden können. Für grundlegende Eigenschaften wie Textinhalt (PGTextContent) oder Farbe (PGColor) gibt es bereits Implementierungen.
- Für die übrig gebliebenen öffentlichen Eigenschaften gilt es zu überlegen, welche Eigenschaften aufgrund ihrer logischen Bedeutung in einer Eigenschaftsgruppe zusammengefaßt werden können. Die Positionseigenschaften x und y sind z.B. in PGLocation zusammengefaßt, da sie unmittelbar zusammengehören.
- Implementieren Sie die benötigten Eigenschaftsgruppen, indem Sie eine Klasse von TAPPropertyGroup ableiten. Als Grundlage können Sie die Vorlage PGTemplate.java verwenden. Diese Datei enthält den Java-Quellcode für eine Eigenschaftsgruppe und kennzeichnet die Stellen, an denen Sie Code ergänzen müssen. Statt der Vorlage-Datei können Sie auch die Beispiel-Eigenschaftsgruppe PGMinimal.java kopieren, umbenennen und dann modifzieren.
- Die grafische Ausgabe eines Elementes erfolgt über eine Swing-Komponente. Entscheiden Sie sich für eine bereits im Paket javax.swing vorhandene Komponente oder erstellen Sie eine neue Komponente, indem Sie von JComponent bzw. deren Unterklassen ableiten.
- Defineren Sie Event-Listener, das Verhalten während der Editzeit und während der Laufzeit festlegen und auf Ereignisse reageren. Häufig werden die Event-Listener für die Laufzeit von der TAPElement-Subklasse selbst implementiert. D.h. Ihr Plug-In erweitert nicht nur TAPElement sondern implementiert auch die Schnittstellen für Event-Listener.
- Implementieren Sie das eigentliche Plug-In, indem Sie von der abstrakten Klasse TAPElement ableiten und eine konkrete Subklasse implementieren. In dieser Klasse verwenden Sie die benötigten Eigenschaftsgruppen, die gewünschte Swing-Komponente und alle Event-Listener und legen diese in Datenfeldern der Klasse ab. Als Basis können Sie die Vorlage TAPTemplateElement.java. Diese Datei enthält den Java-Quellcode für ein neues TAPElement und kennzeichnet die Stellen, an denen Sie Ihren eigenen Code einfügen müssen. Alternativ können Sie auch das Beispiel-Element TAPMinimalElement.java kopieren, umbenennen und dann beliebig modifizieren.
Der Anfang
Wir werden jetzt die einzelnen Schritte bei der Plug-In-Programmierung verfolgen und beispielhaft ein Plug-In entwickeln. Dieses Plug-In wird nur mit minimalen Funktionen ausgestattet sein - deshalb nennen wir es Minimal-Element. Auf alle Fälle genügt das Element aber den Ansprüchen der Javanti-Laufzeitzumgebung und ist somit ein gültiges Plug-In. Sie können es daher als Grundlage für Ihre eigenen Entwicklungen verwenden.
Das Beispiel-Element zeichnet innerhalb seiner Ausmaße ein Oval und gibt textuell aus, welches das letze Mausereigniss der Komponente war und wie oft es angeklickt wurde.
Aus Demonstrationszwecken entscheiden wir uns dafür, eine eigene Swing-Komponente zu entwickeln.Schritt 1: Festlegung, welche Eigenschaften unser Element unterstützen soll:
x = x-Position des Elementes relativ zur Fenstergröße (in Prozent)
y = y-Position des Elementes relativ zur Fenstergröße (in Prozent)
dragable = Flag, ob das Element zur Laufzeit veschoben werden darf.
visible = Flag, ob das Element gerade sichtbar ist
width = Breite des Elementes
height = Höhe des Elementes
statement = beliebige Textausgabe
isBold = gibt an, ob dieser Text fett ausgegeben werden soll
offset = gibt die relative horizontale Postion des Fetts innerhalb
der Swing-Komponente an
clickCounter = Anzahl der Klicks in die Komponente
lastMouseEvent = Information über das letzte Mausereignis.
Eigenschaften
Für unser Beispielelement wollen wir alle Eigenschaften außer clickCounter und lastMouseEvent als öffentliche Eigenschaften verwalten. Für die Eigenschaften x,y,dragable und visible gibt es bereits die Eigenschaftsgruppe PGLocation. Die Eigenschaften width und height sind schon in der Eigenschaftsgruppe PGSize enthalten. Die Eigenschaftsgruppen PGLocation und PGSize sind bereits Datenfelder der absrtrakten Basisklasse TAPElement, da eigentlich jedes Element Angeben zur Position und Größe verwalten muss.
Für die Eigenschaften statement, isBold und offset müssen wir eine neue Eigenschaftsgruppe entwickeln. Die Eigenschaften clickCounter und lastMouseEvent sind interne Eigenschaften eines Elementes und werden nicht in einer Eigenschaftsgruppe verwaltet.
Implementierung einer neuen Eigenschaftsgruppe.
Klassenrumpf und Klassenfile
Die Eigenschaften statement, isBold und offset sollen in einer Eigenschaftsgruppe zusammengefasst werden. Wir nennen diese Eigenschaftsgruppe PGMinimal. PGMinimal.java können Sie hier herunterladen. Sie können diese Klasse als Grundlage für Ihre eigenen Eigenschaftsgruppen verwenden, indem Sie den Klassennamen und die einzelnen Methoden verändern. Sie können aber auch die Vorlage PGTemplate verwenden, in der alle Stellen markiert sind, an denen Sie noch etwas verändern müssen.
Als erstes beginen wir mit dem Grundgerüst einer Klassendatei. Wir deklarieren die Paketzugehörigkeit, so dass unser Plug-In zum Paket TAPplugins gehört.
Alle neuen Eigenschaftsgruppen müssen zum Paket TAPplugins gehören, da nur in diesem Ordner nach neuen Eigenschaftsgruppen gesucht wird! Als nächstes imortieren wir die benötigten Klassen. Dazu gehören natürlich die Klassen aus dem Paket TAP. In diesem Paket befindet sich unter anderem die Basisklasse TAPPropertyGroup. Außerdem importieren wir NoSuchElementException, da wir diese Aussahme ggf. in einer der Klassenmethoden aufrufen werden. Dazu später mehr. Eine Eigenschaftsgruppe wird stets von TAPPropertyGroup abgeleitet. Der Name der Klasse entspricht stets dem Namen der Eigenschaftsgrupp mit einem vorangestellten "PG". Die Laufzeitumgebung von Javanti berücksichtigt nur neue Eigenschaftsgruppen, die im Verzeichnis TAPplugins liegen und deren Klassenname mit dem Präfix "PG" beginnt.
// Eigenschaftsgruppen für Plugins gehören grundsetzlich in das
// Paket TAPplugins:
package TAPplugins;
// Das TAP-Paket muss importiert werden, damit Sie von der Klasse
// TAPPropertyGroup ableiten können
import TAP.*;
import java.util.NoSuchElementException;
public class PGMinimal extends TAPPropertyGroup {
}
Datenfelder
Der leere Klassenrumpf kann nun mit Datenfeldern und Methoden gefüllt werden. Wir betrachten zunächst die Datenfelder und anschließend alle Methoden, die implementiert werden müssen.
Die benötigten Datenfelder ergeben sich aus den Eigenschaften, die wir in dieser Eigenschaftsgruppe speichern wollen. Für jede Eigenschaft legen wir drei Datenfelder an:
- ein konstantes, statisches Datenfeld, in dem ein Defaultwert gespeichert wird (Datenfeld wird dürch Präfix "default" gekennzeichnet)
- ein Datenfeld für den aktuellen Eigenschaftswert (Datenfeld wird durch Postfix "Curr" gekennzeichet)
- ein Datenfeld für den zwischengespeicherten Startwert einer Präsentation (Datenfeld wird durch Postfix "Start" gekennzeichnet)
Alle drei Datenfelder müssen vom gleichen Typ sein. Sie können den Typ des Datenfeldes, in dem Sie eine Eigenschaft speichern, frei wählen. Das konstante Datenfeld definiert den voreingestellten Wert einer Eigenschaftsgruppe. Mit diesem Wert wird der aktuelle Wert einer Eigenschaft (abgelegt im Curr-Datenfeld) initialisiert. Beim Speichern werden nur die aktuellen Werte berücksichtigt, die vom voreingestellten Wert abweichen. Das Start-Datenfeld wird benötigt, um den aktuellen Wert beim Starten eines Kurses zwischenzuspeichern. Da es während des Kurses wahrscheinlich ist, dass sich der aktuelle Eigenschaftswert verändert, muss es eine Möglichkeit geben, den ursprünglichen Wert wiederherzustellen.
Für unsere drei Eigenschaften statement, isBool und offset deklarieren wir zunächst die Defaultwerte:
// Defaultwerte für alle Eigenschaften festlegen:
static private String defaultStatement = "I am Minimal.";
static private boolean defaultIsBold = false;
static private float defaultOffset = 0.0f;
Beachten Sie, dass die Defaultwerte in static-Felder abgelegt werden, da sie für alle Exemplare der Klasse gleichbleiben.
Als nächtes legen wir die Datenfeld für die aktuellen und die Startwerte fest. Der aktuelle Wert wird mit dem Defaultwird initialisiert:
// Datenfelder für aktuelle und zwischengespeicherte Eigenschaftswerte:
private String statementStart, statementCurr = defaultStatement;
private boolean isBoldStart, isBoldCurr = defaultIsBold;
private float offsetStart, offsetCurr = defaultOffset;
Zu jeder Eigenschaft müssen wir außerdem eine Eigenschaftsbeschreibung anlegen. Diese Eigenschaftsbeschreibungen werden vom Javanti-Objektinspektor angefordert, um die Eigenschaften einer Eigenschaftsgruppe anzuzeigen. Die Beschreibung umfasst den Namen der Eigenschaft, den Typ der Eigenschaft, ggf. den Wertebereich und alternative Wertausprägungen.
Eine Eigenschaft wird beschrieben durch ein Exemplar der Klasse TAPPropertyInformation. Sie erzeugen eine neue Instanz der Klasse durch Aufruf des öffentlichen Konstruktors, der als Parameter die Beschreibung enthält:
// aus TAPPropertyInformation.java:
// public TAPPropertyInformation(String propName, int propType,
// Integer minValue, Integer maxValue, Enumeration alternatives)
| propName | Name der Eigenschaft, z.B. "statement" |
| propType | Typ der Eigenschaft, der Typ wird durch eine in der Klasse TAPPropertyGroup festgelegte Konstante angebeben, z.B. TAPPropertyGroup.INTEGER_TYPE |
| minValue | kleinster Wert für Eigenschaftstypen, die einen Wertebereich benutzen, z.B. new Integer (-42) für Eigenschaften, die keinen festen Wertebereich benutzen wird ein null-Zeiger übergeben |
| maxValue | größter Wert für Eigenschaftstypen, die einen Wertebereich benutzen, z.B. new Integer (53) für Eigenschaften, die keinen festen Wertebereich benutzen wird ein null-Zeiger übergeben |
| alternatives | Hier übergeben Sie entweder eine leere Enumeration oder - wenn Sie als Eigenschaftstyp TAPPropertyGroup.ENUMERATION_TYPE wählen - eine Enumeration, die Strings enthält. Jeder String enthält einen frei definierbaren Eigenschaftswert, z.B. "blau", "Giraffe", "house coffee" oder was immer zu Ihrer speziellen Eigenschaft passt. Sie können einen leere Enumeration mit AlternativesIterator.getEmptyIterator() erhalten oder eine Enumeration mit new AlternativesIterator(String[] alternatives) erzeugen. |
Die Namen der Eigenschaften unserer Eigenschaftsgruppe haben wir ja bereits festgelegt. Jetzt müssen wir uns nur noch für passende Typen entscheiden. Die Eigenschaft statement soll den Typ TAPPropertyGroup.STRING_TYPE besitzen. isBoolean wird den Typ TAPPropertyGroup.BOOEAN_TYPE erhalten. Die Eigenschaft offset ist in einem Datenfeld vom Typ float abgelegt. Es wäre daher möglich, als Javanti-Typ TAPPropertyGroup.REAL_TYPE zu verwenden. Javanti bietet aber auch speziellere Datentypen an. Unter anderem gibt es den Typ TAPPropertyGroup.REALSCALE_TYPE, der es zusätzlich erlaubt, einen Wertebereich festzulegen. Für die untere und obere Wertegrenze erzeugen wir zwei Integer-Objekte, die wir als Parameter dem TAPPropertyInformation-Konstruktor übergeben.
Für unsere drei Eigenschaften erzeugen wir also drei Eigenschaftsbeschreibungen. Da die Eigenschaftsbeschreibungen für alle Exemplare dieser Klasse gleich und unveränderlich bleiben, werden sie in statischen, konstanten Datenfeldern abgelegt. Die Datenfelder werden durch das Präfix "propertyInfo" gekennzeichnet.
// Einmaliges Erzeugen der Eigenschaftsbeschreibungen:
private final static TAPPropertyInformation propertyInfoStatement =
new TAPPropertyInformation(
"statement",STRING_TYPE,null,null,
AlternativesIterator.getEmptyIterator());
private final static TAPPropertyInformation propertyInfoIsBold =
new TAPPropertyInformation(
"isBold",BOOLEAN_TYPE,null,null,
AlternativesIterator.getEmptyIterator());
private final static TAPPropertyInformation propertyInfoOffset =
new TAPPropertyInformation(
"offset",REALSCALE_TYPE,new Integer (-100), new Integer(100),
AlternativesIterator.getEmptyIterator());
Damit haben wir alle nötigen Datenfelder angelegt. Als nächstes widmen wir uns den zu implementierenden Methoden.
Konstruktoren
Zu erst legen wir zwei Konstruktoren für die Klasse an. Wir definieren einen leeren Konstruktor mit dem Qualifizierer public, damit die Laufzeitumgebung von Javanti neue Exemplare von der Eigenschaftgruppe erzeugen kann:
public PGMinimal() {}
Ein zweiter Konstruktor dient dazu, Kopien von einer Eigenschaftsgruppe zu erzeugen. Dieser Konstruktor soll nur von der zur Klasse gehörenden Methode getACopy() aufgerufen werden; er wird daher als private qualifziert. Der Konstruktor erhält als Parameter die aktuellen Eigenschaften des Original-Exemplars, welches eine Kopie von sich erzeugt. Die Parameter entsprechen also den Eigenschaften dieser Eigenschaftsgruppe. Im Konstruktor-Rumpf werden die Eigenschaften den Datenfeldern zugewiesen:
private PGMinimal(String statement, boolean isBold, float offset) {
statementCurr = statement;
isBoldCurr = isBold;
offsetCurr = offset;
}
Implementieren der abstrakten Methoden
Der gerade definierte Konstruktor wird getACopy() aufgerufen. Diese Methode liefert eine Kopie des Exemplars durch Aufruf des privaten Konstruktors mit den aktuellen Eigenschaftswerten:
public TAPPropertyGroup getACopy() {
return new PGMinimal (statementCurr, isBoldCurr , offsetCurr);
}
Wenn eine Präsentation gestartet wird, dann müssen die aktuellen Eigenschaftswert gesichert werden, damit sie nach Beenden der Präsentation wieder zurückgesetzt werden können. Die Werte der curr-Felder werden daher in die start-Felder kopiert:
public void restartRuntime() {
statementStart = statementCurr;
isBoldStart = isBoldCurr;
offsetStart = offsetCurr;
}
Nach dem Beenden der Präsentation müssen die in den start-Feldern gesicherten Werte wieder in die aktuellen curr-Felder zurückgeschrieben werden, da sich die curr-Werte währende der Präsentation verändert haben können. Diese Veränderungen sollen aber nicht mehr nach der Präsentation gelten.
public void resetEdittime() {
statementCurr = statementStart;
isBoldCurr = isBoldStart;
offsetCurr = offsetStart;
}
Die Javanti-Umgebung erfragt zur Identifikation den Namen einer Eigenschaftsgruppe. Dieser Name entspricht dem Klassennamen ohne das vorangestellte "PG":
public String getPropertyGroupName() {
return "Minimal";
}
Zum Speichern einer Eigenschaftsgruppe ruf die Javanti-Umgebung die Methode getAttrStr() auf. Sie soll einen String zurückliefern, der XML-Tags für alle Eigenschaften, deren aktueller Wert ungleich dem Defaultwert ist, enthält. Das Format für das XML-Tag ist folgendes:
<Property name='PROPERTYNAME'> <![CDATA[PROPERTYVALUE]]> </Property>
PROPERTYNAME ist dabei der Name der Eigenschaft und PROPERTYVALUE ist der aktuelle Wert der Eigenschaft in String-Form. Sie können dieses Tags selbst erzeugen, in der Regel greifen Sie aber auf die Hilfsklassen aus TAPPropertGroup zurück:
| final public void addBooleanPropertyToBuffer(String propertyName, boolean currentValue , boolean defaultValue, StringBuffer attributBuffer) | Erzeugt ein neues xml-Tag für die Eigenschaft properyName, wenn currentValue und defaultValue ungleich sind. Das neue Tag wird an den String-Buffer attributBuffer gehängt. |
| final public void addFloatPropertyToBuffer(String propertyName, float currentValue , float defaultValue, float epsilon , StringBuffer attributBuffer) | Erzeugt ein neues xml-Tag für die Eigenschaft properyName, wenn currentValue und defaultValue ungleich sind. epsilon gibt dabei an, innerhalb welcher Differenz zwei Float-Werte als gleich angesehen werden. Das neue Tag wird an den String-Buffer attributBuffer gehängt. |
| final public void addIntegerPropertyToBuffer(String propertyName, int currentValue , int defaultValue, StringBuffer attributBuffer) | Erzeugt ein neues xml-Tag für die Eigenschaft properyName, wenn currentValue und defaultValue ungleich sind. Das neue Tag wird an den String-Buffer attributBuffer gehängt. |
| final public void addStringPropertyToBuffer(String propertyName, String currentValue , String defaultValue, StringBuffer attributBuffer) | Erzeugt ein neues xml-Tag für die Eigenschaft properyName, wenn currentValue und defaultValue ungleich sind. Das neue Tag wird an den String-Buffer attributBuffer gehängt. |
Sie wählen die Hilfsmethode abhängig vom Typ des Datenfeldes, in dem die Eigenschaft abgelegt wurde.
Zuvor erzeugen Sie noch einen String-Buffer, der dann ggf. um die einzelen xml-Tags erweitert wird. Für unserere Eigenschaftsgruppe sieht das so aus:
public String getAttrStr() {
StringBuffer attrStr = new StringBuffer(512);
addStringPropertyToBuffer("statement",statementCurr,
defaultStatement,attrStr);
addBooleanPropertyToBuffer("isBold",isBoldCurr,
defaultIsBold,attrStr);
addFloatPropertyToBuffer("offset",offsetCurr,defaultOffset,
0.000001f,attrStr);
return attrStr.toString();
}
Javanti verändert die Eigenschaften eines Elementes durch Aufruf von TAPElement.setProperty (String propertyName, String propertyValue). Diese Methode delegiert das Setzen der Eigenschaft weiter an die Eigenschaftsgruppen. Jede Eigenschaftsgruppe muss daher ebenfalls setProperty(...) implementieren. In dieser Methode muss in Abhängigkeit des gegebenen popertyName die entsprechende Eigenschaft verändert werden. Enthält propertyName den Namen einer Eigenschaft, die nicht in dieser Eigenschaftsgruppe abgelegt ist, dann gibt die Methode false zurück. Konnte die Eigenschaft erfolgreich gesetzt werden, gibt die Methode true zurück.
Da die Javanti-Umgebung setProperty(...) für alle Eigenschaften - egal welchen Typs - aufruft, wird der Eigenschaftswert in einer allgemeinen Form übergeben, und zwar als String-Objekt. Da wir für unsere Eigenschaften innerhalb unserer Subklasse passendere Typen für die Datenfelder der Eigenschaften gewählt haben, müssen wir den String in den entprechenden Datentyp umwandeln. Die Methode sieht für unser Beispiel so aus:
public boolean setProperty(String propertyName, String propertyValue) {
if (propertyName.equals("statement")) {
statementCurr = propertyValue;
return true;
}
if (propertyName.equals("isBold")) {
isBoldCurr = Boolean.valueOf(propertyValue).booleanValue();
return true;
}
if (propertyName.equals("offset")) {
offsetCurr = Float.valueOf(propertyValue).floatValue();
return true;
}
// für den Fall, dass propertyName eine Eigenschaft benennt,
// die nicht zu dieser Eigenschaftsgruppe gehört, geben wir false zurück
return false;
}
Für jede Eigenschaft gibt es also ein if-Statement, das immer gleich aussieht. Zunächst wird der propertyName verglichen. Bei Übereinstimmung wird der Eigenschaftswert in den erwarteten Datentyp umgewandelt und die Methode liefert true als Rückgabewert.
Das Gegenstück zu setProperty(..) ist getProperyt(String propertyName). In Abhängigkeit des übergebenen Eigenschaftsnamens wird der passende Eigenschaftswert als String zurückgegeben. Wenn die Eigenschaft nicht in dieser Eigenschaftsgruppe ist, wird der null-Pointer zurückgegeben. Für jede Eigenschaft gibt es als wieder ein if-Statement. Wird ein passender Name gefunden, so wird der String-Wert des Datenfeldes zurückgeliefert.
public String getProperty(String propertyName) {
if (propertyName.equals("statement"))
return statementCurr;
if (propertyName.equals("isBold"))
return String.valueOf(isBoldCurr);
if (propertyName.equals("offset"))
return String.valueOf(offsetCurr);
// für den Fall, dass propertyName eine Eigenschaft benennt,
// die nicht zu dieser Eigenschaftsgruppe gehört, geben wir null zurück
return null;
}
Der Objektinspektor von Javanti muss zur Laufzeit herausfinden können, welche Eigenschaften in einer Eigenschaftsgruppe verwaltet werden. Dazu muss er auf die Eigenschaftsbeschreibungen zurückgreifen. Die Methode getPropertyCount() legt fest, wie viele Eigenschaften in einer Gruppe gespeichert werden und zur Verfügung stehen. Mit propertyAt(int index) kann dann auf die einzelnen Eigenschaftsbeschreibungen zugegriffen werden.
Die Basisklasse TAPPropertyGroup bietet außerdem eine Defaultimplementierung von getProperties(). Dabei wird eine Enumeration zurück geliefert, die alle Eigenschaftsgruppen enthält. Dabei werden die Methoden getPropertyCount() und propertyAt(...) ebenfalls von der Enumeraion-Klasse verwendet.
Da unsere Eigenschaftsgruppe drei Eigenschaften enthält, liefern wir den konstaten Wert "3" zurück:
public int getPropertyCount() {
return 3; // statement, isBold und offset ---> 3 Properties
}
Die Methode propertyAt(int index) liefert innerhalb je nach gewähltem Wert eine andere Eigenschaftsbeschreibung. Dies erreichen wir über einen switch-Verteiler, der für alle definierten index-Werte einen case-Block bestizt, in dem eine bestimmte Eigenschaftbeschreibung zurückgegeben wird. Die Beschreibungen hatten wir ja in Datenfelder des Typs TAPPropertyInformation gespeichert, so dass wir einfach die Referenzen liefern:
public TAPPropertyInformation propertyAt(int index) {
switch (index) {
case 0: return propertyInfoStatement;
case 1: return propertyInfoIsBold;
case 2: return propertyInfoOffset;
default: throw new NoSuchElementException(
"PGLocation contains only n property groups!");
} // end switch
}
Damit haben wir alle geforderten Methoden implementiert. Es sind keine abstrakten Methoden mehr übrig und somit haben wir eine gültige Subklasse für eine Eigenschaftsgruppe erstellt.
Wir können nun noch das Verhalten der Eigenschaftsgruppe verändern, indem wir die Default-Implementierungen der Basisklasse überschreiben. In unserem Beispiel wollen wir dies für die Methode isInterpolatable() durchführen. Da die Eigenschaft offset die Position des Informations-Textes innerhalb des Ausgabe-Komponente angibt, eignet sie sich für die automatische Animation. Sie kann zwischen einem Start- und Endwert interpoliert werden. Dazu müssen wir die Interpolation zu lassen, was durch Überschreiben der Methode isInterpolatable geschieht:
public boolean isInterpolatable() {
return
true;
}
Eine weitere Methode, die manchmal überschrieben werden muss, ist getInspectorStrategyClassName(String propertyName). Sie muss dann implementiert werden, wenn wir für eine unserer Eigenschaften den Javanti-Typ TAPPropertyGroup.USER_TYPE gewählt hätten. Der Objektinspektor benutzt diese Methode unter Angabe des Eigenschaftsnamens, um zu erfragen, welche Anzeige-Strategie verwendet werden soll, um die Eigenschaft zu manipulieren.
Da wir in unserem Beispiel keine benutzerdefinierten Typen verwendet haben, müssen wir die Methode nicht implementieren. Eine Ausführliche Diskussion über benutzerdefinierte Eigenschaften findet sich hier.
Zu guter letzt erweitern wir unsre Subklasse um spezielle get-Methoden, um einen direkten Zugriff auf die Eigenschaftswerte anzubieten. Dies ist nicht erforderlich, aber oft sehr hilfreich. Da die Elemente, die diese Eigenschaftsgruppe verwenden, die spezialisierte Schnittstelle der Eigenschaftsgruppe kennen, können Sie die Eigenschaftswerte direkt auslesen. Dies führt zu einem erheblichen Performance-Gewinn, da die Eigenschaftswerte nicht mehr als String sondern direkt ausgelesen werden:
public String getStatement() { return statementCurr; }
public boolean getIsBold() { return isBoldCurr; }
public float getOffset() { return offsetCurr; }
So, damit ist die Eigenschaftsgruppe fertig implementiert. Der nächste Schritt ist die Entwicklung einer eigenen Komponente.
|