/******************************************************************************* * Copyright Technophobia Ltd 2012 * * This file is part of the Substeps Eclipse Plugin. * * The Substeps Eclipse Plugin is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License v1.0. * * The Substeps Eclipse Plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Eclipse Public License for more details. * * You should have received a copy of the Eclipse Public License * along with the Substeps Eclipse Plugin. If not, see <http://www.eclipse.org/legal/epl-v10.html>. ******************************************************************************/ package com.technophobia.substeps.syntax; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import com.technophobia.eclipse.project.ProjectManager; import com.technophobia.eclipse.transformer.ProjectToJavaProjectTransformer; import com.technophobia.substeps.FeatureEditorPlugin; import com.technophobia.substeps.classloader.ClassLoadedClassAnalyser; import com.technophobia.substeps.classloader.JavaProjectClassLoader; import com.technophobia.substeps.model.ParentStep; import com.technophobia.substeps.model.PatternMap; import com.technophobia.substeps.model.Syntax; import com.technophobia.substeps.nature.SubstepsNature; import com.technophobia.substeps.runner.runtime.ClassLocator; import com.technophobia.substeps.runner.runtime.StepClassLocator; import com.technophobia.substeps.runner.syntax.DefaultSyntaxErrorReporter; import com.technophobia.substeps.runner.syntax.SyntaxBuilder; import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter; import com.technophobia.substeps.supplier.Transformer; public class ProjectToSyntaxTransformer implements Transformer<IProject, Syntax> { private final ProjectManager projectManager; ProjectToSyntaxTransformer(final ProjectManager projectManager) { this.projectManager = projectManager; // package scope constructor to encourage use of the // CachingProjectToSyntaxTransformer } @Override public Syntax from(final IProject project) { if (SubstepsNature.isSubstepsProject(project)) { final IJavaProject javaProject = new ProjectToJavaProjectTransformer().from(project); if (javaProject != null) { final ClassLoader classLoader = new JavaProjectClassLoader(javaProject); final Set<String> outputFolders = outputFoldersForProject(javaProject); final File substepsFolder = new File(projectManager.substepsFolderFor(project).toOSString()); final List<Class<?>> stepClasses = new ArrayList<Class<?>>(); for (final String outputFolder : outputFolders) { final ClassLocator classLocator = new StepClassLocator(outputFolder, classLoader); stepClasses.addAll(stepClasses(outputFolder, classLocator)); } // augment step classes with externally dependent classes stepClasses.addAll(externalDependenciesFor(project, classLoader)); try { return buildSyntaxFor(project, substepsFolder, stepClasses, classLoader, syntaxErrorReporterFor(project)); } catch (final RuntimeException ex) { FeatureEditorPlugin.instance().warn( "Error when building syntax for project " + project + ": " + ex.getMessage(), ex); } } } // If we get to here, we can't resolve a valid syntax, return a null one final Syntax nullSyntax = new Syntax(); nullSyntax.setSubStepsMap(new PatternMap<ParentStep>()); return nullSyntax; } private Collection<Class<?>> externalDependenciesFor(final IProject project, final ClassLoader classLoader) { final List<String> externalDependencies = FeatureEditorPlugin.instance().externalDependencyStepClasses(project); final Collection<Class<?>> classes = new ArrayList<Class<?>>(externalDependencies.size()); for (final String className : externalDependencies) { final Class<?> clazz = loadClass(className, classLoader); if (clazz != null) { classes.add(clazz); } } return Collections.unmodifiableCollection(classes); } private Class<?> loadClass(final String stepClassName, final ClassLoader classLoader) { try { return classLoader.loadClass(stepClassName); } catch (final ClassNotFoundException ex) { FeatureEditorPlugin.instance().error("Could not load class " + stepClassName, ex); return null; } } protected SyntaxErrorReporter syntaxErrorReporterFor(final IProject project) { return new DefaultSyntaxErrorReporter(); } protected Syntax buildSyntaxFor(final IProject project, final File substepsFolder, final List<Class<?>> stepClasses, final ClassLoader classLoader, final SyntaxErrorReporter syntaxErrorReporter) { return SyntaxBuilder.buildSyntax(stepClasses, substepsFolder, true, null, new ClassLoadedClassAnalyser( classLoader), false, syntaxErrorReporter); } private Set<String> outputFoldersForProject(final IJavaProject project) { final Set<String> outputFolders = new HashSet<String>(); final IPath projectLocation = projectLocationPath(project); try { final IPath defaultOutputLocation = project.getOutputLocation(); if (defaultOutputLocation != null) { final IPath fullPath = appendPathTo(projectLocation, defaultOutputLocation); if (fullPath.toFile().exists()) { outputFolders.add(fullPath.toOSString()); } } for (final IClasspathEntry entry : project.getRawClasspath()) { if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { final IPath outputLocation = entry.getOutputLocation(); if (outputLocation != null) { final IPath fullPath = appendPathTo(projectLocation, outputLocation); if (fullPath.toFile().exists()) { outputFolders.add(fullPath.toOSString()); } } } } } catch (final JavaModelException ex) { FeatureEditorPlugin.instance().warn( "Could not get output folder location for project " + project.getElementName()); } return outputFolders; } private IPath appendPathTo(final IPath projectLocation, final IPath outputLocation) { return projectLocation.append(outputLocation.removeFirstSegments(1)); } private IPath projectLocationPath(final IJavaProject project) { return project.getResource().getLocation().makeAbsolute(); } private List<Class<?>> stepClasses(final String outputFolder, final ClassLocator classLocator) { return toList(classLocator.fromPath(outputFolder)); } private List<Class<?>> toList(final Iterator<Class<?>> it) { final List<Class<?>> list = new ArrayList<Class<?>>(); while (it.hasNext()) { final Class<?> nextClass = it.next(); if (nextClass != null) { list.add(nextClass); } } return list; } }