/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * 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: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.ui.swt.lnf; import org.osgi.framework.Bundle; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.Platform; import org.eclipse.riena.core.exception.Failure; import org.eclipse.riena.core.wire.InjectExtension; import org.eclipse.riena.core.wire.Wire; import org.eclipse.riena.internal.core.ignore.IgnoreCheckStyle; import org.eclipse.riena.internal.ui.swt.Activator; import org.eclipse.riena.ui.swt.lnf.IDefaultLnfExtension; import org.eclipse.riena.ui.swt.lnf.rienadefault.RienaDefaultLnf; import org.eclipse.riena.ui.swt.utils.BundleUtil; /** * The {@code LnfManager} manages the current look and feel of the riena (navigation) widgets. * <p> * The {@code LnfManager} has the term of a default look-and-feel (L&F). The default L&F is initially set by Riena to {@code RienaDefaultLnf}. But the default * L&F may also be overridden by frameworks based on Riena. That allows them to define their own default L&F. * <p> * <b>Note:</b> Changing the L&F within a running application might result in system resources such as colors, fonts and images which will not be disposed. * <p> * However, applications can again override the default L&F. This can be done by either setting the system property "riena.lnf" or by specifying their L&F with * the methods {@code LnfManager.setLnf()}. * <p> * When specifying the L&F class via a string (either system property or one of the above mentioned methods) the string should conform to: * * <pre> * lnf := [ Bundle-Symbolic-Name ":" ] LnF-Class-Name * </pre> * * Where Bundle-Symbolic-Name allows to load the L&F class from the bundle with this symbolic name.<br> * If the Bundle-Symbolic-Name is omitted the {@code LnfManager} tries to load the Lnf class with the {LnfMangager}'s class loader. */ public final class LnfManagerInternal { /** * Allows setting of an application L&F. * * @since 1.2 */ public static final String RIENA_LNF_SYSTEM_PROPERTY = "riena.lnf"; //$NON-NLS-1$ private RienaDefaultLnf defaultLnf = new RienaDefaultLnf(); private volatile RienaDefaultLnf currentLnf; private RienaDefaultLnf extensionLnf; private LnfManagerInternal() { // cannot instantiated, because all methods are static } /** * Set a new default look and feel. See class header JavaDoc for details. * * @param defaultLnf * new default L&F * @since 1.2 */ public void setDefaultLnf(final RienaDefaultLnf defaultLnf) { Assert.isNotNull(defaultLnf, "defaultLnf must not be null."); //$NON-NLS-1$ this.defaultLnf = defaultLnf; setLnf((RienaDefaultLnf) null); } /** * Set the new look and feel specified by the given class name (see class header JavaDoc). * <p> * <b>Note:</b> Changing the L&F in a running application might result in system resources such as colors, fonts and images which will not be disposed. * * @param currentLnfClassName * a string specifying the name of the class that implements the look and feel */ public void setLnf(final String currentLnfClassName) { setLnf(createLnf(currentLnfClassName)); } /** * Sets the new look and feel. * <p> * If this is set, it will override the default look and feel. See class header JavaDoc for details. * <p> * <b>Note:</b> Changing the L&F in a running application might result in system resources such as colors, fonts and images which will not be disposed. * * @param currentLnf * new look and feel to install. */ public synchronized void setLnf(final RienaDefaultLnf currentLnf) { if (this.currentLnf == currentLnf) { return; } if (this.currentLnf != null) { this.currentLnf.uninitialize(); } this.currentLnf = currentLnf; if (this.currentLnf != null) { if (Activator.getDefault() != null) { Wire.instance(this.currentLnf).andStart(Activator.getDefault().getContext()); } this.currentLnf.initialize(); } } @InjectExtension public void update(final IDefaultLnfExtension[] lnfExtension) { if (lnfExtension != null && lnfExtension.length > 0) { this.extensionLnf = lnfExtension[0].createDefaultLnf(); } } /** * Returns the current look and feel. If no look and feel is set, the default look and feel is returned. * * @return current look and feel */ @IgnoreCheckStyle("http://www.angelikalanger.com/Articles/EffectiveJava/41.JMM-DoubleCheck/41.JMM-DoubleCheck.html") public RienaDefaultLnf getLnf() { if (currentLnf == null) { synchronized (LnfManagerInternal.class) { if (currentLnf == null) { final String className = System.getProperty(RIENA_LNF_SYSTEM_PROPERTY); if (className != null) { setLnf(className); } else if (extensionLnf != null) { setLnf(extensionLnf); } else { setLnf(defaultLnf); } } } } return currentLnf; } public boolean isLnfCreated() { return (currentLnf != null); } /** * Disposes (uninitializes) the current look and feel. */ public void dispose() { if (currentLnf != null) { currentLnf.uninitialize(); currentLnf = null; } } private static final String BUNDLE_CLASS_NAME_SEPARATOR = ":"; //$NON-NLS-1$ private static RienaDefaultLnf createLnf(final String lnfClassName) { if (lnfClassName == null) { return null; } Class<?> lnfClass = null; if (lnfClassName.contains(BUNDLE_CLASS_NAME_SEPARATOR)) { final String[] parts = lnfClassName.split(BUNDLE_CLASS_NAME_SEPARATOR); final String bundleSymbolicName = parts[0]; final String className = parts[1]; final Bundle bundle = Platform.getBundle(bundleSymbolicName); if (!BundleUtil.isReady(bundle)) { throw new LnfManagerFailure("can't load LnfClass '" + className + "' from bundle " + bundleSymbolicName + " because bundle is not ready."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } try { lnfClass = bundle.loadClass(className); } catch (final ClassNotFoundException e) { throw new LnfManagerFailure("can't load LnfClass '" + className + "' from bundle " + bundleSymbolicName + ".", e); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } else { lnfClass = loadClass(lnfClassName); } try { return (RienaDefaultLnf) lnfClass.newInstance(); } catch (final Exception e) { throw new LnfManagerFailure("can't create instance for LnfClass '" + lnfClass.getName() + ".", e); //$NON-NLS-1$ //$NON-NLS-2$ } } private static Class<?> loadClass(final String className) { try { return LnfManagerInternal.class.getClassLoader().loadClass(className); } catch (final ClassNotFoundException e) { throw new LnfManagerFailure("Can't load LnfClass '" + className //$NON-NLS-1$ + "'. Please use the class format specified in the LnfManager or change your bundle dependencies.", //$NON-NLS-1$ e); } // // This code has been �removed� because it is likely the cause for sporadic errors - which is not good! // // try { // return Thread.currentThread().getContextClassLoader().loadClass(className); // } catch (Exception e) { // Nop.reason("try next"); //$NON-NLS-1$ // } // // throw new LnfManagerFailure("can't load LnfClass '" + className + "."); //$NON-NLS-1$ //$NON-NLS-2$ } @SuppressWarnings("serial") private static class LnfManagerFailure extends Failure { /** * @param msg */ public LnfManagerFailure(final String msg) { super(msg); } /** * @param msg */ public LnfManagerFailure(final String msg, final Throwable thrown) { super(msg, thrown); } } }