spoon.Launcher.java Source code

Java tutorial

Introduction

Here is the source code for spoon.Launcher.java

Source

/**
 * Copyright (C) 2006-2016 INRIA and contributors
 * Spoon - http://spoon.gforge.inria.fr/
 *
 * This software is governed by the CeCILL-C License under French law and
 * abiding by the rules of distribution of free software. You can use, modify
 * and/or redistribute the software under the terms of the CeCILL-C license as
 * circulated by CEA, CNRS and INRIA at http://www.cecill.info.
 *
 * This program 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 CeCILL-C License for more details.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL-C license and that you accept its terms.
 */
package spoon;

import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPException;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.stringparsers.FileStringParser;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import spoon.SpoonModelBuilder.InputType;
import spoon.compiler.Environment;
import spoon.compiler.SpoonCompiler;
import spoon.compiler.SpoonResource;
import spoon.compiler.SpoonResourceHelper;
import spoon.processing.Processor;
import spoon.reflect.CtModel;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtType;
import spoon.reflect.factory.Factory;
import spoon.reflect.factory.FactoryImpl;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.PrettyPrinter;
import spoon.reflect.visitor.filter.AbstractFilter;
import spoon.support.DefaultCoreFactory;
import spoon.support.JavaOutputProcessor;
import spoon.support.StandardEnvironment;
import spoon.support.compiler.FileSystemFile;
import spoon.support.compiler.FileSystemFolder;
import spoon.support.compiler.jdt.JDTBasedSpoonCompiler;
import spoon.support.gui.SpoonModelTree;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

/**
 * This class implements an integrated command-line launcher for processing
 * programs at compile-time using the JDT-based builder (Eclipse). It takes
 * arguments that allow building, processing, printing, and compiling Java
 * programs. Launch with no arguments (see {@link #main(String[])}) for detailed
 * usage.
 */
public class Launcher implements SpoonAPI {

    public static final String SPOONED_CLASSES = "spooned-classes";

    public static final String OUTPUTDIR = "spooned";

    private final Factory factory;

    private SpoonModelBuilder modelBuilder;

    private String[] commandLineArgs = new String[0];

    private Filter<CtType<?>> typeFilter;

    /**
     * Contains the arguments accepted by this launcher (available after
     * construction and accessible by sub-classes).
     */
    private static JSAP jsapSpec;
    protected JSAPResult jsapActualArgs;

    private List<String> processorTypes = new ArrayList<>();
    private List<Processor<? extends CtElement>> processors = new ArrayList<>();

    /**
     * A default program entry point (instantiates a launcher with the given
     * arguments and calls {@link #run()}).
     */
    public static void main(String[] args) throws Exception {
        new Launcher().run(args);
    }

    @Override
    public void run(String[] args) {
        this.setArgs(args);
        if (args.length != 0) {
            this.run();
            // display GUI
            if (this.jsapActualArgs.getBoolean("gui")) {
                new SpoonModelTree(getFactory());
            }
        } else {
            this.printUsage();
        }
    }

    public void setArgs(String[] args2) {
        this.commandLineArgs = args2;
        processArguments();
    }

    public void printUsage() {
        this.commandLineArgs = new String[] { "--help" };
        processArguments();
    }

    static {
        jsapSpec = defineArgs();
    }

    /**
     * Creates a {@link Launcher} using the {@link Factory} returned by {@link #createFactory()}.
     */
    public Launcher() {
        factory = createFactory();
        processArguments();
    }

    /**
     * Creates a {@link Launcher} with {@link Factory} {@code pFactory}.
     *
     * @param pFactory
     *       The {@link Factory} that will be utilized in {@link #buildModel()}.
     * @throws IllegalArgumentException
     *       If {@code pFactory == null}.
     */
    public Launcher(final Factory pFactory) {
        if (pFactory == null) {
            throw new IllegalArgumentException("unable to create launcher with null factory");
        }
        factory = pFactory;
        processArguments();
    }

    @Override
    public void addInputResource(String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            addInputResource(new FileSystemFolder(file));
        } else {
            addInputResource(new FileSystemFile(file));
        }
    }

    /** adds a resource to be parsed to build the spoon model */
    public void addInputResource(SpoonResource resource) {
        modelBuilder.addInputSource(resource);
    }

    @Override
    public void addProcessor(String name) {
        processorTypes.add(name);
    }

    @Override
    public <T extends CtElement> void addProcessor(Processor<T> processor) {
        processors.add(processor);
    }

    public void addTemplateResource(SpoonResource resource) {
        modelBuilder.addTemplateSource(resource);
    }

    @Override
    public Environment getEnvironment() {
        return factory.getEnvironment();
    }

    /**
     * Defines the common arguments for sub-launchers.
     *
     * @return the JSAP arguments
     */
    protected static JSAP defineArgs() {
        try {
            // Verbose output
            JSAP jsap = new JSAP();

            // help
            Switch sw1 = new Switch("help");
            sw1.setShortFlag('h');
            sw1.setLongFlag("help");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // Tabs
            sw1 = new Switch("tabs");
            sw1.setLongFlag("tabs");
            sw1.setDefault("false");
            sw1.setHelp("Use tabulations instead of spaces in the generated code (use spaces by default).");
            jsap.registerParameter(sw1);

            // Tab size
            FlaggedOption opt2 = new FlaggedOption("tabsize");
            opt2.setLongFlag("tabsize");
            opt2.setStringParser(JSAP.INTEGER_PARSER);
            opt2.setDefault("4");
            opt2.setHelp("Define tabulation size.");
            jsap.registerParameter(opt2);

            // Level logging.
            opt2 = new FlaggedOption("level");
            opt2.setLongFlag("level");
            opt2.setHelp("Level of the ouput messages about what spoon is doing. Default value is ALL level.");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setDefault(Level.OFF.toString());
            jsap.registerParameter(opt2);

            // Auto-import
            sw1 = new Switch("imports");
            sw1.setLongFlag("with-imports");
            sw1.setDefault("false");
            sw1.setHelp("Enable imports in generated files.");
            jsap.registerParameter(sw1);

            // java compliance
            opt2 = new FlaggedOption("compliance");
            opt2.setLongFlag("compliance");
            opt2.setHelp("Java source code compliance level (1,2,3,4,5, 6, 7 or 8).");
            opt2.setStringParser(JSAP.INTEGER_PARSER);
            opt2.setDefault("8");
            jsap.registerParameter(opt2);

            // compiler's encoding
            opt2 = new FlaggedOption("encoding");
            opt2.setLongFlag("encoding");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            opt2.setDefault("UTF-8");
            opt2.setHelp("Forces the compiler to use a specific encoding (UTF-8, UTF-16, ...).");
            jsap.registerParameter(opt2);

            // setting Input files & Directory
            opt2 = new FlaggedOption("input");
            opt2.setShortFlag('i');
            opt2.setLongFlag("input");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            opt2.setHelp("List of path to sources files.");
            jsap.registerParameter(opt2);

            // Processor qualified name
            opt2 = new FlaggedOption("processors");
            opt2.setShortFlag('p');
            opt2.setLongFlag("processors");
            opt2.setHelp("List of processor's qualified name to be used.");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // setting input template
            opt2 = new FlaggedOption("template");
            opt2.setShortFlag('t');
            opt2.setLongFlag("template");
            opt2.setHelp("List of source templates.");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            opt2.setHelp("List of path to templates java files.");
            jsap.registerParameter(opt2);

            // Spooned output directory
            opt2 = new FlaggedOption("output");
            opt2.setShortFlag('o');
            opt2.setLongFlag("output");
            opt2.setDefault(OUTPUTDIR);
            opt2.setHelp("Specify where to place generated java files.");
            opt2.setStringParser(FileStringParser.getParser());
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // Source classpath
            opt2 = new FlaggedOption("source-classpath");
            opt2.setLongFlag("source-classpath");
            opt2.setHelp("An optional classpath to be passed to the internal "
                    + "Java compiler when building or compiling the " + "input sources.");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // Template classpath
            opt2 = new FlaggedOption("template-classpath");
            opt2.setLongFlag("template-classpath");
            opt2.setHelp("An optional classpath to be passed to the " + "internal Java compiler when building "
                    + "the template sources.");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // Destination
            opt2 = new FlaggedOption("destination");
            opt2.setShortFlag('d');
            opt2.setLongFlag("destination");
            opt2.setDefault(SPOONED_CLASSES);
            opt2.setHelp("An optional destination directory for the generated class files.");
            opt2.setStringParser(FileStringParser.getParser());
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // Sets output type generation
            opt2 = new FlaggedOption("output-type");
            opt2.setLongFlag(opt2.getID());
            String msg = "States how to print the processed source code: ";
            int i = 0;
            for (OutputType v : OutputType.values()) {
                i++;
                msg += v.toString();
                if (i != OutputType.values().length) {
                    msg += "|";
                }
            }
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setHelp(msg);
            opt2.setDefault("classes");
            jsap.registerParameter(opt2);

            // Enable compilation
            sw1 = new Switch("compile");
            sw1.setLongFlag(sw1.getUsageName());
            sw1.setHelp("Compiles the resulting classes (after transformation) to bytecode.");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // Enable pre-compilation
            sw1 = new Switch("precompile");
            sw1.setLongFlag("precompile");
            sw1.setHelp("[experimental] Enable pre-compilation of input source files "
                    + "before processing. The compiled classes " + "will be added to the classpath.");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // Enable building only outdated files
            sw1 = new Switch("buildOnlyOutdatedFiles");
            sw1.setLongFlag("buildOnlyOutdatedFiles");
            sw1.setHelp("Set Spoon to build only the source files that " + "have been modified since the latest "
                    + "source code generation, for performance " + "purpose. Note that this option requires "
                    + "to have the --ouput-type option not set " + "to none. This option is not appropriate "
                    + "to all kinds of processing. In particular " + "processings that implement or rely on a "
                    + "global analysis should avoid this option " + "because the processor will only have access "
                    + "to the outdated source code (the files " + "modified since the latest processing).");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            sw1 = new Switch("lines");
            sw1.setLongFlag("lines");
            sw1.setHelp("Set Spoon to try to preserve the original line " + "numbers when generating the source "
                    + "code (may lead to human-unfriendly " + "formatting).");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // nobinding
            sw1 = new Switch("noclasspath");
            sw1.setShortFlag('x');
            sw1.setLongFlag("noclasspath");
            sw1.setHelp("Does not assume a full classpath");
            jsap.registerParameter(sw1);

            // show GUI
            sw1 = new Switch("gui");
            sw1.setShortFlag('g');
            sw1.setLongFlag("gui");
            sw1.setHelp("Show spoon model after processing");
            jsap.registerParameter(sw1);

            // Disable copy of resources.
            sw1 = new Switch("no-copy-resources");
            sw1.setShortFlag('r');
            sw1.setLongFlag("no-copy-resources");
            sw1.setHelp("Disable the copy of resources from source to destination folder.");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // Enable generation of javadoc.
            sw1 = new Switch("enable-comments");
            sw1.setShortFlag('c');
            sw1.setLongFlag("enable-comments");
            sw1.setHelp(
                    "Adds all code comments in the Spoon AST (Javadoc, line-based comments), rewrites them when pretty-printing.");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            // Generate only java files specified.
            opt2 = new FlaggedOption("generate-files");
            opt2.setShortFlag('f');
            opt2.setLongFlag("generate-files");
            opt2.setHelp(
                    "Only generate the given fully qualified java classes (separated by ':' if multiple are given).");
            opt2.setStringParser(JSAP.STRING_PARSER);
            opt2.setRequired(false);
            jsap.registerParameter(opt2);

            // Disable checks.
            sw1 = new Switch("disable-model-self-checks");
            sw1.setShortFlag('a');
            sw1.setLongFlag("disable-model-self-checks");
            sw1.setHelp(
                    "Disables checks made on the AST (hashcode violation, method's signature violation and parent violation). Default: false.");
            sw1.setDefault("false");
            jsap.registerParameter(sw1);

            return jsap;
        } catch (JSAPException e) {
            throw new SpoonException(e.getMessage(), e);
        }
    }

    /**
     * Returns the command-line given launching arguments in JSAP format.
     */
    protected final JSAPResult getArguments() {
        return parseArgs();
    }

    protected void processArguments() {
        jsapActualArgs = getArguments();

        Environment environment = factory.getEnvironment();
        // environment initialization
        environment.setComplianceLevel(jsapActualArgs.getInt("compliance"));
        environment.setLevel(jsapActualArgs.getString("level"));
        environment.setAutoImports(jsapActualArgs.getBoolean("imports"));
        environment.setNoClasspath(jsapActualArgs.getBoolean("noclasspath"));
        environment.setPreserveLineNumbers(jsapActualArgs.getBoolean("lines"));
        environment.setTabulationSize(jsapActualArgs.getInt("tabsize"));
        environment.useTabulations(jsapActualArgs.getBoolean("tabs"));
        environment.setCopyResources(!jsapActualArgs.getBoolean("no-copy-resources"));
        environment.setCommentEnabled(jsapActualArgs.getBoolean("enable-comments"));
        environment.setShouldCompile(jsapActualArgs.getBoolean("compile"));
        environment.setSelfChecks(jsapActualArgs.getBoolean("disable-model-self-checks"));

        if (getArguments().getString("generate-files") != null) {
            setOutputFilter(getArguments().getString("generate-files").split(":"));
        }

        // now we are ready to create a spoon compiler
        modelBuilder = createCompiler();

        if (getArguments().getString("input") != null) {
            for (String s : getArguments().getString("input").split("[" + File.pathSeparatorChar + "]")) {
                try {
                    modelBuilder.addInputSource(SpoonResourceHelper.createResource(new File(s)));
                } catch (FileNotFoundException e) {
                    throw new SpoonException(e);
                }
            }
        }

        if (jsapActualArgs.getBoolean("precompile")) {
            modelBuilder.compile(InputType.FILES);
            getEnvironment().setSourceClasspath(new String[] { getEnvironment().getBinaryOutputDirectory() });
        }

        if (getArguments().getFile("output") != null) {
            setSourceOutputDirectory(getArguments().getFile("output"));
        }

        // Adding template from command-line
        if (getArguments().getString("template") != null) {
            for (String s : getArguments().getString("template").split("[" + File.pathSeparatorChar + "]")) {
                try {
                    modelBuilder.addTemplateSource(SpoonResourceHelper.createResource(new File(s)));
                } catch (FileNotFoundException e) {
                    environment.report(null, Level.ERROR, "Unable to add template file: " + e.getMessage());
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }

        if (getArguments().getString("processors") != null) {
            for (String processorName : getArguments().getString("processors").split(File.pathSeparator)) {
                addProcessor(processorName);
            }
        }

    }

    /**
     * Gets the list of processor types to be initially applied during the
     * processing (-p option).
     */
    protected java.util.List<String> getProcessorTypes() {
        return processorTypes;
    }

    /**
     * Gets the list of processors instance to be initially applied during the
     * processing.
     */
    protected List<Processor<? extends CtElement>> getProcessors() {
        return processors;
    }

    /**
     * Parses the arguments given by the command line.
     *
     * @return the JSAP-presented arguments
     */
    protected JSAPResult parseArgs() {
        if (jsapSpec == null) {
            throw new IllegalStateException("no args, please call setArgs before");
        }
        JSAPResult arguments = jsapSpec.parse(commandLineArgs);
        if (!arguments.success()) {
            // print out specific error messages describing the problems
            for (java.util.Iterator<?> errs = arguments.getErrorMessageIterator(); errs.hasNext();) {
                System.err.println("Error: " + errs.next());
            }
        }
        if (!arguments.success() || arguments.getBoolean("help")) {
            System.err.println(getVersionMessage());
            System.err.println("Usage: java <launcher name> [option(s)]");
            System.err.println();
            System.err.println("Options : ");
            System.err.println();
            System.err.println(jsapSpec.getHelp());
            System.exit(-1);
        }

        return arguments;
    }

    /**
     * A default logger to be used by Spoon.
     */
    public static final Logger LOGGER = Logger.getLogger(Launcher.class);

    /**
     * Creates a new Spoon Java compiler in order to process and compile Java
     * source code.
     *
     * @param factory
     *       the factory this compiler works on
     */
    public SpoonCompiler createCompiler(Factory factory) {
        SpoonCompiler comp = new JDTBasedSpoonCompiler(factory);
        Environment env = getEnvironment();
        // building
        comp.setEncoding(getArguments().getString("encoding"));
        comp.setBuildOnlyOutdatedFiles(jsapActualArgs.getBoolean("buildOnlyOutdatedFiles"));
        comp.setBinaryOutputDirectory(jsapActualArgs.getFile("destination"));
        comp.setSourceOutputDirectory(jsapActualArgs.getFile("output"));
        comp.setEncoding(jsapActualArgs.getString("encoding"));

        // backward compatibility
        // we don't have to set the source classpath
        if (jsapActualArgs.contains("source-classpath")) {
            comp.setSourceClasspath(
                    jsapActualArgs.getString("source-classpath").split(System.getProperty("path.separator")));
        }

        env.debugMessage("output: " + comp.getSourceOutputDirectory());
        env.debugMessage("destination: " + comp.getBinaryOutputDirectory());
        env.debugMessage("source classpath: " + Arrays.toString(comp.getSourceClasspath()));
        env.debugMessage("template classpath: " + Arrays.toString(comp.getTemplateClasspath()));

        return comp;
    }

    public SpoonCompiler createCompiler(Factory factory, List<SpoonResource> inputSources) {
        SpoonCompiler c = createCompiler(factory);
        c.addInputSources(inputSources);
        return c;
    }

    /**
     * Creates a new Spoon Java compiler in order to process and compile Java
     * source code.
     */
    public SpoonModelBuilder createCompiler(Factory factory, List<SpoonResource> inputSources,
            List<SpoonResource> templateSources) {
        SpoonModelBuilder c = createCompiler(factory);
        c.addInputSources(inputSources);
        c.addTemplateSources(templateSources);
        return c;
    }

    @Override
    public SpoonCompiler createCompiler() {
        return createCompiler(factory);
    }

    /**
     * Creates a new Spoon Java compiler with a default factory and a list of
     * input sources.
     */
    public SpoonCompiler createCompiler(List<SpoonResource> inputSources) {
        SpoonCompiler c = createCompiler(factory);
        c.addInputSources(inputSources);
        return c;
    }

    @Override
    public Factory createFactory() {
        return new FactoryImpl(new DefaultCoreFactory(), createEnvironment());
    }

    @Override
    public Factory getFactory() {
        return factory;
    }

    @Override
    public Environment createEnvironment() {
        return new StandardEnvironment();
    }

    public JavaOutputProcessor createOutputWriter(File sourceOutputDir, Environment environment) {
        return new JavaOutputProcessor(sourceOutputDir, createPrettyPrinter());
    }

    public PrettyPrinter createPrettyPrinter() {
        return new DefaultJavaPrettyPrinter(getEnvironment());
    }

    /**
     * Runs Spoon using the given compiler, with the given run options. A Spoon
     * run will perform the following tasks:
     *
     * <ol>
     * <li>Source model building in the given compiler:
     * {@link SpoonModelBuilder#build()}.</li>
     * <li>Template model building in the given factory (if any template source
     * is given): {@link SpoonModelBuilder#build()}.</li>
     * <li>Model processing with the list of given processors if any:
     * {@link SpoonModelBuilder#instantiateAndProcess(List)}.</li>
     * <li>Processed Source code printing and generation (can be disabled with
     * {@link OutputType#NO_OUTPUT}):
     * {@link SpoonModelBuilder#generateProcessedSourceFiles(OutputType)}.</li>
     * <li>Processed source code compilation (optional):
     * </ol>
     */
    @Override
    public void run() {
        Environment env = modelBuilder.getFactory().getEnvironment();
        env.reportProgressMessage(getVersionMessage());
        env.reportProgressMessage("running Spoon...");

        env.reportProgressMessage("start processing...");

        long t = 0;
        long tstart = System.currentTimeMillis();

        buildModel();

        process();

        prettyprint();

        if (env.shouldCompile()) {
            // we compile the types from the factory, they may have been modified by some processors
            modelBuilder.compile(InputType.CTTYPES);
        }

        t = System.currentTimeMillis();

        env.debugMessage("program spooning done in " + (t - tstart) + " ms");
        env.reportEnd();

    }

    private String getVersionMessage() {
        return "Spoon version " + ResourceBundle.getBundle("spoon").getString("application.version");
    }

    public static final IOFileFilter RESOURCES_FILE_FILTER = new IOFileFilter() {
        @Override
        public boolean accept(File file) {
            return !file.getName().endsWith(".java");
        }

        @Override
        public boolean accept(File file, String s) {
            return false;
        }
    };

    public static final IOFileFilter ALL_DIR_FILTER = new IOFileFilter() {
        @Override
        public boolean accept(File file) {
            return true;
        }

        @Override
        public boolean accept(File file, String s) {
            return false;
        }
    };

    @Override
    public void buildModel() {
        long tstart = System.currentTimeMillis();
        modelBuilder.build();
        getEnvironment().debugMessage("model built in " + (System.currentTimeMillis() - tstart));
    }

    @Override
    public void process() {
        long tstart = System.currentTimeMillis();
        modelBuilder.instantiateAndProcess(getProcessorTypes());
        modelBuilder.process(getProcessors());
        getEnvironment().debugMessage("model processed in " + (System.currentTimeMillis() - tstart) + " ms");
    }

    @Override
    public void prettyprint() {
        OutputType outputType = OutputType.fromString(jsapActualArgs.getString("output-type"));
        long tstart = System.currentTimeMillis();
        try {
            modelBuilder.generateProcessedSourceFiles(outputType, typeFilter);
        } catch (Exception e) {
            throw new SpoonException(e);
        }

        if (!outputType.equals(OutputType.NO_OUTPUT) && getEnvironment().isCopyResources()) {
            for (File dirInputSource : modelBuilder.getInputSources()) {
                if (dirInputSource.isDirectory()) {
                    final Collection<?> resources = FileUtils.listFiles(dirInputSource, RESOURCES_FILE_FILTER,
                            ALL_DIR_FILTER);
                    for (Object resource : resources) {
                        final String resourceParentPath = ((File) resource).getParent();
                        final String packageDir = resourceParentPath.substring(dirInputSource.getPath().length());
                        final String targetDirectory = modelBuilder.getSourceOutputDirectory() + packageDir;
                        try {
                            FileUtils.copyFileToDirectory((File) resource, new File(targetDirectory));
                        } catch (IOException e) {
                            throw new SpoonException(e);
                        }
                    }
                }
            }
        }

        getEnvironment().debugMessage("pretty-printed in " + (System.currentTimeMillis() - tstart) + " ms");
    }

    public SpoonModelBuilder getModelBuilder() {
        return modelBuilder;
    }

    @Override
    public void setSourceOutputDirectory(String path) {
        setSourceOutputDirectory(new File(path));
    }

    @Override
    public void setSourceOutputDirectory(File outputDirectory) {
        modelBuilder.setSourceOutputDirectory(outputDirectory);
        getEnvironment().setDefaultFileGenerator(createOutputWriter(outputDirectory, getEnvironment()));
    }

    @Override
    public void setOutputFilter(Filter<CtType<?>> typeFilter) {
        this.typeFilter = typeFilter;
    }

    @Override
    public void setOutputFilter(final String... qualifedNames) {
        setOutputFilter(new AbstractFilter<CtType<?>>(CtType.class) {
            @Override
            public boolean matches(CtType<?> element) {
                for (String generateFile : qualifedNames) {
                    if (generateFile.equals(element.getQualifiedName())) {
                        return true;
                    }
                }
                return false;
            }
        });
    }

    @Override
    public void setBinaryOutputDirectory(String path) {
        getFactory().getEnvironment().setBinaryOutputDirectory(path);
    }

    @Override
    public void setBinaryOutputDirectory(File outputDirectory) {
        setBinaryOutputDirectory(outputDirectory.getPath());
    }

    @Override
    public CtModel getModel() {
        return factory.getModel();
    }

}