/******************************************************************************* * Copyright (c) 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 *******************************************************************************/ package org.eclipse.jst.j2ee.internal.classpathdep; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jem.util.emf.workbench.ProjectUtilities; import org.eclipse.jst.common.jdt.internal.javalite.IJavaProjectLite; import org.eclipse.jst.common.jdt.internal.javalite.JavaCoreLite; import org.eclipse.jst.j2ee.classpathdep.ClasspathDependencyUtil; import org.eclipse.jst.j2ee.classpathdep.UpdateClasspathAttributeUtil; import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin; import org.eclipse.jst.j2ee.internal.project.J2EEProjectUtilities; import org.eclipse.wst.common.componentcore.ComponentCore; import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; import org.eclipse.wst.common.frameworks.datamodel.AbstractDataModelOperation; import org.eclipse.wst.common.frameworks.datamodel.IDataModel; /** * DataModelOperation that updates the WTP component dependency attribute on the * the classpath entries for the target Java project. */ public class UpdateClasspathAttributesOperation extends AbstractDataModelOperation implements UpdateClasspathAttributesDataModelProperties { public UpdateClasspathAttributesOperation(final IDataModel dataModel) { super(dataModel); } @Override public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { final IProject project = ProjectUtilities.getProject(model.getStringProperty(PROJECT_NAME)); final boolean isLegacyJ2EE = J2EEProjectUtilities.isLegacyJ2EEProject(project); try { if (project.hasNature(JavaCore.NATURE_ID)) { final IJavaProject javaProject = JavaCore.create(project); validateEdit(project); final Map entriesToAdd = (Map) model.getProperty(ENTRIES_TO_ADD_ATTRIBUTE); final boolean modifyComponentDep = model.getBooleanProperty(MODIFY_CLASSPATH_COMPONENT_DEPENDENCY); if (entriesToAdd == null) { final Map entriesToRemove = (Map ) model.getProperty(ENTRIES_TO_REMOVE_ATTRIBUTE); if (entriesToRemove == null) { final Map entriesToRuntimePath = (Map) model.getProperty(ENTRIES_WITH_ATTRIBUTE); final Map entriesToAttrib = new HashMap(); final Iterator i = entriesToRuntimePath.keySet().iterator(); while (i.hasNext()) { IClasspathEntry entry = (IClasspathEntry) i.next(); IPath runtimePath = (IPath) entriesToRuntimePath.get(entry); IClasspathAttribute attrib = ClasspathDependencyUtil.checkForComponentDependencyAttribute(entry, modifyComponentDep ? DependencyAttributeType.CLASSPATH_COMPONENT_DEPENDENCY : DependencyAttributeType.CLASSPATH_COMPONENT_NONDEPENDENCY); if (attrib == null) { if (modifyComponentDep) { attrib = UpdateClasspathAttributeUtil.createDependencyAttribute(runtimePath); } else { attrib = UpdateClasspathAttributeUtil.createNonDependencyAttribute(); } } entriesToAttrib.put(entry, attrib); } updateDependencyAttributes(javaProject, entriesToAttrib, modifyComponentDep, isLegacyJ2EE); } else { removeDependencyAttributes(javaProject, entriesToRemove, modifyComponentDep); } } else { addDependencyAttributes(javaProject, entriesToAdd, modifyComponentDep); } } } catch (CoreException ce) { J2EEPlugin.logError(ce); return new Status(IStatus.ERROR, J2EEPlugin.PLUGIN_ID, 0, ce.getLocalizedMessage(), ce); } return Status.OK_STATUS; } /** * Performs a validateEdit call on the files impacted by the operation. */ protected void validateEdit(final IProject project) throws CoreException { final List affectedFiles = ProjectUtilities.getFilesAffectedByClasspathChange(project); final IFile[] files = (IFile[]) affectedFiles.toArray(new IFile[affectedFiles.size()]); final IStatus result = J2EEPlugin.getWorkspace().validateEdit(files, null); if (!result.isOK()) { throw new CoreException(result); } } /** * Adds the WTP component dependency attribute to the specified classpath entries. * @param javaProject Target Java project. * @param entries Classpath entries to which the component dependency attribute should be added. * @param modifyComponentDep True if adding the dependency attribute, false if adding the non-dependency attribute. * @throws CoreException Thrown if an error is encountered. */ private void addDependencyAttributes(final IJavaProject javaProject, final Map entries, final boolean modifyComponentDep) throws CoreException { alterDependencyAttributes(javaProject, entries, true, modifyComponentDep); } /** * Removes the WTP component dependency attribute from the specified classpath entries. * @param javaProject Target Java project. * @param entries Classpath entries from which the component dependency attribute should be removed. * @param modifyComponentDep True if removing the dependency attribute, false if removing the non-dependency attribute. * @throws CoreException Thrown if an error is encountered. */ private void removeDependencyAttributes(final IJavaProject javaProject, final Map entries, final boolean modifyComponentDep) throws CoreException { alterDependencyAttributes(javaProject, entries, false, modifyComponentDep); } private void alterDependencyAttributes(final IJavaProject javaProject, final Map entries, final boolean add, final boolean modifyComponentDep) throws CoreException { final boolean isLegacyJ2EE = J2EEProjectUtilities.isLegacyJ2EEProject(javaProject.getProject()); final IJavaProjectLite javaProjectLite = JavaCoreLite.create(javaProject); // initialize to the set of raw entries with the attrib final Map entriesWithAttrib = ClasspathDependencyUtil.getRawComponentClasspathDependencies(javaProjectLite, modifyComponentDep ? DependencyAttributeType.CLASSPATH_COMPONENT_DEPENDENCY : DependencyAttributeType.CLASSPATH_COMPONENT_NONDEPENDENCY, isLegacyJ2EE); ClasspathDependencyExtensionManager extensionManager = ClasspathDependencyExtensionManager.instance(); Iterator i = entries.keySet().iterator(); while (i.hasNext()) { final IClasspathEntry entry = (IClasspathEntry) i.next(); if (add) { if (getMatchingEntryIgnoreAttributes(entriesWithAttrib, entry) == null) { IPath runtimePath = (IPath) entries.get(entry); if (runtimePath == null) { // compute the default runtime path IVirtualComponent virtualComponent = ComponentCore.createComponent(javaProject.getProject()); runtimePath = ClasspathDependencyUtil.getDefaultRuntimePath(virtualComponent, entry); } IClasspathAttribute attrib = null; if (modifyComponentDep) { // Check to see if an extender has a value for this classpath entry. String extenderValue = extensionManager.getDependencyValue( javaProject.getProject(), entry ); if( extenderValue == null ) { attrib = UpdateClasspathAttributeUtil.createDependencyAttribute(runtimePath); } else { attrib = UpdateClasspathAttributeUtil.createDependencyAttribute( extenderValue ); } } else { attrib = UpdateClasspathAttributeUtil.createNonDependencyAttribute(); } entriesWithAttrib.put(entry, attrib); } } else { IClasspathEntry matching = getMatchingEntryIgnoreAttributes(entriesWithAttrib, entry); if (matching != null) { entriesWithAttrib.remove(matching); } } } updateDependencyAttributes(javaProject, entriesWithAttrib, modifyComponentDep, isLegacyJ2EE); } private IClasspathEntry getMatchingEntryIgnoreAttributes(final Map entries, final IClasspathEntry entry) { final Iterator i = entries.keySet().iterator(); while (i.hasNext()) { final IClasspathEntry e = (IClasspathEntry) i.next(); if (e.getEntryKind() == entry.getEntryKind() && e.getPath().equals(entry.getPath()) && e.isExported() == entry.isExported()) { return e; } } return null; } /** * Updates the specified Java project so that only the specified classpath entries have * the WTP component dependency attribute. * @param javaProject Target Java project. * @param entries Classpath entries that should have the component dependency attribute. Map from IClasspathEntry * to the IClasspathAttribute for the WTP classpath component dependency. * @param modifyComponentDep True if modifying the dependency attribute, false if modifying the non-dependency attribute. * @throws CoreException Thrown if an error is encountered. */ private void updateDependencyAttributes(final IJavaProject javaProject, final Map entriesWithAttrib, final boolean modifyComponentDep, final boolean isLegacyJ2EE) throws CoreException { if (javaProject == null || !javaProject.getProject().isAccessible()) { return; } final List updatedClasspath = new ArrayList(); final IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); boolean needToUpdateClasspath = false; IClasspathAttribute attrib = UpdateClasspathAttributeUtil.createDependencyAttribute(); if (!modifyComponentDep) { attrib = UpdateClasspathAttributeUtil.createNonDependencyAttribute(); } for (int i = 0; i < rawClasspath.length; i++) { IClasspathEntry entry = rawClasspath[i]; final int kind = entry.getEntryKind(); boolean hasAttribute = ClasspathDependencyUtil.checkForComponentDependencyAttribute(entry, modifyComponentDep ? DependencyAttributeType.CLASSPATH_COMPONENT_DEPENDENCY : DependencyAttributeType.CLASSPATH_COMPONENT_NONDEPENDENCY, isLegacyJ2EE) != null; boolean shouldHaveAttribute = entriesWithAttrib.containsKey(entry); boolean updateAttributes = false; IClasspathAttribute[] updatedAttributes = null; if (shouldHaveAttribute) { if (!hasAttribute) { // should have the attribute and currently missing it attrib = (IClasspathAttribute) entriesWithAttrib.get(entry); updatedAttributes = updateAttributes(entry.getExtraAttributes(), attrib, true); needToUpdateClasspath = true; updateAttributes = true; } } else if (hasAttribute) { // should not have the attribute and currently has it updatedAttributes = updateAttributes(entry.getExtraAttributes(), attrib, false); needToUpdateClasspath = true; updateAttributes = true; } if (updateAttributes) { switch(kind) { case IClasspathEntry.CPE_CONTAINER: entry = JavaCore.newContainerEntry(entry.getPath(), entry.getAccessRules(), updatedAttributes, entry.isExported()); break; case IClasspathEntry.CPE_LIBRARY: entry = JavaCore.newLibraryEntry(entry.getPath(), entry.getSourceAttachmentPath(), entry.getSourceAttachmentRootPath(), entry.getAccessRules(), updatedAttributes, entry.isExported()); break; case IClasspathEntry.CPE_VARIABLE: entry = JavaCore.newVariableEntry(entry.getPath(), entry.getSourceAttachmentPath(), entry.getSourceAttachmentRootPath(), entry.getAccessRules(), updatedAttributes, entry.isExported()); break; case IClasspathEntry.CPE_PROJECT: // although project entries are not yet supported, allow the attribute here and let the validator flag as an error entry = JavaCore.newProjectEntry(entry.getPath(), entry.getAccessRules(), entry.combineAccessRules(), updatedAttributes, entry.isExported()); break; case IClasspathEntry.CPE_SOURCE: // although source entries are not supported, allow the attribute here and let the validator flag as an error entry = JavaCore.newSourceEntry(entry.getPath(), entry.getInclusionPatterns(), entry.getExclusionPatterns(), entry.getOutputLocation(), updatedAttributes); break; } } updatedClasspath.add(entry); } if (needToUpdateClasspath) { final IClasspathEntry[] updatedCPArray = (IClasspathEntry[]) updatedClasspath.toArray(new IClasspathEntry[updatedClasspath.size()]); javaProject.setRawClasspath(updatedCPArray, null); } } private IClasspathAttribute[] updateAttributes(final IClasspathAttribute[] currentAttribs, final IClasspathAttribute targetAttrib, final boolean add) { final List updatedAttribs = new ArrayList(); boolean hasAttrib = false; for (int i = 0; i < currentAttribs.length; i++) { if (currentAttribs[i].getName().equals(targetAttrib.getName())) { hasAttrib = true; if (!add) { continue; } } updatedAttribs.add(currentAttribs[i]); } if (add && !hasAttrib) { updatedAttribs.add(targetAttrib); } return (IClasspathAttribute[]) updatedAttribs.toArray(new IClasspathAttribute[updatedAttribs.size()]); } }