hu.bme.mit.sette.common.tasks.RunnerProjectGenerator.java Source code

Java tutorial

Introduction

Here is the source code for hu.bme.mit.sette.common.tasks.RunnerProjectGenerator.java

Source

/*
 * SETTE - Symbolic Execution based Test Tool Evaluator
 *
 * SETTE is a tool to help the evaluation and comparison of symbolic execution
 * based test input generator tools.
 *
 * Budapest University of Technology and Economics (BME)
 *
 * Authors: Lajos Cseppent <lajos.cseppento@inf.mit.bme.hu>, Zoltn Micskei
 * <micskeiz@mit.bme.hu>
 *
 * Copyright 2014
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package hu.bme.mit.sette.common.tasks;

import hu.bme.mit.sette.common.Tool;
import hu.bme.mit.sette.common.descriptors.eclipse.EclipseClasspathDescriptor;
import hu.bme.mit.sette.common.descriptors.eclipse.EclipseClasspathEntry;
import hu.bme.mit.sette.common.descriptors.eclipse.EclipseClasspathEntry.Kind;
import hu.bme.mit.sette.common.descriptors.eclipse.EclipseProject;
import hu.bme.mit.sette.common.exceptions.RunnerProjectGeneratorException;
import hu.bme.mit.sette.common.exceptions.SetteConfigurationException;
import hu.bme.mit.sette.common.exceptions.SetteException;
import hu.bme.mit.sette.common.model.runner.RunnerProjectSettings;
import hu.bme.mit.sette.common.model.snippet.SnippetProject;
import hu.bme.mit.sette.common.snippets.JavaVersion;
import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.body.TypeDeclaration;
import japa.parser.ast.expr.AnnotationExpr;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.Validate;

/**
 * A SETTE task which provides base for runner project generation. The phases
 * are the following: validation, preparation, writing.
 *
 * @param <T>
 *            the type of the tool
 */
public abstract class RunnerProjectGenerator<T extends Tool> extends SetteTask<T> {
    /** The Eclipse project. */
    private EclipseProject eclipseProject;

    /**
     * Instantiates a new runner project generator.
     *
     * @param snippetProject
     *            the snippet project
     * @param outputDirectory
     *            the output directory
     * @param tool
     *            the tool
     */
    public RunnerProjectGenerator(final SnippetProject snippetProject, final File outputDirectory, final T tool) {
        super(snippetProject, outputDirectory, tool);
    }

    /**
     * Generates the runner project.
     *
     * @throws RunnerProjectGeneratorException
     *             if generation has failed
     */
    public final void generate() throws RunnerProjectGeneratorException {
        String phase = null;

        try {
            // validate preconditions
            phase = "validate (do)";
            validate();
            phase = "validate (after)";
            afterValidate();

            phase = "prepare runner project (do)";
            prepareRunnerProject();
            phase = "prepare runner project (after)";
            afterPrepareRunnerProject(this.eclipseProject);

            phase = "write runner project (do)";
            writeRunnerProject();
            phase = "write runner project (after)";
            afterWriteRunnerProject(this.eclipseProject);

            phase = "complete";
        } catch (Exception e) {
            String message = String.format(
                    "The runner project generation has failed\n" + "(phase: [%s])\n(tool: [%s])", phase,
                    getTool().getFullName());
            throw new RunnerProjectGeneratorException(message, this, e);
        }
    }

    /**
     * Validates both the snippet and runner project settings.
     *
     * @throws SetteConfigurationException
     *             if a SETTE configuration problem occurred
     */
    private void validate() throws SetteConfigurationException {
        Validate.isTrue(getSnippetProject().getState().equals(SnippetProject.State.PARSED),
                "The snippet project must be parsed (state: [%s]) ", getSnippetProject().getState().name());

        // TODO snippet proj. val. can fail even if it is valid
        // getSnippetProjectSettings().validateExists();
        getRunnerProjectSettings().validateNotExists();
    }

    /**
     * Prepares the writing of the runner project, i.e. make everything ready
     * for writing out.
     */
    private void prepareRunnerProject() {
        // create the Eclipse project
        this.eclipseProject = new EclipseProject(getRunnerProjectSettings().getProjectName());

        // create the classpath for the project
        EclipseClasspathDescriptor cp = new EclipseClasspathDescriptor();
        this.eclipseProject.setClasspathDescriptor(cp);

        // add default JRE and bin directory
        cp.addEntry(EclipseClasspathEntry.JRE_CONTAINER);
        cp.addEntry(Kind.OUTPUT, "bin");

        // add snippet source directory
        cp.addEntry(Kind.SOURCE, getSnippetProjectSettings().getSnippetSourceDirectoryPath());

        // add libraries used by the snippet project
        for (File libraryFile : getSnippetProject().getFiles().getLibraryFiles()) {
            cp.addEntry(Kind.LIBRARY,
                    getSnippetProjectSettings().getLibraryDirectoryPath() + '/' + libraryFile.getName());
        }
    }

    /**
     * Writes the runner project out.
     *
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws ParseException
     *             If the source code has parser errors.
     * @throws ParserConfigurationException
     *             If a DocumentBuilder cannot be created which satisfies the
     *             configuration requested or when it is not possible to create
     *             a Transformer instance.
     * @throws TransformerException
     *             If an unrecoverable error occurs during the course of the
     *             transformation.
     */
    private void writeRunnerProject()
            throws IOException, ParseException, ParserConfigurationException, TransformerException {
        // TODO revise whole method
        // TODO now using JAPA, which does not support Java 7 -> maybe ANTLR
        // supports
        // better

        // create INFO file
        // TODO later maybe use an XML file!!!

        File infoFile = new File(getRunnerProjectSettings().getBaseDirectory(), "SETTE-INFO");

        StringBuilder infoFileData = new StringBuilder();
        infoFileData.append("Tool name: " + getTool().getName()).append('\n');
        infoFileData.append("Tool version: " + getTool().getVersion()).append('\n');
        infoFileData.append("Tool supported Java version: " + getTool().getSupportedJavaVersion()).append('\n');

        // TODO externalise somewhere the date format string
        String generatedAt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
        infoFileData.append("Generated at: ").append(generatedAt).append('\n');

        String id = generatedAt + ' ' + getTool().getName() + " (" + getTool().getVersion() + ')';
        infoFileData.append("ID: ").append(id).append('\n');

        FileUtils.write(infoFile, infoFileData);

        // copy snippets
        FileUtils.copyDirectory(getSnippetProjectSettings().getSnippetSourceDirectory(),
                getRunnerProjectSettings().getSnippetSourceDirectory(), false);

        // remove SETTE annotations and imports from file
        Collection<File> filesWritten = FileUtils.listFiles(getRunnerProjectSettings().getSnippetSourceDirectory(),
                null, true);

        mainLoop: for (File file : filesWritten) {
            CompilationUnit compilationUnit = JavaParser.parse(file);

            // remove SETTE annotations
            if (compilationUnit.getTypes() != null) {
                for (TypeDeclaration type : compilationUnit.getTypes()) {
                    if (type.getAnnotations() != null) {
                        for (Iterator<AnnotationExpr> iterator = type.getAnnotations().iterator(); iterator
                                .hasNext();) {
                            AnnotationExpr annot = iterator.next();

                            String annotStr = annot.toString().trim();

                            // TODO enhance
                            // if container and has req version and it is java 7
                            // and tool does not support -> remove
                            if (annotStr.startsWith("@SetteSnippetContainer")
                                    && annotStr.contains("requiredJavaVersion")
                                    && annotStr.contains("JavaVersion.JAVA_7")
                                    && !getTool().supportsJavaVersion(JavaVersion.JAVA_7)) {
                                // TODO error handling
                                // remove file
                                System.err.println("Skipping file: " + file + " (required Java version: "
                                        + JavaVersion.JAVA_7 + ")");
                                FileUtils.forceDelete(file);
                                continue mainLoop;

                            }

                            // TODO enhance
                            if (annot.getName().toString().startsWith("Sette")) {
                                iterator.remove();
                            }
                        }
                    }

                    if (type.getMembers() != null) {
                        for (BodyDeclaration member : type.getMembers()) {
                            if (member.getAnnotations() != null) {
                                for (Iterator<AnnotationExpr> iterator = member.getAnnotations()
                                        .iterator(); iterator.hasNext();) {
                                    AnnotationExpr annotation = iterator.next();

                                    // TODO enhance
                                    if (annotation.getName().toString().startsWith("Sette")) {
                                        iterator.remove();
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // remove SETTE imports
            if (compilationUnit.getImports() != null) {
                for (Iterator<ImportDeclaration> iterator = compilationUnit.getImports().iterator(); iterator
                        .hasNext();) {
                    ImportDeclaration importDeclaration = iterator.next();

                    // TODO enhance
                    String p1 = "hu.bme.mit.sette.annotations";
                    String p2 = "hu.bme.mit.sette.snippets.inputs";
                    // TODO enhance
                    String p3 = "catg.CATG";
                    String p4 = "hu.bme.mit.sette.common.snippets.JavaVersion";

                    if (importDeclaration.getName().toString().equals(p3)) {
                        // keep CATG
                    } else if (importDeclaration.getName().toString().equals(p4)) {
                        iterator.remove();
                    } else if (importDeclaration.getName().toString().startsWith(p1)) {
                        iterator.remove();
                    } else if (importDeclaration.getName().toString().startsWith(p2)) {
                        iterator.remove();
                    }
                }
            }

            // save edited source code
            FileUtils.write(file, compilationUnit.toString());
        }

        // copy libraries
        if (getSnippetProjectSettings().getLibraryDirectory().exists()) {
            FileUtils.copyDirectory(getSnippetProjectSettings().getLibraryDirectory(),
                    getRunnerProjectSettings().getSnippetLibraryDirectory(), false);
        }

        // create project
        this.eclipseProject.save(getRunnerProjectSettings().getBaseDirectory());
    }

    /**
     * This method is called after validation but before preparation.
     *
     * @throws SetteConfigurationException
     *             if a SETTE configuration problem occurred
     */
    protected void afterValidate() throws SetteConfigurationException {
    }

    /**
     * This method is called after preparation but before writing.
     *
     * @param pEclipseProject
     *            the Eclipse project
     */
    protected void afterPrepareRunnerProject(final EclipseProject pEclipseProject) {
    }

    /**
     * This method is called after writing.
     *
     * @param pEclipseProject
     *            the Eclipse project
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws SetteException
     *             if a SETTE problem occurred
     */
    protected void afterWriteRunnerProject(final EclipseProject pEclipseProject)
            throws IOException, SetteException {
    }

    /**
     * This method adds the generated directory to the classpath of the Eclipse
     * project.
     */
    protected final void addGeneratedDirectoryToProject() {
        this.eclipseProject.getClasspathDescriptor().addEntry(Kind.SOURCE, RunnerProjectSettings.GENERATED_DIRNAME);
    }
}