org.javascool.compiler.Compiler.java Source code

Java tutorial

Introduction

Here is the source code for org.javascool.compiler.Compiler.java

Source

/*
 * Java's Cool, IDE for French Computer Sciences Students
 * Copyright (C) 2012  Philippe VIENNE, INRIA
 *
 * This program 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.javascool.compiler;

import org.apache.commons.io.FileUtils;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;

import javax.tools.*;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;

/**
 * Compilateur de Classes Java. Cette classe permet de compiler des fichiers Java d'un rpertoire puis ensuite d'en
 * disposer dans la JVM par un ClassLoader prvu  cette effet. Ce compilateur est une sur-couche du Apache Commons JCI
 * Eclipse. Ce compilateur necessite pour fonctionner : <ul> <li>Un rpertoire de sources</li> <li>Des fichiers 
 * compiler, cela peut tre : <ul> <li>Un tableau de fichiers absolut ou relatifs tant dans les sources : {@link
 * #Compiler(java.io.File, java.io.File...)}</li> <li>Un nom de classe (ex. org.javascool.Main) : {@link
 * #Compiler(java.io.File, String)}</li> <li>Une recherche automatis des fichiers dans le rpertoire source : {@link
 * #Compiler(java.io.File)}</li> </ul> </li> </ul>
 *
 * @see org.eclipse.jdt.internal.compiler.tool.EclipseCompiler Librairie utilis pour la compilation
 * @see org.apache.commons.io Librairie utilis pour la recherche et manipulation de fichiers
 */
public class Compiler {

    /**
     * Liste des fichiers  compiler
     */
    private File[] filesToCompile;
    /**
     * Rpertoire contenant les sources
     */
    private File srcDirectory;

    /**
     * Initialise le compilateur avec un rpertoire et une liste de fichiers
     *
     * @param directory Le rpertoire o on doit travailler
     * @param javas     Les fichiers  compiler
     */
    public Compiler(File directory, File... javas) {
        assertDirectoryExists(directory);
        srcDirectory = directory;
        ArrayList<File> sources = new ArrayList<File>(javas.length);
        for (File javaSource : javas) {
            if (!javaSource.isAbsolute())
                javaSource = new File(directory, javaSource.getPath());
            assertFileExists(javaSource);
            sources.add(javaSource);
        }
        filesToCompile = FileUtils.convertFileCollectionToFileArray(sources);
    }

    /**
     * Initialise le compilateur avec un rpertoire et un nom de classe.
     *
     * @param directory Le rpertoire o on doit travailler
     * @param clazz     La classe  compiler
     */
    public Compiler(File directory, String clazz) {
        assertDirectoryExists(directory);
        srcDirectory = directory;
        assertFileExists(directory, decomposePathForAJavaSource(clazz));
        filesToCompile = new File[] { FileUtils.getFile(directory, decomposePathForAJavaSource(clazz)) };
    }

    /**
     * Initialize un compilateur pour compiler tous les java d'un dossier.
     *
     * @param directory Le rpertoire o on doit travailler
     */
    public Compiler(File directory) {
        assertDirectoryExists(directory);
        srcDirectory = directory;
        filesToCompile = FileUtils
                .convertFileCollectionToFileArray(FileUtils.listFiles(directory, new String[] { "java" }, true));
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////
    /////   Fonctions d'assertion
    //////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * S'assure que le dossier pass en argument existe bien et que c'est un dossier.
     *
     * @param directory Le dossier  tester
     * @throws IllegalArgumentException Si l'assertion n'est pas respect.
     */
    private static void assertDirectoryExists(File directory) {
        if (!directory.exists())
            throw new IllegalArgumentException(
                    "Le dossier " + directory + " n'existe pas, on ne peut pas continuer");
        if (directory.isFile())
            throw new IllegalArgumentException(
                    directory + " est un fichier et non un dossier, on ne peut pas continuer");
    }

    /**
     * S'assure que le fichier pass en argument existe bien et que c'est un fichier.
     *
     * @param file Le fichier  tester
     * @throws IllegalArgumentException Si l'assertion n'est pas respect.
     */
    private static void assertFileExists(File file) {
        if (!file.exists())
            throw new IllegalArgumentException("Le fichier " + file + " n'existe pas, on ne peut pas continuer");
        if (file.isDirectory())
            throw new IllegalArgumentException(
                    file + " est un dossier et non un fichier, on ne peut pas continuer");
    }

    /**
     * S'assure que le fichier pass en argument existe bien et que c'est un fichier.
     *
     * @param file Le fichier  tester
     * @param path Le reste du chemin au fichier
     * @see #assertFileExists(java.io.File)
     */
    private static void assertFileExists(File file, String... path) {
        assertFileExists(FileUtils.getFile(file, path));
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////
    /////   Utilitaires
    //////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Dcompose un nom de classe vers le chemin du fichier source. Permet de passer de "org.javascool.compiler.Main" 
     * ["org","javascool","compiler","Main.java"] qui une reprsentation du chemin du source sous la forme d'un tableau
     * de chaines.
     *
     * @param className Le nom de la classe au format Java
     * @return Le tableau reprsentant le chemin.
     */
    private static String[] decomposePathForAJavaSource(String className) {
        String[] path = className.split("\\.");
        path[path.length - 1] = path[path.length - 1] + ".java";
        return path;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////
    /////   Fonctions pour la compilation
    //////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Lance la compilation des fichiers. Va compiler les fichier dans le rpertoire des sources. Les .class se
     * retrouverons donc avec les .java.
     *
     * @return Le rsultat de la compilation
     * @see #compile(java.io.File)
     */
    public ArrayList<Diagnostic<? extends JavaFileObject>> compile() {
        return compile(srcDirectory);
    }

    /**
     * Lance la compilation des fichiers vers un autre rpertoire. Va compiler les fichier dans un autre rpertoire.
     *
     * @param binDirectory Rpertoire cible pour la compilation. Il doit dj exister et tre un dossier
     * @return Les erreurs de la compilation
     */
    public ArrayList<Diagnostic<? extends JavaFileObject>> compile(File binDirectory) {
        assertDirectoryExists(binDirectory);
        final ArrayList<Diagnostic<? extends JavaFileObject>> errors = new ArrayList<Diagnostic<? extends JavaFileObject>>();

        ArrayList<String> sourceFiles = new ArrayList<String>(filesToCompile.length);
        for (File srcFile : filesToCompile)
            sourceFiles.add(srcDirectory.toURI().relativize(srcFile.toURI()).getPath());
        classLoader = new JVSClassLoader(binDirectory);

        JavaCompiler eclipseCompiler;
        ArrayList<String> options;
        eclipseCompiler = new EclipseCompiler();
        options = getCompilerOptions();

        DiagnosticListener<? super JavaFileObject> diagnosticListener = new DiagnosticListener<JavaFileObject>() {
            public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
                errors.add(diagnostic);
            }
        };

        StandardJavaFileManager fileManager = eclipseCompiler.getStandardFileManager(diagnosticListener, null,
                Charset.forName("UTF-8"));
        Iterable<? extends JavaFileObject> compilationUnits = fileManager
                .getJavaFileObjects(sourceFiles.toArray(new String[sourceFiles.size()]));

        eclipseCompiler.getTask(null, fileManager, diagnosticListener, options, null, compilationUnits).call();

        return errors;
    }

    /**
     * Gnre les options pour le compilateur Eclipse. <a href="http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.jdt.doc.isv/guide/jdt_api_compile.htm">
     * Specs du Compilateur</a>
     *
     * @return Le tableau des options
     */
    private ArrayList<String> getCompilerOptions() {
        ArrayList<String> o = new ArrayList<String>();
        o.add("-nowarn");
        o.add("-1.6");
        o.add("-Xemacs");
        return o;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////
    /////   Fonctions pour obtenir un classloader sur les classes compils
    //////////////////////////////////////////////////////////////////////////////////////////////////

    private ClassLoader classLoader;

    /**
     * Permet d'obtenir un ClassLoader sur les classes compils. Le ClassLoader est cr lors de la compilation. Il
     * permet d'acceder aux classes compils mme si elle n'ont pas t incluses dans le classpath d'origine.
     *
     * @return Un magnifique ClassLoader
     * @see JVSClassLoader
     */
    public ClassLoader getClassLoader() {
        if (classLoader == null)
            throw new IllegalStateException("On ne peut pas crer un ClassLoader sur des sources non-compils");
        return classLoader;
    }

}