| /******************************************************************************* |
| * Copyright (c) 2010, 2017 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * Steven Spungin <steven@spungin.tv> - Bug 391061 |
| * Lars.Vogel <Lars.Vogel@vogella.com> - Bug 472654 |
| * Alex Blewitt <alex.blewitt@gmail.com> - Bug 476364 |
| *******************************************************************************/ |
| package org.eclipse.e4.core.di.internal.extensions; |
| |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; |
| import org.eclipse.core.runtime.preferences.IPreferencesService; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.di.IInjector; |
| import org.eclipse.e4.core.di.extensions.Preference; |
| 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.osgi.framework.FrameworkUtil; |
| import org.osgi.service.component.annotations.Component; |
| import org.osgi.service.component.annotations.Deactivate; |
| import org.osgi.service.component.annotations.Reference; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventHandler; |
| |
| /** |
| * Note: we do not support byte arrays in preferences at this time. This class |
| * is instantiated and wired by declarative services. |
| */ |
| @Component(service = { ExtendedObjectSupplier.class, EventHandler.class }, property = { |
| "dependency.injection.annotation=org.eclipse.e4.core.di.extensions.Preference", |
| "event.topics=" + IEclipseContext.TOPIC_DISPOSE }, immediate = true) |
| public class PreferencesObjectSupplier extends ExtendedObjectSupplier implements EventHandler { |
| |
| private IPreferencesService preferencesService; |
| |
| public IPreferencesService getPreferencesService() { |
| return preferencesService; |
| } |
| |
| @Reference |
| public void setPreferencesService(IPreferencesService preferenceService) { |
| this.preferencesService = preferenceService; |
| } |
| |
| static private class PrefInjectionListener implements IPreferenceChangeListener { |
| |
| final private IRequestor requestor; |
| final private IEclipsePreferences node; |
| final private String key; |
| |
| public PrefInjectionListener(IEclipsePreferences node, String key, IRequestor requestor) { |
| this.node = node; |
| this.key = key; |
| this.requestor = requestor; |
| } |
| |
| @Override |
| public void preferenceChange(final PreferenceChangeEvent event) { |
| if (!requestor.isValid()) { |
| node.removePreferenceChangeListener(this); |
| return; |
| } |
| |
| if (!event.getKey().equals(key)) { |
| return; |
| } |
| |
| requestor.resolveArguments(false); |
| requestor.execute(); |
| } |
| |
| public IRequestor getRequestor() { |
| return requestor; |
| } |
| |
| public void stopListening() { |
| node.removePreferenceChangeListener(this); |
| } |
| } |
| |
| // Hash (nodePath -> Hash (key -> list)) |
| private Map<String, HashMap<String, List<PrefInjectionListener>>> listenerCache = new HashMap<>(); |
| |
| @Override |
| public Object get(IObjectDescriptor descriptor, IRequestor requestor, boolean track, boolean group) { |
| if (descriptor == null) |
| return null; |
| Class<?> descriptorsClass = getDesiredClass(descriptor.getDesiredType()); |
| String nodePath = getNodePath(descriptor, requestor.getRequestingObjectClass()); |
| if (IEclipsePreferences.class.equals(descriptorsClass)) { |
| return InstanceScope.INSTANCE.getNode(nodePath); |
| } |
| |
| String key = getKey(descriptor); |
| if (key == null || nodePath == null || key.length() == 0 || nodePath.length() == 0) |
| return IInjector.NOT_A_VALUE; |
| if (track) |
| addListener(nodePath, key, requestor); |
| |
| if (descriptorsClass.isPrimitive()) { |
| if (descriptorsClass.equals(boolean.class)) |
| return getPreferencesService().getBoolean(nodePath, key, false, null); |
| else if (descriptorsClass.equals(int.class)) |
| return getPreferencesService().getInt(nodePath, key, 0, null); |
| else if (descriptorsClass.equals(double.class)) |
| return getPreferencesService().getDouble(nodePath, key, 0.0d, null); |
| else if (descriptorsClass.equals(float.class)) |
| return getPreferencesService().getFloat(nodePath, key, 0.0f, null); |
| else if (descriptorsClass.equals(long.class)) |
| return getPreferencesService().getLong(nodePath, key, 0L, null); |
| } |
| |
| if (String.class.equals(descriptorsClass)) |
| return getPreferencesService().getString(nodePath, key, null, null); |
| else if (Boolean.class.equals(descriptorsClass)) |
| return getPreferencesService().getBoolean(nodePath, key, false, null); |
| else if (Integer.class.equals(descriptorsClass)) |
| return getPreferencesService().getInt(nodePath, key, 0, null); |
| else if (Double.class.equals(descriptorsClass)) |
| return getPreferencesService().getDouble(nodePath, key, 0.0d, null); |
| else if (Float.class.equals(descriptorsClass)) |
| return getPreferencesService().getFloat(nodePath, key, 0.0f, null); |
| else if (Long.class.equals(descriptorsClass)) |
| return getPreferencesService().getLong(nodePath, key, 0L, null); |
| |
| return getPreferencesService().getString(nodePath, key, null, null); |
| } |
| |
| 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; |
| } |
| |
| private String getKey(IObjectDescriptor descriptor) { |
| if (descriptor == null) |
| return null; |
| Preference qualifier = descriptor.getQualifier(Preference.class); |
| return qualifier.value(); |
| } |
| |
| private String getNodePath(IObjectDescriptor descriptor, Class<?> requestingObject) { |
| if (descriptor == null) |
| return null; |
| Preference qualifier = descriptor.getQualifier(Preference.class); |
| String nodePath = qualifier.nodePath(); |
| |
| if (nodePath == null || nodePath.length() == 0) { |
| if (requestingObject == null) |
| return null; |
| nodePath = FrameworkUtil.getBundle(requestingObject).getSymbolicName(); |
| } |
| return nodePath; |
| } |
| |
| private void addListener(String nodePath, String key, final IRequestor requestor) { |
| if (requestor == null) |
| return; |
| synchronized (listenerCache) { |
| if (listenerCache.containsKey(nodePath)) { |
| HashMap<String, List<PrefInjectionListener>> map = listenerCache.get(nodePath); |
| if (map.containsKey(key)) { |
| for (PrefInjectionListener listener : map.get(key)) { |
| IRequestor previousRequestor = listener.getRequestor(); |
| if (previousRequestor.equals(requestor)) |
| return; // avoid adding duplicate listeners |
| } |
| } |
| } |
| } |
| final IEclipsePreferences node = InstanceScope.INSTANCE.getNode(nodePath); |
| PrefInjectionListener listener = new PrefInjectionListener(node, key, requestor); |
| node.addPreferenceChangeListener(listener); |
| |
| synchronized (listenerCache) { |
| HashMap<String, List<PrefInjectionListener>> map = listenerCache.get(nodePath); |
| if (map == null) { |
| map = new HashMap<>(); |
| listenerCache.put(nodePath, map); |
| } |
| List<PrefInjectionListener> listeningRequestors = map.get(key); |
| if (listeningRequestors == null) { |
| listeningRequestors = new ArrayList<>(); |
| map.put(key, listeningRequestors); |
| } |
| listeningRequestors.add(listener); |
| } |
| } |
| |
| @Deactivate |
| public void removeAllListeners() { |
| synchronized (listenerCache) { |
| for (HashMap<String, List<PrefInjectionListener>> map : listenerCache.values()) { |
| for (List<PrefInjectionListener> listeners : map.values()) { |
| if (listeners == null) |
| continue; |
| for (PrefInjectionListener listener : listeners) { |
| listener.stopListening(); |
| } |
| } |
| } |
| listenerCache.clear(); |
| } |
| } |
| |
| @Override |
| public void handleEvent(Event event) { |
| synchronized (listenerCache) { |
| for (Iterator<Map.Entry<String, HashMap<String, List<PrefInjectionListener>>>> nodesIterator = listenerCache |
| .entrySet().iterator(); nodesIterator.hasNext();) { |
| HashMap<String, List<PrefInjectionListener>> map = nodesIterator.next().getValue(); |
| for (Iterator<HashMap.Entry<String, List<PrefInjectionListener>>> valuesIterator = map.entrySet() |
| .iterator(); valuesIterator.hasNext();) { |
| List<PrefInjectionListener> listeners = valuesIterator.next().getValue(); |
| if (listeners != null) { |
| for (Iterator<PrefInjectionListener> listenerIterator = listeners.iterator(); listenerIterator |
| .hasNext();) { |
| PrefInjectionListener listener = listenerIterator.next(); |
| if (!listener.getRequestor().isValid()) { |
| listener.stopListening(); |
| listenerIterator.remove(); |
| } |
| } |
| |
| if (listeners.isEmpty()) { |
| valuesIterator.remove(); |
| } |
| } |
| } |
| |
| if (map.isEmpty()) { |
| nodesIterator.remove(); |
| } |
| } |
| } |
| } |
| |
| } |