blob: 07ff0ee7b0e77925a3997d2866c5b1f8cb819a41 [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.Arrays;
import java.util.HashMap;
import java.util.List;
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.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.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.internal.builder.IDependencyGraph;
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;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerWorkingCopy;
import org.eclipse.wst.server.core.ServerUtil;
import org.eclipse.wst.server.core.internal.DeletedModule;
/**
* 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();
Set<IProject> referencingComponents = IDependencyGraph.INSTANCE.getReferencingComponents(project);
IProject [] referencingProjects = referencingComponents.toArray(new IProject[referencingComponents.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.REMOVED) {
if (hasDeletedRemovedFlags(flags)) {
// if the kind is REMOVED and there are no special flags, the project was deleted
ProjectRefactorMetadata metadata = (ProjectRefactorMetadata) deletedProjectMetadata.remove(project.getName());
// note: only projects with ModuleCoreNature will have cached metadata
if (metadata != null && OptionalRefactorHandler.getInstance().shouldRefactorDeletedProject(metadata)) {
//Delete refactoring is now being done by the LTK refactoring framework
//for all java ee modules. Please refer to JavaEERefactoringParticipant
//we are only cleaning up the server references here
updateServerRefs(metadata);
}
}
} else 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;
}
/*
* Determines if the removed project was deleted based on the IResourceDelta flags
*/
private boolean hasDeletedRemovedFlags(final int flags) {
if ((flags & IResourceDelta.MOVED_TO) == 0
&& (flags & IResourceDelta.REPLACED) == 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();
}
private void updateServerRefs(final ProjectRefactorMetadata refactorMetadata) {
WorkspaceJob job = new WorkspaceJob("ServerRefreshJob") { //$NON-NLS-1$
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
final IModule moduleToRemove = refactorMetadata.getModule();
if (moduleToRemove == null) {
return Status.OK_STATUS;
}
// Need to replace the original module with a DeletedModule
final IModule[] toUpdate = new IModule[1];
toUpdate[0] = new DeletedModule(moduleToRemove.getId(), moduleToRemove
.getName(), moduleToRemove.getModuleType());
IServer[] affectedServers = refactorMetadata.getServers();
IServerWorkingCopy wc = null;
for (int i = 0; i < affectedServers.length; i++) {
try {
wc = affectedServers[i].createWorkingCopy();
List list = Arrays.asList(affectedServers[i].getModules());
if (list.contains(moduleToRemove)) {
ServerUtil.modifyModules(wc, null, toUpdate, null);
}
} catch (CoreException ce) {
J2EEPlugin.logError(ce);
} finally {
try {
if (wc != null) {
wc.saveAll(true, null);
}
} catch (CoreException ce) {
J2EEPlugin.logError(ce);
}
}
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(final Object family) {
return ProjectRefactoringListener.PROJECT_REFACTORING_JOB_FAMILY.equals(family);
}
};
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.schedule();
}
}