/* * Copyright (c) 2007-2012, 2015, 2016 Eike Stepper (Berlin, Germany) 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: * Eike Stepper - initial API and implementation */ package org.eclipse.net4j.internal.util.bundle; import org.eclipse.net4j.internal.util.om.pref.Preferences; import org.eclipse.net4j.util.ReflectUtil; import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.net4j.util.om.OMBundle; import org.eclipse.net4j.util.om.OMPlatform; import org.eclipse.net4j.util.om.log.Logger; import org.eclipse.net4j.util.om.log.OMLogger; import org.eclipse.net4j.util.om.trace.OMTracer; import org.eclipse.net4j.util.om.trace.Tracer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.MissingResourceException; import java.util.Properties; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.concurrent.ConcurrentHashMap; /** * @author Eike Stepper */ public abstract class AbstractBundle implements OMBundle, OMBundle.DebugSupport, OMBundle.TranslationSupport { private static final String CLASS_EXTENSION = ".class"; private AbstractPlatform platform; private String bundleID; private Class<?> accessor; private Object bundleContext; private boolean debugging; private Map<String, Tracer> tracers = new ConcurrentHashMap<String, Tracer>(0); private OMLogger logger; private Preferences preferences; private ResourceBundle resourceBundle; private ResourceBundle untranslatedResourceBundle; private Map<String, String> strings = new HashMap<String, String>(0); private Map<String, String> untranslatedStrings = new HashMap<String, String>(0); private boolean shouldTranslate = true; public AbstractBundle(AbstractPlatform platform, String bundleID, Class<?> accessor) { this.platform = platform; this.bundleID = bundleID; this.accessor = accessor; boolean debug = getDebugOption("debug", false); //$NON-NLS-1$ setDebugging(debug); } public OMPlatform getPlatform() { return platform; } public String getBundleID() { return bundleID; } public Class<?> getAccessor() { return accessor; } public void setAccessor(Class<?> accessor) { this.accessor = accessor; } public Object getBundleContext() { return bundleContext; } @Deprecated public void setBundleContext(Object bundleContext) { this.bundleContext = bundleContext; } public DebugSupport getDebugSupport() { return this; } public TranslationSupport getTranslationSupport() { return this; } public boolean isDebugging() { if (!platform.isDebugging()) { return false; } return debugging; } public void setDebugging(boolean debugging) { this.debugging = debugging; } public String getDebugOption(String option, String defaultValue) { String value = getDebugOption(option); return value == null ? defaultValue : value; } public boolean getDebugOption(String option, boolean defaultValue) { String value = getDebugOption(option); return value == null ? defaultValue : Boolean.parseBoolean(value); } public void setDebugOption(String option, boolean value) { setDebugOption(option, Boolean.toString(value)); } public int getDebugOption(String option, int defaultValue) { try { String value = getDebugOption(option); return value == null ? defaultValue : Integer.parseInt(value); } catch (NumberFormatException e) { return defaultValue; } } public void setDebugOption(String option, int value) { setDebugOption(option, Integer.toString(value)); } public String getDebugOption(String option) { return platform.getDebugOption(bundleID, option); } public void setDebugOption(String option, String value) { platform.setDebugOption(bundleID, option, value); } public synchronized OMTracer tracer(String name) { OMTracer tracer = tracers.get(name); if (tracer == null) { tracer = createTracer(name); } return tracer; } public synchronized OMLogger logger() { if (logger == null) { logger = createLogger(); } return logger; } public IStatus getStatus(Object obj) { if (obj instanceof CoreException) { CoreException coreException = (CoreException)obj; return coreException.getStatus(); } if (obj instanceof Throwable) { Throwable t = (Throwable)obj; String msg = t.getLocalizedMessage(); if (msg == null || msg.length() == 0) { msg = t.getClass().getName(); } return new Status(IStatus.ERROR, getBundleID(), msg, t); } return new Status(IStatus.INFO, getBundleID(), obj.toString(), null); } public void coreException(Throwable t) throws CoreException { if (t instanceof CoreException) { CoreException ex = (CoreException)t; IStatus status = ex.getStatus(); if (status != null && status.getSeverity() == IStatus.CANCEL) { throw new OperationCanceledException(); } throw ex; } if (t instanceof OperationCanceledException) { throw (OperationCanceledException)t; } if (t instanceof Error) { throw (Error)t; } IStatus status = getStatus(t); throw new CoreException(status); } public File getConfigFile() { return platform.getConfigFile(getConfigFileName()); } public Properties getConfigProperties() { return platform.getConfigProperties(getConfigFileName()); } public synchronized Preferences preferences() { if (preferences == null) { preferences = new Preferences(this); } return preferences; } public InputStream getInputStream(String path) throws IOException { String base = getBaseURL().toString(); if (!base.endsWith("/")) //$NON-NLS-1$ { base += "/"; //$NON-NLS-1$ } if (path.startsWith("/")) //$NON-NLS-1$ { path = path.substring(1); } URL url = new URL(base + path); return url.openStream(); } public boolean shouldTranslate() { return shouldTranslate; } public void setShouldTranslate(boolean shouldTranslate) { this.shouldTranslate = shouldTranslate; } public String getString(String key, boolean translate) { Map<String, String> stringMap = translate ? strings : untranslatedStrings; String result = stringMap.get(key); if (result == null) { ResourceBundle bundle = translate ? resourceBundle : untranslatedResourceBundle; if (bundle == null) { String packageName = ReflectUtil.getPackageName(accessor); if (translate) { try { bundle = resourceBundle = ResourceBundle.getBundle(packageName + ".plugin"); //$NON-NLS-1$ } catch (MissingResourceException exception) { // If the bundle can't be found the normal way, try to find it as // the base URL. If that also doesn't work, rethrow the original // exception. InputStream inputStream = null; try { inputStream = getInputStream("plugin.properties"); //$NON-NLS-1$ bundle = new PropertyResourceBundle(inputStream); untranslatedResourceBundle = resourceBundle = bundle; inputStream.close(); } catch (IOException ignore) { } finally { IOUtil.closeSilent(inputStream); } if (resourceBundle == null) { throw exception; } } } else { InputStream inputStream = null; try { inputStream = getInputStream("plugin.properties"); //$NON-NLS-1$ bundle = untranslatedResourceBundle = new PropertyResourceBundle(inputStream); inputStream.close(); } catch (IOException ioException) { throw new MissingResourceException("Missing resource: plugin.properties", accessor //$NON-NLS-1$ .getName(), key); } finally { IOUtil.closeSilent(inputStream); } } } result = bundle.getString(key); stringMap.put(key, result); } return result; } public String getString(String key) { return getString(key, shouldTranslate()); } public String getString(String key, Object... args) { return getString(key, shouldTranslate(), args); } public String getString(String key, boolean translate, Object... args) { return MessageFormat.format(getString(key, translate), args); } @Override public String toString() { return bundleID; } public void start() throws Exception { invokeMethod("start"); //$NON-NLS-1$ } public void stop() throws Exception { try { if (preferences != null) { preferences.save(); } } catch (RuntimeException ex) { OM.LOG.error(ex); } invokeMethod("stop"); //$NON-NLS-1$ } protected OMTracer createTracer(String name) { return new Tracer(this, name); } protected OMLogger createLogger() { return new Logger(this); } protected String getConfigFileName() { return bundleID + ".properties"; //$NON-NLS-1$ } protected final Class<?> getClassFromBundle(String path) { if (path.endsWith(CLASS_EXTENSION)) { int start = path.startsWith("/") ? 1 : 0; int end = path.length() - CLASS_EXTENSION.length(); String className = path.substring(start, end).replace('/', '.'); for (;;) { try { ClassLoader classLoader = getAccessor().getClassLoader(); Class<?> c = classLoader.loadClass(className); if (c != null) { return c; } } catch (NoClassDefFoundError ex) { //$FALL-THROUGH$ } catch (ClassNotFoundException ex) { //$FALL-THROUGH$ } int dot = className.indexOf('.'); if (dot == -1) { break; } className = className.substring(dot + 1); } } return null; } private void invokeMethod(String name) throws Exception { try { Method method = accessor.getDeclaredMethod(name, ReflectUtil.NO_PARAMETERS); if (!method.isAccessible()) { method.setAccessible(true); } method.invoke(null, ReflectUtil.NO_ARGUMENTS); } catch (NoSuchMethodException ignore) { } catch (IllegalAccessException ignore) { } catch (InvocationTargetException ex) { Throwable targetException = ex.getTargetException(); if (targetException instanceof Exception) { throw (Exception)targetException; } else if (targetException instanceof Error) { throw (Error)targetException; } else { OM.LOG.error(targetException); } } } }