blob: 7586463e5946c3ea8495305788b0fbcb785b5b5a [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2003, 2005 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
**********************************************************************/
package org.eclipse.wst.server.core.util;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.IPath;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.internal.Trace;
import org.eclipse.wst.server.core.model.ModuleFactoryDelegate;
/**
*
*
* @since 1.0
*/
public abstract class ProjectModuleFactoryDelegate extends ModuleFactoryDelegate {
protected static IResourceChangeListener listener;
protected static List factories = new ArrayList();
protected List added;
protected List removed;
// map from IProject to IModule[]
protected Map projects;
/**
* Construct a new ProjectModuleFactoryDelegate.
*/
public ProjectModuleFactoryDelegate() {
super();
factories.add(this);
addListener();
}
/**
* Cache any preexisting module.
* TODO: When/where is this called?
*/
protected void cacheModules() {
projects = new HashMap();
try {
IProject[] projects2 = getWorkspaceRoot().getProjects();
int size = projects2.length;
for (int i = 0; i < size; i++) {
//Trace.trace("caching: " + this + " " + projects[i] + " " + isValidModule(projects[i]));
if (isValidModule(projects2[i])) {
addModules(projects2[i]);
}
}
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error caching modules", e);
}
fireEvents();
}
/**
* Return the workspace root.
*
* @return the workspace root
*/
private static IWorkspaceRoot getWorkspaceRoot() {
return ResourcesPlugin.getWorkspace().getRoot();
}
/**
* Returns the modules for the given project, or null
* if this factory does not have a module for the given project.
*
* @param project a project
* @return an array of modules.
*/
public IModule[] getModules(IProject project) {
try {
return (IModule[]) projects.get(project);
} catch (Exception e) {
// ignore
}
return null;
}
/**
* Add a resource listener to the workspace.
*/
protected static void addListener() {
if (listener != null)
return;
listener = new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
Trace.trace(Trace.FINEST, "->- ProjectModuleFactoryDelegate listener responding to resource change: " + event.getType() + " ->-");
try {
IResourceDelta delta = event.getDelta();
//if (delta.getFlags() == IResourceDelta.MARKERS || delta.getFlags() == IResourceDelta.NO_CHANGE)
// return;
delta.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta visitorDelta) {
IResource resource = visitorDelta.getResource();
//Trace.trace(Trace.FINEST, "resource: " + resource);
// only respond changes within projects
if (resource != null && resource instanceof IProject) {
IProject project = (IProject) resource;
handleGlobalProjectChange(project, visitorDelta);
return true;
} else if (resource != null && resource.getProject() != null) {
return false;
} else
return true;
}
});
} catch (Exception e) {
//Trace.trace(Trace.SEVERE, "Error responding to resource change", e);
}
fireGlobalEvents();
Trace.trace(Trace.FINEST, "-<- Done ProjectModuleFactoryDelegate responding to resource change -<-");
}
};
ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
}
/**
* Handle changes to a project.
*
* @param project a project
* @param delta a resource delta
*/
protected static void handleGlobalProjectChange(final IProject project, IResourceDelta delta) {
// handle project level changes
Iterator iterator = factories.iterator();
while (iterator.hasNext()) {
ProjectModuleFactoryDelegate factory = (ProjectModuleFactoryDelegate) iterator.next();
//Trace.trace("Firing to: " + factory);
factory.handleProjectChange(project, delta);
}
// handle internal updates
iterator = factories.iterator();
while (iterator.hasNext()) {
ProjectModuleFactoryDelegate factory = (ProjectModuleFactoryDelegate) iterator.next();
//Trace.trace("Firing to: " + factory);
factory.handleProjectInternalChange(project, delta);
}
}
/**
* Fire the accumulated module factory events.
*/
protected static void fireGlobalEvents() {
Trace.trace(Trace.FINEST, "Firing global module event");
Iterator iterator = factories.iterator();
while (iterator.hasNext()) {
ProjectModuleFactoryDelegate factory = (ProjectModuleFactoryDelegate) iterator.next();
factory.updateProjects();
}
iterator = factories.iterator();
while (iterator.hasNext()) {
ProjectModuleFactoryDelegate factory = (ProjectModuleFactoryDelegate) iterator.next();
factory.fireEvents();
}
}
/**
* Temporary to make sure that all project modules are updated.
*/
private void updateProjects() {
IModule[] modules2 = getModules();
if (modules2 != null) {
int size = modules2.length;
for (int i = 0; i < size; i++) {
if (modules2[i] instanceof ProjectModule)
((ProjectModule) modules2[i]).update();
}
}
}
/**
* Handle changes to a project.
*
* @param project a project
* @param delta a resource delta
*/
private void handleProjectChange(final IProject project, IResourceDelta delta) {
if (projects.containsKey(project)) {
// already a module
if (((delta.getKind() & IResourceDelta.REMOVED) != 0) || !isValidModule(project)) {
removeModules(project);
}
} else {
// not a module
if (isValidModule(project)) {
addModules(project);
}
}
}
/**
* Handle changes to a project.
*
* @param project a project
* @param delta a resource delta
*/
private void handleProjectInternalChange(final IProject project, IResourceDelta delta) {
final IPath[] paths = getListenerPaths();
if (paths != null) {
final IModule[] modules = getModules(project);
for (int i = 0; i < modules.length; i++) {
final IModule module = modules[i];
if (module != null && module instanceof ProjectModule) {
// check for any changes to the module
final IPath root = ((ProjectModule) module).getRootFolder();
IResourceDelta rootDelta = delta.findMember(root);
if (rootDelta != null)
((ProjectModule) module).fireModuleChangeEvent(true, null, null, null);
// check for listener paths
final int size = paths.length;
class Temp {
boolean found = false;
}
final Temp temp = new Temp();
try {
delta.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta visitorDelta) {
if (temp.found)
return false;
IPath path = visitorDelta.getProjectRelativePath();
boolean prefix = false;
for (int j = 0; j < size && !temp.found; j++) {
if (paths[j].equals(path))
temp.found = true;
else if (path.isPrefixOf(paths[j]))
prefix = true;
}
if (temp.found) {
((ProjectModule) module).update();
return false;
} else if (prefix)
return true;
else
return false;
}
});
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error searching for listening paths", e);
}
}
}
}
}
/**
* Add a module for the given project.
*
* @param project a project
*/
private void addModules(IProject project) {
if (projects == null)
cacheModules();
IModule[] modules = createModules(project);
if (modules == null || modules.length == 0)
return;
projects.put(project, modules);
added = new ArrayList(2);
added.addAll(Arrays.asList(modules));
}
/**
* Remove the modules that represents the given project.
*
* @param project a project
*/
private void removeModules(IProject project) {
if (projects == null)
cacheModules();
try {
IModule[] modules = (IModule[]) projects.get(project);
projects.remove(project);
if (removed == null)
removed = new ArrayList(2);
removed.addAll(Arrays.asList(modules));
} catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error removing module project", e);
}
}
/**
* Fire the accumulated module factory events.
*/
protected void fireEvents() {
if ((added == null || added.isEmpty()) && (removed == null || removed.isEmpty()))
return;
IModule[] add = null;
if (added != null) {
add = new IModule[added.size()];
added.toArray(add);
}
IModule[] remove = null;
if (removed != null) {
remove = new IModule[removed.size()];
removed.toArray(remove);
}
fireModuleFactoryEvent(add, remove);
added = new ArrayList(2);
removed = new ArrayList(2);
}
/**
* Returns true if the project may contain modules of the correct type.
* This method is used only to improve performance.
*
* @param project a project
* @return <code>true</code> if the project may contain modules, and
* <code>false</code> if it definitely does not
*/
protected abstract boolean isValidModule(IProject project);
/**
* Creates the modules for a given project.
*
* @param project a project to create modules for
* @return a possibly empty array of modules
*/
protected abstract IModule[] createModules(IProject project);
/**
* Returns the list of resources that the module should listen to
* for state changes. The paths should be project relative paths.
* Subclasses can override this method to provide the paths.
*
* @return a possibly empty array of paths
*/
protected IPath[] getListenerPaths() {
return null;
}
}