Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDirk Fauth2013-04-17 17:18:35 +0000
committerTom Schindl2013-04-17 17:18:35 +0000
commit1fbfddbcd5235a2a01b8eae7ba2aa771335b8add (patch)
treef09d52bd33705714201e4195841de798ad14e568
parentde90c3bba7c2477963b268548086979ab97b34bd (diff)
downloadorg.eclipse.e4.tools-I20130417-2200.tar.gz
org.eclipse.e4.tools-I20130417-2200.tar.xz
org.eclipse.e4.tools-I20130417-2200.zip
Bug 396198 - Enhancement of translation service in tools.servicesI20130417-2200
-rw-r--r--bundles/org.eclipse.e4.tools.services/META-INF/MANIFEST.MF1
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/IMessageFactoryService.java4
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/Message.java5
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/ToolsServicesActivator.java63
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/AbstractTranslationProvider.java1
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/MessageFactoryServiceImpl.java66
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleHelper.java501
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleTranslationProvider.java70
-rw-r--r--bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/TranslationObjectSupplier.java14
9 files changed, 706 insertions, 19 deletions
diff --git a/bundles/org.eclipse.e4.tools.services/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.tools.services/META-INF/MANIFEST.MF
index 9374d6b9..f0ce9437 100644
--- a/bundles/org.eclipse.e4.tools.services/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.tools.services/META-INF/MANIFEST.MF
@@ -17,3 +17,4 @@ Service-Component: OSGI-INF/resourcepoolfunction.xml, OSGI-INF/resourceservice.x
Bundle-ActivationPolicy: lazy
Import-Package: javax.annotation;version="1.0.0",
javax.inject;version="1.0.0"
+Bundle-Activator: org.eclipse.e4.tools.services.ToolsServicesActivator
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/IMessageFactoryService.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/IMessageFactoryService.java
index fc223330..b5512df7 100644
--- a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/IMessageFactoryService.java
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/IMessageFactoryService.java
@@ -1,6 +1,8 @@
package org.eclipse.e4.tools.services;
+import org.eclipse.osgi.service.localization.BundleLocalization;
+
public interface IMessageFactoryService {
- public <M> M createInstance(final String locale, final Class<M> messages)
+ public <M> M createInstance(final String locale, final Class<M> messages, BundleLocalization localization)
throws InstantiationException, IllegalAccessException;
}
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/Message.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/Message.java
index d2948f97..05afcf7f 100644
--- a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/Message.java
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/Message.java
@@ -1,9 +1,14 @@
package org.eclipse.e4.tools.services;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
public @interface Message {
public enum ReferenceType {
NONE, SOFT, WEAK
}
ReferenceType referenceType() default ReferenceType.SOFT;
+ String contributorURI() default "";
}
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/ToolsServicesActivator.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/ToolsServicesActivator.java
new file mode 100644
index 00000000..04d63672
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/ToolsServicesActivator.java
@@ -0,0 +1,63 @@
+package org.eclipse.e4.tools.services;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+@SuppressWarnings("deprecation")
+public class ToolsServicesActivator implements BundleActivator {
+
+ static private ToolsServicesActivator defaultInstance;
+ private BundleContext bundleContext;
+ private ServiceTracker<PackageAdmin, PackageAdmin> pkgAdminTracker;
+ private ServiceTracker<LogService, LogService> logTracker;
+
+ public ToolsServicesActivator() {
+ defaultInstance = this;
+ }
+
+ public static ToolsServicesActivator getDefault() {
+ return defaultInstance;
+ }
+
+ public void start(BundleContext context) throws Exception {
+ bundleContext = context;
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ if (pkgAdminTracker != null) {
+ pkgAdminTracker.close();
+ pkgAdminTracker = null;
+ }
+ if (logTracker != null) {
+ logTracker.close();
+ logTracker = null;
+ }
+ bundleContext = null;
+ }
+
+ public PackageAdmin getPackageAdmin() {
+ if (pkgAdminTracker == null) {
+ if (bundleContext == null)
+ return null;
+ pkgAdminTracker = new ServiceTracker<PackageAdmin, PackageAdmin>(bundleContext,
+ PackageAdmin.class, null);
+ pkgAdminTracker.open();
+ }
+ return (PackageAdmin) pkgAdminTracker.getService();
+ }
+
+ public LogService getLogService() {
+ if (logTracker == null) {
+ if (bundleContext == null)
+ return null;
+ logTracker = new ServiceTracker<LogService, LogService>(bundleContext,
+ LogService.class, null);
+ logTracker.open();
+ }
+ return logTracker.getService();
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/AbstractTranslationProvider.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/AbstractTranslationProvider.java
index c255ee13..20d0deb7 100644
--- a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/AbstractTranslationProvider.java
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/AbstractTranslationProvider.java
@@ -20,6 +20,7 @@ import java.util.Locale;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
+
import org.eclipse.osgi.service.localization.BundleLocalization;
/**
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/MessageFactoryServiceImpl.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/MessageFactoryServiceImpl.java
index 2d8d3496..e09b23b6 100644
--- a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/MessageFactoryServiceImpl.java
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/MessageFactoryServiceImpl.java
@@ -19,15 +19,25 @@ import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
import org.eclipse.e4.tools.services.IMessageFactoryService;
import org.eclipse.e4.tools.services.Message;
import org.eclipse.e4.tools.services.Message.ReferenceType;
+import org.eclipse.e4.tools.services.ToolsServicesActivator;
+import org.eclipse.osgi.service.localization.BundleLocalization;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.log.LogService;
public class MessageFactoryServiceImpl implements IMessageFactoryService {
+ private static LogService logService = ToolsServicesActivator.getDefault().getLogService();
+
// Cache so when multiple instance use the same message class
private Map<Object, Reference<Object>> SOFT_CACHE = Collections
.synchronizedMap(new HashMap<Object, Reference<Object>>());
@@ -37,9 +47,9 @@ public class MessageFactoryServiceImpl implements IMessageFactoryService {
private int CLEANUPCOUNT = 0;
- public <M> M createInstance(final String locale, final Class<M> messages)
+ public <M> M createInstance(final String locale, final Class<M> messages, final BundleLocalization localization)
throws InstantiationException, IllegalAccessException {
- String key = messages.getName() + "_" + locale;
+ String key = messages.getName() + "_" + locale; //$NON-NLS-1$
final Message annotation = messages.getAnnotation(Message.class);
Map<Object, Reference<Object>> cache = null;
@@ -85,13 +95,13 @@ public class MessageFactoryServiceImpl implements IMessageFactoryService {
M instance;
if (System.getSecurityManager() == null) {
- instance = doCreateInstance(locale, messages, annotation);
+ instance = doCreateInstance(locale, messages, annotation, localization);
} else {
instance = AccessController.doPrivileged(new PrivilegedAction<M>() {
public M run() {
try {
- return doCreateInstance(locale, messages, annotation);
+ return doCreateInstance(locale, messages, annotation, localization);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
@@ -115,13 +125,49 @@ public class MessageFactoryServiceImpl implements IMessageFactoryService {
}
private static <M> M doCreateInstance(String locale, Class<M> messages,
- Message annotation) throws InstantiationException,
+ Message annotation, BundleLocalization localization) throws InstantiationException,
IllegalAccessException {
- String basename = messages.getName().replace('.', '/');
- PropertiesBundleTranslationProvider provider = new PropertiesBundleTranslationProvider(
- messages.getClassLoader(), basename);
-
+ Locale loc = null;
+ try {
+ loc = locale == null ? Locale.getDefault() : ResourceBundleHelper.toLocale(locale);
+ }
+ catch (Exception e) {
+ //parsing the locale String to a Locale failed, so we use the default Locale
+ if (logService != null)
+ logService.log(LogService.LOG_ERROR, "Invalid locale", e); //$NON-NLS-1$
+ loc = Locale.getDefault();
+ }
+
+ ResourceBundle resourceBundle = null;
+ if (annotation != null && annotation.contributorURI().length() > 0) {
+ resourceBundle = ResourceBundleHelper.getResourceBundleForUri(annotation.contributorURI(), loc, localization);
+ }
+
+ if (resourceBundle == null) {
+ //check for the resource bundle relative to the messages class
+ String baseName = messages.getName().replace('.', '/');
+
+ try {
+ resourceBundle = ResourceBundleHelper.getEquinoxResourceBundle(baseName, loc, messages.getClassLoader());
+ }
+ catch (MissingResourceException e) {
+ //do nothing as this just means there is no resource bundle named
+ //like the messages class in the same package
+ //therefore we will go on and search for the OSGi resource bundle
+ }
+ }
+
+ if (resourceBundle == null) {
+ //retrieve the OSGi resource bundle
+ Bundle bundle = FrameworkUtil.getBundle(messages);
+ resourceBundle = localization.getLocalization(bundle, locale);
+ }
+
+ //always create a provider, if there is no resource bundle found, simply the modified keys will
+ //be returned by this provider to show that there is something wrong on loading it
+ ResourceBundleTranslationProvider provider = new ResourceBundleTranslationProvider(resourceBundle);
+
M instance = messages.newInstance();
Field[] fields = messages.getDeclaredFields();
@@ -131,7 +177,7 @@ public class MessageFactoryServiceImpl implements IMessageFactoryService {
}
fields[i].set(instance,
- provider.translate(locale, fields[i].getName()));
+ provider.translate(fields[i].getName()));
}
return instance;
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleHelper.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleHelper.java
new file mode 100644
index 00000000..fb3f017e
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleHelper.java
@@ -0,0 +1,501 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Dirk Fauth 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:
+ * Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.tools.services.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+import java.util.Locale;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+import java.util.ResourceBundle.Control;
+
+import org.eclipse.e4.tools.services.ToolsServicesActivator;
+import org.eclipse.osgi.service.localization.BundleLocalization;
+import org.osgi.framework.Bundle;
+import org.osgi.service.log.LogService;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Helper class for retrieving {@link ResourceBundle}s out of OSGi {@link Bundle}s.
+ *
+ * @author Dirk Fauth
+ */
+// There is no replacement for PackageAdmin#getBundles()
+@SuppressWarnings("deprecation")
+public class ResourceBundleHelper {
+
+ /**
+ * The schema identifier used for Eclipse platform references
+ */
+ private static final String PLATFORM_SCHEMA = "platform"; //$NON-NLS-1$
+ /**
+ * The schema identifier used for Eclipse bundle class references
+ */
+ private static final String BUNDLECLASS_SCHEMA = "bundleclass"; //$NON-NLS-1$
+ /**
+ * Identifier part of the Eclipse platform schema to point to a plugin
+ */
+ private static final String PLUGIN_SEGMENT = "/plugin/"; //$NON-NLS-1$
+ /**
+ * Identifier part of the Eclipse platform schema to point to a fragment
+ */
+ private static final String FRAGMENT_SEGMENT = "/fragment/"; //$NON-NLS-1$
+ /**
+ * The separator character for paths in the platform schema
+ */
+ private static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$
+
+ /**
+ * Parses the specified contributor URI and loads the {@link ResourceBundle} for the specified {@link Locale}
+ * out of an OSGi {@link Bundle}.
+ * <p>Following URIs are supported:
+ * <ul>
+ * <li>platform:/[plugin|fragment]/[Bundle-SymbolicName]<br>
+ * Load the OSGi resource bundle out of the bundle/fragment named [Bundle-SymbolicName]</li>
+ * <li>platform:/[plugin|fragment]/[Bundle-SymbolicName]/[Path]/[Basename]<br>
+ * Load the resource bundle specified by [Path] and [Basename] out of the bundle/fragment named [Bundle-SymbolicName].</li>
+ * <li>bundleclass://[plugin|fragment]/[Full-Qualified-Classname]<br>
+ * Instantiate the class specified by [Full-Qualified-Classname] out of the bundle/fragment named [Bundle-SymbolicName].
+ * Note that the class needs to be a subtype of {@link ResourceBundle}.</li>
+ * </ul>
+ * </p>
+ * @param contributorURI The URI that points to a {@link ResourceBundle}
+ * @param locale The {@link Locale} to use for loading the {@link ResourceBundle}
+ * @param localization The service for retrieving a {@link ResourceBundle} for a given {@link Locale} out of
+ * the given {@link Bundle} which is specified by URI.
+ * @return
+ */
+ public static ResourceBundle getResourceBundleForUri(String contributorURI, Locale locale, BundleLocalization localization) {
+ if (contributorURI == null)
+ return null;
+
+ LogService logService = ToolsServicesActivator.getDefault().getLogService();
+
+ URI uri;
+ try {
+ uri = new URI(contributorURI);
+ } catch (URISyntaxException e) {
+ if (logService != null)
+ logService.log(LogService.LOG_ERROR, "Invalid contributor URI: " + contributorURI); //$NON-NLS-1$
+ return null;
+ }
+
+ String bundleName = null;
+ Bundle bundle = null;
+ String resourcePath = null;
+ String classPath = null;
+
+ //the uri follows the platform schema, so we search for .properties files in the bundle
+ if (PLATFORM_SCHEMA.equals(uri.getScheme())) {
+ bundleName = uri.getPath();
+ if (bundleName.startsWith(PLUGIN_SEGMENT))
+ bundleName = bundleName.substring(PLUGIN_SEGMENT.length());
+ else if (bundleName.startsWith(FRAGMENT_SEGMENT))
+ bundleName = bundleName.substring(FRAGMENT_SEGMENT.length());
+
+ resourcePath = ""; //$NON-NLS-1$
+ if (bundleName.contains(PATH_SEPARATOR)) {
+ resourcePath = bundleName.substring(bundleName.indexOf(PATH_SEPARATOR) + 1);
+ bundleName = bundleName.substring(0, bundleName.indexOf(PATH_SEPARATOR));
+ }
+ } else if (BUNDLECLASS_SCHEMA.equals(uri.getScheme())) {
+ if (uri.getAuthority() == null) {
+ if (logService != null)
+ logService.log(LogService.LOG_ERROR, "Failed to get bundle for: " + contributorURI); //$NON-NLS-1$
+ }
+ bundleName = uri.getAuthority();
+ //remove the leading /
+ classPath = uri.getPath().substring(1);
+ }
+
+ ResourceBundle result = null;
+
+ if (bundleName != null) {
+ bundle = getBundleForName(bundleName);
+
+ if (bundle != null) {
+ if (resourcePath == null && classPath != null) {
+ //the URI points to a class within the bundle classpath
+ //therefore we are trying to instantiate the class
+ try {
+ Class<?> resourceBundleClass = bundle.loadClass(classPath);
+ result = getEquinoxResourceBundle(classPath, locale, resourceBundleClass.getClassLoader());
+ } catch (Exception e) {
+ if (logService != null)
+ logService.log(LogService.LOG_ERROR, "Failed to load specified ResourceBundle: " + contributorURI, e); //$NON-NLS-1$
+ }
+ }
+ else if (resourcePath.length() > 0) {
+ //the specified URI points to a resource
+ //therefore we try to load the .properties files into a ResourceBundle
+ result = getEquinoxResourceBundle(resourcePath.replace('.', '/'), locale, bundle);
+ }
+ else {
+ //there is no class and no special resource specified within the URI
+ //therefore we load the OSGi resource bundle out of the specified Bundle
+ //for the current Locale
+ result = localization.getLocalization(bundle, locale.toString());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * This method searches for the {@link ResourceBundle} in a modified way by inspecting the configuration option
+ * <code>equinox.root.locale</code>.
+ * <p>
+ * If the value for this system property is set to an empty String the default search order for ResourceBundles is used:
+ * <ul>
+ * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
+ * <li>bn + Ls + "_" + Cs</li>
+ * <li>bn + Ls</li>
+ * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
+ * <li>bn + Ld + "_" + Cd</li>
+ * <li>bn + Ld</li>
+ * <li>bn</li>
+ * </ul>
+ * Where bn is this bundle's localization basename, Ls, Cs and Vs are the specified locale (language, country, variant) and
+ * Ld, Cd and Vd are the default locale (language, country, variant).
+ * </p>
+ * <p>
+ * If Ls equals the value of <code>equinox.root.locale</code> then the following search order is used:
+ * <ul>
+ * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
+ * <li>bn + Ls + "_" + Cs</li>
+ * <li>bn + Ls</li>
+ * <li>bn</li>
+ * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
+ * <li>bn + Ld + "_" + Cd</li>
+ * <li>bn + Ld</li>
+ * <li>bn</li>
+ * </ul>
+ * </p>
+ * If <code>equinox.root.locale=en</code> and en_XX or en is asked for then this allows the root file to be used instead of
+ * falling back to the default locale.
+ *
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param locale the locale for which a resource bundle is desired
+ * @param loader the class loader from which to load the resource bundle
+ * @return a resource bundle for the given base name and locale
+ *
+ * @see ResourceBundle#getBundle(String, Locale, ClassLoader)
+ */
+ public static ResourceBundle getEquinoxResourceBundle(String baseName, Locale locale, ClassLoader loader) {
+ ResourceBundle resourceBundle = null;
+
+ String equinoxLocale = getEquinoxRootLocale();
+ //if the equinox.root.locale is not empty and the specified locale equals the equinox.root.locale
+ // -> use the special search order
+ if (equinoxLocale.length() > 0 && locale.toString().startsWith(equinoxLocale)) {
+ //there is a equinox.root.locale configured that matches the specified locale
+ //so the special search order is used
+ //to achieve this we first search without a fallback to the default locale
+ resourceBundle = ResourceBundle.getBundle(baseName, locale, loader,
+ ResourceBundle.Control.getNoFallbackControl(Control.FORMAT_DEFAULT));
+ //if there is no ResourceBundle found for that path, we will now search for the default locale ResourceBundle
+ if (resourceBundle == null) {
+ resourceBundle = ResourceBundle.getBundle(baseName, Locale.getDefault(), loader,
+ ResourceBundle.Control.getNoFallbackControl(Control.FORMAT_DEFAULT));
+ }
+ }
+ else {
+ //there is either no equinox.root.locale configured or it does not match the specified locale
+ // -> use the default search order
+ resourceBundle = ResourceBundle.getBundle(baseName, locale, loader);
+ }
+
+ return resourceBundle;
+ }
+
+ /**
+ * This method searches for the {@link ResourceBundle} in a modified way by inspecting the configuration option
+ * <code>equinox.root.locale</code>.
+ * <p><b>Note: This method will only search for ResourceBundles based on properties files.</b></p>
+ * <p>
+ * If the value for this system property is set to an empty String the default search order for ResourceBundles is used:
+ * <ul>
+ * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
+ * <li>bn + Ls + "_" + Cs</li>
+ * <li>bn + Ls</li>
+ * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
+ * <li>bn + Ld + "_" + Cd</li>
+ * <li>bn + Ld</li>
+ * <li>bn</li>
+ * </ul>
+ * Where bn is this bundle's localization basename, Ls, Cs and Vs are the specified locale (language, country, variant) and
+ * Ld, Cd and Vd are the default locale (language, country, variant).
+ * </p>
+ * <p>
+ * If Ls equals the value of <code>equinox.root.locale</code> then the following search order is used:
+ * <ul>
+ * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
+ * <li>bn + Ls + "_" + Cs</li>
+ * <li>bn + Ls</li>
+ * <li>bn</li>
+ * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
+ * <li>bn + Ld + "_" + Cd</li>
+ * <li>bn + Ld</li>
+ * <li>bn</li>
+ * </ul>
+ * </p>
+ * If <code>equinox.root.locale=en</code> and en_XX or en is asked for then this allows the root file to be used instead of
+ * falling back to the default locale.
+ *
+ * @param baseName the base name of the resource bundle, a fully qualified class name
+ * @param locale the locale for which a resource bundle is desired
+ * @param bundle The OSGi {@link Bundle} to lookup the {@link ResourceBundle}
+ * @return a resource bundle for the given base name and locale
+ *
+ * @see ResourceBundle#getBundle(String, Locale, Control)
+ */
+ public static ResourceBundle getEquinoxResourceBundle(String baseName, Locale locale, Bundle bundle) {
+ ResourceBundle resourceBundle = null;
+
+ String equinoxLocale = getEquinoxRootLocale();
+ //if the equinox.root.locale is not empty and the specified locale equals the equinox.root.locale
+ // -> use the special search order
+ if (equinoxLocale.length() > 0 && locale.toString().startsWith(equinoxLocale)) {
+ //there is a equinox.root.locale configured that matches the specified locale
+ //so the special search order is used
+ //to achieve this we first search without a fallback to the default locale
+ resourceBundle = ResourceBundle.getBundle(baseName, locale, new BundleResourceBundleControl(bundle, false));
+ //if there is no ResourceBundle found for that path, we will now search for the default locale ResourceBundle
+ if (resourceBundle == null) {
+ resourceBundle = ResourceBundle.getBundle(baseName, Locale.getDefault(), new BundleResourceBundleControl(bundle, false));
+ }
+ }
+ else {
+ //there is either no equinox.root.locale configured or it does not match the specified locale
+ // -> use the default search order
+ resourceBundle = ResourceBundle.getBundle(baseName, locale, new BundleResourceBundleControl(bundle, true));
+ }
+
+ return resourceBundle;
+ }
+
+ /**
+ * @return The value for the system property for key <code>equinox.root.locale</code>.
+ * If none is specified than <b>en</b> will be returned as default.
+ */
+ private static String getEquinoxRootLocale() {
+ // Logic from FrameworkProperties.getProperty("equinox.root.locale", "en")
+ String root = System.getProperties().getProperty("equinox.root.locale"); //$NON-NLS-1$
+ if (root == null) {
+ root = "en"; //$NON-NLS-1$
+ }
+ return root;
+ }
+
+ /**
+ * This method is copied out of org.eclipse.e4.ui.internal.workbench.Activator
+ * because as it is a internal resource, it is not accessible for us.
+ *
+ * @param bundleName
+ * the bundle id
+ * @return A bundle if found, or <code>null</code>
+ */
+ public static Bundle getBundleForName(String bundleName) {
+ PackageAdmin packageAdmin = ToolsServicesActivator.getDefault().getPackageAdmin();
+ Bundle[] bundles = packageAdmin.getBundles(bundleName, null);
+ if (bundles == null)
+ return null;
+ // Return the first bundle that is not installed or uninstalled
+ for (int i = 0; i < bundles.length; i++) {
+ if ((bundles[i].getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
+ return bundles[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <p>Converts a String to a Locale.</p>
+ *
+ * <p>This method takes the string format of a locale and creates the
+ * locale object from it.</p>
+ *
+ * <pre>
+ * MessageFactoryServiceImpl.toLocale("en") = new Locale("en", "")
+ * MessageFactoryServiceImpl.toLocale("en_GB") = new Locale("en", "GB")
+ * MessageFactoryServiceImpl.toLocale("en_GB_xxx") = new Locale("en", "GB", "xxx")
+ * </pre>
+ *
+ * <p>This method validates the input strictly.
+ * The language code must be lowercase.
+ * The country code must be uppercase.
+ * The separator must be an underscore.
+ * The length must be correct.
+ * </p>
+ *
+ * <p>This method is inspired by <code>org.apache.commons.lang.LocaleUtils.toLocale(String)</code> by
+ * fixing the parsing error for uncommon Locales like having a language and a variant code but
+ * no country code, or a Locale that only consists of a country code.
+ * </p>
+ *
+ * @param str the locale String to convert
+ * @return a Locale that matches the specified locale String or <code>null</code>
+ * if the specified String is <code>null</code>
+ * @throws IllegalArgumentException if the String is an invalid format
+ */
+ public static Locale toLocale(String str) {
+ if (str == null) {
+ return null;
+ }
+
+ String language = ""; //$NON-NLS-1$
+ String country = ""; //$NON-NLS-1$
+ String variant = ""; //$NON-NLS-1$
+
+ String[] localeParts = str.split("_"); //$NON-NLS-1$
+ if (localeParts.length == 0 || localeParts.length > 3
+ || (localeParts.length == 1 && localeParts[0].length() == 0)) {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ } else {
+ if (localeParts[0].length() == 1 || localeParts[0].length() > 2) {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ }
+ else if (localeParts[0].length() == 2) {
+ char ch0 = localeParts[0].charAt(0);
+ char ch1 = localeParts[0].charAt(1);
+ if (ch0 < 'a' || ch0 > 'z' || ch1 < 'a' || ch1 > 'z') {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ }
+ }
+
+ language = localeParts[0];
+
+ if (localeParts.length > 1) {
+ if (localeParts[1].length() == 1 || localeParts[1].length() > 2) {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ }
+ else if (localeParts[1].length() == 2) {
+ char ch3 = localeParts[1].charAt(0);
+ char ch4 = localeParts[1].charAt(1);
+ if (ch3 < 'A' || ch3 > 'Z' || ch4 < 'A' || ch4 > 'Z') {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ }
+ }
+
+ country = localeParts[1];
+ }
+
+ if (localeParts.length == 3) {
+ if (localeParts[0].length() == 0 && localeParts[1].length() == 0) {
+ throw new IllegalArgumentException("Invalid locale format: " + str); //$NON-NLS-1$
+ }
+ variant = localeParts[2];
+ }
+ }
+
+ return new Locale(language, country, variant);
+ }
+
+ /**
+ * Specialization of {@link Control} which loads the {@link ResourceBundle} out of an
+ * OSGi {@link Bundle} instead of using a classloader.
+ *
+ * <p>It only supports properties based {@link ResourceBundle}s. If you want to use
+ * source based {@link ResourceBundle}s you have to use the bundleclass URI with the
+ * Message annotation.
+ *
+ * @author Dirk Fauth
+ *
+ */
+ static class BundleResourceBundleControl extends ResourceBundle.Control {
+
+ /**
+ * Flag to determine whether the default locale should be used as fallback locale
+ * in case there is no {@link ResourceBundle} found for the specified locale.
+ */
+ private final boolean useFallback;
+
+ /**
+ * The OSGi {@link Bundle} to lookup the {@link ResourceBundle}
+ */
+ private final Bundle osgiBundle;
+
+ /**
+ *
+ * @param osgiBundle The OSGi {@link Bundle} to lookup the {@link ResourceBundle}
+ * @param useFallback <code>true</code> if the default locale should be used as fallback
+ * locale in the search path or <code>false</code> if there should be no fallback.
+ */
+ public BundleResourceBundleControl(Bundle osgiBundle, boolean useFallback) {
+ this.osgiBundle = osgiBundle;
+ this.useFallback = useFallback;
+ }
+
+ @Override
+ public ResourceBundle newBundle(String baseName, Locale locale,
+ String format, ClassLoader loader, boolean reload)
+ throws IllegalAccessException, InstantiationException, IOException {
+
+ String bundleName = toBundleName(baseName, locale);
+ ResourceBundle bundle = null;
+ if (format.equals("java.properties")) { //$NON-NLS-1$
+ final String resourceName = toResourceName(bundleName, "properties"); //$NON-NLS-1$
+ InputStream stream = null;
+ try {
+ stream = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<InputStream>() {
+ public InputStream run() throws IOException {
+ InputStream is = null;
+ URL url = osgiBundle.getEntry(resourceName);
+ if (url != null) {
+ URLConnection connection = url.openConnection();
+ if (connection != null) {
+ // Disable caches to get fresh data for
+ // reloading.
+ connection.setUseCaches(false);
+ is = connection.getInputStream();
+ }
+ }
+ return is;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (IOException) e.getException();
+ }
+ if (stream != null) {
+ try {
+ bundle = new PropertyResourceBundle(stream);
+ } finally {
+ stream.close();
+ }
+ }
+ }
+ else {
+ throw new IllegalArgumentException("unknown format: " + format); //$NON-NLS-1$
+ }
+ return bundle;
+ }
+
+ @Override
+ public List<String> getFormats(String baseName) {
+ return FORMAT_PROPERTIES;
+ }
+
+ @Override
+ public Locale getFallbackLocale(String baseName, Locale locale) {
+ return this.useFallback ? super.getFallbackLocale(baseName, locale) : null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleTranslationProvider.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleTranslationProvider.java
new file mode 100644
index 00000000..163087b0
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/ResourceBundleTranslationProvider.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Dirk Fauth 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:
+ * Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.e4.tools.services.impl;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Wrapper class for accessing translations out of a {@link ResourceBundle}.
+ *
+ * @author Dirk Fauth
+ *
+ */
+public class ResourceBundleTranslationProvider {
+
+ /**
+ * The {@link ResourceBundle} to use for translations.
+ */
+ private ResourceBundle resourceBundle;
+
+ /**
+ *
+ * @param resourceBundle The {@link ResourceBundle} to use for translations.
+ * Can be <code>null</code>, which will lead to simply return the key
+ * modified by prefixing and suffixing it with "!" when calling translate(String).
+ */
+ public ResourceBundleTranslationProvider(ResourceBundle resourceBundle) {
+ this.resourceBundle = resourceBundle;
+ }
+
+ /**
+ * Tries to retrieve the translation value for the given key out of the {@link ResourceBundle}
+ * set to this {@link ResourceBundleTranslationProvider}. If there is no {@link ResourceBundle}
+ * set or there is no translation found for the given key, the key itself prefixed and suffixed
+ * with "!" will be returned to indicate that there is no translation found.
+ * <p>This implementation also supports the usage of dot separation for property keys. As in Java
+ * variables can not be separated with a dot, the underscore needs to be used for separation of
+ * the variable. This will be replaced automatically to a dot, if there is no translation found
+ * with an underscore as separator.
+ * </p>
+ * @param key The key of the requested translation property.
+ * @return The translation for the given key or the key itself prefixed and suffixed
+ * with "!" to indicate that there is no translation available for the
+ * given key.
+ */
+ public String translate(String key) {
+ String result = ""; //$NON-NLS-1$
+ try {
+ if (this.resourceBundle == null) {
+ result = "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ result = resourceBundle.getString(key);
+ } catch (MissingResourceException e) {
+ if (key.contains("_")) { //$NON-NLS-1$
+ result = translate(key.replace('_', '.'));
+ } else {
+ result = "!" + key + "!"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/TranslationObjectSupplier.java b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/TranslationObjectSupplier.java
index a7ee2584..31c06b87 100644
--- a/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/TranslationObjectSupplier.java
+++ b/bundles/org.eclipse.e4.tools.services/src/org/eclipse/e4/tools/services/impl/TranslationObjectSupplier.java
@@ -11,20 +11,17 @@
package org.eclipse.e4.tools.services.impl;
import java.lang.reflect.ParameterizedType;
-
import java.lang.reflect.Type;
import java.util.Locale;
+import org.eclipse.e4.core.di.suppliers.ExtendedObjectSupplier;
+import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
+import org.eclipse.e4.core.di.suppliers.IRequestor;
import org.eclipse.e4.core.internal.contexts.ContextObjectSupplier;
-
import org.eclipse.e4.core.internal.di.Requestor;
import org.eclipse.e4.core.services.translation.TranslationService;
-
-import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
-import org.eclipse.e4.core.di.suppliers.IRequestor;
-
-import org.eclipse.e4.core.di.suppliers.ExtendedObjectSupplier;
import org.eclipse.e4.tools.services.IMessageFactoryService;
+import org.eclipse.osgi.service.localization.BundleLocalization;
public class TranslationObjectSupplier extends ExtendedObjectSupplier {
@@ -39,10 +36,11 @@ public class TranslationObjectSupplier extends ExtendedObjectSupplier {
String locale = (String) sub.getContext().get(TranslationService.LOCALE);
locale = locale == null ? Locale.getDefault().toString() : locale;
+ BundleLocalization localization = sub.getContext().get(BundleLocalization.class);
IMessageFactoryService factoryService = sub.getContext().get(IMessageFactoryService.class);
try {
- return factoryService.createInstance(locale,descriptorsClass);
+ return factoryService.createInstance(locale, descriptorsClass, localization);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();

Back to the top