org.javascool.compiler.ProgletCodeCompiler.java Source code

Java tutorial

Introduction

Here is the source code for org.javascool.compiler.ProgletCodeCompiler.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.apache.commons.io.IOUtils;

import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Gestionnaire de compilation pour un code ayant une relation avec une Proglet. Gre d'un bout  l'autre la compilation
 * et l'execution d'un code JVS dpendant d'une proglet. Si aucune proglet n'est communiqu alors on peut considrer que
 * c'est un Java pur  compiler.
 * <p/>
 * La proglet JVS par dfaut est "ingredients" est en fait un code JVS qui est retranscrit en Java sans ajout de
 * fonctions.
 * 
 * @eprecated Vous devez utiliser le compilateur du package org.javascool.manager.proglets. Celui ci n'est plus maintenu.
 *
 * @author Philippe VIENNE (PhilippeGeek@gmail.com)
 * @since 5.0
 */
public class ProgletCodeCompiler {

    /**
     * Le rpertoire pour les compilations temporaires
     */
    private static final File tmpDirectory = new File(FileUtils.getTempDirectory(), "javascool-compiler");

    static {
        tmpDirectory.mkdirs();
    }

    /**
     * L'identificateur de la proglet native de la version compil.
     */
    public static final String DEFAULT_PROGLET = "ingrediants";
    /**
     * L'identificateur de la proglet utilis.
     */
    private String proglet;
    /**
     * Le translator de la proget. Si la proglet n'en dclare pas, alors on prend l'officiel {@link
     * DefaultJVSTranslator}
     */
    private JVSTranslator translator;
    /**
     * Le compilateur utilis.
     */
    private Compiler compiler;
    /**
     * Le fichier  compiler.
     */
    private File jvsFile;

    /**
     * Cre un compilateur pour une proglet dsign
     *
     * @param progletName Le nom de code de la proglet
     */
    protected ProgletCodeCompiler(String progletName) {
        if (isDefaultProglet(progletName))
            return; // On ne charge rien si c'est la proglet native
        assertProgletExists(progletName);
        proglet = progletName;
    }

    /**
     * Crer un compilateur pour une proglet avec un code JVS pass en argument. Ce constructeur permet de faire de la
     * compilation  la vol sans tre oblig de pass par un Fichier. Les resources du code seront alors considr
     * comme tant dans le rpertoire racine de l'utilisateur.
     *
     * @param progletName Le nom de code de la proglet
     * @param code        Le code JVS  compiler
     */
    public ProgletCodeCompiler(String progletName, String code) {
        this(progletName);
        try {
            File tmpFolder = new File(tmpDirectory, code.hashCode() + "-compile");
            tmpFolder.mkdirs();
            jvsFile = File.createTempFile("JVSCompileSource", ".jvs", tmpFolder);
            FileUtils.writeStringToFile(jvsFile, code);
        } catch (IOException e) {
            throw new IllegalStateException("On ne peut pas crer le fichier temporaire dans "
                    + new File(tmpDirectory, code.hashCode() + "-compile"));
        }
    }

    /**
     * Cre un compilateur pour une proglet avec un Fichier JVS pass en argument. Ce constructeur va permtre de
     * controller une compilation au sein d'un rpertoire donne qui sera par la suite le rpertoire de resource du
     * programme.
     *
     * @param progletName Le nom de code de la proglet
     * @param jvsFile     Le fichier JVS  compiler
     */
    public ProgletCodeCompiler(String progletName, File jvsFile) {
        this(progletName);
        assertFileExists(jvsFile);
        this.jvsFile = jvsFile.getAbsoluteFile();
    }

    /**
     * Permet de dterminer si la proglet est celle native ou pas. <p>La proglet Native est une proglet implmentant
     * uniquement les specs par dfaut. C'est la proglet "ABCDAlgo" qui est aussi appel "ingrediants". Cette proglet
     * native n'a aucun Translator, Functions et autres artifacts dfinit.</p> <p/> <p>A contrario, les autres proglets
     * peuvent avoir des artifacts dfinits selon les specifications de Java's Cool. Pour les connatres, il faut aller
     * sur : <a href="http://javascool.github.com/doc/developper/specification.html">Le site officiel</a></p>
     *
     * @param progletName Le nom de la proglet  tester
     * @return vrai si c'est la proglet native
     */
    private static boolean isDefaultProglet(String progletName) {
        return progletName == null || progletName.equals(DEFAULT_PROGLET);
    }

    /**
     * Vrifie qu'un fichier existe bel et bien et que c'est un fichier
     *
     * @param file Le fichier  vrifier
     */
    private static void assertFileExists(File file) {
        try {
            if (file == null)
                throw new Exception();
            if (!file.exists())
                throw new Exception();
            if (file.isDirectory())
                throw new Exception();
        } catch (Exception e) {
            throw new IllegalArgumentException("Le fichier " + file + " n'existe pas");
        }
    }

    /**
     * Vrifie si une proglet est prsente dans le Classpath actuelle
     *
     * @param progletName Le nom de la proglet  valider
     */
    private static void assertProgletExists(String progletName) {
        try {
            IOUtils.toString(ProgletCodeCompiler.class.getClassLoader()
                    .getResourceAsStream("org/javascool/proglets/" + progletName + "/proglet.json"));
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "La proglet " + progletName + " n'existe pas dans le Classpath actuel.");
        }
    }

    /**
     * Lance la compilation du code.
     *
     * @return Les erreurs de compilations provenant de Java
     */
    public ArrayList<Diagnostic<? extends JavaFileObject>> compile() {
        translator = getTranslatorForProglet(proglet);
        setUpTranslator();

        translator.getJavaCode(); // On lance un premier Parse

        // On tablie l o sera le fichier compil
        String[] path = translator.getFullClassname().split("\\.");
        path[path.length - 1] = path[path.length - 1] + ".class";
        File classFile = FileUtils.getFile(jvsFile.getParentFile(), path);
        classFile.getParentFile().mkdirs();

        // On en fait de mme pour le fichier java
        path = translator.getFullClassname().split("\\.");
        path[path.length - 1] = path[path.length - 1] + ".java";
        File javaFile = FileUtils.getFile(jvsFile.getParentFile(), path);
        javaFile.getParentFile().mkdirs();

        // On traduit le JVS vers du Java
        try {
            FileUtils.writeStringToFile(javaFile, translator.getJavaCode());
        } catch (IOException e) {
            throw new RuntimeException("Impossible d'crire le fichier Java", e);
        }

        compiler = new Compiler(jvsFile.getParentFile(), translator.getFullClassname());

        // On compile le Java et on retourne le rsultat
        return compiler.compile();
    }

    /**
     * Configure le translator pour lui ajouter les Imports necessaires
     */
    private void setUpTranslator() {
        translator.addDefaultImports(); // On vrifie bien qu'on est dans un monde Java's Cool (Imports, Runnable ...)
        try {
            ProgletCodeCompiler.class.getClassLoader()
                    .loadClass("org.javascool.proglets." + this.proglet + ".Functions");
            translator.addImport("org.javascool.proglets." + this.proglet + ".Functions.*", true);
        } catch (ClassNotFoundException e) {
            Logger.getAnonymousLogger().log(Level.INFO,
                    "Aucun fonction  ajouter avec la proglet " + this.proglet);
        } // Dans ce cas on ne fais rien car il n'y a pas de Functions.java
    }

    /**
     * Permet d'obtenir un instance du Runnable compil.
     *
     * @return Une instance du runnable
     * @throws ClassNotFoundException Bien souvent, cela veut dire que la classe n'as pas pu tre compil
     * @throws IllegalStateException  Dans le cas o une erreur impromptu survient.
     */
    public Runnable getCompiledRunnable() throws ClassNotFoundException, IllegalStateException {
        Class<?> compiledClass = compiler.getClassLoader().loadClass(translator.getFullClassname());
        try {
            return (Runnable) compiledClass.newInstance();
        } catch (ClassCastException e) {
            throw new IllegalStateException("La classe compil n'est pas un Runnable", e);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("La classe compil n'est pas accessible", e);
        } catch (InstantiationException e) {
            throw new IllegalStateException("La classe compil ne peut pas tre cr comme objet", e);
        }
    }

    /**
     * Cherche si la proglet dfinit un Translator. Permet d'acceder aux translators des package du Classpath.
     *
     * @param progletName L'identificateur de la proglet
     * @return Le Translator  utiliser pour cette proglet
     */
    protected JVSTranslator getTranslatorForProglet(String progletName) {
        if (!isDefaultProglet(progletName)) {
            try {
                Class<?> translatorClass = ProgletCodeCompiler.class.getClassLoader()
                        .loadClass("org.javascool.proglets." + this.proglet + ".Translator");
                translator = (JVSTranslator) translatorClass.getDeclaredConstructor(File.class)
                        .newInstance(jvsFile);
            } catch (ClassNotFoundException e) {
                Logger.getAnonymousLogger().log(Level.INFO, "Aucun Translator pour " + progletName);
            } // Dans ce cas on ne fais rien car il n'y a pas de Functions.java
            catch (Exception e) {
                throw new IllegalStateException("Impossible de crer un translator.");
            }
        }
        try {
            return new DefaultJVSTranslator(jvsFile);
        } catch (IOException e) {
            throw new IllegalStateException("Impossible de crer un translator sur le fichier  ouvrir");
        }
    }

}