blob: 3d95164d06d315cd658d0739e76967c44c460ce5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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.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;
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());
}
/**
* 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
* singleton manager method.
*/
public DependencyGraph getDependencyGraph() {
constructIfNecessary();
return DependencyGraph.getInstance();
}
public void forceRefresh() {
buildDependencyGraph();
}
public long getModStamp() {
return modStamp;
}
private void setModStamp(long modStamp) {
this.modStamp = modStamp;
}
public boolean checkIfStillValid(long timeStamp) {
return (getModStamp() == timeStamp && !metadataChanged());
}
}