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.