gool.parser.java.JavaParser.java Source code

Java tutorial

Introduction

Here is the source code for gool.parser.java.JavaParser.java

Source

/*
 * Copyright 2010 Pablo Arrighi, Alex Concha, Miguel Lezama for version 1.
 * Copyright 2013 Pablo Arrighi, Miguel Lezama, Kevin Mazet for version 2.    
 *
 * This file is part of GOOL.
 *
 * GOOL is free software: you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation, version 3.
 *
 * GOOL 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 GNU General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU General Public License along with GOOL,
 * in the file COPYING.txt.  If not, see <http://www.gnu.org/licenses/>.
 */

package gool.parser.java;

import gool.ParseGOOL;
import gool.Settings;
import gool.ast.core.ClassDef;
import gool.generator.GoolGeneratorController;
import gool.generator.common.Platform;
import gool.recognizer.java.JavaRecognizer;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import logger.Log;

import org.apache.commons.lang.StringUtils;

import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Trees;

import gool.executor.ExecutorHelper;

/**
 * This class parses concrete Java into abstract GOOL. For this purpose it
 * relies on Sun's Java parser.
 */
public class JavaParser extends ParseGOOL {

    /**
     * Parsing concrete Java into abstract GOOL is done in three steps. - We
     * call Sun's java parser to produce abstract Java; - We visit abstract Java
     * with the JavaRecognizer to produce abstract GOOL; - We annotate the
     * abstract GOOL so that it carries the Target language.
     * 
     * @param defaultPlatform
     *            : specifies the Target language of the code generation that
     *            will later be applied to the abstract GOOL, once this Java
     *            parsing is performed.
     * @param compilationUnits
     *            : An Iterable of JavaFileObject, which are Sun's java parser's
     *            representation of the files to be parsed.
     * @param dependencies
     *            : specifies imported libraries
     * @param visitor
     *            : this is the class that transforms Sun's abstract java, into
     *            abstract GOOL, i.e. the JavaRecognizer.
     * @return a list of classdefs, i.e. of abstract GOOL classes.
     * @throws Exception
     */

    public static Collection<ClassDef> parseGool(Platform defaultPlatform,
            Iterable<? extends JavaFileObject> compilationUnits, List<File> dependencies, JavaRecognizer visitor)
            throws Exception {
        if (visitor == null) {
            throw new IllegalArgumentException("The gool visitor is null.");
        }

        /**
         * concreteJavaToConcretePlatform We will now setup the options to Sun's
         * java compiler This requires working out the dependencies
         */
        // convert dependencies into a list of file paths to reach them
        List<String> stringDependencies = new ArrayList<String>();
        if (dependencies != null && !dependencies.isEmpty()) {
            for (File file : dependencies) {
                stringDependencies.add(file.getAbsolutePath());
            }
        }
        // further, add the GOOL library as a dependency
        // so that the program can use gool.imports.java
        stringDependencies.add(Settings.get("gool_library").toString());

        // with the dependencies all set, we can make up the options
        List<String> options = Arrays.asList("-classpath",
                StringUtils.join(stringDependencies, File.pathSeparator));

        /**
         * We now parse using Sun's java compiler
         */
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavacTask task = (JavacTask) compiler.getTask(null, null, null, options, null, compilationUnits);
        Iterable<? extends CompilationUnitTree> asts = task.parse();
        visitor.setTypes(task.getTypes());
        /**
         * We now analyze using Sun's java compiler so as to get a Java abstract
         * type tree.
         */
        task.analyze();
        Trees typetrees = Trees.instance(task);

        /**
         * We now prepare the JavaRecognizer for conversion of abstract Java to
         * abstract GOOL.
         */

        // The visitor needs to know what the Target language is
        // Because it will annotate the abstract GOOL with this information.
        visitor.setDefaultPlatform(defaultPlatform);
        GoolGeneratorController.setCodeGenerator(defaultPlatform.getCodePrinter().getCodeGenerator());

        // The visitor might need Sun's analyzed Java abstract type tree.
        visitor.setTrees(typetrees);

        /**
         * We launch the JavaRecognizer against each abstract Java AST
         */
        for (CompilationUnitTree ast : asts) {
            visitor.setCurrentCompilationUnit(ast);
            visitor.scan();
        }

        /**
         * Register each abstract GOOL class, so that they can see each other
         * and therefore represent valid types
         */
        for (ClassDef classDef : visitor.getGoolClasses()) {
            classDef.getPlatform().registerCustomDependency(classDef.toString(), classDef);
        }

        return visitor.getGoolClasses();
    }

    /**
     * Initially, call the parser with input files.
     */
    public Collection<ClassDef> parseGool(Platform defaultPlatform, Collection<? extends File> inputFiles)
            throws Exception {
        return parseGool(defaultPlatform, ExecutorHelper.getJavaFileObjects(inputFiles));
    }

    /**
     * Then, call the parser with no dependency yet, and with the
     * JavaRecognizer.
     */
    public Collection<ClassDef> parseGool(Platform defaultPlatform,
            Iterable<? extends JavaFileObject> compilationUnits) throws Exception {
        return parseGool(defaultPlatform, compilationUnits, null, new JavaRecognizer());
    }

    /**
     * if the parser is called on a directory, wrap it into a compilation unit,
     * and call the parser on that file.
     */
    public Collection<ClassDef> parseGoolFiles(Platform defaultPlatform, List<File> dirs) throws Exception {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(dirs);
        return parseGool(defaultPlatform, compilationUnits);
    }

    /**
     * If the parser is called on an input string, wrap it into a compilation
     * unit, and call the parser on that file.
     */
    public Collection<ClassDef> parseGool(Platform defaultPlatform, //
            String input) throws Exception {
        ArrayList<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
        compilationUnits.add(new MyFileObject(input, "Random.java"));
        Log.i(input);
        return parseGool(defaultPlatform, compilationUnits);
    }

    /**
     * Sun's java parser takes his inputs as compilation units, which themselves
     * are the source files loaded into objects called SimpleJavaFileObject
     * Those objects have a name and: - a kind to say that they are source
     * files, - a content, retrieved by getCharContent. When we wrap a string
     * input into a file, we create directly such a SimpleJavaFileObject; with
     * getCharContent overriden to yield that input.
     */
    static class MyFileObject extends SimpleJavaFileObject {

        private String input;

        public MyFileObject(String input, String name) {
            super(URI.create(name), JavaFileObject.Kind.SOURCE);
            this.input = input;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return input;
        }
    }

}