/********************************************************************** * Copyright (c) 2005-2009 ant4eclipse project team. * * 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: * Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich **********************************************************************/ package org.ant4eclipse.lib.jdt.internal.tools; import org.ant4eclipse.lib.core.Assure; import org.ant4eclipse.lib.core.Lifecycle; import org.ant4eclipse.lib.core.exception.Ant4EclipseException; import org.ant4eclipse.lib.core.logging.A4ELogging; import org.ant4eclipse.lib.jdt.JdtExceptionCode; import org.ant4eclipse.lib.jdt.internal.tools.classpathentry.ClasspathEntryResolver; import org.ant4eclipse.lib.jdt.model.ClasspathEntry; import org.ant4eclipse.lib.jdt.model.project.JavaProjectRole; import org.ant4eclipse.lib.jdt.tools.container.ClasspathResolverContext; import org.ant4eclipse.lib.platform.model.resource.EclipseProject; import java.util.EmptyStackException; import java.util.LinkedList; import java.util.List; import java.util.Stack; /** * <p> * Helper class that resolves class path entries. The {@link ClasspathEntryResolverExecutor} holds two lists of * referenced projects: * <ul> * <li>The resolved projects list contains all projects that are referenced and transitively resolved, e.g. jdt projects * that are referenced through a 'src' class path entry</li> * <li>The referenced projects list contains all projects that are only referenced (and transitively resolved), e.g. * plug-in projects that are referenced through the 'org.eclipse.pde.core.requiredPlugins' container</li> * </ul> * </p> * * @author Gerd Wütherich (gerd@gerd-wuetherich.de) */ public class ClasspathEntryResolverExecutor { /** stack of 'current projects' */ private Stack<EclipseProject> _currentProject; /** list with all projects that are (transitively) resolved */ private List<EclipseProject> _resolvedProjects; /** list with all projects that references. These projects are not transitively resolved */ private List<EclipseProject> _referencedProjects; /** array that contains all resolvers for raw class path entries * */ private ClasspathEntryResolver[] _entryResolvers; /** the resolver context */ private ClasspathResolverContext _resolverContext; /** indicates if an exception is thrown in the case that a container could not be resolved */ private boolean _failOnNonHandledEntry; /** * <p> * Creates a new instance of type {@link ClasspathEntryResolverExecutor}. * </p> * * @param failOnNonHandledEntry */ public ClasspathEntryResolverExecutor(boolean failOnNonHandledEntry) { // initialize the executor attributes this._resolvedProjects = new LinkedList<EclipseProject>(); this._referencedProjects = new LinkedList<EclipseProject>(); this._currentProject = new Stack<EclipseProject>(); this._failOnNonHandledEntry = failOnNonHandledEntry; } /** * <p> * Returns the current project or <code>null</code> if no current project is set. * </p> * * @return the current project or <code>null</code> if no current project is set. */ public final EclipseProject getCurrentProject() { // returns the current project try { return this._currentProject.peek(); } catch (EmptyStackException e) { return null; } } /** * <p> * Returns <code>true</code> if a current project is set. * </p> * * @return <code>true</code> if a current project is set. */ public final boolean hasCurrentProject() { return !this._currentProject.empty(); } /** * <p> * Returns all referenced projects. Both lists (resolved and referenced) will be merged. * </p> * * @return all referenced projects. */ public List<EclipseProject> getReferencedProjects() { // create the result List<EclipseProject> result = new LinkedList<EclipseProject>(); // add all resolved projects result.addAll(this._resolvedProjects); // add all referenced projects for (EclipseProject eclipseProject : this._referencedProjects) { if (!result.contains(eclipseProject)) { result.add(eclipseProject); } } // return result return result; } /** * <p> * Main entry for resolving a eclipse project. * </p> * * @param rootProject * the root project * @param classpathEntryResolvers * the list of all entry resolvers * @param classpathResolverContext * the resolver context */ public final void resolve(EclipseProject rootProject, ClasspathEntryResolver[] classpathEntryResolvers, ClasspathResolverContext classpathResolverContext) { // Initialize the ProjectClasspathResolver instance this._resolvedProjects.clear(); this._currentProject.clear(); this._referencedProjects.clear(); // set the entry resolvers this._entryResolvers = classpathEntryResolvers; // set the resolver context this._resolverContext = classpathResolverContext; // Initialize Entry Resolvers for (ClasspathEntryResolver entryResolver : this._entryResolvers) { if (entryResolver instanceof Lifecycle) { ((Lifecycle) entryResolver).initialize(); } } // resolve the class path resolveReferencedProject(rootProject); // Dispose Entry Resolvers for (ClasspathEntryResolver entryResolver : this._entryResolvers) { if (entryResolver instanceof Lifecycle) { ((Lifecycle) entryResolver).dispose(); } } } /** * <p> * Adds a referenced project. The project will not (transitively) resolved. * </p> * * @param project * the project to add. */ public final void addReferencedProject(EclipseProject project) { Assure.notNull("project", project); // adds the referenced project if (!this._referencedProjects.contains(project)) { this._referencedProjects.add(project); } } /** * <p> * Adds a referenced project. The project will (transitively) resolved. * </p> * * @param project * the project to add. */ public final void resolveReferencedProject(EclipseProject project) { Assure.notNull("project", project); // detect circular dependencies if (this._currentProject.contains(project)) { // TODO it should be configurable if the task fails on circular // dependencies // TODO detect which projects reference each other A4ELogging.warn("Circular dependency detected! Project: '%s'", project.getFolderName()); return; } // return if project already has been resolved if (this._resolvedProjects.contains(project)) { return; } // add project to the list of all resolved projects this._resolvedProjects.add(project); // push the project to the stack this._currentProject.push(project); // assert raw class path entries // TODO: NLS Assure.assertTrue(project.getRole(JavaProjectRole.class).hasRawClasspathEntries(), String.format( "The JDT project '%s' (%s) doesn't contain any class path entries.", project.getFolderName(), project .getFolder().getAbsolutePath())); // resolve the class path entries for this project resolveClasspathEntries(project.getRole(JavaProjectRole.class).getRawClasspathEntries()); // pop the project from the stack this._currentProject.pop(); } /** * <p> * Resolves the class path entries. * </p> * * @param classpathEntries * the class path entries to resolve */ private void resolveClasspathEntries(ClasspathEntry[] classpathEntries) { // iterate over all entries for (ClasspathEntry classpathEntry : classpathEntries) { try { resolveClasspathEntry(classpathEntry); } catch (Ant4EclipseException e) { throw e; } catch (Exception e) { throw new Ant4EclipseException(e, JdtExceptionCode.EXCEPTION_WHILE_RESOLVING_CLASSPATH_ENTRY, classpathEntry, (hasCurrentProject() ? getCurrentProject().getSpecifiedName() : "<unkown>"), e.getMessage()); } } } /** * <p> * Resolves a eclipse class path entry. * </p> * * @param entry * the class path entry to resolve. */ private final void resolveClasspathEntry(ClasspathEntry entry) { Assure.notNull("entry", entry); // initialize handled boolean handled = false; // iterate over all the entry resolvers and try to resolve the entry for (ClasspathEntryResolver entryResolver : this._entryResolvers) { if (entryResolver.canResolve(entry)) { handled = true; entryResolver.resolve(entry, this._resolverContext); break; } } // if the entry is not handled, we have to throw an exception here if (!handled && this._failOnNonHandledEntry) { // TODO: NLS throw new RuntimeException("Unsupported Entrykind!" + entry); } } }