blob: a4face219ff0ef1f0efcae976a3a80eac614424a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems, Inc.
* 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:
* rfrost@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.j2ee.refactor.listeners;
import java.util.HashMap;
import java.util.HashSet;
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.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
import org.eclipse.jst.j2ee.project.EarUtilities;
import org.eclipse.jst.j2ee.refactor.RefactorResourceHandler;
import org.eclipse.jst.j2ee.refactor.operations.OptionalRefactorHandler;
import org.eclipse.jst.j2ee.refactor.operations.ProjectRefactorMetadata;
import org.eclipse.jst.j2ee.refactor.operations.ProjectRefactoringDataModelProvider;
import org.eclipse.jst.j2ee.refactor.operations.ProjectRenameDataModelProvider;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.internal.builder.IDependencyGraph;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
import org.eclipse.wst.common.frameworks.datamodel.DataModelFactory;
import org.eclipse.wst.common.frameworks.datamodel.IDataModel;
import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
/**
* Listens for project rename/delete events and, if the project had the
* ModuleCore nature, executes the appropriate logic to update
* project references.
*/
public final class ProjectRefactoringListener implements IResourceChangeListener, IResourceDeltaVisitor {
/**
* Name of the Job family in which all project rename/delete refactoring jobs belong.
*/
public static final String PROJECT_REFACTORING_JOB_FAMILY = "org.eclipse.jst.j2ee.refactor.project"; //$NON-NLS-1$
/*
* Map from name of deleted project to ProjectRefactorMetadata instances.
*/
private final Map deletedProjectMetadata = new HashMap();
/**
* Maintains a cache of project depencencies;
*/
public ProjectRefactoringListener() {
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(final IResourceChangeEvent event) {
// need to capture PRE_DELETE events so that metadata about the
// deleted project can be collected and cached
try {
if (event.getType() == IResourceChangeEvent.PRE_DELETE) {
// for now, only dependencies on ModuleCoreNature projects
final IProject project = (IProject) event.getResource();
// ensure project is accessible and has both module core and faceted natures
if (ModuleCoreNature.isFlexibleProject(project)
&& ProjectFacetsManager.create(project) != null) {
cacheDeletedProjectMetadata(project);
}
} else {
event.getDelta().accept(this);
}
} catch (CoreException ce) {
J2EEPlugin.logError(ce);
}
}
private synchronized void cacheDeletedProjectMetadata(final IProject project) {
final ProjectRefactorMetadata metadata = new ProjectRefactorMetadata(project, ProjectRefactorMetadata.REFERER_CACHING);
// precompute the metadata while the project still exists
metadata.computeMetadata();
metadata.computeServers();
//the list of reference projects that have a .settings/org.eclipse.wst.common.component entry
//typically these will be EAR projects
Set<IProject> dotComponentReferences = IDependencyGraph.INSTANCE.getReferencingComponents(project);
Set<IProject> modulesAlreadyChecked = new HashSet<IProject>();
modulesAlreadyChecked.add(project);
modulesAlreadyChecked.addAll(dotComponentReferences);
Set<IProject> allReferences = new HashSet <IProject>();
for(IProject earProject: dotComponentReferences){
allReferences.add(earProject);
if(EarUtilities.isEARProject(earProject)){
//for each ear, get the modules, and
//for each module see if it has a reference back to the project being deleted
IVirtualComponent earComponent = ComponentCore.createComponent(earProject);
IVirtualReference [] earRefs = earComponent.getReferences();
for(IVirtualReference earRef : earRefs){
IVirtualComponent moduleComponent = earRef.getReferencedComponent();
IProject moduleProject = moduleComponent.getProject();
if(!moduleComponent.isBinary() && !modulesAlreadyChecked.contains(moduleProject)){
modulesAlreadyChecked.add(moduleProject);
IVirtualReference [] moduleRefs = moduleComponent.getReferences();
for (IVirtualReference moduleRef : moduleRefs) {
IVirtualComponent manifestComponent = moduleRef.getReferencedComponent();
if(manifestComponent.getProject().equals(project)){
allReferences.add(moduleProject);
break;
}
}
}
}
}
}
IProject [] referencingProjects = allReferences.toArray(new IProject[allReferences.size()]);
metadata.computeDependentMetadata(ProjectRefactorMetadata.REF_CACHING, referencingProjects);
deletedProjectMetadata.put(project.getName(), metadata);
}
/* (non-Javadoc)
* @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
*/
public boolean visit(final IResourceDelta delta) throws CoreException {
final IResource resource = delta.getResource();
if (resource instanceof IWorkspaceRoot) {
// delta is at the workspace root so keep going
return true;
} else if (resource instanceof IProject) {
processProjectDelta((IProject) resource, delta);
}
return false;
}
/*
* Process the project delta in a sync block.
*/
private synchronized void processProjectDelta(final IProject project, final IResourceDelta delta) throws CoreException {
final int kind = delta.getKind();
final int flags = delta.getFlags();
if (kind == IResourceDelta.ADDED && hasRenamedAddedFlags(flags)) { // was renamed
// get the original name
final String originalName = delta.getMovedFromPath().lastSegment();
//Logger.getLogger().logInfo("Added event for " + originalName + " with flags " + flags);
// we get PRE_DELETE events on rename so retrieve this
ProjectRefactorMetadata originalMetadata = (ProjectRefactorMetadata) deletedProjectMetadata.remove(originalName);
// get the metadata for the new project
final ProjectRefactorMetadata newMetadata = new ProjectRefactorMetadata(project);
// note: only projects with ModuleCoreNature will have cached metadata
if (originalMetadata != null && OptionalRefactorHandler.getInstance().shouldRefactorRenamedProject(originalMetadata)) {
newMetadata.computeMetadata(originalMetadata.getProject());
processRename(originalMetadata, newMetadata, delta);
}
}
}
/*
* Determines if the added project was renamed based on the IResourceDelta flags
*/
private boolean hasRenamedAddedFlags(final int flags) {
if ((flags & IResourceDelta.DESCRIPTION) > 0
&& (flags & IResourceDelta.MOVED_FROM) > 0) {
return true;
}
return false;
}
/*
* Processes the renaming of a project.
*/
private void processRename(final ProjectRefactorMetadata originalMetadata, final ProjectRefactorMetadata newMetadata, final IResourceDelta delta) {
WorkspaceJob job = new WorkspaceJob(RefactorMessages.ProjectRefactoringListener_J2EE_Project_Rename_) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
final IDataModel dataModel = DataModelFactory.createDataModel(new ProjectRenameDataModelProvider());
dataModel.setProperty(ProjectRefactoringDataModelProvider.PROJECT_METADATA, newMetadata);
dataModel.setProperty(ProjectRenameDataModelProvider.ORIGINAL_PROJECT_METADATA, originalMetadata);
dataModel.setProperty(ProjectRenameDataModelProvider.RESOURCE_DELTA, delta);
try {
dataModel.getDefaultOperation().execute(monitor, null);
} catch (Exception e) {
final String msg = RefactorResourceHandler.getString("error_updating_project_on_rename", new Object[]{originalMetadata.getProjectName()}); //$NON-NLS-1$
J2EEPlugin.logError(msg);
J2EEPlugin.logError(e);
return new Status(Status.ERROR, J2EEPlugin.PLUGIN_ID, 0, msg, e);
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(final Object family) {
return PROJECT_REFACTORING_JOB_FAMILY.equals(family);
}
};
// XXX note: might want to consider switching to a MultiRule for optimization
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.schedule();
}
}