Beispiel 7.10. Synchrones Erzeugen eines FontManagers
Dieses Beispiel zeigt die Erstellung einer FontManager Instanz. Die ID ist
                                            ein frei wählbarer eindeutiger String, der zur späteren Identifikation
                                            (beispielsweise in Log-Ausgaben) dient. Die Erzeugung erfolgt synchron auf dem
                                            aktuellen Thread.
                     
String id = "any unique identifier";
FontManagerFuture future = FontManagers.startBuilding(id)
        .synchronously()
        .withSystemFonts()
        .finish();
FontManager fontManager = future.get();| 
                                     
  | 
                                 
                                     Da die Erzeugung synchron auf dem aktuellen Thread erfolgte, kehrt dieser Aufruf sofort zurück.  | 
                              
Beispiel 7.11. Asynchrones Erzeugen eines FontManagers
Ähnlich zum vorhergehenden Beispiel wird hier ebenfalls ein FontManager
                                            erzeugt. Inhalt und ID sind identisch. Die Erzeugung erfolgt jedoch asynchron im
                                            Hintergrund.
                     
String id = "any unique identifier"; FontManagerFuture future = FontManagers.startBuilding(id) .asynchronously() .withSystemFonts() .finish(); …FontManager fontManager = future.get();
| 
                                     
  | 
                                 
                                     Während der FontManager im Hintergrund erzeugt wird, kann auf dem aktuellen Thread weiterer Code ausgeführt werden.  | 
                              
| 
                                     
  | 
                                 
                                     Der Aufruf kehrt erst zurück, wenn die Erzeugung im Hintergrund vollständig abgeschlossen ist.  | 
                              
Beispiel 7.12. Erzeugen eines FontManagers mit den Standard-14 Fonts
Das folgende Beispiel zeigt die Erzeugung eines FontManagers, der Open
                                            Source Alternativen für die allgemein gebräuchlichen Standard-14 Fonts enthält.
                                            Die jar-Datei welche die Font-Dateien
                                            beinhaltet, muss dabei auf dem Klassenpfad liegen.
                     
FontManagers.startBuilding("Standard-14 Font Manager") .asynchronously() .withStandard14() .finish();
Beispiel 7.13. Erzeugen eines FontManagers aus verschiedenen Quellen
Der folgende Quelltext zeigt einige Möglichkeiten zur Definition der Inhalte
                                            eines FontManagers. Die zu verwendeten Font-Dateien können auf verschiedene
                                            Wege bekannt gemacht werden.
                     
FontManagers.startBuilding("big Font Manager") .asynchronously() .withStandard14() .withSystemFonts() .with(Paths.get("path/to/local/fonts")) .with(new File("additional/local/fonts")) .with(new FileInputStream("some/folder/arial.ttf")) .finish();
Beispiel 7.14. Typisches Pattern zur Implementation einer FontFactory
Das Beispiel zeigt das Grundgerüst einer typischen FontFactory
                                            Realisierung. Die asynchrone Erzeugung des FontManagers sollte generell
                                            möglichst frühzeitig im Programmablauf angestoßen werden. Der Einfachheit halber
                                            wird sie hier im Konstruktor dargestellt. Alternativ könnte das
                                            FontManagerFuture dem Konstruktor von außen übergeben werden. Da der Aufbau
                                            des FontManagers im Hintergrund läuft, kann parallel dazu die weitere
                                            Initialisierung der Anwendung durchgeführt werden.
                     
Der Abruf des FontManagers vom FontManagerFuture erfolgt zum
                                            spätestmöglichen Zeitpunkt – also dann, wenn ein Font benötigt wird damit ein
                                            Dokument geladen werden kann. Falls die asynchrone Erzeugung des FontManagers
                                            zu diesem Zeitpunkt bereits abgeschlossen ist, entsteht keine zusätzliche
                                            Wartezeit. Andernfalls pausiert der Dokument-Ladevorgang bis der FontManager
                                            zur Verfügung steht. In diesem Fall kann es zu einmaligen, initialen
                                            Verzögerungen in der Dokumentanzeige kommen.
                     
public class ExampleFontFactory implements FontFactory {
  final FontManagerFuture fontManagerFuture;
  public ExampleFontFactory() {
    fontManagerFuture = FontManagers.startBuilding("example font manager")
        .asynchronously()
        .withStandard14()
        .finish();
  }
  @Override
  public Font create(FontAttributeSet fontAttributeSet, Map<String, Object> scope) {
    Font resultingFont;
    try {
      final FontManager fontManager = fontManagerFuture.get();
      resultingFont = findTheBestFont(fontManager, fontAttributeSet, scope);
    } catch (InterruptedException | ExecutionException e) {
      resultingFont = new BasicFontFactory().create(fontAttributeSet, scope);
    }
    return resultingFont;
  }
  private Font findTheBestFont(FontManager fontManager, 
      FontAttributeSet fontAttributeSet,
      Map<String, Object> scope) {
    …                                                                          
  }
}| 
                                     
  | 
                                 
                                     Implementations-spezifische Logik um einen geeigneten Font zu ermitteln.  | 
                              
Beispiel 7.15. Nutzung der AbstractFontFactory
Die AbstractFontFactory bietet Methoden, welche bei der Implementierung
                                            einer eigenen FontFactory hilfreich sind. Unter anderem beinhaltet diese
                                            Methoden zum Laden einer FontSource basierend auf einem oder mehreren
                                            Attribute, oder auch das Laden einer Font 
                     
Im Beispiel werden die Methoden der AbstractFontFactory verwendet, um eine
                                            Font basierend auf dem PostScript-Name aufzulösen. Falls keine Font aufgelöst
                                            werden kann, wird null zurückgegeben um zu signalisieren,
                                            dass das Handling von einer anderen FontFactory übernommen werden
                                            soll.
                     
public class CalibriFontFactory extends AbstractFontFactory {
  protected CalibriFontFactory(FontManagerFuture fontManagerFuture) {
    super(fontManagerFuture);
  }
  @Override
  public Font create(FontAttributeSet fontAttributeSet, Map<String, Object> scope) {
    if (fontAttributeSet.contains(postScriptName("Calibri"))) {
      return getFontFor(postScriptName("Carlito")).orElse(null);
    } else {
      // let other FontFactories deal with this request
      return null;
    }
  }
}Beispiel 7.16. Verwendung der ChainedFontFactory
Die ChainedFontFactory ist ein Hilfsmittel zur Trennung von Zuständigkeiten
                                            in FontFactory-Implementationen. Der folgende Quelltext zeigt die Erstellung
                                            einer ChainedFontFactory, die im Nachgang für Dokument-Ladevorgänge nutzbar
                                            ist.
                     
Die ChainedFontFactory nimmt in ihrem Konstruktor weitere
                                            FontFactory-Instanzen entgegen. Sie reagiert auf Font-Anfragen, indem sie
                                            diese nacheinander an die übergebenen Instanzen delegiert. Sobald ein Wert
                                            ungleich null vorliegt, wird dieser als Ergebnis der
                                            ChainedFontFactory zurückgegeben. Die verbleibenden Instanzen werden nicht
                                            mehr befragt.
                     
Da die FontFactory, die für einen Ladevorgang registriert ist, zwingend
                                            einen validen Font zurückgeben muss, sollte dies durch die übergebenen Instanzen
                                            sichergestellt werden. Die ChainedFontFactory selbst nimmt keine Validierung
                                            vor. Dies bedeutet, dass die angefragten Instanzen null
                                            zurückgeben dürfen um damit zu signalisieren, dass weitere Instanzen befragt
                                            werden sollen. Die letzte FontFactory muss jedoch zwingend einen Font
                                            zurückgeben damit das Gesamtergebnis nicht null wird. Im
                                            Beispiel kommt die BasicFontFactory zum Einsatz, die unabhängig von der
                                            Anfrage stets einen Fallback-Font zurückliefert.
                     
FontManagerFuture fontManagerFuture = …;
FontFactory fontFactory = new ChainedFontFactory(
    new FuzzyStandard14FontFactory(fontManagerFuture),
    new FuzzyStyleFontFactory(fontManagerFuture),
    new BasicFontFactory()                                                     
);| 
                                     
  | 
                                 
                                     Die BasicFontFactory gibt immer einen Font zurück. Da die FontFactory, die für den Lesevorgang registriert ist, immer einen Font zurückgeben muss, empfiehlt es sich eine Chain zu verwenden, in der die BasicFontFactory als letzte Instanz registriert ist.  | 
                              
Beispiel 7.17. Generieren von Log-Meldungen via Logger
Zur Entwicklungszeit und im Fehlerfall ist es hilfreich, Informationen darüber
                                            zu erhalten, welche Entscheidungen im Font-Auswahlprozess getroffen werden.
                                            Insbesondere wenn eine ChainedFontFactory zum Einsatz kommt, steigt die
                                            Gesamtkomplexität durch das Zusammenspiel der verschiedenen Klassen. Das
                                            Beispiel zeigt, wie Log-Ausgaben bei Bedarf angefordert werden können. Falls
                                            kein Logging aktiviert ist, wird der Logging-Code nie durchlaufen und es
                                            entsteht somit kein unnötiger Mehraufwand.
                     
FontFactory fontFactory = …;
Logger logger = LoggerFactory.getLogger(getClass());
if (logger.isDebugEnabled()) {
  fontFactory = new ActivateLoggingFontFactory(fontFactory, logger);           
}| 
                                     
  | 
                                 
                                     Falls der verwendete Logger mindestens auf Level DEBUG steht, wird das Logging von Anfragen an die FontFactory aktiviert. Die ActivateLoggingFontFactory umhüllt eine andere FontFactory und initialisiert das Generieren von Log-Meldungen.  | 
                              
Beispiel 7.18. Generieren von Log-Meldungen mit frei definierbarem Ziel
Ähnlich wie im vorhergehenden Beispiel werden auch hier Log-Meldungen
                                            generiert. Zusätzlich wird die Möglichkeit genutzt, das Ziel der Log-Strings
                                            selbst zu bestimmen. Es muss nicht zwingend ein Logger verwendet werden.
                                            Stattdessen kommt hier der Standard Error Stream zum Einsatz. Auch Logging in
                                            eine Datenbank oder ein anderes entferntes System wäre möglich. Da der Code
                                            während des Ladevorgangs ausgeführt wird, muss dafür gesorgt werden, dass
                                            möglichst wenig Zeit investiert wird. Langlaufende Operationen, wie
                                            beispielsweise eine Netzwerk-Kommunikation, müssen auf einen anderen Thread
                                            ausgelagert werden.
                     
FontFactory fontFactory = …;
LogSink systemErrLogSink = new LogSink() {                                     
  @Override
  public void log(String logMessage) {
    System.err.println(logMessage);
  }
};
String loggingFontFactoryID = "My Logging Font Factory";                       
final ActivateLoggingFontFactory activateLoggingFontFactory = 
    new ActivateLoggingFontFactory(fontFactory, 
        systemErrLogSink, 
        loggingFontFactoryID);| 
                                     
  | 
                                 
                                     Ziel der Log-Ausgaben. Die anonyme Klasse leitet alle Ausgaben nach System.err weiter, könnte aber auch andere Ziele ansprechen.  | 
                              
| 
                                     
  | 
                                 
                                     Optionaler, frei wählbarer Identifikations-String. Insbesondere falls mehrere FontFactory Instanzen existieren, können sie durch diese ID in den Log-Ausgaben identifiziert werden.  | 
                              
| 
                                     
  | 
                                 
                                     Umhüllen der eigentlichen FontFactory um Log-Ausgaben zu aktivieren. Sämtliche Aufrufe, die über die ActivateLoggingFontFactory laufen, generieren automatisch Log-Meldungen.  | 
                              
Beispiel 7.19. Setzen der FontFactory für einen Lesevorgang
Das Beispiel zeigt, wie eine FontFactory für alle folgenden Lesevorgänge
                                            einer Reader-Instanz gesetzt werden kann. Für andere Reader-Instanzen muss
                                            die unabhängig davon erneut erfolgen.
                     
FontFactory fontFactory = …;
Reader reader = …;
FontFactoryReaderSettings settings = 
    reader.getSettings(FontFactoryReaderSettings.class);
settings.setFontFactory(fontFactory);
reader.read(…);| 
                                     
  | 
                                 
                                     Die veränderten Settings sind für alle weiteren read(…)-Aufrufe gültig.  | 
                              
Beispiel 7.20. Setzen der FontFactory für alle Lesevorgänge mittels
                                            ComponentConfigurer
Um nicht jede Reader-Instanz einzeln konfigurieren zu müssen, existiert ein
                                            allgemeingültiger Konfigurationsweg. Dazu wird ein ComponentConfigurer
                                            definiert, der vor der ersten Erzeugung einer Reader-Instanz registriert sein
                                            muss. Die Registrierung erfolgt global über die Methoden der Klasse Jadice.
                                            Als JUnit Test formuliert zeigt das Beispiel, dass der ComponentConfigurer
                                            für jede Instanz von FontFactoryReaderSettings, die über die Methoden der
                                            Klasse Jadice erzeugt wird, ausgeführt wird. Da der Reader seine
                                            Settings-Objekte auf diesem Weg erzeugt, ist die FontFactory-Instanz für
                                            jeden Lesevorgang gesetzt.
                     
@Test public void testThat_configurer_configuresFontFactoryReaderSettings() { final FontFactory fontFactory = new BasicFontFactory(); ComponentConfigurer<FontFactoryReaderSettings> configurer = new ComponentConfigurer<FontFactoryReaderSettings>() { @Override public void configure(FontFactoryReaderSettings settings) { settings.setFontFactory(fontFactory); } };Jadice.registerComponentConfigurer(configurer, FontFactoryReaderSettings.class);
FontFactoryReaderSettings settings1 = Jadice.create(FontFactoryReaderSettings.class); assertThat(settings1.getFontFactory(), is(sameInstance(fontFactory))); Reader reader = new Reader(); FontFactoryReaderSettings settings2 = reader.getSettings(FontFactoryReaderSettings.class); assertThat(settings2.getFontFactory(), is(sameInstance(fontFactory))); }
| 
                                     
  | 
                                 
                                     Configurer als anonyme Klasse, die auf jeder Instanz von FontFactoryReaderSettings dieselbe FontFactory setzt.  | 
                              
| 
                                     
  | 
                                 
                                     Globales Registrieren des Configurers. Er wird nun für jede Instanz aufgerufen, die über den Jadice.create(…) Mechanismus erzeugt wird. Dies betrifft im Besonderen die FontFactoryReaderSettings, die von Reader-Instanzen automatisch erzeugt werden.  | 
                              
Beispiel 7.21. Beispielkonfiguration für Fonts
Eine beispielhafte Komplett-Konfiguration für das Laden von Fonts wird in den
                                            Klassen FontEnvironments SwingFontEnvironments bereitgestellt.
                     
Für die Integration des Konfigurationsbeispiels genügt ein Aufruf der Konfiguration zu Beginn der Anwendung:
FontEnvironments.configureFontEnvironment();
Sollten zudem noch GUI-Komponenten verwendet werden kann man die Beispielkonfiguration wie folgt übernehmen:
SwingFontEnvironments.configureFontEnvironment();


