[204567] Dependency Graph Manager rewrite
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraph.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraph.java
index e811f34..74341f2 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraph.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraph.java
@@ -10,45 +10,39 @@
*******************************************************************************/
package org.eclipse.wst.common.componentcore.internal.builder;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.util.Collections;
import java.util.Set;
import org.eclipse.core.resources.IProject;
+/**
+ * @deprecated use {@link IDependencyGraph}
+ */
public class DependencyGraph {
-
+
private static final DependencyGraph INSTANCE = new DependencyGraph();
-
- private final Map dependencies = new HashMap();
-
+
public static DependencyGraph getInstance() {
return INSTANCE;
}
public IProject[] getReferencingComponents(IProject target) {
- Set referencingComponents = internalGetReferencingComponents(target);
+ Set<IProject> referencingComponents = IDependencyGraph.INSTANCE.getReferencingComponents(target);
return (IProject[]) referencingComponents.toArray(new IProject[referencingComponents.size()]);
}
-
+
public void addReference(IProject target, IProject referencingComponent) {
- internalGetReferencingComponents(target).add(referencingComponent);
+ // do nothing
}
-
+
public void removeReference(IProject target, IProject referencingComponent) {
- internalGetReferencingComponents(target).remove(referencingComponent);
-
+ // do nothing
}
-
+
protected Set internalGetReferencingComponents(IProject target) {
- Set referencingComponents = (Set) dependencies.get(target);
- if(referencingComponents == null)
- dependencies.put(target, (referencingComponents = new HashSet()));
- return referencingComponents;
+ return Collections.EMPTY_SET;
}
-
+
public void clear() {
- dependencies.clear();
}
}
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphImpl.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphImpl.java
new file mode 100644
index 0000000..075591e
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphImpl.java
@@ -0,0 +1,447 @@
+package org.eclipse.wst.common.componentcore.internal.builder;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.wst.common.componentcore.ComponentCore;
+import org.eclipse.wst.common.componentcore.internal.ModulecorePlugin;
+import org.eclipse.wst.common.componentcore.internal.impl.WTPModulesResourceFactory;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
+import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
+
+public class DependencyGraphImpl implements IDependencyGraph {
+
+ /**
+ * Don't read or write the graph without first obtaining the graphLock.
+ */
+ private Object graphLock = new Object();
+
+ /**
+ * If projects and and B both depend on C an entry in this graph would be {C ->
+ * {A, B} }
+ */
+ private Map<IProject, Set<IProject>> graph = null;
+
+ private long modStamp = 0;
+
+ /**
+ * This is not public; only {@link IDependencyGraph#INSTANCE} should be
+ * used.
+ *
+ * @return
+ */
+ static IDependencyGraph getInstance() {
+ if (instance == null) {
+ instance = new DependencyGraphImpl();
+ instance.initGraph();
+ }
+ return instance;
+ }
+
+ private static DependencyGraphImpl instance = null;
+
+ private DependencyGraphImpl() {
+ }
+
+ public long getModStamp() {
+ synchronized (graphLock) {
+ return modStamp;
+ }
+ }
+
+ public Set<IProject> getReferencingComponents(IProject targetProject) {
+ waitForAllUpdates(null);
+ synchronized (graphLock) {
+ Set<IProject> set = graph.get(targetProject);
+ if (set == null) {
+ return Collections.EMPTY_SET;
+ } else {
+ for (Iterator<IProject> iterator = set.iterator(); iterator.hasNext();) {
+ IProject project = iterator.next();
+ if (!project.isAccessible()) {
+ iterator.remove();
+ }
+ }
+ Set<IProject> copy = new HashSet<IProject>();
+ copy.addAll(set);
+ return copy;
+ }
+ }
+ }
+
+ private class DependencyGraphResourceChangedListener implements IResourceChangeListener, IResourceDeltaVisitor {
+ // only registered for post change events
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ preUpdate();
+ event.getDelta().accept(this);
+ } catch (CoreException e) {
+ ModulecorePlugin.logError(e);
+ } finally {
+ postUpdate();
+ }
+ }
+
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ IResource resource = delta.getResource();
+ switch (resource.getType()) {
+ case IResource.ROOT:
+ return true;
+ case IResource.PROJECT: {
+ int kind = delta.getKind();
+ if ((IResourceDelta.ADDED & kind) != 0) {
+ queueProjectAdded((IProject) resource);
+ return false;
+ } else if ((IResourceDelta.REMOVED & kind) != 0) {
+ queueProjectDeleted((IProject) resource);
+ return false;
+ } else if ((IResourceDelta.CHANGED & kind) != 0) {
+ int flags = delta.getFlags();
+ if ((IResourceDelta.OPEN & flags) != 0) {
+ boolean isOpen = ((IProject) resource).isOpen();
+ if (isOpen) {
+ queueProjectAdded((IProject) resource);
+ } else {
+ queueProjectDeleted((IProject) resource);
+ }
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+ case IResource.FOLDER:
+ if (resource.getName().equals(".settings")) {
+ return true;
+ }
+ return false;
+ case IResource.FILE:
+ String name = resource.getName();
+ if (name.equals(WTPModulesResourceFactory.WTP_MODULES_SHORT_NAME)) {
+ update(resource.getProject());
+ }
+ default:
+ return false;
+ }
+ }
+ };
+
+ private DependencyGraphResourceChangedListener listener = null;
+
+ /**
+ * The graph is built lazily once. Afterwards, the graph is updated as
+ * necessary.
+ */
+ private void initGraph() {
+ synchronized (graphLock) {
+ try {
+ preUpdate();
+ graph = new HashMap<IProject, Set<IProject>>();
+ listener = new DependencyGraphResourceChangedListener();
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
+ IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for (IProject sourceProject : allProjects) {
+ queueProjectAdded(sourceProject);
+ }
+ } finally {
+ postUpdate();
+ }
+ }
+ }
+
+ private void removeAllReferences(IProject project) {
+ synchronized (graphLock) {
+ graph.remove(project);
+ for (Iterator<Set<IProject>> iterator = graph.values().iterator(); iterator.hasNext();) {
+ iterator.next().remove(project);
+ }
+ modStamp++;
+ }
+ }
+
+ private void removeReference(IProject sourceProject, IProject targetProject) {
+ synchronized (graphLock) {
+ Set<IProject> referencingProjects = graph.get(targetProject);
+ if (referencingProjects != null) {
+ referencingProjects.remove(sourceProject);
+ }
+ modStamp++;
+ }
+ }
+
+ private void addReference(IProject sourceProject, IProject targetProject) {
+ synchronized (graphLock) {
+ Set<IProject> referencingProjects = graph.get(targetProject);
+ if (referencingProjects == null) {
+ referencingProjects = new HashSet<IProject>();
+ graph.put(targetProject, referencingProjects);
+ }
+ referencingProjects.add(sourceProject);
+ modStamp++;
+ }
+ }
+
+ public static final Object GRAPH_UPDATE_JOB_FAMILY = new Object();
+
+ private static final int JOB_DELAY = 100;
+
+ private final GraphUpdateJob graphUpdateJob = new GraphUpdateJob();
+ private final Object jobLock = new Object();
+
+ private class GraphUpdateJob extends Job {
+
+ public GraphUpdateJob() {
+ super("Graph Update Job");
+ setSystem(true);
+ setRule(null);
+ }
+
+ public boolean belongsTo(Object family) {
+ if (family == GRAPH_UPDATE_JOB_FAMILY) {
+ return true;
+ }
+ return super.belongsTo(family);
+ }
+
+ // We use the listener list as a thread safe queue.
+ private class Queue extends ListenerList {
+ public synchronized Object[] getListeners() {
+ Object[] data = super.getListeners();
+ clear();
+ return data;
+ }
+
+ public synchronized boolean isEmpty() {
+ return super.isEmpty();
+ }
+ };
+
+ private Queue projectsAdded = new Queue();
+
+ private Queue projectsRemoved = new Queue();
+
+ private Queue projectsUpdated = new Queue();
+
+ public void queueProjectAdded(IProject project) {
+ synchronized (graphLock) {
+ modStamp++;
+ }
+ projectsAdded.add(project);
+ }
+
+ public void queueProjectDeleted(IProject project) {
+ synchronized (graphLock) {
+ modStamp++;
+ }
+ projectsRemoved.add(project);
+ }
+
+ public void queueProjectUpdated(IProject project) {
+ synchronized (graphLock) {
+ modStamp++;
+ }
+ projectsUpdated.add(project);
+ }
+
+ @Override
+ public boolean shouldSchedule() {
+ boolean isEmpty = projectsAdded.isEmpty() && projectsRemoved.isEmpty() && projectsUpdated.isEmpty();
+ return !isEmpty;
+ }
+
+ protected IStatus run(IProgressMonitor monitor) {
+ final Object[] removed = projectsRemoved.getListeners();
+ final Object[] updated = projectsUpdated.getListeners();
+ final Object[] added = projectsAdded.getListeners();
+ if (removed.length == 0 && updated.length == 0 && added.length == 0) {
+ return Status.OK_STATUS;
+ }
+ synchronized (graphLock) {
+ modStamp++;
+ }
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable e) {
+ ModulecorePlugin.logError(e);
+ }
+
+ public void run() throws Exception {
+ // this is the simple case; just remove them all
+ synchronized (graphLock) {
+ for (Object o : removed) {
+ IProject project = (IProject) o;
+ removeAllReferences(project);
+ }
+ }
+ // get the updated queue in case there are any adds
+ // if there are any added projects, then unfortunately the
+ // entire workspace needs to be processed
+ if (added.length > 0) {
+ IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ for (IProject sourceProject : allProjects) {
+ IVirtualComponent component = ComponentCore.createComponent(sourceProject);
+ if (component != null) {
+ IVirtualReference[] references = component.getReferences();
+ for (IVirtualReference ref : references) {
+ IVirtualComponent targetComponent = ref.getReferencedComponent();
+ if (targetComponent != null) {
+ IProject targetProject = targetComponent.getProject();
+ if (targetProject != null && !targetProject.equals(sourceProject)) {
+ addReference(sourceProject, targetProject);
+ }
+ }
+ }
+ }
+ }
+ } else if (updated.length > 0) {
+ IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ Set<IProject> validRefs = new HashSet<IProject>();
+ for (Object o : updated) {
+ IProject sourceProject = (IProject) o;
+ IVirtualComponent component = ComponentCore.createComponent(sourceProject);
+ if (component != null) {
+ validRefs.clear();
+ IVirtualReference[] references = component.getReferences();
+ for (IVirtualReference ref : references) {
+ IVirtualComponent targetComponent = ref.getReferencedComponent();
+ if (targetComponent != null) {
+ IProject targetProject = targetComponent.getProject();
+ if (targetProject != null && !targetProject.equals(sourceProject)) {
+ validRefs.add(targetProject);
+ }
+ }
+ }
+ synchronized (graphLock) {
+ for (IProject targetProject : allProjects) {
+ // if the reference was identified
+ // above, be sure to add it
+ // otherwise, remove it
+ if (validRefs.remove(targetProject)) {
+ addReference(sourceProject, targetProject);
+ } else {
+ removeReference(sourceProject, targetProject);
+ }
+ }
+ }
+ } else {
+ // if this project is not a component, then it
+ // should be completely removed.
+ removeAllReferences(sourceProject);
+ }
+ }
+ }
+ }
+ });
+ // System.err.println(IDependencyGraph.INSTANCE);
+ return Status.OK_STATUS;
+ }
+ };
+
+ public void queueProjectAdded(IProject project) {
+ graphUpdateJob.queueProjectAdded(project);
+ synchronized (jobLock) {
+ if (pauseCount > 0) {
+ return;
+ }
+ }
+ graphUpdateJob.schedule(JOB_DELAY);
+ }
+
+ public void queueProjectDeleted(IProject project) {
+ graphUpdateJob.queueProjectDeleted(project);
+ synchronized (jobLock) {
+ if (pauseCount > 0) {
+ return;
+ }
+ }
+ graphUpdateJob.schedule(JOB_DELAY);
+ }
+
+ public void update(IProject project) {
+ graphUpdateJob.queueProjectUpdated(project);
+ synchronized (jobLock) {
+ if (pauseCount > 0) {
+ return;
+ }
+ }
+ graphUpdateJob.schedule(JOB_DELAY);
+ }
+
+ private int pauseCount = 0;
+
+ /**
+ * Pauses updates; any caller of this method must ensure through a
+ * try/finally block that resumeUpdates is subsequently called.
+ */
+ public void preUpdate() {
+ synchronized (jobLock) {
+ pauseCount++;
+ }
+ }
+
+ public void postUpdate() {
+ synchronized (jobLock) {
+ if (pauseCount > 0) {
+ pauseCount--;
+ }
+ if (pauseCount > 0) {
+ return;
+ }
+ }
+ graphUpdateJob.schedule(JOB_DELAY);
+ }
+
+ /**
+ * Blocks until the graph is finished updating
+ */
+ public void waitForAllUpdates(IProgressMonitor monitor) {
+ try {
+ graphUpdateJob.schedule(0);
+ Job.getJobManager().join(GRAPH_UPDATE_JOB_FAMILY, monitor);
+ } catch (OperationCanceledException e) {
+ ModulecorePlugin.logError(e);
+ } catch (InterruptedException e) {
+ ModulecorePlugin.logError(e);
+ }
+ }
+
+ public String toString() {
+ synchronized (graphLock) {
+ StringBuffer buff = new StringBuffer("Dependency Graph:\n{\n");
+ for (Iterator<Map.Entry<IProject, Set<IProject>>> iterator = graph.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry<IProject, Set<IProject>> entry = iterator.next();
+ buff.append(" " + entry.getKey().getName() + " -> {");
+ for (Iterator<IProject> mappedProjects = entry.getValue().iterator(); mappedProjects.hasNext();) {
+ buff.append(mappedProjects.next().getName());
+ if (mappedProjects.hasNext()) {
+ buff.append(", ");
+ }
+ }
+ buff.append("}\n");
+ }
+ buff.append("}");
+ return buff.toString();
+ }
+
+ }
+
+}
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphManager.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphManager.java
index 3d95164..0b275c8 100644
--- a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphManager.java
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/DependencyGraphManager.java
@@ -10,264 +10,46 @@
*******************************************************************************/
package org.eclipse.wst.common.componentcore.internal.builder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.jem.util.emf.workbench.ProjectUtilities;
-import org.eclipse.jem.util.logger.proxy.Logger;
-import org.eclipse.wst.common.componentcore.ComponentCore;
-import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent;
-import org.eclipse.wst.common.componentcore.internal.util.ComponentUtilities;
-import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
-import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
-import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
+/**
+ * @deprecated use {@link IDependencyGraph}
+ */
public class DependencyGraphManager {
-
- private class DependencyReference {
-
- public IProject componentProject;
- public IProject targetProject;
-
- public DependencyReference(IProject target, IProject component) {
- super();
- componentProject=component;
- targetProject=target;
- }
- }
private static DependencyGraphManager INSTANCE = null;
- private static final String MANIFEST_URI = "META-INF/MANIFEST.MF";
- private HashMap wtpModuleTimeStamps = null;
- private HashMap manifestTimeStamps = null;
- private long modStamp = System.currentTimeMillis();
-
+
private DependencyGraphManager() {
super();
}
-
+
public synchronized static final DependencyGraphManager getInstance() {
if (INSTANCE == null)
INSTANCE = new DependencyGraphManager();
return INSTANCE;
}
-
+
public void construct(IProject project) {
- if (project!=null && project.isAccessible() && getComponentFile(project) !=null) //$NON-NLS-1$
- constructIfNecessary();
- }
-
- private IResource getComponentFile(IProject project) {
- IResource componentFile = project.findMember(IModuleConstants.COMPONENT_FILE_PATH);
- if (componentFile == null)
- componentFile = project.findMember(IModuleConstants.R1_MODULE_META_FILE_PATH);
- return componentFile;
-
- }
-
- private void constructIfNecessary() {
- if (metadataChanged()) {
- buildDependencyGraph();
- }
- }
-
- private boolean metadataChanged() {
- return moduleTimeStampsChanged() || manifestTimeStampsChanged();
- }
-
- private boolean manifestTimeStampsChanged() {
- HashMap workspaceTimeStamps = collectManifestTimeStamps();
- if (timestampsMatch(getManifestTimeStamps(),workspaceTimeStamps))
- return false;
- return true;
- }
-
- private HashMap getManifestTimeStamps() {
- if (manifestTimeStamps == null)
- manifestTimeStamps = new HashMap();
- return manifestTimeStamps;
- }
-
- private HashMap collectManifestTimeStamps() {
- HashMap timeStamps = new HashMap();
- IProject[] projects = ProjectUtilities.getAllProjects();
- for (int i=0; i<projects.length; i++) {
- IFile manifestFile = null;
- if (projects[i]==null || !projects[i].isAccessible())
- continue;
- manifestFile = getTimeStampFile(projects[i]);
- if (manifestFile != null && manifestFile.exists() && ComponentCore.createComponent(projects[i]) != null) {
- Long currentTimeStamp = new Long(manifestFile.getModificationStamp());
- timeStamps.put(projects[i],currentTimeStamp);
- }
- }
- return timeStamps;
- }
-
- /**
- * This returns the file used for time stamp purposes. Typically this will be the manifest file.
- * @param p
- * @return
- */
- private IFile getTimeStampFile(IProject p) {
- IVirtualComponent component = ComponentCore.createComponent(p);
- if(null == component){
- return null;
- }
- if(component.isBinary()){
- return ((VirtualArchiveComponent)component).getUnderlyingWorkbenchFile();
- } else {
- try {
- IFile file = ComponentUtilities.findFile(component, new Path(MANIFEST_URI));
- if (file != null)
- return file;
- } catch (CoreException ce) {
- Logger.getLogger().log(ce);
- }
- }
- return null;
- }
-
- private boolean moduleTimeStampsChanged() {
- HashMap workspaceTimeStamps = collectModuleTimeStamps();
- if (timestampsMatch(getWtpModuleTimeStamps(),workspaceTimeStamps))
- return false;
- return true;
- }
-
- private boolean timestampsMatch(HashMap savedTimeStamps, HashMap workspaceTimeStamps) {
- return savedTimeStamps.equals(workspaceTimeStamps);
-
- }
-
- private HashMap collectModuleTimeStamps() {
- HashMap timeStamps = new HashMap();
- IProject[] projects = ProjectUtilities.getAllProjects();
- for (int i=0; i<projects.length; i++) {
- if (projects[i]==null || !projects[i].isAccessible())
- continue;
- IResource wtpModulesFile = getComponentFile(projects[i]); //$NON-NLS-1$
- if (wtpModulesFile != null && wtpModulesFile.exists() && ComponentCore.createComponent(projects[i]) != null) {
- Long currentTimeStamp = new Long(wtpModulesFile.getModificationStamp());
- timeStamps.put(projects[i],currentTimeStamp);
- }
- }
- return timeStamps;
- }
-
- private void buildDependencyGraph() {
- // Process and collect dependency references to add
- List referencesToAdd = new ArrayList();
- List componentProjects = new ArrayList();
- IProject[] projects = ProjectUtilities.getAllProjects();
-
- for (int k=0; k<projects.length; k++) {
- if (!projects[k].isAccessible() || getComponentFile(projects[k])==null)
- continue;
- IVirtualComponent component= ComponentCore.createComponent(projects[k]);
- if (component == null) continue;
- referencesToAdd.addAll(getDependencyReferences(component));
- componentProjects.add(projects[k]);
- }
-
- //Update the actual graph/timestamps and block other threads here
- synchronized (this) {
- cleanDependencyGraph();
- for (Iterator iter = componentProjects.iterator(); iter.hasNext();) {
- IProject proj = (IProject) iter.next();
- //For All projects (regardless if involved in references), update timestamps
- addTimeStamp(proj);
- }
- for (int i=0; i<referencesToAdd.size(); i++) {
- DependencyReference ref = (DependencyReference) referencesToAdd.get(i);
- if (ref.targetProject == null || ref.componentProject == null || ref.targetProject.equals(ref.componentProject))
- continue;
- DependencyGraph.getInstance().addReference(ref.targetProject,ref.componentProject);
- }
- }
- }
-
- private List getDependencyReferences(IVirtualComponent component) {
- List refs = new ArrayList();
- IProject componentProject = component.getProject();
- IVirtualReference[] depRefs = component.getReferences();
- for(int i = 0; i<depRefs.length; i++){
- IVirtualComponent targetComponent = depRefs[i].getReferencedComponent();
- if (targetComponent!=null) {
- IProject targetProject = targetComponent.getProject();
- refs.add(new DependencyReference(targetProject,componentProject));
- }
- }
- return refs;
- }
-
- private boolean addTimeStamp(IProject project) {
- // Get the .component file for the given project
- IResource wtpModulesFile = getComponentFile(project);
- if (wtpModulesFile==null)
- return false;
- Long currentTimeStamp = new Long(wtpModulesFile.getModificationStamp());
- getWtpModuleTimeStamps().put(project,currentTimeStamp);
- // Get the MANIFEST file for the given project
- IResource manifestFile = getTimeStampFile(project);
-
- if (manifestFile==null)
- return false;
- currentTimeStamp = new Long(manifestFile.getModificationStamp());
- getManifestTimeStamps().put(project,currentTimeStamp);
- return true;
- }
-
- private void cleanDependencyGraph() {
- DependencyGraph.getInstance().clear();
- getWtpModuleTimeStamps().clear();
- getManifestTimeStamps().clear();
- setModStamp(System.currentTimeMillis());
+ // do nothing
}
/**
- * Lazy initialization and return of the key valued pair of projects and wtp modules file
- * timestamps.
- *
- * @return HashMap of projects to .component file stamps
- */
- private HashMap getWtpModuleTimeStamps() {
- if (wtpModuleTimeStamps == null)
- wtpModuleTimeStamps = new HashMap();
- return wtpModuleTimeStamps;
- }
-
- /**
- * Return the dependency graph which was initialized if need be in the
+ * Return the dependency graph which was initialized if need be in the
* singleton manager method.
- */
+ */
public DependencyGraph getDependencyGraph() {
- constructIfNecessary();
return DependencyGraph.getInstance();
}
-
+
public void forceRefresh() {
- buildDependencyGraph();
+ // do nothing
}
-
public long getModStamp() {
- return modStamp;
- }
-
-
- private void setModStamp(long modStamp) {
- this.modStamp = modStamp;
+ return IDependencyGraph.INSTANCE.getModStamp();
}
public boolean checkIfStillValid(long timeStamp) {
- return (getModStamp() == timeStamp && !metadataChanged());
+ return IDependencyGraph.INSTANCE.getModStamp() == timeStamp;
}
}
diff --git a/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/IDependencyGraph.java b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/IDependencyGraph.java
new file mode 100644
index 0000000..1396949
--- /dev/null
+++ b/plugins/org.eclipse.wst.common.modulecore/modulecore-src/org/eclipse/wst/common/componentcore/internal/builder/IDependencyGraph.java
@@ -0,0 +1,106 @@
+package org.eclipse.wst.common.componentcore.internal.builder;
+
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
+
+/**
+ * This graph provides a backward mapping of project component dependencies. It
+ * provides a project limited inverse of
+ * {@link IVirtualComponent#getReferences()}.
+ *
+ * For example:
+ * <ul>
+ * <li>if the IVirtualComponent for project A has a dependency on the
+ * IVirtualComponent for project B, then calling
+ * {@link #getReferencingComponents(IProject)} on project B will return project
+ * A. </li>
+ * <li>if the IVirtualComponent for project A has a dependency on on the
+ * IVirtualComponent for a jar in project B, then calling
+ * {@link #getReferencingComponents(IProject)} for project B will return project
+ * A. This is true even if project B is not defined as an IVirtualComponent.
+ * </li>
+ * </ul>
+ *
+ * Any call to {@link #getReferencingComponents(IProject)} is always expected to
+ * be up to date. The only case where a client may need to force an update is if
+ * that client is also defining dynamic IVirtualComponent dependencies, i.e. the
+ * client is using the org.eclipse.wst.common.modulecore.componentimpl extension
+ * point. Only in this case should a client be calling any of
+ * {@link #preUpdate()}, {@link #postUpdate()}, or {@link #update(IProject)}
+ *
+ */
+public interface IDependencyGraph {
+
+ /**
+ * The static instance of this graph
+ */
+ public static IDependencyGraph INSTANCE = DependencyGraphImpl.getInstance();
+
+ /**
+ * Returns the set of component projects referencing the specified target
+ * project.
+ *
+ * @param targetProject
+ * @return
+ */
+ public Set<IProject> getReferencingComponents(IProject targetProject);
+
+ /**
+ * Returns a modification stamp. This modification stamp will be different
+ * if the project dependencies ever change.
+ */
+ public long getModStamp();
+
+ /**
+ * WARNING: this should only be called by implementors of the
+ * org.eclipse.wst.common.modulecore.componentimpl extension point.
+ *
+ * This method is part of the update API.
+ *
+ * @see {@link #update(IProject)}
+ */
+ public void preUpdate();
+
+ /**
+ * WARNING: this should only be called by implementors of the
+ * org.eclipse.wst.common.modulecore.componentimpl extension point.
+ *
+ * This method is part of the update API.
+ *
+ * @see {@link #update(IProject)}
+ */
+ public void postUpdate();
+
+ /**
+ * WARNING: this should only be called by implementors of the
+ * org.eclipse.wst.common.modulecore.componentimpl extension point.
+ *
+ * This method must be called when a resource change is detected which will
+ * affect how dependencies behave. For example, the core IVirtualComponent
+ * framework updates when changes are made to the
+ * .settings/org.eclipse.wst.common.component file changes, and also when
+ * IProjects are added or removed from the workspace. In the case for J2EE,
+ * this occurs when changes are made to the META-INF/MANIFEST.MF file. In
+ * general a call to update should only be made from a fast
+ * {@link IResourceDeltaVisitor}.
+ *
+ * In order to improve efficiency and avoid unnecessary update processing,
+ * it is necessary to always proceed calls to update() with a call to
+ * preUpdate() and follow with a call to postUpdate() using a try finally
+ * block as follows: <code>
+ * try {
+ * preUpdate();
+ * // perform 0 or more update() calls here
+ * } finally {
+ * IDependencyGraph.INSTANCE.postUpdate();
+ * }
+ * </code>
+ *
+ *
+ */
+ public void update(IProject sourceProject);
+
+}