Die Generic Widget Factory bietet die folgenden Möglichkeiten zum Austauschen und Dekorieren von Widgets:
Eine registrierte Widget Implementierung durch eine andere ersetzen
Eine registrierte Widget Implementierung dekorieren
Eine registrierte Widget Factory dekorieren
Diese Möglichkeiten sollen Anhand eines durchgängigen Beispiels erläutert werden:
Für ein existierendes Produkt wird bei einem Neukunden Akquise gemacht. Das Produkt bietet die Möglichkeit, Entitäten und deren Beziehungen individuell zu definieren, um diese in einer Datenbank zu verwalten und Workflows auf den Daten durchzuführen. Nachdem der Kunde eine Demoversion des Produktes getestet, und dabei einige für ihn spezifische Masken für Maschinenteile und Baugruppen modelliert hat, kommt der folgende Wunsch auf:
In seinem System werden sehr viele komplexe technische Parameter Bezeichnungen und Identifikationsnummern als Attributname für Teile und Baugruppen verwendet. Der Kunde würde diese Informationen gelegentlich gerne in einem Legacy System für Suchanfragen verwenden. Dies gilt sowohl für die Attributnamen als auch für den Inhalt. Da die Attributnamen derzeit mit Hilfe von Text Label Widgets angezeigt werden, kann man Sie nicht in die Zwischenablage kopieren, sondern muss diese abschreiben. Die folgende Abbildung zeigt eine für den Kunden typische Datenmaske für ein Maschinenteil[35]:
Die Anpassung sollte so kostengünstig wie möglich sein (Kundenwunsch), und möglichst wenig Auswirkungen auf das zugrundeliegende Produkt und somit Systeme bei andere Kunden haben (Wunsch des Auftragnehmer). Dem Kunden werden die folgende Lösungsmöglichkeiten angeboten:
Alle Text Label werden durch ein ITextField ersetzt,
welche wie ein Text Label aussehen, weil sie keinen Border
haben und die Hintergrundfarbe vom Parent Container erben.
Diese sind readonly, können also nicht geändert werden, es
ist aber möglich, den Text zu markieren und so in die
Zwischenablage zu kopieren. Dies könnte umgesetzt werden,
indem die registrierte Widget Implementierung für
ITextLabelBluePrint
durch eine andere
Implementierung
ersetzt wird.
Alle Text Label erhalten ein Kontextmenü, welches eine
Copy to clipboard Aktion enthält.
Dies könnte umgesetzt werden, indem für
ITextLabelBluePrint
die registrierte
Implementierung des
Widget dekoriert
wird.
Der Kunde stellt beide Lösungen einer Gruppe von Benutzern vor, wobei der eine Teil die Erste, der andere Teil die zweite Lösung bevorzugt. Der Kunde fragt, ob man auf einfache und kostengünstige Weise beide Lösungen anbieten können.
Es wird angeboten, das man in den Einstellungen einen
Parameter hinzufügt, der das Verhalten festlegt. Um die Lösung
möglichst kostengünstig zu halten, wird vom Kunden akzeptiert,
das sich die Änderung nur auf neu erstellte Masken und nicht
auf bereits erzeugte auswirken, und somit u.U. ein Neustart
der Client Applikation notwendig ist. Da laut Nutzer dieser
Parameter nach einmaliger Konfiguration in der Regel nicht
mehr geändert wird, stellt dies für den Kunden kein Problem
dar. Diese Lösung könnte umgesetzt werden, indem für
ITextLabelBluePrint
die
Widget Factory
dekoriert wird.
Die folgende Klasse implementiert die Schnittstelle
ITextLabel
mit Hilfe eines Textfeldes:
1 public final class TextFieldLabel extends ControlWrapper implements ITextLabel { 2 3 private final ITextControl textField; 4 5 public TextFieldLabel(final ITextControl textField) { 6 super(textField); 7 this.textField = textField; 8 } 9 10 @Override 11 public void setFontSize(final int size) { 12 textField.setFontSize(size); 13 } 14 15 @Override 16 public void setFontName(final String fontName) { 17 textField.setFontName(fontName); 18 } 19 20 @Override 21 public void setMarkup(final Markup markup) { 22 textField.setMarkup(markup); 23 } 24 25 @Override 26 public void setText(final String text) { 27 textField.setText(text); 28 } 29 30 @Override 31 public String getText() { 32 return textField.getText(); 33 } 34 35 }
Die meisten Methoden werden vom [Control Wrapper] geerbt, die
anderen werden an das textField
delegiert.
Die folgende Factory erzeugt Instanzen dieser TextFieldLabel’s:
1 public final class TextFieldLabelFactory
2 implements IWidgetFactory<ITextLabel, ITextLabelBluePrint> {
3
4 @Override
5 public ITextLabel create(
6 final Object parentUiReference,
7 final ITextLabelBluePrint textLabelBp) {
8
9 final ITextFieldBluePrint textFieldBp = BPF.textField();
10
11 textFieldBp.setSetup(textLabelBp);
12 textFieldBp.setBorder(false).setEditable(false).setInheritBackground(true);
13
14 final ITextControl textField = Toolkit.getWidgetFactory().create(
15 parentUiReference,
16 textFieldBp);
17
18 return new TextFieldLabel(textField);
19 }
20
21 }
In Zeile 9 wird ein neues BluePrint für ein Text Field
erzeugt. In Zeile 11 wird das Setup des Textlabels auf dem
BluePrint des Texfeldes gesetzt. Dabei werden alle Properties,
welche das Textfeld und das Textlabel gemeinsam haben (wie zum
Beispiel die Vordergrundfarbe, die Schriftart, etc,) auch für
das Textfeld übernommen. In Zeile 14 wird dann ein Texfeld mit
Hilfe des textFieldBp
erzeugt. Dieses wird
der Klasse TextFieldLabel
übergeben, welche
die Schnittstelle ITextLabel
implementiert
(sie weiter oben).
Der folgende Code tauscht in der
Generic Widget
Factory die aktuelle Implementierung für Text Labels
durch die TextFieldLabelFactory
aus. Dies
passiert mit Hilfe eines
Toolkit
Interceptors:
1 @Override
2 public void onToolkitCreate(final IToolkit toolkit) {
3 toolkit.getWidgetFactory().unRegister(ITextLabelDescriptor.class);
4
5 toolkit.getWidgetFactory().register(
6 ITextLabelDescriptor.class,
7 new TextFieldLabelFactory());
8 }
Die folgende Abbildung zeigt den Effekt:
Durch dass globale Austauschen der Label Implementierung wird nun für alle Labels ein Textfeld für die Darstellung verwendet. Insbesondere lassen sich die Attribute nun markieren und zum Beispiel per STRG-C in die Zwischenablage kopieren.
Bei der zweiten Variante soll allen Labels ein Kontextmenü
hinzufügen, welche eine Copy Action enthält, die den Text des
Labels in die Zwischenablage kopiert. Um dies umzusetzen wird
zuerst ein IDecorator<ITextLabel>
wie
folgt implementiert:
1 public final class LabelPopupDecorator implements IDecorator<ITextLabel> { 2 3 @Override 4 public ITextLabel decorate(final ITextLabel original) { 5 final IMenuModel copyMenu = new MenuModel(); 6 7 final IActionItemModelBuilder copyActionBuilder = ActionItemModel.builder(); 8 copyActionBuilder 9 .setText("Copy to Clipboard") 10 .setToolTipText("Copies the label text to the system clipboard") 11 .setIcon(IconsSmall.COPY); 12 13 final IActionItemModel copyAction = copyMenu.addItem(copyActionBuilder); 14 copyAction.addActionListener(new IActionListener() { 15 @Override 16 public void actionPerformed() { 17 Clipboard.setContents(new StringTransfer(original.getText())); 18 } 19 }); 20 21 original.setPopupMenu(copyMenu); 22 return original; 23 } 24 }
Der Dekorierer wird dann mit Hilfe eines Toolkit Interceptors wie folgt hinzugefügt:
1 @Override
2 public void onToolkitCreate(final IToolkit toolkit) {
3 toolkit.getWidgetFactory().addWidgetDecorator(
4 ITextLabelDescriptor.class,
5 new LabelPopupDecorator());
6 }
Die folgende Abbildung zeigt den Effekt:
Alle Labels haben jetzt ein Kontextmenü zum Kopieren des Labelinhalts.
Nun soll noch gezeigt werden, wie man mit Hilfe eines Widget Factory Decorator je nach Konfiguration die eine oder andere Variante wählen kann.
1 public final class LabelFactoryDecorator 2 implements IDecorator<IWidgetFactory<ITextLabel, ITextLabelBluePrint>> { 3 4 private final LabelPopupDecorator labelPopupDecorator; 5 private final TextFieldLabelFactory textFieldLabelFactory; 6 7 //will be injected 8 private ILabelConfig labelConfig; 9 10 public LabelFactoryDecorator() { 11 this.textFieldLabelFactory = new TextFieldLabelFactory(); 12 this.labelPopupDecorator = new LabelPopupDecorator(); 13 } 14 15 @Override 16 public IWidgetFactory<ITextLabel, ITextLabelBluePrint> decorate( 17 final IWidgetFactory<ITextLabel, ITextLabelBluePrint> originalFactory) { 18 19 //no config or default type so use original 20 if (labelConfig == null || LabelType.DEFAULT == labelConfig.getLabelType()) { 21 return originalFactory; 22 } 23 //use TextFieldLabel 24 else if (LabelType.TEXT_FIELD == labelConfig.getLabelType()) { 25 return textFieldLabelFactory; 26 } 27 //use copy action 28 else if (LabelType.COPY_ACTION == labelConfig.getLabelType()) { 29 return new IWidgetFactory<ITextLabel, ITextLabelBluePrint>() { 30 @Override 31 public ITextLabel create( 32 final Object parentUiReference, 33 final ITextLabelBluePrint descriptor) { 34 35 //create the original widget with the original factory 36 final ITextLabel originalWidget = originalFactory.create( 37 parentUiReference, 38 descriptor); 39 40 //decorate the popup menus 41 return labelPopupDecorator.decorate(originalWidget); 42 } 43 }; 44 } 45 else { 46 throw new IllegalStateException( 47 "Lable type '" + labelConfig.getLabelType() + "' is not supported."); 48 } 49 } 50 51 }
Dieser Dekorierer kann wie folgt registriert werden:
1 @Override
2 public void onToolkitCreate(final IToolkit toolkit) {
3 toolkit.getWidgetFactory().addWidgetFactoryDecorator(
4 ITextLabelDescriptor.class,
5 new LabelFactoryDecorator());
6 }
Das Überschreiben und Dekorieren aus den beiden vorigen Abschnitten wird dadurch obsolet.