blob: 2d03d983a3b67379d40706231aeba6b84f7dea0f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 John Krasnay 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:
* John Krasnay - initial API and implementation
* Igor Jacy Lino Campista - Java 5 warnings fixed (bug 311325)
*******************************************************************************/
package org.eclipse.wst.xml.vex.ui.internal.config;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.widgets.Display;
import org.eclipse.wst.xml.vex.core.internal.core.ListenerList;
/**
* Singleton registry of configuration sources and listeners.
*
* The configuration sources may be accessed by multiple threads, and are
* protected by a lock. All methods that modify or iterate over config sources
* do so after acquiring the lock. Callers that wish to perform multiple
* operations as an atomic transaction must lock and unlock the registry as
* follows.
*
* <pre>
* ConfigRegistry reg = ConfigRegistry.getInstance();
* try {
* reg.lock();
* // make modifications
* } finally {
* reg.unlock();
* }
* </pre>
*
* <p>
* This class also maintains a list of ConfigListeners. The addConfigListener
* and removeConfigListener methods must be called from the main UI thread. The
* fireConfigXXX methods may be called from other threads; this class will
* ensure the listeners are called on the UI thread.
*/
public class ConfigRegistry {
/**
* Returns the singleton instance of the registry.
*/
public static ConfigRegistry getInstance() {
return instance;
}
/**
* Add a VexConfiguration to the list of configurations.
*
* @param config
* VexConfiguration to be added.
*/
public void addConfigSource(ConfigSource config) {
try {
this.lock();
this.configs.add(config);
} finally {
this.unlock();
}
}
/**
* Adds a ConfigChangeListener to the notification list.
*
* @param listener
* Listener to be added.
*/
public void addConfigListener(IConfigListener listener) {
this.configListeners.add(listener);
}
/**
* Call the configChanged method on all registered ConfigChangeListeners.
* The listeners are called from the display thread, even if this method was
* called from another thread.
*
* @param e
* ConfigEvent to be fired.
*/
public void fireConfigChanged(final ConfigEvent e) {
if (this.isConfigLoaded()) {
Runnable runnable = new Runnable() {
public void run() {
configListeners.fireEvent("configChanged", e); //$NON-NLS-1$
}
};
Display display = Display.getDefault();
if (display.getThread() == Thread.currentThread()) {
runnable.run();
} else {
display.asyncExec(runnable);
}
}
}
/**
* Call the configLoaded method on all registered ConfigChangeListeners from
* the display thread. This method is called from the ConfigLoaderJob
* thread.
*
* @param e
* ConfigEvent to be fired.
*/
public void fireConfigLoaded(final ConfigEvent e) {
Runnable runnable = new Runnable() {
public void run() {
configLoaded = true;
configListeners.fireEvent("configLoaded", e); //$NON-NLS-1$
}
};
Display.getDefault().asyncExec(runnable);
}
/**
* Returns an array of all config item factories.
*/
public IConfigItemFactory[] getAllConfigItemFactories() {
return configItemFactories.toArray(new IConfigItemFactory[configItemFactories.size()]);
}
/**
* Returns an array of all registered ConfigItem objects implementing the
* given extension point.
*
* @param extensionPoint
* ID of the desired extension point.
*/
public List<ConfigItem> getAllConfigItems(String extensionPoint) {
try {
this.lock();
List<ConfigItem> items = new ArrayList<ConfigItem>();
for (ConfigSource config : this.configs) {
items.addAll(config.getValidItems(extensionPoint));
}
return items;
} finally {
this.unlock();
}
}
/**
* Returns a list of all registered ConfigSource objects.
*
* @return
*/
public List<ConfigSource> getAllConfigSources() {
try {
this.lock();
List<ConfigSource> result = new ArrayList<ConfigSource>();
result.addAll(this.configs);
return result;
} finally {
this.unlock();
}
}
/**
* Returns a specific configuration item given an extension point id and the
* item's id. Returns null if either the extension point or the item is not
* found.
*
* @param extensionPoint
* ID of the desired extension point.
* @param id
* ID of the desired item.
*/
public ConfigItem getConfigItem(String extensionPoint, String id) {
try {
this.lock();
for (ConfigItem item : this.getAllConfigItems(extensionPoint)) {
if (item.getUniqueId().equals(id)) {
return item;
}
}
return null;
} finally {
this.unlock();
}
}
/**
* Returns the IConfigItemFactory object for the given extension point or
* null if none exists.
*
* @param extensionPointId
* Extension point ID for which to search.
*/
public IConfigItemFactory getConfigItemFactory(String extensionPointId) {
for (IConfigItemFactory factory : this.configItemFactories) {
if (factory.getExtensionPointId().equals(extensionPointId)) {
return factory;
}
}
return null;
}
/**
* Returns true if the Vex configuration has been loaded.
*
* @see org.eclipse.wst.xml.vex.ui.internal.config.ConfigLoaderJob
*/
public boolean isConfigLoaded() {
return this.configLoaded;
}
/**
* Locks the registry for modification or iteration over its config sources.
*/
public void lock() {
this.lock.acquire();
}
/**
* Remove a VexConfiguration from the list of configs.
*
* @param config
* VexConfiguration to remove.
*/
public void removeConfigSource(ConfigSource config) {
try {
this.lock();
this.configs.remove(config);
} finally {
this.unlock();
}
}
/**
* Removes a ConfigChangeListener from the notification list.
*
* @param listener
* Listener to be removed.
*/
public void removeConfigListener(IConfigListener listener) {
this.configListeners.remove(listener);
}
/**
* Unlocks the registry.
*/
public void unlock() {
this.lock.release();
}
// ======================================================== PRIVATE
private static ConfigRegistry instance = new ConfigRegistry();
private ILock lock = Job.getJobManager().newLock();
private List<ConfigSource> configs = new ArrayList<ConfigSource>();
private ListenerList<IConfigListener, ConfigEvent> configListeners =
new ListenerList<IConfigListener, ConfigEvent>(IConfigListener.class);
private boolean configLoaded = false;
private List<IConfigItemFactory> configItemFactories =
new ArrayList<IConfigItemFactory>();
/**
* Class constructor. All initialization is performed here.
*/
private ConfigRegistry() {
configItemFactories.add(new DoctypeFactory());
configItemFactories.add(new StyleFactory());
// TODO do we ever unregister this?
ResourcesPlugin.getWorkspace().addResourceChangeListener(
this.resourceChangeListener);
}
private IResourceChangeListener resourceChangeListener = new IResourceChangeListener() {
public void resourceChanged(final IResourceChangeEvent event) {
// System.out.println("resourceChanged, type is " + event.getType()
// + ", resource is " + event.getResource());
if (event.getType() == IResourceChangeEvent.PRE_CLOSE
|| event.getType() == IResourceChangeEvent.PRE_DELETE) {
PluginProject pp = PluginProject.get((IProject) event
.getResource());
if (pp != null) {
// System.out.println(" removing project from config registry");
removeConfigSource(pp);
fireConfigChanged(new ConfigEvent(this));
}
} else if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
IResourceDelta[] resources = event.getDelta()
.getAffectedChildren();
for (final IResourceDelta delta : resources) {
if (delta.getResource() instanceof IProject) {
final IProject project = (IProject) delta.getResource();
// System.out.println("Project " + project.getName() +
// " changed, isOpen is " + project.isOpen());
PluginProject pluginProject = PluginProject
.get(project);
boolean hasPluginProjectNature = false;
try {
hasPluginProjectNature = project
.hasNature(PluginProjectNature.ID);
} catch (CoreException ex) {
// yup, sometimes checked exceptions really blow
}
if (!project.isOpen() && pluginProject != null) {
// System.out.println(" closing project: " +
// project.getName());
removeConfigSource(pluginProject);
fireConfigChanged(new ConfigEvent(this));
} else if (project.isOpen() && pluginProject == null
&& hasPluginProjectNature) {
// System.out.println(" newly opened project: " +
// project.getName() + ", rebuilding");
// Must be run in another thread, since the
// workspace is locked here
Runnable runnable = new Runnable() {
public void run() {
PluginProject.load(project);
}
};
Display.getDefault().asyncExec(runnable);
} else {
// System.out.println(" no action taken");
}
}
}
}
}
};
}