Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 331260 | Differences between
and this patch

Collapse All | Expand All

(-).project (+5 lines)
Lines 25-30 Link Here
25
			<arguments>
25
			<arguments>
26
			</arguments>
26
			</arguments>
27
		</buildCommand>
27
		</buildCommand>
28
		<buildCommand>
29
			<name>org.eclipse.pde.ds.core.builder</name>
30
			<arguments>
31
			</arguments>
32
		</buildCommand>
28
	</buildSpec>
33
	</buildSpec>
29
	<natures>
34
	<natures>
30
		<nature>org.eclipse.pde.PluginNature</nature>
35
		<nature>org.eclipse.pde.PluginNature</nature>
(-)META-INF/MANIFEST.MF (+3 lines)
Lines 11-16 Link Here
11
 CDC-1.1/Foundation-1.1
11
 CDC-1.1/Foundation-1.1
12
Import-Package: javax.inject,
12
Import-Package: javax.inject,
13
 org.eclipse.osgi.service.debug;version="1.1.0",
13
 org.eclipse.osgi.service.debug;version="1.1.0",
14
 org.eclipse.osgi.service.localization;version="1.1.0",
14
 org.osgi.framework;version="1.3.0",
15
 org.osgi.framework;version="1.3.0",
15
 org.osgi.service.event;resolution:=optional,
16
 org.osgi.service.event;resolution:=optional,
16
 org.osgi.util.tracker;version="1.4.2"
17
 org.osgi.util.tracker;version="1.4.2"
Lines 42-49 Link Here
42
   org.eclipse.e4.ui.workbench.swt,
43
   org.eclipse.e4.ui.workbench.swt,
43
   org.eclipse.ui.workbench",
44
   org.eclipse.ui.workbench",
44
 org.eclipse.e4.core.services.statusreporter;x-friends:="org.eclipse.e4.ui.workbench.swt",
45
 org.eclipse.e4.core.services.statusreporter;x-friends:="org.eclipse.e4.ui.workbench.swt",
46
 org.eclipse.e4.core.services.translation,
45
 org.eclipse.e4.core.services.util;x-internal:=true,
47
 org.eclipse.e4.core.services.util;x-internal:=true,
46
 org.eclipse.e4.core.services.work;x-friends:="org.eclipse.e4.ui.workbench"
48
 org.eclipse.e4.core.services.work;x-friends:="org.eclipse.e4.ui.workbench"
47
Eclipse-ExtensibleAPI: true
49
Eclipse-ExtensibleAPI: true
48
Bundle-ClassPath: injection_annotations.jar, .
50
Bundle-ClassPath: injection_annotations.jar, .
49
Bundle-Activator: org.eclipse.e4.core.internal.services.ServicesActivator
51
Bundle-Activator: org.eclipse.e4.core.internal.services.ServicesActivator
52
Service-Component: OSGI-INF/translationService.xml
(-)OSGI-INF/translationService.xml (+8 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.e4.core.services">
3
   <implementation class="org.eclipse.e4.core.internal.services.TranslationServiceImpl"/>
4
   <service>
5
      <provide interface="org.eclipse.e4.core.services.translation.ITranslationService"/>
6
   </service>
7
   <reference bind="dsRegisterTranslationProvider" cardinality="0..n" interface="org.eclipse.e4.core.services.translation.ITranslationProvider" name="ITranslationProvider" policy="static" unbind="dsUnregisterTranslationProvider"/>
8
</scr:component>
(-)build.properties (-12 / +3 lines)
Lines 1-19 Link Here
1
###############################################################################
2
#  Copyright (c) 2008 IBM Corporation and others.
3
#  All rights reserved. This program and the accompanying materials
4
#  are made available under the terms of the Eclipse Public License v1.0
5
#  which accompanies this distribution, and is available at
6
#  http://www.eclipse.org/legal/epl-v10.html
7
# 
8
#  Contributors:
9
#      IBM Corporation - initial API and implementation
10
###############################################################################
11
source.. = src/
12
output.. = bin/
1
output.. = bin/
13
bin.includes = META-INF/,\
2
bin.includes = META-INF/,\
14
               .,\
3
               .,\
15
               plugin.xml,\
4
               plugin.xml,\
16
               .options,\
5
               .options,\
17
               about.html,\
6
               about.html,\
18
               plugin.properties
7
               plugin.properties,\
8
               OSGI-INF/
19
src.includes = schema/
9
src.includes = schema/
10
source.. = src/
(-)src/org/eclipse/e4/core/internal/services/ServicesActivator.java (+37 lines)
Lines 11-16 Link Here
11
package org.eclipse.e4.core.internal.services;
11
package org.eclipse.e4.core.internal.services;
12
12
13
import org.eclipse.osgi.service.debug.DebugOptions;
13
import org.eclipse.osgi.service.debug.DebugOptions;
14
import org.eclipse.osgi.service.localization.BundleLocalization;
15
import org.eclipse.osgi.service.localization.LocaleProvider;
14
import org.osgi.framework.BundleActivator;
16
import org.osgi.framework.BundleActivator;
15
import org.osgi.framework.BundleContext;
17
import org.osgi.framework.BundleContext;
16
import org.osgi.util.tracker.ServiceTracker;
18
import org.osgi.util.tracker.ServiceTracker;
Lines 20-25 Link Here
20
	static private ServicesActivator defaultInstance;
22
	static private ServicesActivator defaultInstance;
21
	private BundleContext bundleContext;
23
	private BundleContext bundleContext;
22
	private ServiceTracker debugTracker = null;
24
	private ServiceTracker debugTracker = null;
25
	private ServiceTracker localeProviderTracker = null;
26
	private ServiceTracker bundleLocalizationTracker = null;
23
27
24
	public ServicesActivator() {
28
	public ServicesActivator() {
25
		defaultInstance = this;
29
		defaultInstance = this;
Lines 38-43 Link Here
38
			debugTracker.close();
42
			debugTracker.close();
39
			debugTracker = null;
43
			debugTracker = null;
40
		}
44
		}
45
46
		if (localeProviderTracker != null) {
47
			localeProviderTracker.close();
48
			localeProviderTracker = null;
49
		}
50
51
		if (bundleLocalizationTracker != null) {
52
			bundleLocalizationTracker.close();
53
			bundleLocalizationTracker = null;
54
		}
55
41
		bundleContext = null;
56
		bundleContext = null;
42
	}
57
	}
43
58
Lines 55-58 Link Here
55
		return defaultValue;
70
		return defaultValue;
56
	}
71
	}
57
72
73
	public LocaleProvider getLocaleProvider() {
74
		if (localeProviderTracker == null) {
75
			localeProviderTracker = new ServiceTracker(bundleContext,
76
					LocaleProvider.class.getName(), null);
77
			localeProviderTracker.open();
78
		}
79
80
		return (LocaleProvider) localeProviderTracker.getService();
81
	}
82
83
	public BundleLocalization getBundleLocalization() {
84
		if (bundleLocalizationTracker == null) {
85
			bundleLocalizationTracker = new ServiceTracker(bundleContext,
86
					BundleLocalization.class.getName(), null);
87
			bundleLocalizationTracker.open();
88
		}
89
		return (BundleLocalization) bundleLocalizationTracker.getService();
90
	}
91
92
	public BundleContext getContext() {
93
		return bundleContext;
94
	}
58
}
95
}
(-)src/org/eclipse/e4/core/internal/services/TranslationServiceImpl.java (+136 lines)
Added Link Here
1
package org.eclipse.e4.core.internal.services;
2
3
import java.util.Collections;
4
import java.util.HashMap;
5
import java.util.Locale;
6
import java.util.Map;
7
import java.util.MissingResourceException;
8
import java.util.ResourceBundle;
9
import org.eclipse.e4.core.services.translation.ITranslationProvider;
10
import org.eclipse.e4.core.services.translation.ITranslationService;
11
import org.eclipse.osgi.service.localization.LocaleProvider;
12
import org.osgi.framework.Bundle;
13
14
public class TranslationServiceImpl implements ITranslationService {
15
	private Map<String, ITranslationProvider> providerMap = Collections
16
			.synchronizedMap(new HashMap<String, ITranslationProvider>());
17
18
	public TranslationServiceImpl() {
19
		System.err.println("Service is created!!!");
20
	}
21
22
	private Locale getLocale() {
23
		LocaleProvider localeProvider = ServicesActivator.getDefault().getLocaleProvider();
24
		if (localeProvider != null) {
25
			Locale currentLocale = localeProvider.getLocale();
26
			if (currentLocale != null)
27
				return currentLocale;
28
		}
29
		return Locale.getDefault();
30
	}
31
32
	public String translate(String category, String key) {
33
		return translate(getLocale(), category, key);
34
	}
35
36
	public String translate(Locale locale, String category, String key) {
37
		ITranslationProvider pv = getProvider(category);
38
39
		if (pv != null) {
40
			return pv.translate(locale, category, key);
41
		}
42
43
		throw new MissingResourceException("Category '" + category + " is not known'", null, key);
44
	}
45
46
	private ITranslationProvider getProvider(String category) {
47
		ITranslationProvider pv = providerMap.get(category);
48
		if (pv == null) {
49
			pv = tryCreateBundleLocalization(category);
50
			// TODO Should we cache it? Then we need to add a tracker if the bundle is
51
			// installed/uninstalled
52
		}
53
54
		return pv;
55
	}
56
57
	private ITranslationProvider tryCreateBundleLocalization(String category) {
58
		for (Bundle b : ServicesActivator.getDefault().getContext().getBundles()) {
59
			String[] parts = category.split("@");
60
			if (b.getSymbolicName().equals(parts[0])
61
					&& (parts.length == 1 || parts[1].equals(b.getVersion().toString()))) {
62
				final Bundle bundle = b;
63
				return new ITranslationProvider() {
64
65
					public String translate(Locale locale, String category, String key) {
66
						ResourceBundle resBundle = ServicesActivator.getDefault()
67
								.getBundleLocalization().getLocalization(bundle, locale.toString());
68
						if (resBundle != null) {
69
							try {
70
								return resBundle.getString(key);
71
							} catch (Exception e) {
72
								throw new MissingResourceException("Could not find '" + key
73
										+ "' in bundle localization.", resBundle.getClass()
74
										.getName(), key);
75
							}
76
						}
77
						throw new MissingResourceException("Bundle '" + category
78
								+ " has no localization informations.'", null, key);
79
					}
80
				};
81
			}
82
		}
83
84
		return null;
85
	}
86
87
	public String[] translate(String category, String... keys) {
88
		return translate(getLocale(), category, keys);
89
	}
90
91
	public String[] translate(Locale locale, String category, String... keys) {
92
		ITranslationProvider pv = getProvider(category);
93
		if (pv != null) {
94
			String[] rv = new String[keys.length];
95
			for (int i = 0; i < keys.length; i++) {
96
				rv[i] = pv.translate(locale, category, keys[i]);
97
			}
98
			return rv;
99
		}
100
		throw new MissingResourceException("Category '" + category + " is not known'", null, null);
101
	}
102
103
	public void registerTranslationProvider(String category, ITranslationProvider provider) {
104
		if (providerMap.containsKey(category)) {
105
			throw new IllegalArgumentException(
106
					"Can not register more than one provider for category '" + category + "'."); //$NON-NLS-1$//$NON-NLS-2$
107
		}
108
		providerMap.put(category, provider);
109
	}
110
111
	public boolean unregisterTranslationProvider(String category, ITranslationProvider provider) {
112
		if (providerMap.get(category) == provider) {
113
			providerMap.remove(category);
114
			return true;
115
		}
116
		return false;
117
	}
118
119
	public void dsRegisterTranslationProvider(ITranslationProvider provider,
120
			Map<String, String> data) {
121
		for (String s : data.get("CATEGORIES").split(";")) { //$NON-NLS-1$//$NON-NLS-2$
122
			try {
123
				registerTranslationProvider(s, provider);
124
			} catch (IllegalArgumentException e) {
125
				// TODO: handle exception
126
			}
127
		}
128
	}
129
130
	public void dsUnregisterTranslationProvider(ITranslationProvider provider,
131
			Map<String, String> data) {
132
		for (String s : data.get("CATEGORIES").split(";")) { //$NON-NLS-1$//$NON-NLS-2$
133
			unregisterTranslationProvider(s, provider);
134
		}
135
	}
136
}
(-)src/org/eclipse/e4/core/services/events/IEventBroker.java (-13 / +7 lines)
Lines 10-19 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.e4.core.services.events;
11
package org.eclipse.e4.core.services.events;
12
12
13
import org.eclipse.e4.core.contexts.IEclipseContext;
14
15
import java.util.Dictionary;
13
import java.util.Dictionary;
16
import java.util.Map;
14
import java.util.Map;
15
import org.eclipse.e4.core.contexts.IEclipseContext;
17
import org.osgi.service.event.EventHandler;
16
import org.osgi.service.event.EventHandler;
18
17
19
/**
18
/**
Lines 28-33 Link Here
28
 */
27
 */
29
public interface IEventBroker {
28
public interface IEventBroker {
30
29
30
	public interface Subscription {
31
		public void unsubscribe();
32
	}
33
31
	/**
34
	/**
32
	 * The name of the default event attribute used to pass data.
35
	 * The name of the default event attribute used to pass data.
33
	 */
36
	 */
Lines 78-84 Link Here
78
	 * @return <code>true</code> if this operation was performed successfully; <code>false</code>
81
	 * @return <code>true</code> if this operation was performed successfully; <code>false</code>
79
	 *         otherwise
82
	 *         otherwise
80
	 */
83
	 */
81
	public boolean subscribe(String topic, EventHandler eventHandler);
84
	public Subscription subscribe(String topic, EventHandler eventHandler);
82
85
83
	/**
86
	/**
84
	 * Subscribe for events on the given topic.
87
	 * Subscribe for events on the given topic.
Lines 98-113 Link Here
98
	 * @return <code>true</code> if this operation was performed successfully; <code>false</code>
101
	 * @return <code>true</code> if this operation was performed successfully; <code>false</code>
99
	 *         otherwise
102
	 *         otherwise
100
	 */
103
	 */
101
	public boolean subscribe(String topic, String filter, EventHandler eventHandler,
104
	public Subscription subscribe(String topic, String filter, EventHandler eventHandler,
102
			boolean headless);
105
			boolean headless);
103
106
104
	/**
105
	 * Unsubscribe handler previously registered using {@link #subscribe(String, EventHandler)}.
106
	 * 
107
	 * @param eventHandler
108
	 *            previously registered event handler
109
	 * @return <code>true</code> if this operation was performed successfully; <code>false</code>
110
	 *         otherwise
111
	 */
112
	public boolean unsubscribe(EventHandler eventHandler);
113
}
107
}
(-)src/org/eclipse/e4/core/services/translation/ITranslationProvider.java (+51 lines)
Added Link Here
1
/*******************************************************************************
2
 *  Copyright (c) 2010 BestSolution.at and others.
3
 *  All rights reserved. This program and the accompanying materials
4
 *  are made available under the terms of the Eclipse Public License v1.0
5
 *  which accompanies this distribution, and is available at
6
 *  http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 *  Contributors:
9
 *      Tom Schind<tom.schindl@bestsolution.at> - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.e4.core.services.translation;
12
13
import java.util.Locale;
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
17
/**
18
 * Provides translation for a given category
19
 */
20
public interface ITranslationProvider {
21
	/**
22
	 * <p>
23
	 * Translate the give key in the given locale's text.
24
	 * </p>
25
	 * The lookup of the translation the same than the one in
26
	 * {@link ResourceBundle#getBundle(String, Locale, ClassLoader)}
27
	 * <ul>
28
	 * <li>Ls + "_" + Cs + "_" + Vs</li>
29
	 * <li>Ls + "_" + Cs</li>
30
	 * <li>Ls</li>
31
	 * <li>Ld + "_" + Cd + "_" + Vd</li>
32
	 * <li>Ld + "_" + Cd</li>
33
	 * <li>Ld</li>
34
	 * <li>DEFAULT</li>
35
	 * </ul>
36
	 * Where Ls, Cs and Vs are the specified locale (language, country, variant) and Ld, Cd and Vd
37
	 * are the default locale (language, country, variant).
38
	 * 
39
	 * @param locale
40
	 *            the locale, must not be <code>null</code>
41
	 * @param category
42
	 *            the category
43
	 * @param key
44
	 *            the key
45
	 * @return the translated value must not be <code>null</code>
46
	 * @throws MissingResourceException
47
	 *             if translation fails because of whatever reason
48
	 */
49
	public String translate(Locale locale, String category, String key)
50
			throws MissingResourceException;
51
}
(-)src/org/eclipse/e4/core/services/translation/ITranslationService.java (+146 lines)
Added Link Here
1
/*******************************************************************************
2
 *  Copyright (c) 2010 BestSolution.at and others.
3
 *  All rights reserved. This program and the accompanying materials
4
 *  are made available under the terms of the Eclipse Public License v1.0
5
 *  which accompanies this distribution, and is available at
6
 *  http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 *  Contributors:
9
 *      Tom Schind<tom.schindl@bestsolution.at> - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.e4.core.services.translation;
12
13
import java.util.Locale;
14
import java.util.MissingResourceException;
15
import java.util.ResourceBundle;
16
import org.eclipse.osgi.service.localization.BundleLocalization;
17
import org.eclipse.osgi.service.localization.LocaleProvider;
18
19
/**
20
 * Service which can be used to translate texts into locale specific ones.
21
 * 
22
 * The lookup of the translation the same than the one in
23
 * {@link ResourceBundle#getBundle(String, Locale, ClassLoader)}
24
 * <ul>
25
 * <li>Ls + "_" + Cs + "_" + Vs</li>
26
 * <li>Ls + "_" + Cs</li>
27
 * <li>Ls</li>
28
 * <li>Ld + "_" + Cd + "_" + Vd</li>
29
 * <li>Ld + "_" + Cd</li>
30
 * <li>Ld</li>
31
 * <li>DEFAULT</li>
32
 * </ul>
33
 * Where Ls, Cs and Vs are the specified locale (language, country, variant) and Ld, Cd and Vd are
34
 * the default locale (language, country, variant).
35
 */
36
public interface ITranslationService {
37
	/**
38
	 * Translate the key into the default locale.
39
	 * <p>
40
	 * <b>Locale lookup</b>
41
	 * <ul>
42
	 * <li>try to use {@link LocaleProvider} service</li>
43
	 * <li>use {@link Locale#getDefault()}</li>
44
	 * </ul>
45
	 * </p>
46
	 * <p>
47
	 * <b>Category lookup</b>
48
	 * <ul>
49
	 * <li>try to find a {@link ITranslationProvider} for the category</li>
50
	 * <li>interpret the value as a symbolic bundle name and try to access the
51
	 * {@link BundleLocalization}</li>
52
	 * </ul>
53
	 * </p>
54
	 * 
55
	 * @param category
56
	 *            the category/bundle name to use
57
	 * @param key
58
	 *            the key to translate
59
	 * @return the translated value, must not be <code>null</code>
60
	 * @throws MissingResourceException
61
	 *             if some problem occurrs while translating
62
	 */
63
	public String translate(String category, String key) throws MissingResourceException;
64
65
	/**
66
	 * Translate the key into the default locale.
67
	 * 
68
	 * <p>
69
	 * <b>Category lookup</b>
70
	 * <ul>
71
	 * <li>try to find a {@link ITranslationProvider} for the category</li>
72
	 * <li>interpret the value as a symbolic bundle name and try to access the
73
	 * {@link BundleLocalization}</li>
74
	 * </ul>
75
	 * </p>
76
	 * 
77
	 * @param locale
78
	 *            the locale to use
79
	 * @param category
80
	 *            the category/bundle name to use
81
	 * @param key
82
	 *            the key to translate
83
	 * @return the translated value, must not be <code>null</code>
84
	 * @throws MissingResourceException
85
	 *             if some problem occurrs while translating
86
	 */
87
	public String translate(Locale locale, String category, String key)
88
			throws MissingResourceException;
89
90
	/**
91
	 * Translate multiple keys at once. See {@link #translate(String, String)} for a detailed
92
	 * explanation how the locale and category look up is done
93
	 * 
94
	 * @param category
95
	 *            the category/bundle name to use
96
	 * @param keys
97
	 *            the keys
98
	 * @return the translated keys
99
	 * @throws MissingResourceException
100
	 *             if one of the keys is not found
101
	 * @see #translate(String, String)
102
	 */
103
	public String[] translate(String category, String... keys) throws MissingResourceException;
104
105
	/**
106
	 * Translate multiple keys at once. See {@link #translate(String, String)} for a detailed
107
	 * explanation how the category look up is done
108
	 * 
109
	 * @param locale
110
	 *            the locale to use
111
	 * @param category
112
	 *            the category/bundle name to use
113
	 * @param keys
114
	 *            the keys
115
	 * @return the translated keys
116
	 * @throws MissingResourceException
117
	 *             if one of the keys is not found
118
	 * @see #translate(String, String)
119
	 */
120
	public String[] translate(Locale locale, String category, String... keys)
121
			throws MissingResourceException;
122
123
	/**
124
	 * Register a translation provider for a category
125
	 * 
126
	 * @param category
127
	 *            the category
128
	 * @param provider
129
	 *            the provider
130
	 * @throws IllegalArgumentException
131
	 *             thrown if there's already a provider registered
132
	 */
133
	public void registerTranslationProvider(String category, ITranslationProvider provider)
134
			throws IllegalArgumentException;
135
136
	/**
137
	 * Unregister a translation provider for a category
138
	 * 
139
	 * @param category
140
	 *            the category of the provider
141
	 * @param provider
142
	 *            the provider instance to unregister
143
	 * @return <code>true</code> if unregistering succeeded
144
	 */
145
	public boolean unregisterTranslationProvider(String category, ITranslationProvider provider);
146
}
(-)src/org/eclipse/e4/core/services/translation/PropertiesBundleTranslationProvider.java (+218 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation in org.eclipse.osgi.framework.internal.core.ManifestLocalization
10
 *     Tom Schindl <tom.schindl@bestsolution.at> - Adjusted for useage out of equinox
11
 *******************************************************************************/
12
package org.eclipse.e4.core.services.translation;
13
14
import java.io.IOException;
15
import java.io.InputStream;
16
import java.util.ArrayList;
17
import java.util.Enumeration;
18
import java.util.Hashtable;
19
import java.util.List;
20
import java.util.Locale;
21
import java.util.MissingResourceException;
22
import java.util.PropertyResourceBundle;
23
import java.util.ResourceBundle;
24
import org.eclipse.osgi.service.localization.BundleLocalization;
25
26
/**
27
 * The lookup of the translation the same than the one in {@link BundleLocalization} which is based
28
 * upon the value found in equinox.root.locale which defaults to "en":
29
 * <ul>
30
 * <li>If set to empty string then the search order for:
31
 * <ul>
32
 * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
33
 * <li>bn + Ls + "_" + Cs</li>
34
 * <li>bn + Ls</li>
35
 * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
36
 * <li>bn + Ld + "_" + Cd</li>
37
 * <li>bn + Ld</li>
38
 * </ul>
39
 * </li>
40
 * <li>If Ls equals the value of equinox.root.locale then the following search order is used:
41
 * <ul>
42
 * <li>bn + Ls + "_" + Cs + "_" + Vs</li>
43
 * <li>bn + Ls + "_" + Cs</li>
44
 * <li>bn + Ls</li>
45
 * <li>bn + Ld + "_" + Cd + "_" + Vd</li>
46
 * <li>bn + Ld + "_" + Cd</li>
47
 * <li>bn + Ld</li>
48
 * </ul>
49
 * </li>
50
 * </ul>
51
 * Where bn is this bundle's localization basename, Ls, Cs and Vs are the specified locale
52
 * (language, country, variant) and Ld, Cd and Vd are the default locale (language, country,
53
 * variant).
54
 */
55
public class PropertiesBundleTranslationProvider implements ITranslationProvider {
56
	final static String DEFAULT_ROOT = getEquinoxRootLocale();
57
58
	private ClassLoader loader;
59
	private String baseName;
60
61
	private final Hashtable<String, BundleResourceBundle> cache = new Hashtable<String, BundleResourceBundle>(
62
			5);
63
64
	public PropertiesBundleTranslationProvider(ClassLoader loader, String baseName) {
65
		this.loader = loader;
66
		this.baseName = baseName;
67
	}
68
69
	private static String getEquinoxRootLocale() {
70
		// Logic from FrameworkProperties.getProperty("equinox.root.locale", "en")
71
		String root = System.getProperties().getProperty("equinox.root.locale");
72
		if (root == null) {
73
			root = "en";
74
		}
75
		return root;
76
	}
77
78
	public String translate(Locale locale, String category, String key) {
79
		String defaultLocale = Locale.getDefault().toString();
80
		String localeString = locale.toString();
81
		ResourceBundle bundle = getResourceBundle(locale.toString(),
82
				defaultLocale.equals(localeString));
83
		return bundle.getString(key);
84
	}
85
86
	private ResourceBundle getResourceBundle(String localeString, boolean isDefaultLocale) {
87
		BundleResourceBundle resourceBundle = lookupResourceBundle(localeString);
88
		if (isDefaultLocale)
89
			return (ResourceBundle) resourceBundle;
90
		// need to determine if this is resource bundle is an empty stem
91
		// if it is then the default locale should be used
92
		if (resourceBundle == null || resourceBundle.isStemEmpty())
93
			return (ResourceBundle) lookupResourceBundle(Locale.getDefault().toString());
94
		return (ResourceBundle) resourceBundle;
95
	}
96
97
	private interface BundleResourceBundle {
98
		void setParent(ResourceBundle parent);
99
100
		boolean isEmpty();
101
102
		boolean isStemEmpty();
103
	}
104
105
	private BundleResourceBundle lookupResourceBundle(String localeString) {
106
		// get the localization header as late as possible to avoid accessing the raw headers
107
		// getting the first value from the raw headers forces the manifest to be parsed (bug
108
		// 332039)
109
		String localizationHeader = baseName;
110
		synchronized (cache) {
111
			BundleResourceBundle result = cache.get(localeString);
112
			if (result != null)
113
				return result.isEmpty() ? null : result;
114
			String[] nlVarients = buildNLVariants(localeString);
115
			BundleResourceBundle parent = null;
116
117
			for (int i = nlVarients.length - 1; i >= 0; i--) {
118
				BundleResourceBundle varientBundle = null;
119
				InputStream resourceStream = loader.getResourceAsStream(localizationHeader
120
						+ (nlVarients[i].equals("") ? nlVarients[i] : '_' + nlVarients[i])
121
						+ ".properties");
122
123
				if (resourceStream == null) {
124
					varientBundle = cache.get(nlVarients[i]);
125
				} else {
126
					try {
127
						varientBundle = new LocalizationResourceBundle(resourceStream);
128
					} catch (IOException e) {
129
						// ignore and continue
130
					} finally {
131
						if (resourceStream != null) {
132
							try {
133
								resourceStream.close();
134
							} catch (IOException e3) {
135
								// Ignore exception
136
							}
137
						}
138
					}
139
				}
140
141
				if (varientBundle == null) {
142
					varientBundle = new EmptyResouceBundle(nlVarients[i]);
143
				}
144
				if (parent != null)
145
					varientBundle.setParent((ResourceBundle) parent);
146
				cache.put(nlVarients[i], varientBundle);
147
				parent = varientBundle;
148
			}
149
			result = cache.get(localeString);
150
			return result.isEmpty() ? null : result;
151
		}
152
	}
153
154
	private String[] buildNLVariants(String nl) {
155
		List<String> result = new ArrayList<String>();
156
		while (nl.length() > 0) {
157
			result.add(nl);
158
			int i = nl.lastIndexOf('_');
159
			nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
160
		}
161
		result.add(""); //$NON-NLS-1$
162
		return result.toArray(new String[result.size()]);
163
	}
164
165
	private class LocalizationResourceBundle extends PropertyResourceBundle implements
166
			BundleResourceBundle {
167
		public LocalizationResourceBundle(InputStream in) throws IOException {
168
			super(in);
169
		}
170
171
		public void setParent(ResourceBundle parent) {
172
			super.setParent(parent);
173
		}
174
175
		public boolean isEmpty() {
176
			return false;
177
		}
178
179
		public boolean isStemEmpty() {
180
			return parent == null;
181
		}
182
	}
183
184
	class EmptyResouceBundle extends ResourceBundle implements BundleResourceBundle {
185
		private final String localeString;
186
187
		public EmptyResouceBundle(String locale) {
188
			super();
189
			this.localeString = locale;
190
		}
191
192
		public Enumeration<String> getKeys() {
193
			return null;
194
		}
195
196
		protected Object handleGetObject(String arg0) throws MissingResourceException {
197
			return null;
198
		}
199
200
		public void setParent(ResourceBundle parent) {
201
			super.setParent(parent);
202
		}
203
204
		public boolean isEmpty() {
205
			if (parent == null)
206
				return true;
207
			return ((BundleResourceBundle) parent).isEmpty();
208
		}
209
210
		public boolean isStemEmpty() {
211
			if (DEFAULT_ROOT.equals(localeString))
212
				return false;
213
			if (parent == null)
214
				return true;
215
			return ((BundleResourceBundle) parent).isStemEmpty();
216
		}
217
	}
218
}
(-).project (+5 lines)
Lines 20-25 Link Here
20
			<arguments>
20
			<arguments>
21
			</arguments>
21
			</arguments>
22
		</buildCommand>
22
		</buildCommand>
23
		<buildCommand>
24
			<name>org.eclipse.pde.ds.core.builder</name>
25
			<arguments>
26
			</arguments>
27
		</buildCommand>
23
	</buildSpec>
28
	</buildSpec>
24
	<natures>
29
	<natures>
25
		<nature>org.eclipse.pde.PluginNature</nature>
30
		<nature>org.eclipse.pde.PluginNature</nature>
(-)CoreTestSuite.launch (-2 / +2 lines)
Lines 1-4 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
1
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
2
<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
3
<booleanAttribute key="append.args" value="true"/>
3
<booleanAttribute key="append.args" value="true"/>
4
<stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
4
<stringAttribute key="application" value="org.eclipse.pde.junit.runtime.coretestapplication"/>
Lines 25-31 Link Here
25
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
25
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
26
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
26
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
27
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.e4.core.tests.CoreTestSuite"/>
27
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.e4.core.tests.CoreTestSuite"/>
28
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
28
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console"/>
29
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.e4.core.tests"/>
29
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.e4.core.tests"/>
30
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
30
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
31
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx384m"/>
31
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx384m"/>
(-)META-INF/MANIFEST.MF (-2 / +3 lines)
Lines 13-19 Link Here
13
 org.eclipse.osgi.services;bundle-version="3.2.100",
13
 org.eclipse.osgi.services;bundle-version="3.2.100",
14
 com.google.code.atinject.tck;bundle-version="1.0.0",
14
 com.google.code.atinject.tck;bundle-version="1.0.0",
15
 org.eclipse.equinox.registry;bundle-version="3.5.0",
15
 org.eclipse.equinox.registry;bundle-version="3.5.0",
16
 org.eclipse.core.tests.harness;bundle-version="3.6.0"
16
 org.eclipse.core.tests.harness;bundle-version="3.6.0",
17
 org.eclipse.e4.core.services;bundle-version="0.9.1"
17
Bundle-ActivationPolicy: lazy
18
Bundle-ActivationPolicy: lazy
18
Bundle-RequiredExecutionEnvironment: J2SE-1.5
19
Bundle-RequiredExecutionEnvironment: J2SE-1.5
19
Import-Package: javax.annotation;version="1.0.0",
20
Import-Package: javax.annotation;version="1.0.0",
Lines 26-29 Link Here
26
 org.eclipse.e4.core.internal.tests.di.extensions;x-internal:=true,
27
 org.eclipse.e4.core.internal.tests.di.extensions;x-internal:=true,
27
 org.eclipse.e4.core.internal.tests.manual;x-internal:=true,
28
 org.eclipse.e4.core.internal.tests.manual;x-internal:=true,
28
 org.eclipse.e4.core.tests
29
 org.eclipse.e4.core.tests
29
Service-Component: OSGI-INF/adder.xml
30
Service-Component: OSGI-INF/adder.xml, OSGI-INF/customtranslations.xml
(-)OSGI-INF/customtranslations.xml (+8 lines)
Added Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.e4.core.tests.customtranslations">
3
   <implementation class="org.eclipse.e4.core.internal.tests.translation.CustomTranslationProvider"/>
4
   <service>
5
      <provide interface="org.eclipse.e4.core.services.translation.ITranslationProvider"/>
6
   </service>
7
   <property name="CATEGORIES" type="String" value="org.eclipse.e4.core.tests.customcat"/>
8
</scr:component>
(-)OSGI-INF/l10n/bundle.properties (+2 lines)
Added Link Here
1
key1 = Value En
2
key2 = Value En 2
(-)OSGI-INF/l10n/bundle_de.properties (+2 lines)
Added Link Here
1
key1 = Wert De
2
key2 = Wert De 2
(-)OSGI-INF/l10n/messages.properties (+2 lines)
Added Link Here
1
key1 = Cust Value En
2
key2 = Cust Value En 2
(-)OSGI-INF/l10n/messages_de.properties (+2 lines)
Added Link Here
1
key1 = Cust Wert De
2
key2 = Cust Wert De 2
(-)build.properties (-2 / +3 lines)
Lines 1-6 Link Here
1
source.. = src/
2
output.. = bin/
1
output.. = bin/
3
bin.includes = META-INF/,\
2
bin.includes = META-INF/,\
4
               .,\
3
               .,\
5
               test.xml,\
4
               test.xml,\
6
               OSGI-INF/
5
               OSGI-INF/,\
6
               OSGI-INF/customtranslations.xml
7
source.. = src/
(-)src/org/eclipse/e4/core/internal/tests/translation/CustomTranslationProvider.java (+12 lines)
Added Link Here
1
package org.eclipse.e4.core.internal.tests.translation;
2
3
import org.eclipse.e4.core.services.translation.PropertiesBundleTranslationProvider;
4
5
public class CustomTranslationProvider extends
6
		PropertiesBundleTranslationProvider {
7
	public CustomTranslationProvider() {
8
		super(CustomTranslationProvider.class.getClassLoader(),
9
				"OSGI-INF/l10n/messages");
10
11
	}
12
}
(-)src/org/eclipse/e4/core/internal/tests/translation/TestTranslationService.java (+132 lines)
Added Link Here
1
package org.eclipse.e4.core.internal.tests.translation;
2
3
import java.util.Locale;
4
import java.util.MissingResourceException;
5
6
import org.eclipse.e4.core.internal.tests.CoreTestsActivator;
7
import org.eclipse.e4.core.services.translation.ITranslationService;
8
import org.osgi.framework.BundleContext;
9
import org.osgi.framework.ServiceReference;
10
11
import junit.framework.TestCase;
12
13
public class TestTranslationService extends TestCase {
14
	private ITranslationService service;
15
16
	@Override
17
	protected void setUp() throws Exception {
18
		super.setUp();
19
		Locale.setDefault(Locale.ENGLISH);
20
	}
21
22
	public ITranslationService getService() {
23
		if (service == null) {
24
			BundleContext ctx = CoreTestsActivator.getDefault()
25
					.getBundleContext();
26
			ServiceReference<ITranslationService> ref = ctx
27
					.getServiceReference(ITranslationService.class);
28
			service = ctx.getService(ref);
29
		}
30
31
		return service;
32
	}
33
34
	public void testBundleTranslations() {
35
		String category = "org.eclipse.e4.core.tests";
36
		ITranslationService s = getService();
37
38
		assertEquals("Value En", s.translate(category, "key1"));
39
		assertEquals("Value En", s.translate(Locale.ENGLISH, category, "key1"));
40
41
		Locale.setDefault(Locale.GERMAN);
42
43
		assertEquals("Wert De", s.translate(category, "key1"));
44
		assertEquals("Wert De", s.translate(Locale.GERMAN, category, "key1"));
45
46
		assertEquals("Wert De",
47
				s.translate(Locale.FRENCH, "org.eclipse.e4.core.tests", "key1"));
48
49
		assertEquals("Value En", s.translate(Locale.ENGLISH, category, "key1"));
50
51
		try {
52
			s.translate(category, "unknown");
53
			fail("Unknown key has to fail with MissingResourceException");
54
		} catch (MissingResourceException e) {
55
		}
56
57
		try {
58
			s.translate(Locale.ENGLISH, category, "unknown");
59
			fail("Unknown key has to fail with MissingResourceException");
60
		} catch (MissingResourceException e) {
61
		}
62
63
		assertEquals(2, s.translate(category, "key1", "key2").length);
64
		assertEquals("Wert De", s.translate(category, "key1", "key2")[0]);
65
		assertEquals("Wert De 2", s.translate(category, "key1", "key2")[1]);
66
67
		assertEquals(2,
68
				s.translate(Locale.FRENCH, category, "key1", "key2").length);
69
		assertEquals("Wert De",
70
				s.translate(Locale.FRENCH, category, "key1", "key2")[0]);
71
		assertEquals("Wert De 2",
72
				s.translate(Locale.FRENCH, category, "key1", "key2")[1]);
73
74
		assertEquals(2,
75
				s.translate(Locale.ENGLISH, category, "key1", "key2").length);
76
		assertEquals("Value En",
77
				s.translate(Locale.ENGLISH, category, "key1", "key2")[0]);
78
		assertEquals("Value En 2",
79
				s.translate(Locale.ENGLISH, category, "key1", "key2")[1]);
80
	}
81
82
	public void testTranslationProvider() {
83
		String category = "org.eclipse.e4.core.tests.customcat";
84
		ITranslationService s = getService();
85
86
		assertEquals("Cust Value En", s.translate(category, "key1"));
87
		assertEquals("Cust Value En",
88
				s.translate(Locale.ENGLISH, category, "key1"));
89
90
		Locale.setDefault(Locale.GERMAN);
91
92
		assertEquals("Cust Wert De", s.translate(category, "key1"));
93
		assertEquals("Cust Wert De",
94
				s.translate(Locale.GERMAN, category, "key1"));
95
96
		assertEquals("Cust Wert De",
97
				s.translate(Locale.FRENCH, category, "key1"));
98
99
		assertEquals("Cust Value En",
100
				s.translate(Locale.ENGLISH, category, "key1"));
101
102
		try {
103
			s.translate(category, "unknown");
104
			fail("Unknown key has to fail with MissingResourceException");
105
		} catch (MissingResourceException e) {
106
		}
107
108
		try {
109
			s.translate(Locale.ENGLISH, category, "unknown");
110
			fail("Unknown key has to fail with MissingResourceException");
111
		} catch (MissingResourceException e) {
112
		}
113
114
		assertEquals(2, s.translate(category, "key1", "key2").length);
115
		assertEquals("Cust Wert De", s.translate(category, "key1", "key2")[0]);
116
		assertEquals("Cust Wert De 2", s.translate(category, "key1", "key2")[1]);
117
118
		assertEquals(2,
119
				s.translate(Locale.FRENCH, category, "key1", "key2").length);
120
		assertEquals("Cust Wert De",
121
				s.translate(Locale.FRENCH, category, "key1", "key2")[0]);
122
		assertEquals("Cust Wert De 2",
123
				s.translate(Locale.FRENCH, category, "key1", "key2")[1]);
124
125
		assertEquals(2,
126
				s.translate(Locale.ENGLISH, category, "key1", "key2").length);
127
		assertEquals("Cust Value En",
128
				s.translate(Locale.ENGLISH, category, "key1", "key2")[0]);
129
		assertEquals("Cust Value En 2",
130
				s.translate(Locale.ENGLISH, category, "key1", "key2")[1]);
131
	}
132
}
(-)src/org/eclipse/e4/core/tests/CoreTestSuite.java (+4 lines)
Lines 48-53 Link Here
48
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionEventTest;
48
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionEventTest;
49
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionMixedSuppliersTest;
49
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionMixedSuppliersTest;
50
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionPreferencesTest;
50
import org.eclipse.e4.core.internal.tests.di.extensions.InjectionPreferencesTest;
51
import org.eclipse.e4.core.internal.tests.translation.TestTranslationService;
51
52
52
public class CoreTestSuite extends TestSuite {
53
public class CoreTestSuite extends TestSuite {
53
	public static Test suite() {
54
	public static Test suite() {
Lines 96-100 Link Here
96
		addTestSuite(Bug317183Test.class);
97
		addTestSuite(Bug317183Test.class);
97
		addTestSuite(DependenciesLeakTest.class);
98
		addTestSuite(DependenciesLeakTest.class);
98
		addTestSuite(ActivationInjectionTest.class);
99
		addTestSuite(ActivationInjectionTest.class);
100
		
101
		// Translation
102
		addTestSuite(TestTranslationService.class);
99
	}
103
	}
100
}
104
}

Return to bug 331260