Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 0da507f3f599960da65a7459c08b23b5466f0900 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*******************************************************************************
 * Copyright (c) 2011 BestSolution.at 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:
 *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
 *     Dirk Fauth <dirk.fauth@gmail.com> - modifications to support locale changes at runtime
 ******************************************************************************/
package org.eclipse.e4.core.internal.services;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.e4.core.di.annotations.Optional;
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.services.nls.IMessageFactoryService;
import org.eclipse.e4.core.services.nls.Message;
import org.eclipse.e4.core.services.translation.ResourceBundleProvider;
import org.eclipse.e4.core.services.translation.TranslationService;
import org.eclipse.osgi.service.localization.BundleLocalization;
import org.osgi.service.log.LogService;

@SuppressWarnings({ "restriction" })
public class TranslationObjectSupplier extends ExtendedObjectSupplier {

	private static LogService logService = ServicesActivator.getDefault().getLogService();

	/**
	 * The current active locale that gets injected for updating the message instances.
	 */
	private Locale locale;

	/**
	 * The service that gets {@link ResourceBundle} objects from a bundle with a given locale.
	 */
	@Inject
	private ResourceBundleProvider provider;

	/**
	 * The service that creates instances of message classes based on the current active locale, the
	 * {@link BundleLocalization} and the configuration used in the {@link Message} annotation.
	 */
	@Inject
	private IMessageFactoryService factoryService;

	/**
	 * Map that contains all {@link IRequestor} that requested an instance of a messages class. Used
	 * to inform all requestor if the instances have changed due to a locale change.
	 */
	private Map<Class<?>, Set<IRequestor>> listeners = new HashMap<Class<?>, Set<IRequestor>>();

	@Override
	public Object get(IObjectDescriptor descriptor, IRequestor requestor, boolean track,
			boolean group) {

		Class<?> descriptorsClass = getDesiredClass(descriptor.getDesiredType());

		if (track)
			addListener(descriptorsClass, requestor);

		return getMessageInstance(descriptorsClass);
	}

	/**
	 * Setting the {@link Locale} by using this method will cause to create new instances for all
	 * message classes that were requested before. It also notifys all {@link IRequestor} that
	 * requested those messages instance which causes dynamic reinjection.
	 * 
	 * @param locale
	 *            The {@link Locale} to use for creating the message instances.
	 */
	@Inject
	public void setLocale(@Optional @Named(TranslationService.LOCALE) String locale) {
		try {
			this.locale = locale == null ? Locale.getDefault() : ResourceBundleHelper
					.toLocale(locale);
		} catch (IllegalArgumentException e) {
			// parsing the locale String to a Locale failed because of invalid String, use the
			// default locale
			if (logService != null)
				logService.log(LogService.LOG_ERROR, e.getMessage()
						+ " - Default Locale will be used instead."); //$NON-NLS-1$
			this.locale = Locale.getDefault();
		} 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$
			this.locale = Locale.getDefault();
		}

		// update listener
		updateMessages();
	}

	/**
	 * Notify the {@link IRequestor}s of those instances that they need to update their message
	 * class instances.
	 */
	private void updateMessages() {
		for (Map.Entry<Class<?>, Set<IRequestor>> entry : this.listeners.entrySet()) {
			notifyRequestor(entry.getValue());
		}
	}

	/**
	 * Checks if for the specified descriptor class there is already an instance in the local cache.
	 * If not a new instance is created using the local configuration on {@link Locale},
	 * {@link BundleLocalization} and given descriptor class.
	 * 
	 * @param descriptorsClass
	 *            The class for which an instance is requested.
	 * @return The instance of the requested message class
	 */
	private Object getMessageInstance(Class<?> descriptorsClass) {
		return this.factoryService.getMessageInstance(this.locale, descriptorsClass, this.provider);
	}

	/**
	 * Remember the {@link IRequestor} that requested an instance of the given descriptor class.
	 * This is needed to be able to inform all {@link IRequestor} if the {@link Locale} changes at
	 * runtime.
	 * 
	 * @param descriptorsClass
	 *            The class for which an instance was requested.
	 * @param requestor
	 *            The {@link IRequestor} that requested the instance.
	 */
	private void addListener(Class<?> descriptorsClass, IRequestor requestor) {
		Set<IRequestor> registered = this.listeners.get(descriptorsClass);
		if (registered == null) {
			registered = new HashSet<IRequestor>();
			this.listeners.put(descriptorsClass, registered);
		}
		registered.add(requestor);
	}

	/**
	 * Notify all given {@link IRequestor} about changes for their injected values. This way the
	 * dynamic injection is performed.
	 * 
	 * @param requestors
	 *            The {@link IRequestor} to inform about the instance changes.
	 */
	private void notifyRequestor(Collection<IRequestor> requestors) {
		if (requestors != null) {
			for (Iterator<IRequestor> it = requestors.iterator(); it.hasNext();) {
				IRequestor requestor = it.next();
				if (!requestor.isValid()) {
					it.remove();
					continue;
				}
				requestor.resolveArguments(false);
				requestor.execute();
			}
		}
	}

	private Class<?> getDesiredClass(Type desiredType) {
		if (desiredType instanceof Class<?>)
			return (Class<?>) desiredType;
		if (desiredType instanceof ParameterizedType) {
			Type rawType = ((ParameterizedType) desiredType).getRawType();
			if (rawType instanceof Class<?>)
				return (Class<?>) rawType;
		}
		return null;
	}
}

Back to the top