/*******************************************************************************
* Copyright (c) 2005, 2010 Intel 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:
* Intel Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.core;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedOptionValueHandler;
import org.eclipse.cdt.managedbuilder.core.IManagedProject;
import org.eclipse.cdt.managedbuilder.core.IResourceConfiguration;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin;
import org.eclipse.cdt.managedbuilder.core.ManagedCProjectNature;
import org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator;
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.IResourceRuleFactory;
import org.eclipse.core.resources.ISaveContext;
import org.eclipse.core.resources.ISaveParticipant;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.MultiRule;
public class ResourceChangeHandler implements IResourceChangeListener, ISaveParticipant {
private Map<IProject, IManagedBuildInfo> fRmProjectToBuildInfoMap = new HashMap<IProject, IManagedBuildInfo>();
private class ResourceConfigurationChecker implements IResourceDeltaVisitor{
private IResourceDelta fRootDelta;
private HashMap<IProject, IManagedBuilderMakefileGenerator> fBuildFileGeneratorMap = new HashMap<IProject, IManagedBuilderMakefileGenerator>();
private HashSet<IPath> fValidatedFilesSet = new HashSet<IPath>();
private HashSet<IProject> fModifiedProjects = new HashSet<IProject>();
public ResourceConfigurationChecker(IResourceDelta rootDelta){
fRootDelta = rootDelta;
}
public IProject[] getModifiedProjects(){
return fModifiedProjects.toArray(new IProject[fModifiedProjects.size()]);
}
public boolean visit(IResourceDelta delta) throws CoreException {
IResource dResource = delta.getResource();
int rcType = dResource.getType();
if(rcType == IResource.PROJECT || rcType == IResource.FOLDER){
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IProject project = null;
IResource rcToCheck = null;
switch (delta.getKind()) {
case IResourceDelta.REMOVED :
if (rcType == IResource.PROJECT){
IManagedBuildInfo info = fRmProjectToBuildInfoMap.remove(dResource);
if((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
if(info != null){
sendClose(info);
PropertyManager.getInstance().clearProperties(info.getManagedProject());
}
break;
}
}
case IResourceDelta.CHANGED :
if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
IPath path = delta.getMovedToPath();
if(path != null){
project = root.findMember(path.segment(0)).getProject();
if(project != null && rcType == IResource.FOLDER)
rcToCheck = root.getFolder(substituteProject(dResource.getFullPath(),project.getName()));
}
break;
}
default:
project = dResource.getProject();
if(rcType == IResource.FOLDER)
rcToCheck = dResource;
break;
}
if(project != null) {
IManagedBuilderMakefileGenerator makeGen = getInitializedGenerator(project);
if(makeGen != null){
if(rcToCheck == null || !makeGen.isGeneratedResource(rcToCheck))
return true;
}
}
return false;
} else if (rcType == IResource.FILE && !dResource.isDerived()) {
int flags = delta.getFlags();
switch (delta.getKind()) {
case IResourceDelta.REMOVED :
if ((flags & IResourceDelta.MOVED_TO) == 0) {
handleDeleteFile(dResource.getFullPath());
break;
}
case IResourceDelta.ADDED :
case IResourceDelta.CHANGED :
if ((flags & IResourceDelta.MOVED_TO) != 0) {
IPath path = delta.getMovedToPath();
if (path != null) {
handleRenamedFile(
dResource.getFullPath(),
path);
}
} else if ((flags & IResourceDelta.MOVED_FROM) != 0) {
IPath path = delta.getMovedFromPath();
if (path != null) {
handleRenamedFile(
path,
dResource.getFullPath());
}
}
break;
default:
break;
}
return false;
}
return true; // visit the children
}
private IPath substituteProject(IPath path, String projectName){
return new Path(projectName).makeAbsolute().append(path.removeFirstSegments(1));
}
private void handleRenamedFile(IPath fromPath, IPath toPath){
if(!fValidatedFilesSet.add(fromPath))
return;
IProject fromProject = findModifiedProject(fromPath.segment(0));
if(fromProject == null)
return;
IManagedBuilderMakefileGenerator fromMakeGen = getInitializedGenerator(fromProject);
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
if(fromMakeGen == null || fromMakeGen.isGeneratedResource(root.getFile(substituteProject(fromPath,fromProject.getName()))))
return;
IManagedBuildInfo fromInfo = ManagedBuildManager.getBuildInfo(fromProject);
IProject toProject = root.findMember(toPath.uptoSegment(1)).getProject();
IManagedBuildInfo toInfo = toProject != null ?
ManagedBuildManager.getBuildInfo(toProject) :
null;
IManagedBuilderMakefileGenerator toMakeGen = toProject != null ?
getInitializedGenerator(toProject) :
null;
if(toMakeGen != null && toMakeGen.isGeneratedResource(root.getFile(toPath)))
toInfo = null;
if(fromInfo == toInfo){
//the resource was moved within the project scope
if(updateResourceConfigurations(fromInfo,fromPath,toPath) && toProject != null)
fModifiedProjects.add(toProject);
} else {
if(fromInfo != null && toInfo != null){
//TODO: this is the case when the resource
//is moved from one managed project to another
//should we handle this?
//e.g. add resource configurations to the destination project?
}
if(fromInfo != null && removeResourceConfigurations(fromInfo,fromPath))
fModifiedProjects.add(fromProject);
}
}
private void handleDeleteFile(IPath path){
IProject project = findModifiedProject(path.segment(0));
if(project != null){
IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project);
if(info != null
&& removeResourceConfigurations(info,path))
fModifiedProjects.add(project);
}
}
//finds the project geven the initial project name
//That is:
// if the project of a given name was renamed returns the renamed project
// if the project of a given name was removed returns null
// if the project of a given name was neither renamed or removed
// returns the project of that name or null if the project does not exist
//
private IProject findModifiedProject(final String oldProjectName){
IResourceDelta projectDelta = fRootDelta.findMember(new Path(oldProjectName));
boolean replaced = false;
if(projectDelta != null) {
switch(projectDelta.getKind()){
case IResourceDelta.REMOVED :
if ((projectDelta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
return null;
}
case IResourceDelta.CHANGED :
if ((projectDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
IPath path = projectDelta.getMovedToPath();
if(path != null)
return ResourcesPlugin.getWorkspace().getRoot().findMember(path).getProject();
}
break;
}
}
final IProject project[] = new IProject[1];
try {
fRootDelta.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta) throws CoreException {
IResource dResource = delta.getResource();
int rcType = dResource.getType();
if(rcType == IResource.ROOT) {
return true;
} else if(rcType == IResource.PROJECT){
switch(delta.getKind()){
case IResourceDelta.ADDED :
case IResourceDelta.CHANGED :
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
IPath path = delta.getMovedFromPath();
if (path != null && path.segment(0).equals(oldProjectName)) {
project[0] = dResource.getProject();
}
}
break;
default:
break;
}
}
return false;
}
});
} catch (CoreException e) {
}
if(project[0] == null && !replaced)
project[0] = ResourcesPlugin.getWorkspace().getRoot().findMember(oldProjectName).getProject();
return project[0];
}
private IManagedBuilderMakefileGenerator getInitializedGenerator(IProject project){
IManagedBuilderMakefileGenerator makeGen = fBuildFileGeneratorMap.get(project);
if (makeGen == null) {
try {
if (project.hasNature(ManagedCProjectNature.MNG_NATURE_ID)) {
// Determine if we can access the build info before actually trying
// If not, don't try, to avoid putting up a dialog box warning the user
if (!ManagedBuildManager.canGetBuildInfo(project)) return null;
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
if (buildInfo != null){
IConfiguration defaultCfg = buildInfo.getDefaultConfiguration();
if (defaultCfg != null) {
makeGen = ManagedBuildManager.getBuildfileGenerator(defaultCfg);
makeGen.initialize(project,buildInfo,new NullProgressMonitor());
fBuildFileGeneratorMap.put(project,makeGen);
}
}
}
} catch (CoreException e){
return null;
}
}
return makeGen;
}
}
public void sendClose(IProject project){
sendClose(ManagedBuildManager.getBuildInfo(project,false));
}
private void sendClose(IManagedBuildInfo info){
if(info != null){
IManagedProject managedProj = info.getManagedProject();
if (managedProj != null) {
IConfiguration cfgs[] = managedProj.getConfigurations();
for(int i = 0; i < cfgs.length; i++)
ManagedBuildManager.performValueHandlerEvent(cfgs[i], IManagedOptionValueHandler.EVENT_CLOSE, true);
}
}
}
/*
* I R e s o u r c e C h a n g e L i s t e n e r
*/
/* (non-Javadoc)
*
* Handle the renaming and deletion of project resources
* This is necessary in order to update ResourceConfigurations and AdditionalInputs
*
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
if (event.getSource() instanceof IWorkspace) {
switch (event.getType()) {
case IResourceChangeEvent.PRE_CLOSE:
IResource proj = event.getResource();
if(proj instanceof IProject)
sendClose((IProject)proj);
break;
case IResourceChangeEvent.PRE_DELETE :
IResource rc = event.getResource();
if(rc instanceof IProject){
IProject project = (IProject)rc;
try {
if (project.hasNature(ManagedCProjectNature.MNG_NATURE_ID)) {
IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project);
if(info != null)
fRmProjectToBuildInfoMap.put(project, info);
}
} catch (CoreException e) {
}
}
case IResourceChangeEvent.POST_CHANGE :
case IResourceChangeEvent.POST_BUILD :
IResourceDelta resDelta = event.getDelta();
if (resDelta == null) {
break;
}
try {
ResourceConfigurationChecker rcChecker = new ResourceConfigurationChecker(resDelta);
resDelta.accept(rcChecker);
//saving info for the modified projects
initInfoSerialization(rcChecker.getModifiedProjects());
} catch (CoreException e) {
ManagedBuilderCorePlugin.log(e);
}
break;
default :
break;
}
}
}
private void initInfoSerialization(final IProject projects[]){
if(projects.length == 0)
return;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResourceRuleFactory ruleFactory = workspace.getRuleFactory();
ISchedulingRule buildInfoSaveRule;
if(projects.length == 1){
buildInfoSaveRule = ruleFactory.modifyRule(projects[0]);
} else {
ISchedulingRule rules[] = new ISchedulingRule[projects.length];
for(int i = 0; i < rules.length; i++)
rules[i] = ruleFactory.modifyRule(projects[i]);
buildInfoSaveRule = MultiRule.combine(rules);
}
Job savingJob = new Job(ManagedMakeMessages.getResourceString("ResourceChangeHandler.buildInfoSerializationJob")){ //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
for(int i = 0; i < projects.length; i++){
ManagedBuildManager.saveBuildInfo(projects[i],true);
}
return new Status(
IStatus.OK,
ManagedBuilderCorePlugin.getUniqueIdentifier(),
IStatus.OK,
new String(),
null);
}
};
savingJob.setRule(buildInfoSaveRule);
savingJob.schedule();
}
private boolean updateResourceConfigurations(IManagedBuildInfo info, IPath oldPath, IPath newPath){
boolean changed = false;
if(!oldPath.equals(newPath)){
IManagedProject mngProj = info.getManagedProject();
if(mngProj != null){
IConfiguration configs[] = mngProj.getConfigurations();
if(configs != null && configs.length > 0){
for(int i = 0; i < configs.length; i++){
if(updateResourceConfiguration(configs[i],oldPath,newPath))
changed = true;
}
}
}
}
return changed;
}
private boolean removeResourceConfigurations(IManagedBuildInfo info, IPath path){
boolean changed = false;
IManagedProject mngProj = info.getManagedProject();
if(mngProj != null){
IConfiguration configs[] = mngProj.getConfigurations();
if(configs != null && configs.length > 0){
for(int i = 0; i < configs.length; i++){
if(removeResourceConfiguration(configs[i],path))
changed = true;
}
}
}
return changed;
}
private boolean updateResourceConfiguration(IConfiguration config, IPath oldPath, IPath newPath){
IResourceConfiguration rcCfg = config.getResourceConfiguration(oldPath.toString());
if(rcCfg != null && !oldPath.equals(newPath)){
// config.removeResourceConfiguration(rcCfg);
rcCfg.setResourcePath(newPath.toString());
// rcCfg.setRebuildState(true);
// ((Configuration)config).addResourceConfiguration((ResourceConfiguration)rcCfg);
// config.setRebuildState(true);
return true;
}
return false;
}
private boolean removeResourceConfiguration(IConfiguration config, IPath path){
IResourceConfiguration rcCfg = config.getResourceConfiguration(path.toString());
if(rcCfg != null){
config.removeResourceConfiguration(rcCfg);
// config.setRebuildState(true);
return true;
}
return false;
}
/*
* I S a v e P a r t i c i p a n t
*/
/* (non-Javadoc)
* @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext)
*/
public void saving(ISaveContext context) throws CoreException {
PropertyManager.getInstance().serialize();
//Request a resource delta to be used on next activation.
context.needDelta();
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext)
*/
public void doneSaving(ISaveContext context) {
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext)
*/
public void prepareToSave(ISaveContext context) throws CoreException {
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext)
*/
public void rollback(ISaveContext context) {
}
}