blob: dee7f0531a30ac4da062a8a3b57e9fb61275301e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
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.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.j2ee.internal.J2EEConstants;
import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.internal.ComponentResource;
import org.eclipse.wst.common.componentcore.internal.StructureEdit;
import org.eclipse.wst.common.componentcore.internal.WorkbenchComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
/**
* Implementation of <code>IElementChangedListener that updates mappings for src folders
* in the .component file in response to changes in a project's Java classpath.
*/
public class J2EEElementChangedListener implements IElementChangedListener {
/**
* Name of the Job family in which all component update jobs belong.
*/
public static final String PROJECT_COMPONENT_UPDATE_JOB_FAMILY = "org.eclipse.jst.j2ee.refactor.component"; //$NON-NLS-1$
/* (non-Javadoc)
* @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent)
*/
@Override
public void elementChanged(final ElementChangedEvent event) {
processJavaElementDelta(event.getDelta());
}
private void processJavaElementDelta(final IJavaElementDelta delta) {
final int kind = delta.getKind();
if (kind == IJavaElementDelta.CHANGED) {
final int flags = delta.getFlags();
final IJavaElement element = delta.getElement();
if (element instanceof IJavaModel) {
if ((flags & IJavaElementDelta.F_CHILDREN) == IJavaElementDelta.F_CHILDREN) {
final IJavaElementDelta[] children = delta.getChangedChildren();
for (int i = 0; i < children.length; i++) {
// handle all of the IJavaProject children
processJavaElementDelta(children[i]);
}
} else {
// not a Java project (i.e. could be an EAR project)
processResourceDeltas(flags, kind, delta);
}
} else if (element instanceof IJavaProject) {
processJavaProject((IJavaProject) element, flags, kind, delta);
}
}
}
private void processJavaProject(final IJavaProject jproject,
final int flags, final int kind, final IJavaElementDelta delta) {
final IProject project = jproject.getProject();
final List pathsToAdd = new ArrayList();
final List pathsToRemove = new ArrayList();
final List changedJavaPaths = new ArrayList();
// make certain this is a J2EE project
if (ModuleCoreNature.isFlexibleProject(project)) {
WorkspaceJob job = new WorkspaceJob(
RefactorMessages.J2EEElementChangedListener_J2EE_Component_Mapping_Update_) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor)
throws CoreException {// Did the classpath change?
if ((flags & IJavaElementDelta.F_CHILDREN) == IJavaElementDelta.F_CHILDREN) {
final boolean cpChanged = (flags & IJavaElementDelta.F_CLASSPATH_CHANGED) != 0;
getJavaSrcMappings(delta.getAffectedChildren(),
cpChanged, jproject, pathsToAdd, pathsToRemove,
changedJavaPaths);
}
// Did a non-Java folder change name?
final IResourceDelta[] deltas = delta.getResourceDeltas();
if (deltas != null && deltas.length > 0) {
getNonJavaFolderMappings(deltas, project, pathsToAdd,
pathsToRemove, changedJavaPaths);
}
for(int i=0;i<pathsToAdd.size(); i++){
Object[] toAdd = (Object[]) pathsToAdd.get(i);
final IVirtualFolder destFolder = (IVirtualFolder) toAdd[1];
final IPath pathToAdd = (IPath) toAdd[0];
destFolder.createLink(pathToAdd, 0, monitor);
}
for(int i=0;i<pathsToRemove.size(); i++){
Object[] toRemove = (Object[]) pathsToRemove.get(i);
final IVirtualFolder destFolder = (IVirtualFolder) toRemove[1];
final IPath pathToRemove = (IPath) toRemove[0];
destFolder.removeLink(pathToRemove, 0, monitor);
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(final Object family) {
return PROJECT_COMPONENT_UPDATE_JOB_FAMILY.equals(family);
}
};
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.setSystem(true);
job.schedule();
}
}
private void processResourceDeltas(final int flags, final int kind, final IJavaElementDelta delta) {
final List pathsToAdd = new ArrayList();
final List pathsToRemove = new ArrayList();
final IResourceDelta[] deltas = delta.getResourceDeltas();
if (deltas != null && deltas.length > 0) {
try {
getNonJavaFolderMappings(deltas, null, pathsToAdd, pathsToRemove, Collections.EMPTY_LIST);
} catch (CoreException ce) {
J2EEPlugin.logError(ce);
return;
}
}
updateMappingsInJob(pathsToAdd, pathsToRemove);
}
/*
* Computes the virtual component path mapping changes the need to be made due to
* Java src path changes.
*/
private void getJavaSrcMappings(final IJavaElementDelta[] children, final boolean cpChanged, final IJavaProject jproject, final List pathsToAdd, final List pathsToRemove, final List changedPaths)
throws CoreException {
IVirtualComponent c = ComponentCore.createComponent(jproject.getProject());
if(c == null)
return;
// get the default destination folder
final IVirtualFolder defaultDestFolder = getDestinationFolder(c);
for (int i = 0; i < children.length; i++) {
final IJavaElementDelta delta = children[i];
final IJavaElement element = delta.getElement();
if(element.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT){
final IPackageFragmentRoot root = (IPackageFragmentRoot) element;
int cpeKind = IPackageFragmentRoot.K_SOURCE;
boolean abortAdd = false;
try {
cpeKind = root.getKind();
} catch (JavaModelException jme) {
// this is thrown if the folder corresponding to the CPE has been deleted
// since it could represent another error, we need to abort adding.
abortAdd = true;
}
// only update if we know it is a src folder
if (cpeKind == IPackageFragmentRoot.K_SOURCE) {
final int kind = delta.getKind();
if (!cpChanged) {
// if the classpath is not changed, save modifications to the Java src path
if (kind == IJavaElementDelta.CHANGED || kind == IJavaElementDelta.REMOVED) {
changedPaths.add(root.getPath().removeFirstSegments(1));
}
} else {
// kind and flags for CP additions are somewhat sporadic; either:
// -kind is ADDED and flags are 0
// or
// -kind is CHANGED and flags are F_ADDED_TO_CLASSPATH
final int flags = delta.getFlags();
if (kind == IJavaElementDelta.ADDED ||
(flags & IJavaElementDelta.F_ADDED_TO_CLASSPATH) == IJavaElementDelta.F_ADDED_TO_CLASSPATH) {
if (!abortAdd) {
final IPath pathToAdd = root.getPath().removeFirstSegments(1);
pathsToAdd.add(new Object[]{pathToAdd, defaultDestFolder});
// if the added src path was moved from another location, remove any mapping for that
// location
if ((flags & IJavaElementDelta.F_MOVED_FROM) == IJavaElementDelta.F_MOVED_FROM) {
final IJavaElement movedFromElement = delta.getMovedFromElement();
final IPath pathToRemove = movedFromElement.getPath().removeFirstSegments(1);
pathsToRemove.add(new Object[]{pathToRemove, defaultDestFolder});
}
}
// getting a kind = REMOVED and flags = 0 for removal of the folder (w/o removing the CPE), probably
// should not be generated
} else if (kind == IJavaElementDelta.REMOVED || (flags & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) == IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) {
IPath path = root.getPath().removeFirstSegments(1);
pathsToRemove.add(new Object[]{path, defaultDestFolder});
}
}
}
}
}
}
/*
* Computes the virtual component path mapping changes the need to be made due to changes to
* non-Java folders.
*/
private void getNonJavaFolderMappings(final IResourceDelta[] deltas, final IProject project, final List pathsToAdd, final List pathsToRemove, final List changedJavaPaths) throws CoreException {
IVirtualFolder rootFolder = null;
IVirtualComponent c = ComponentCore.createComponent(project);
if(c == null)
return;
if (project != null) {
rootFolder = c.getRootFolder();
}
Map sourceToRuntime = null;
if (project != null) {
sourceToRuntime = getResourceMappings(project);
}
for (int i = 0; i < deltas.length; i++) {
final IResourceDelta delta = deltas[i];
processResourceDelta(delta, rootFolder, sourceToRuntime, pathsToAdd, pathsToRemove, changedJavaPaths);
}
}
/*
* Processes a single IResourceDelta.
*/
private void processResourceDelta(final IResourceDelta delta, IVirtualFolder rootFolder, Map sourceToRuntime, final List pathsToAdd, final List pathsToRemove, final List changedJavaPaths) throws CoreException {
IVirtualFolder localRootFolder = rootFolder;
Map localSourceToRuntime = sourceToRuntime;
final int kind = delta.getKind();
if (kind == IResourceDelta.CHANGED) {
IResourceDelta[] childDeltas = delta.getAffectedChildren();
for (int i = 0; i < childDeltas.length; i++) {
processResourceDelta(childDeltas[i], localRootFolder, localSourceToRuntime, pathsToAdd, pathsToRemove, changedJavaPaths);
}
} else {
final int flags = delta.getFlags();
if ((flags & IResourceDelta.MOVED_FROM) == IResourceDelta.MOVED_FROM) {
if (localRootFolder == null) {
final IProject project = delta.getResource().getProject();
// make certain this is a J2EE project
if (ModuleCoreNature.getModuleCoreNature(project) != null) {
IVirtualComponent c = ComponentCore.createComponent(project);
localRootFolder = c.getRootFolder();
localSourceToRuntime = getResourceMappings(project);
} else {
// not a J2EE project
return;
}
}
final IPath movedFrom = delta.getMovedFromPath().removeFirstSegments(1);
if (changedJavaPaths.contains(movedFrom)) {
// don't update renamed Java src paths
return;
}
final IPath movedTo = delta.getFullPath().removeFirstSegments(1);
final IPath runtimePath = (IPath) localSourceToRuntime.get(movedFrom);
// does the old path have a virtual component mapping?
if (runtimePath != null) {
final IVirtualFolder folder = localRootFolder.getFolder(runtimePath);
// only add if the project relative paths are not equal (these can be equal when the root folder is mapped and the project is renamed)
if (!movedFrom.equals(movedTo)) {
pathsToRemove.add(new Object[]{movedFrom, folder});
pathsToAdd.add(new Object[]{movedTo, folder});
}
}
}
}
}
private Map getResourceMappings(final IProject project){
final Map sourceToRuntime = new HashMap();
StructureEdit core = null;
try {
core = StructureEdit.getStructureEditForRead(project);
final WorkbenchComponent component = core.getComponent();
if (null != component) {
final List currentResources = new ArrayList(component.getResources());
for (Iterator iter = currentResources.iterator(); iter.hasNext();) {
final ComponentResource resource = (ComponentResource) iter.next();
sourceToRuntime.put(resource.getSourcePath().makeRelative(), resource.getRuntimePath());
}
}
} catch (NullPointerException e) {
J2EEPlugin.logError(e);
} finally {
if (core != null)
core.dispose();
}
return sourceToRuntime;
}
/*
* Retrieves the IVirtualFolder to which Java src folders should be mapped
*/
private IVirtualFolder getDestinationFolder(final IVirtualComponent c) throws CoreException {
final IVirtualFolder root = c.getRootFolder();
if( JavaEEProjectUtilities.usesJavaEEComponent(c )){
if (JavaEEProjectUtilities.isDynamicWebProject(c.getProject())) {
return root.getFolder(new Path(J2EEConstants.WEB_INF_CLASSES));
}
return root;
}
//get source folders
List<IPath> srcFolders = getSourceFolders(c.getProject());
//get existing deploy mappings
Map mapping = getResourceMappings(c.getProject());
IPath deployPath = null;
//if existing source folder has mapping set for this one
for( IPath srcFolder : srcFolders )
{
if( mapping.containsKey(srcFolder) ){
deployPath = (IPath)mapping.get(srcFolder);
if( deployPath == null )
return root;
return root.getFolder(deployPath);
}
}
return root;
}
private void updateMappingsInJob(final List pathsToAdd, final List pathsToRemove) {
// If there are corrections to the virtual path mappings, execute them in a Job
if (!pathsToAdd.isEmpty() || !pathsToRemove.isEmpty()) {
WorkspaceJob job = new WorkspaceJob(RefactorMessages.J2EEElementChangedListener_J2EE_Component_Mapping_Update_) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
for(int i=0;i<pathsToAdd.size(); i++){
Object[] toAdd = (Object[]) pathsToAdd.get(i);
final IVirtualFolder destFolder = (IVirtualFolder) toAdd[1];
final IPath pathToAdd = (IPath) toAdd[0];
destFolder.createLink(pathToAdd, 0, monitor);
}
for(int i=0;i<pathsToRemove.size(); i++){
Object[] toRemove = (Object[]) pathsToRemove.get(i);
final IVirtualFolder destFolder = (IVirtualFolder) toRemove[1];
final IPath pathToRemove = (IPath) toRemove[0];
destFolder.removeLink(pathToRemove, 0, monitor);
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(final Object family) {
return PROJECT_COMPONENT_UPDATE_JOB_FAMILY.equals(family);
}
};
job.setRule(ResourcesPlugin.getWorkspace().getRoot());
job.setSystem(true);
job.schedule();
}
}
protected static List<IPath> getSourceFolders(IProject project) {
IJavaProject javaProj = JavaCore.create(project);
if (javaProj == null)
return null;
if( !javaProj.exists() )
return null;
IClasspathEntry[] cp = null;
try {
cp = javaProj.getRawClasspath();
} catch (JavaModelException ex) {
J2EEPlugin.logError(ex);
return null;
}
List sourcePaths = new ArrayList();
for (int i = 0; i < cp.length; i++) {
if (cp[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) {
sourcePaths.add(cp[i].getPath().removeFirstSegments(1));
}
}
return sourcePaths;
}
}