blob: c52c2afdb36411a03e609e03d1f9bde244ca7254 [file] [log] [blame]
/*******************************************************************************
* 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.tools.services.impl;
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.translation.TranslationService;
import org.eclipse.e4.tools.services.IMessageFactoryService;
import org.eclipse.e4.tools.services.Message;
import org.eclipse.e4.tools.services.ToolsServicesActivator;
import org.eclipse.osgi.service.localization.BundleLocalization;
import org.osgi.service.log.LogService;
@SuppressWarnings("rawtypes")
public class TranslationObjectSupplier extends ExtendedObjectSupplier {
private static LogService logService = ToolsServicesActivator.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 BundleLocalization localization;
/**
* 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.localization);
}
/**
* 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;
}
}