/******************************************************************************* * Copyright (c) 2009 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 * Zend Technologies *******************************************************************************/ package org2.eclipse.php.internal.core.preferences; import java.io.File; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; /** * PreferencesPropagator for propagation of preferences events that arrive as a * result from changes in the workspace preferences store and in the * project-specific preferences nodes. * * @author shalom */ @SuppressWarnings({"unchecked", "deprecation", "rawtypes"}) public class PreferencesPropagator extends AbstractPreferencesPropagator { private HashMap projectToPropagator; private HashMap projectToScope; private HashMap projectToNodeListener; private HashMap preferenceChangeListeners; // Inside the node listeners private String nodeQualifier; private IPropertyChangeListener propertyChangeListener; private Preferences preferenceStore; /** * Constructs a new PreferencesPropagator. */ protected PreferencesPropagator(String nodeQualifier, Preferences preferenceStore) { this.nodeQualifier = nodeQualifier; this.preferenceStore = preferenceStore; install(); } /** * Adds an IPreferencesPropagatorListener with a preferences key to listen * to. * * @param listener * An IPreferencesPropagatorListener. * @param preferencesKey * The preferences key that will screen the relevant changes. */ public void addPropagatorListener(IPreferencesPropagatorListener listener, String preferencesKey) { addNodeListener(listener.getProject(), getProjectScope(listener .getProject())); if (isProjectSpecific(listener.getProject(), preferencesKey)) { addToProjectPropagator(listener, preferencesKey); } else { super.addPropagatorListener(listener, preferencesKey); } } /** * Removes an IPreferencesPropagatorListener that was assigned to listen to * the given preferences key. * * @param listener * An IPreferencesPropagatorListener. * @param preferencesKey * The preferences key that is the screening key for the * IPreferencesPropagatorListener. */ public void removePropagatorListener( IPreferencesPropagatorListener listener, String preferencesKey) { if (isProjectSpecific(listener.getProject(), preferencesKey)) { removeFromProjectPropagator(listener, preferencesKey); } else { super.removePropagatorListener(listener, preferencesKey); } } /** * Sets a list of listeners for the given preferences key. This list will * replace any previous list of listeners for the key. * * @param listeners * A List of listeners that contains * IPreferencesPropagatorListeners. * @param preferencesKey * The preferences key that will screen the relevant changes. */ public void setPropagatorListeners(List listeners, String preferencesKey) { super.setPropagatorListeners(listeners, preferencesKey); } /** * Install the preferences propagator. */ protected synchronized void install() { if (isInstalled) { return; } projectToPropagator = new HashMap(); projectToScope = new HashMap(); projectToNodeListener = new HashMap(); preferenceChangeListeners = new HashMap(); propertyChangeListener = new InnerPropertyChangeListener(); preferenceStore.addPropertyChangeListener(propertyChangeListener); super.install(); } /** * Uninstall the preferences propagator. */ protected synchronized void uninstall() { if (!isInstalled) { return; } // remove the node listeners Iterator projects = projectToNodeListener.keySet().iterator(); try { while (projects.hasNext()) { Object project = projects.next(); ProjectScope scope = (ProjectScope) projectToScope.get(project); IEclipsePreferences node = scope.getNode(nodeQualifier); if (node != null) { node .removeNodeChangeListener((INodeChangeListener) projectToNodeListener .get(project)); } } } catch (Exception e) { } // uninstall the project propagators Iterator propagators = projectToPropagator.values().iterator(); while (propagators.hasNext()) { ((ProjectPreferencesPropagator) propagators.next()).uninstall(); } preferenceChangeListeners = null; // TODO - remove the listening, if // needed ??? projectToScope = null; projectToPropagator = null; projectToNodeListener = null; super.uninstall(); } /* * Add the listener to a ProjectPreferencesPropagator. Create a new * propagator if needed. */ private void addToProjectPropagator( IPreferencesPropagatorListener listener, String preferencesKey) { ProjectPreferencesPropagator propagator = (ProjectPreferencesPropagator) projectToPropagator .get(listener.getProject()); if (propagator == null) { propagator = new ProjectPreferencesPropagator( listener.getProject(), nodeQualifier); projectToPropagator.put(listener.getProject(), propagator); } propagator.addPropagatorListener(listener, preferencesKey); } /* * Removes a listener from a ProjectPreferencesPropagator. If not * ProjectPreferencesPropagator exists, nothing will happen. */ private void removeFromProjectPropagator( IPreferencesPropagatorListener listener, String preferencesKey) { ProjectPreferencesPropagator propagator = (ProjectPreferencesPropagator) projectToPropagator .get(listener.getProject()); if (propagator != null) { propagator.removePropagatorListener(listener, preferencesKey); } } /* * Returns true if the given project has a specific settings for the given * key; false, otherwise. */ private boolean isProjectSpecific(IProject project, String preferencesKey) { ProjectScope projectScope = getProjectScope(project); IPath location = projectScope.getLocation(); if (location != null && new File(location.toOSString()).exists()) { return projectScope.getNode(nodeQualifier) .get(preferencesKey, null) != null; } return false; } /* * Returns a ProjectScope for the given IProject. */ private ProjectScope getProjectScope(IProject project) { ProjectScope scope = (ProjectScope) projectToScope.get(project); if (scope == null) { scope = new ProjectScope(project); projectToScope.put(project, scope); } return scope; } /* * Adds a node listener to the parent node of the project preferences scope. */ private void addNodeListener(IProject project, ProjectScope projectScope) { if (projectToNodeListener.get(project) != null) { // We already have a node listener defined return; } IEclipsePreferences node = projectScope.getNode(nodeQualifier); if (node != null) { IEclipsePreferences.INodeChangeListener nodeListener = new InnerNodeChangeListener( project); ((IEclipsePreferences) node.parent()) .addNodeChangeListener(nodeListener); projectToNodeListener.put(project, nodeListener); if (!preferenceChangeListeners.containsValue(node)) { IPreferenceChangeListener changeListener = new NodePreferenceChangeListener( project); node.addPreferenceChangeListener(changeListener); preferenceChangeListeners.put(changeListener, node); } } } private void notifyEvent(PreferencesPropagatorEvent event, String propertyKey, IProject project) { synchronized (lock) { if (project != null) { // The event arrived from the NodePreferenceChangeListener when // we moved to a project-specific settings. ProjectPreferencesPropagator ppp = (ProjectPreferencesPropagator) projectToPropagator .get(project); if (ppp == null) { ppp = new ProjectPreferencesPropagator(project, nodeQualifier); projectToPropagator.put(project, ppp); } ppp.notifyPropagatorEvent(event); } else { List list = getPropagatorListeners(propertyKey); if (list == null) return; IPreferencesPropagatorListener[] listeners = new IPreferencesPropagatorListener[list .size()]; list.toArray(listeners); for (IPreferencesPropagatorListener element : listeners) { element.preferencesEventOccured(event); } } } } /* * An inner node change listener that should listen to any change in the * project-scope parent node and should make all the needed listener swaps. */ private class InnerNodeChangeListener implements IEclipsePreferences.INodeChangeListener { private IProject project; public InnerNodeChangeListener(IProject project) { this.project = project; } /* * When a node is added, there is a move for a project-specific * prefernces, thus, we should divert all the listeners for the project * to the ProjectPreferencesPropagator. */ public void added(NodeChangeEvent event) { IEclipsePreferences pNode = null; if (event.getChild() instanceof IEclipsePreferences) { pNode = (IEclipsePreferences) event.getChild(); } else { return; } if (!preferenceChangeListeners.containsValue(pNode)) { IPreferenceChangeListener changeListener = new NodePreferenceChangeListener( project); pNode.addPreferenceChangeListener(changeListener); preferenceChangeListeners.put(changeListener, pNode); } } /* * (non-Javadoc) * * @seeorg.eclipse.core.runtime.preferences. * IEclipsePreferences$INodeChangeListener * #removed(org.eclipse.core.runtime * .preferences.IEclipsePreferences.NodeChangeEvent) */ public void removed(NodeChangeEvent event) { Object childNode = event.getChild(); if (preferenceChangeListeners.containsValue(childNode)) { // search for the listener to be removed Iterator keys = preferenceChangeListeners.keySet().iterator(); while (keys.hasNext()) { Object key = keys.next(); IEclipsePreferences aNode = (IEclipsePreferences) preferenceChangeListeners .get(key); if (aNode == childNode) { preferenceChangeListeners.remove(key); return; } } } } } /* * An inner preferences node change listener */ public class NodePreferenceChangeListener implements IPreferenceChangeListener { private IProject project; public NodePreferenceChangeListener(IProject project) { this.project = project; } public void preferenceChange(PreferenceChangeEvent event) { String key = event.getKey(); String newValue = (String) event.getNewValue(); if (newValue == null) { // We are moving from the project scope to the workspace scope removeFromProjectPropagator(key, project); } else { // We are moving from the workspace to the project specific // preferences moveToProjectPropagator(key, project); } PreferencesPropagatorEvent e = new PreferencesPropagatorEvent(event .getSource(), event.getOldValue(), event.getNewValue(), event.getKey()); notifyEvent(e, key, (newValue == null) ? null : project); } } /* * Move the listeners to the project preferences propagator. */ private void moveToProjectPropagator(String key, IProject project) { List list = null; IPreferencesPropagatorListener[] listeners = null; synchronized (lock) { list = getPropagatorListeners(key); if (list == null) { return; } listeners = new IPreferencesPropagatorListener[list.size()]; list.toArray(listeners); } for (IPreferencesPropagatorListener listener : listeners) { if (project.equals(listener.getProject())) { // Move the listener from this propagator to the project // specific preferences propagator super.removePropagatorListener(listener, key); addToProjectPropagator(listener, key); } } } /* * Remove the listeners from the project preferences propagator. */ private void removeFromProjectPropagator(String key, IProject project) { synchronized (lock) { ProjectPreferencesPropagator propagator = (ProjectPreferencesPropagator) projectToPropagator .get(project); if (propagator != null) { // Remove the listeners from the project propagator and place // them in this propagator. List listeners = propagator.removePropagatorListeners(key); if (listeners != null && listeners.size() > 0) { Iterator iter = listeners.iterator(); while (iter.hasNext()) { addPropagatorListener( (IPreferencesPropagatorListener) iter.next(), key); } } } } } /* * An inner IPropertyChangeListener that listens to the workspace changes * and notify all the registered listeners. */ private class InnerPropertyChangeListener implements IPropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { // Collect and notify the listeners that are defined to listen to // workspace preferences changes // for the arrived event. PreferencesPropagatorEvent e = new PreferencesPropagatorEvent(event .getSource(), event.getOldValue(), event.getNewValue(), event.getProperty()); notifyEvent(e, event.getProperty(), null); } } }