Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [albireo-dev] setComponentFont comes too late

Gordon Hirsch wrote:

Bruno Haible wrote:
The reason is that setComponentFont is called after the first label has
been created. IMO it comes too late. There's no reason to wait for the first
component to be created before setting the - system-wide - look&feel parameters.

Correct, and in fact, some of that code could be moved even earlier. We
don't yet properly distinguish between the default "system" font and the
current component font. I've been meaning to clean this up.

I've done this now. The initial l&f settings happen much earlier now, in AwtEnvironment.

Some of the work done by setComponentFont and setComponentColors was not happening anymore because they were called so early. I refactored the code so that the work could be properly divided up.

With these changes, this happens before the swing component is created:

* default (system) SWT font propagation
* setting the frame's background color

After the swing component is created:

* setting content pane's font and colors based on any non-default font for the parent composite.

As far as I can tell, this will still avoid any of the previous problems with flicker and sizing.

I've also moved font and image conversion code to a separate ResourceConverter class.


### Eclipse Workspace Patch 1.0
#P org.eclipse.albireo.examples.plugin
Index: src/org/eclipse/albireo/examples/plugin/views/EmbeddedJTableView.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.examples.plugin/src/org/eclipse/albireo/examples/plugin/views/EmbeddedJTableView.java,v
retrieving revision 1.8
diff -u -r1.8 EmbeddedJTableView.java
--- src/org/eclipse/albireo/examples/plugin/views/EmbeddedJTableView.java	13 Feb 2008 21:15:35 -0000	1.8
+++ src/org/eclipse/albireo/examples/plugin/views/EmbeddedJTableView.java	12 Mar 2008 05:59:16 -0000
@@ -10,8 +10,6 @@
 import org.eclipse.albireo.core.SwingControl;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.part.ViewPart;
 
 /**
@@ -24,6 +22,10 @@
     
     public void createPartControl(final Composite parent) {
         swingControl = new SwingControl(parent, SWT.NONE) {
+            {
+                setBackground(getDisplay().getSystemColor(SWT.COLOR_YELLOW));
+                setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_RED));
+            }
             protected JComponent createSwingComponent() {
                 /* Creating components */
                 int nrows = 1000, ncolumns = 10;
@@ -55,13 +57,8 @@
             public Composite getLayoutableAncestor() {
                 return parent;
             }
-            
+
         };
-        Display display = PlatformUI.getWorkbench().getDisplay();
-        swingControl.setBackground(display.getSystemColor(SWT.COLOR_YELLOW));
-        swingControl.setForeground(display.getSystemColor(SWT.COLOR_DARK_RED));
-        
-        
     }
 
     public void setFocus() {
#P org.eclipse.albireo.core
Index: src/org/eclipse/albireo/core/AwtEnvironment.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/AwtEnvironment.java,v
retrieving revision 1.15
diff -u -r1.15 AwtEnvironment.java
--- src/org/eclipse/albireo/core/AwtEnvironment.java	6 Mar 2008 23:16:33 -0000	1.15
+++ src/org/eclipse/albireo/core/AwtEnvironment.java	12 Mar 2008 05:59:18 -0000
@@ -28,7 +28,9 @@
 import java.util.Map;
 
 import javax.swing.JPopupMenu;
+import javax.swing.UIManager;
 import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.plaf.FontUIResource;
 
 import org.eclipse.albireo.internal.AwtDialogListener;
 import org.eclipse.albireo.internal.FocusDebugging;
@@ -38,6 +40,7 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
@@ -124,6 +127,8 @@
     private AwtEnvironment(Display display) {
         assert display != null;
         
+        this.display = display;        
+        
         /*
          * This property removes a large amount of flicker from embedded swing
          * components in JDK 1.4 and 1.5. Ideally it would be set lazily,
@@ -144,10 +149,12 @@
          * before any (potential deadlocking) activity occurs on the 
          * AWT thread.
          */
+        final Font initialFont = display.getSystemFont();
         try {
             EventQueue.invokeAndWait(new Runnable() {
                 public void run() {
                     setLookAndFeel();
+                    propagateSwtFont(initialFont);
                     if (FocusHandler.verboseKFHEvents)
                         FocusDebugging.enableKeyboardFocusManagerLogging();
                 }
@@ -158,8 +165,6 @@
             SWT.error(SWT.ERROR_FAILED_EXEC, e.getTargetException());
         }
 
-        this.display = display;
-        
         // Listen for AWT modal dialogs to make them modal application-wide
         dialogListener = new AwtDialogListener(display);
         
@@ -485,4 +490,89 @@
             return control.getShell();
         }
     }
+    
+    //==================== Font Management ================================
+    private Font lastPropagatedSwtFont;
+    private static boolean isDefaultSwtFontPropagated = true;
+
+    protected java.awt.Font propagateSwtFont(Font swtFont) {
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+
+        java.awt.Font awtFont = ResourceConverter.getInstance().convertFont(swtFont);
+        if (isDefaultSwtFontPropagated && (lastPropagatedSwtFont != swtFont) && !swtFont.getDevice().isDisposed()) {
+            lastPropagatedSwtFont = swtFont;
+            
+            // Update the look and feel defaults to use new font.
+            // Swing should take care of this on its own, but it does not seem to do it when mixed with SWT
+            updateLookAndFeelFonts(awtFont);
+        }
+        return awtFont;
+    }
+
+    protected void updateLookAndFeelFonts(java.awt.Font awtFont) {
+        assert awtFont != null;
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+        
+        // The FontUIResource class marks the font as replaceable by the look and feel
+        // implementation if font settings are later changed.
+        FontUIResource fontResource = new FontUIResource(awtFont);
+
+        // Assign the new font to the relevant L&F font properties. These are
+        // the properties that are initially assigned to the system font
+        // under the Windows look and feel.
+        // TODO: It's possible that other platforms will need other assignments.
+        // TODO: This does not handle fonts other than the "system" font.
+        // TODO: Swing does not render the Vista default Segoe UI font well.
+        // Other fonts may change, and the Swing L&F may not be adjusting.
+        
+        UIManager.put("Button.font", fontResource); //$NON-NLS-1$
+        UIManager.put("CheckBox.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ComboBox.font", fontResource); //$NON-NLS-1$
+        UIManager.put("EditorPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Label.font", fontResource); //$NON-NLS-1$
+        UIManager.put("List.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Panel.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ProgressBar.font", fontResource); //$NON-NLS-1$
+        UIManager.put("RadioButton.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ScrollPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TabbedPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("Table.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TableHeader.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TextField.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TextPane.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TitledBorder.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ToggleButton.font", fontResource); //$NON-NLS-1$
+        UIManager.put("TreeFont.font", fontResource); //$NON-NLS-1$
+        UIManager.put("ViewportFont.font", fontResource); //$NON-NLS-1$
+    }
+
+    /**
+     * Returns whether SWT default font changes are automatically propagated to Swing. 
+     * See {@link #setSwtDefaultFontPropagated(boolean)} for more information. 
+     *  
+     * @return boolean 
+     */
+    public static boolean isSwtDefaultFontPropagated() {
+        return isDefaultSwtFontPropagated;
+    }
+
+    /**
+     * Configures automatic propagation of changes to the SWT default font to the 
+     * underlying Swing look and feel. 
+     * <p>
+     * The default value of this flag is <code>true</code>. 
+     * Normally, this ensures that Swing fonts will closely match the corresponding
+     * SWT fonts, and that changes to the system font settings will be obeyed by both
+     * Swing and SWT. However, Swing does not render certain fonts very well (e.g. the 
+     * default "Segoe UI" font in Windows Vista). Setting the flag to <code>false</code>
+     * will disable the propagation in cases where the Swing rendering is not acceptable, 
+     * and Swing will use its default fonts. Note, however, that if propagation is disabled, 
+     * changes to system font settings may not be detected by Swing.
+     * 
+     * @param val boolean flag. If <code>true</code>, default fonts will be propagated to 
+     * Swing.
+     */
+    public static void setSwtDefaultFontPropagated(boolean val) {
+        isDefaultSwtFontPropagated = val;
+    }
 }
Index: src/org/eclipse/albireo/core/SwingControl.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/SwingControl.java,v
retrieving revision 1.51
diff -u -r1.51 SwingControl.java
--- src/org/eclipse/albireo/core/SwingControl.java	29 Feb 2008 22:58:19 -0000	1.51
+++ src/org/eclipse/albireo/core/SwingControl.java	12 Mar 2008 05:59:19 -0000
@@ -11,11 +11,11 @@
  *******************************************************************************/
 package org.eclipse.albireo.core;
 
+import java.awt.Component;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.EventQueue;
 import java.awt.Frame;
-import java.awt.Toolkit;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -30,8 +30,6 @@
 import javax.swing.JRootPane;
 import javax.swing.RootPaneContainer;
 import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-import javax.swing.plaf.FontUIResource;
 
 import org.eclipse.albireo.internal.CleanResizeListener;
 import org.eclipse.albireo.internal.ComponentDebugging;
@@ -47,7 +45,6 @@
 import org.eclipse.swt.events.KeyListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
-import org.eclipse.swt.graphics.FontData;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.FillLayout;
@@ -117,7 +114,6 @@
         super(parent, style | SWT.EMBEDDED | SWT.NO_BACKGROUND);
         setLayout(new FillLayout());
         display = getDisplay();
-        currentSystemFont = getFont();
 
         display.addListener(SWT.Settings, settingsListener);
 
@@ -210,6 +206,7 @@
 
         final Color foreground = getForeground();
         final Color background = getBackground();
+        final Font font = getFont();
 
         // Create AWT/Swing components on the AWT thread. This is
         // especially necessary to avoid an AWT leak bug (Sun bug 6411042).
@@ -218,19 +215,22 @@
                 rootPaneContainer = addRootPaneContainer(frame);
                 initPopupMenuSupport(rootPaneContainer.getRootPane());
 
-                // Customize the UI look&feel before calling
-                // createSwingComponent(), so that preferred sizes will be
-                // correctly computed during createSwingComponent().
-                setComponentFont();
-                setComponentColors(foreground, background);
-
+                // The color of the frame is visible during redraws. Use the
+                // same color, to reduce flickering, and set it as soon as possible
+                setComponentBackground(frame, background, true);
+                
                 swingComponent = createSwingComponent();
                 if (swingComponent != null) {
+                    // Pass on color and font values
+                    // The color of the content Pane is visible permanently.
+                    setComponentForeground(rootPaneContainer.getContentPane(), foreground, true);
+                    setComponentBackground(rootPaneContainer.getContentPane(), background, true);
+                    setComponentFont(font, true);
+                    
                     rootPaneContainer.getRootPane().getContentPane().add(swingComponent);
                     swingComponent.putClientProperty(SWT_PARENT_PROPERTY_KEY, SwingControl.this);
                 }
-                setComponentFont();
-                setComponentColors(foreground, background);
+                
                 // Invoke hooks, for use by the application.
                 afterComponentCreatedAWTThread();
                 try {
@@ -795,36 +795,29 @@
     // ========================================================================
     // Font management
 
-    private Font currentSystemFont;
-
-    public void setFont(Font font) {
+    public void setFont(final Font font) {
         super.setFont(font);
-        // TODO Convert font to AWT. Currently we pass on the system font and don't propagate any sets through this method
+        EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                setComponentFont(font, false);
+            }
+        });
     }
+    
+    
 
-
-    private void setComponentFont() {
-        assert currentSystemFont != null;
+    private void updateDefaultFont(Font swtFont) {
         assert EventQueue.isDispatchThread();    // On AWT event thread
 
-        if ((swingComponent != null) && !currentSystemFont.getDevice().isDisposed()) {
-            FontData fontData = currentSystemFont.getFontData()[0];
-
-            // AWT font sizes assume a 72 dpi resolution, always. The true screen resolution must be
-            // used to convert the platform font size into an AWT point size that matches when displayed.
-            int resolution = Toolkit.getDefaultToolkit().getScreenResolution();
-            int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0);
-
-            // The style constants for SWT and AWT map exactly, and since they are int constants, they should
-            // never change. So, the SWT style is passed through as the AWT style.
-            java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize);
-
-            // Update the look and feel defaults to use new font.
-            updateLookAndFeel(awtFont);
-
-            // Allow subclasses to react to font change if necessary.
-            updateAwtFont(awtFont);
+        java.awt.Font awtFont = AwtEnvironment.getInstance(display).propagateSwtFont(swtFont);
+        if (awtFont == null) {
+            return;
+        }
+        
+        // Allow subclasses to react to font change if necessary.
+        updateAwtFont(awtFont);
 
+        if (swingComponent != null) {
             // Allow components to update their UI based on new font
             // TODO: should the update method be called on the root pane instead?
             Container contentPane = swingComponent.getRootPane().getContentPane();
@@ -832,6 +825,23 @@
         }
     }
 
+    protected void setComponentFont(Font swtFont, boolean preserveDefaults) {
+        assert EventQueue.isDispatchThread();
+
+        ResourceConverter converter = ResourceConverter.getInstance();
+        java.awt.Font awtFont = converter.convertFont(swtFont);
+
+        // Allow subclasses to react to font change if necessary.
+        updateAwtFont(awtFont);
+
+        if (rootPaneContainer != null) {
+            Container contentPane = rootPaneContainer.getContentPane();
+            if (!contentPane.getFont().equals(awtFont) || !preserveDefaults) {
+                contentPane.setFont(awtFont);
+            }
+        }
+    }
+    
     /**
      * Performs custom updates to newly set fonts. This method is called whenever a change
      * to the system font through the system settings (i.e. control panel) is detected.
@@ -849,53 +859,13 @@
         // Do nothing by default; subclasses can override to insert behavior
     }
 
-    private void updateLookAndFeel(java.awt.Font awtFont) {
-        assert awtFont != null;
-        assert EventQueue.isDispatchThread();    // On AWT event thread
-
-        // The FontUIResource class marks the font as replaceable by the look and feel
-        // implementation if font settings are later changed.
-        FontUIResource fontResource = new FontUIResource(awtFont);
-
-        // Assign the new font to the relevant L&F font properties. These are
-        // the properties that are initially assigned to the system font
-        // under the Windows look and feel.
-        // TODO: It's possible that other platforms will need other assignments.
-        // TODO: This does not handle fonts other than the "system" font.
-        // TODO: Font not properly set when running under Vista
-        // Other fonts may change, and the Swing L&F may not be adjusting.
-
-        UIManager.put("Button.font", fontResource); //$NON-NLS-1$
-        UIManager.put("CheckBox.font", fontResource); //$NON-NLS-1$
-        UIManager.put("ComboBox.font", fontResource); //$NON-NLS-1$
-        UIManager.put("EditorPane.font", fontResource); //$NON-NLS-1$
-        UIManager.put("Label.font", fontResource); //$NON-NLS-1$
-        UIManager.put("List.font", fontResource); //$NON-NLS-1$
-        UIManager.put("Panel.font", fontResource); //$NON-NLS-1$
-        UIManager.put("ProgressBar.font", fontResource); //$NON-NLS-1$
-        UIManager.put("RadioButton.font", fontResource); //$NON-NLS-1$
-        UIManager.put("ScrollPane.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TabbedPane.font", fontResource); //$NON-NLS-1$
-        UIManager.put("Table.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TableHeader.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TextField.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TextPane.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TitledBorder.font", fontResource); //$NON-NLS-1$
-        UIManager.put("ToggleButton.font", fontResource); //$NON-NLS-1$
-        UIManager.put("TreeFont.font", fontResource); //$NON-NLS-1$
-        UIManager.put("ViewportFont.font", fontResource); //$NON-NLS-1$
-    }
-
     private void handleSettingsChange() {
-        Font newFont = getDisplay().getSystemFont();
-        if (!newFont.equals(currentSystemFont)) {
-            currentSystemFont = newFont;
-            EventQueue.invokeLater(new Runnable() {
-                public void run() {
-                    setComponentFont();
-                }
-            });
-        }
+        final Font newFont = getDisplay().getSystemFont();
+        EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                updateDefaultFont(newFont);
+            }
+        });
     }
 
     private void handleDispose() {
@@ -906,25 +876,19 @@
     // ============================= Painting =============================
 
     /**
-     * Converts a color from SWT to Swing.
-     * The argument Color remains owned by the caller.
-     */
-    public static java.awt.Color convertColor(Color c) {
-        return new java.awt.Color(c.getRed(), c.getGreen(), c.getBlue());
-    }
-
-    /**
      * Overridden to propagate the background color change to the embedded AWT
      * component.
      */
     public void setBackground(final Color background) {
         super.setBackground(background);
 
-        ThreadingHandler.getInstance().asyncExec(display, new Runnable() {
-            public void run() {
-                setComponentColors(getForeground(), background);
-            }
-        });
+        if (rootPaneContainer != null) {
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    setComponentBackground(rootPaneContainer.getContentPane(), background, false);
+                }
+            });
+        }
     }
 
     /**
@@ -934,38 +898,35 @@
     public void setForeground(final Color foreground) {
         super.setForeground(foreground);
 
-        ThreadingHandler.getInstance().asyncExec(display, new Runnable() {
-            public void run() {
-                setComponentColors(foreground, getBackground());
-            }
-        });
+        if (rootPaneContainer != null) {
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    setComponentForeground(rootPaneContainer.getContentPane(), foreground, false);
+                }
+            });
+        }
     }
 
-    protected void setComponentColors(Color foreground, Color background) {
-        // TODO: on which thread?
+    protected void setComponentForeground(Component component, Color foreground, boolean preserveDefaults) {
+        assert EventQueue.isDispatchThread();
+        assert component != null;
 
-        java.awt.Color bg = convertColor(background);
-        java.awt.Color fg = convertColor(foreground);
+        ResourceConverter converter = ResourceConverter.getInstance();
+        java.awt.Color fg = converter.convertColor(foreground);
 
-        // The color of the content Pane is visible permanently.
-        if (rootPaneContainer != null) {
-            Container contentPane = rootPaneContainer.getContentPane();
-            if (!contentPane.getBackground().equals(bg)) {
-                contentPane.setBackground(bg);
-            }
-            if (!contentPane.getForeground().equals(fg)) {
-                contentPane.setForeground(fg);
-            }
+        if (!component.getForeground().equals(fg) || !preserveDefaults) {
+            component.setForeground(fg);
         }
-        // The color of the frame is visible during redraws. Use the
-        // same color, to reduce flickering.
-        if (frame != null) {
-            if (!frame.getBackground().equals(bg)) {
-                frame.setBackground(bg);
-            }
-            if (!frame.getForeground().equals(bg)) {
-                frame.setForeground(fg);
-            }
+    }
+    
+    protected void setComponentBackground(Component component, Color background, boolean preserveDefaults) {
+        assert EventQueue.isDispatchThread();
+
+        ResourceConverter converter = ResourceConverter.getInstance();
+        java.awt.Color bg = converter.convertColor(background);
+
+        if (!component.getBackground().equals(bg) || !preserveDefaults) {
+            component.setBackground(bg);
         }
     }
     
Index: src/org/eclipse/albireo/core/ResourceConverter.java
===================================================================
RCS file: src/org/eclipse/albireo/core/ResourceConverter.java
diff -N src/org/eclipse/albireo/core/ResourceConverter.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/core/ResourceConverter.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2005-2008 SAS Institute Inc., ILOG S.A.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAS Institute Inc. - initial API and implementation
+ *     ILOG S.A. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.albireo.core;
+
+import java.awt.Toolkit;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+
+
+
+/**
+ * Converter of resources from AWT/Swing to SWT and vice versa. 
+ */
+public class ResourceConverter {
+
+    /**
+     * Converts a color from SWT to Swing.
+     * The argument Color remains owned by the caller.
+     */
+    public java.awt.Color convertColor(Color c) {
+        return new java.awt.Color(c.getRed(), c.getGreen(), c.getBlue());
+    }
+
+    /**
+     * Converts a font from SWT to Swing.
+     * The argument Color remains owned by the caller.
+     */
+    public java.awt.Font convertFont(Font swtFont) {
+        FontData fontData = swtFont.getFontData()[0];
+
+        // AWT font sizes assume a 72 dpi resolution, always. The true screen resolution must be
+        // used to convert the platform font size into an AWT point size that matches when displayed.
+        int resolution = Toolkit.getDefaultToolkit().getScreenResolution();
+        int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0);
+
+        // The style constants for SWT and AWT map exactly, and since they are int constants, they should
+        // never change. So, the SWT style is passed through as the AWT style.
+        return new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize);
+    }
+
+
+    // ========================================================================
+    // Singleton design pattern
+
+    private static ResourceConverter theInstance = new ResourceConverter();
+
+    /**
+     * Returns the currently active singleton of this class.
+     */
+    public static ResourceConverter getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * Replaces the singleton of this class.
+     * @param instance An instance of this class or of a customized subclass.
+     */
+    public static void setInstance(ResourceConverter instance) {
+        theInstance = instance;
+    }
+
+}

Back to the top