/******************************************************************************* * 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(); } }