/* This file is part of RouteConverter. RouteConverter is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. RouteConverter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RouteConverter; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Copyright (C) 2007 Christian Pesch. All Rights Reserved. */ package slash.navigation.gui; import slash.navigation.gui.jarinjar.ClassPathExtender; import slash.navigation.jnlp.SingleInstance; import slash.navigation.jnlp.SingleInstanceCallback; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Logger; import java.util.prefs.Preferences; import static java.lang.String.format; import static java.util.Locale.ROOT; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static java.util.prefs.Preferences.userNodeForPackage; import static javax.swing.SwingUtilities.invokeLater; import static slash.common.system.Platform.getBits; import static slash.common.system.Platform.getOperationSystem; import static slash.navigation.gui.helpers.UIHelper.setLookAndFeel; import static slash.navigation.gui.helpers.UIHelper.setUseSystemProxies; /** * The base of all graphical user interfaces. * * @author Christian Pesch */ public abstract class Application { private static final Logger log = getLogger(SingleFrameApplication.class.getName()); private static Application application = null; private final List<ExitListener> exitListeners; private final ApplicationContext context; private Preferences preferences = userNodeForPackage(getClass()); private static final String PREFERRED_LANGUAGE_PREFERENCE = "preferredLanguage"; private static final String PREFERRED_COUNTRY_PREFERENCE = "preferredCountry"; Application() { exitListeners = new CopyOnWriteArrayList<>(); context = new ApplicationContext(); } public static synchronized Application getInstance() { return application; } private static synchronized void setInstance(Application theApplication) { application = theApplication; } public final ApplicationContext getContext() { return context; } public Locale getLocale() { String language = preferences.get(PREFERRED_LANGUAGE_PREFERENCE, ""); String country = preferences.get(PREFERRED_COUNTRY_PREFERENCE, ""); return new Locale(language, country); } public void setLocale(Locale locale) { if (!ROOT.equals(locale)) { preferences.put(PREFERRED_LANGUAGE_PREFERENCE, locale.getLanguage()); preferences.put(PREFERRED_COUNTRY_PREFERENCE, locale.getCountry()); } else { preferences.remove(PREFERRED_LANGUAGE_PREFERENCE); preferences.remove(PREFERRED_COUNTRY_PREFERENCE); } } private static void initializeLocale(Preferences preferences) { String language = preferences.get(PREFERRED_LANGUAGE_PREFERENCE, Locale.getDefault().getLanguage()); String country = preferences.get(PREFERRED_COUNTRY_PREFERENCE, Locale.getDefault().getCountry()); Locale.setDefault(new Locale(language, country)); } private static ResourceBundle initializeBundles(String[] bundleNames) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { ResourceBundle lastBundle = null; for (String bundleName : bundleNames) { ResourceBundle bundle = ResourceBundle.getBundle(bundleName); if (lastBundle != null) setParentBundle(bundle, lastBundle); lastBundle = bundle; } return lastBundle; } private static void setParentBundle(ResourceBundle bundle, ResourceBundle parentBundle) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Field field = ResourceBundle.class.getDeclaredField("parent"); field.setAccessible(true); ResourceBundle bundlesParentOrNull = ResourceBundle.class.cast(field.get(bundle)); Method method = ResourceBundle.class.getDeclaredMethod("setParent", ResourceBundle.class); method.setAccessible(true); method.invoke(bundlesParentOrNull != null ? bundlesParentOrNull : bundle, parentBundle); } private static ClassLoader extendClassPath() { ClassPathExtender extender = new ClassPathExtender(); String swtJar = "swt-" + getOperationSystem() + "-" + getBits() + ".jar"; try { extender.addJarInJar(swtJar); } catch (Exception e) { log.info("Cannot extend classpath with SWT from " + swtJar + ": " + e); } File javaFxJar = new File(System.getProperty("java.home"), "lib/jfxrt.jar"); if (javaFxJar.exists()) { try { extender.addExternalFile(javaFxJar); } catch (Exception e) { log.info("Cannot extend classpath with JavaFX from " + javaFxJar + ": " + e); } } return extender.getClassLoader(); } private static void invokeNativeInterfaceMethod(String name) { try { Class<?> clazz = Class.forName("chrriis.dj.nativeswing.swtimpl.NativeInterface"); Method method = clazz.getMethod(name); method.invoke(null); } catch (Exception e) { log.info("Cannot invoke NativeInterface#" + name + "(): " + e); } } private static void openNativeInterface() { invokeNativeInterfaceMethod("open"); } private static void runNativeInterfaceEventPump() { invokeNativeInterfaceMethod("runEventPump"); } private SingleInstance singleInstance; private void initializeSingleInstance() { try { singleInstance = new SingleInstance(new SingleInstanceCallback() { public void newActivation(String[] args) { Application.this.parseNewActivationArgs(args); } }); } catch (Throwable t) { // intentionally left empty } } public static <T extends Application> void launch(final Class<T> applicationClass, final String[] bundleNames, final String[] args) { final ClassLoader contextClassLoader = extendClassPath(); if (contextClassLoader != null) Thread.currentThread().setContextClassLoader(contextClassLoader); Runnable doCreateAndShowGUI = new Runnable() { public void run() { try { if (contextClassLoader != null) Thread.currentThread().setContextClassLoader(contextClassLoader); setLookAndFeel(); setUseSystemProxies(); openNativeInterface(); initializeLocale(userNodeForPackage(applicationClass)); ResourceBundle bundle = initializeBundles(bundleNames); Application application = create(applicationClass); setInstance(application); application.getContext().setBundle(bundle); application.initializeSingleInstance(); application.startup(); application.parseInitialArgs(args); } catch (Exception e) { String msg = format("Application %s failed to launch", applicationClass); log.log(SEVERE, msg, e); throw new Error(msg, e); } } }; invokeLater(doCreateAndShowGUI); runNativeInterfaceEventPump(); } private static <T extends Application> T create(Class<T> applicationClass) throws Exception { Constructor<T> ctor = applicationClass.getDeclaredConstructor(); return ctor.newInstance(); } protected abstract void startup(); protected void parseInitialArgs(String[] args) { } protected void parseNewActivationArgs(String[] args) { } public/*for ExitAction*/ void exit(EventObject event) { for (ExitListener listener : exitListeners) { if (!listener.canExit(event)) { return; } } try { for (ExitListener listener : exitListeners) { try { listener.willExit(event); } catch (Exception e) { log.log(WARNING, "ExitListener.willExit() failed", e); } } shutdown(); } catch (Exception e) { log.log(WARNING, "Unexpected error in Application.shutdown()", e); } finally { end(); } } public interface ExitListener extends EventListener { boolean canExit(EventObject event); void willExit(EventObject event); } public void addExitListener(ExitListener listener) { exitListeners.add(listener); } protected void shutdown() { getContext().getNotificationManager().dispose(); } void end() { if (singleInstance != null) singleInstance.dispose(); Runtime.getRuntime().exit(0); } }