Draft of JIMS based on external object
[scilab.git] / scilab / modules / external_objects_java / src / java / org / scilab / modules / external_objects_java / ScilabJavaCompiler.java
1 /*
2  * JIMS ( http://forge.scilab.org/index.php/p/JIMS/ ) - This file is a part of JIMS
3  * Copyright (C) 2010 - 2011 - Calixte DENIZET <calixte@contrib.scilab.org>
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 package org.scilab.modules.external_objects_java;
14
15 import java.io.File;
16 import java.io.StringWriter;
17 import java.lang.reflect.InvocationTargetException;
18 import java.lang.reflect.Method;
19 import java.net.MalformedURLException;
20 import java.net.URLClassLoader;
21 import java.net.URI;
22 import java.net.URL;
23 import java.util.Arrays;
24 import java.util.Locale;
25 import java.util.ServiceLoader;
26 import java.util.logging.Level;
27
28 import javax.tools.Diagnostic;
29 import javax.tools.DiagnosticCollector;
30 import javax.tools.JavaCompiler;
31 import javax.tools.JavaFileObject;
32 import javax.tools.SimpleJavaFileObject;
33 import javax.tools.StandardJavaFileManager;
34 import javax.tools.ToolProvider;
35 import javax.tools.JavaCompiler.CompilationTask;
36 import javax.tools.JavaFileObject.Kind;
37
38 /**
39  * Class to provide a java compiler to JIMS.
40  * Try to find the compiler provide with JDK and if it is not found, use the Eclipse Compiler for Java
41  * @author Calixte DENIZET
42  */
43 @SuppressWarnings("serial")
44 public class ScilabJavaCompiler {
45
46     private static final String JAVACOMPILER = "javax.tools.JavaCompiler";
47     private static final String binpath = System.getProperty("java.io.tmpdir") + File.separator + "JIMS" + File.separator + "bin";
48
49     private static JavaCompiler compiler;
50
51     static {
52         new File(System.getProperty("java.io.tmpdir") + File.separator + "JIMS").mkdir();
53         new File(binpath).mkdir();
54         try {
55             URL binURL = new File(binpath).toURI().toURL();
56             addURLToClassPath(binURL);
57         } catch (MalformedURLException e) {
58             System.err.println(e);
59         }
60     }
61
62     /**
63      * Just find a compiler
64      */
65     private static void findCompiler() throws ScilabJavaException {
66         if (compiler == null) {
67             try {
68                 compiler = ToolProvider.getSystemJavaCompiler();
69             } catch (Exception e) { }
70
71             if (compiler == null) {
72                 ServiceLoader<JavaCompiler> jcompilers = ServiceLoader.load(JavaCompiler.class);
73                 for (JavaCompiler jc : jcompilers) {
74                     if (jc != null) {
75                         compiler = jc;
76                         break;
77                     }
78                 }
79             }
80
81             if (compiler == null) {
82                 throw new ScilabJavaException("No java compiler in the classpath\nCheck for tools.jar (comes from JDK) or ecj-3.6.x.ajr (Eclipse Compiler for Java)");
83             }
84         }
85     }
86
87     /**
88      * Compile code got as string
89      * @param className the class name
90      * @param code the lines giving the code to compile
91      * @return an integer corresponding to the compiled and loaded class.
92      */
93     public static int compileCode(String className, String[] code) throws ScilabJavaException {
94         findCompiler();
95
96         if (ScilabJavaObject.debug) {
97             ScilabJavaObject.logger.log(Level.INFO, "Compilation of class \'" + className + "\'");
98         }
99
100         DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
101
102         SourceString file = new SourceString(className, code);
103         StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);
104         Iterable <? extends JavaFileObject > compilationUnits = Arrays.asList(file);
105         String[] compileOptions = new String[] {"-d", binpath} ;
106         Iterable<String> options = Arrays.asList(compileOptions);
107
108         CompilationTask task = compiler.getTask(null, stdFileManager, diagnostics, options, null, compilationUnits);
109
110         boolean success = task.call();
111         StringBuffer buf = new StringBuffer();
112         for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
113             buf.append(diagnostic.toString());
114             buf.append("\n");
115         }
116
117         if (success) {
118             return ScilabClassLoader.loadJavaClass(className, true);
119         } else {
120             throw new ScilabJavaException(buf.toString());
121         }
122     }
123
124     /**
125      * Add a class in the classpath
126      * @param url the class url
127      */
128     public static void addURLToClassPath(URL url) {
129         URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
130         try {
131             final Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] {URL.class});
132             method.setAccessible(true);
133             method.invoke(sysloader , new Object[] {url});
134         } catch (NoSuchMethodException e) {
135             System.err.println("Error: Cannot find the declared method: " + e.getLocalizedMessage());
136         } catch (IllegalAccessException e) {
137             System.err.println("Error: Illegal access: " + e.getLocalizedMessage());
138         } catch (InvocationTargetException e) {
139             System.err.println("Error: Could not invocate target: " + e.getLocalizedMessage());
140         }
141     }
142
143     /**
144      * Inner class to handle String as File
145      */
146     private static class SourceString extends SimpleJavaFileObject {
147
148         private final String code;
149
150         private SourceString(String className, String[] code) {
151             super(new File(binpath + "/" + className.replace('.', '/') + Kind.SOURCE.extension).toURI(), Kind.SOURCE);
152
153             StringBuffer buf = new StringBuffer();
154             for (String str : code) {
155                 buf.append(str);
156                 buf.append("\n");
157             }
158             this.code = buf.toString();
159         }
160
161         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
162             return code;
163         }
164     }
165 }