2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - 2011 - Calixte DENIZET <calixte@contrib.scilab.org>
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
16 package org.scilab.modules.external_objects_java;
18 import java.io.BufferedWriter;
19 import java.io.CharArrayWriter;
21 import java.io.FileReader;
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.io.StringWriter;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.net.MalformedURLException;
29 import java.net.URLClassLoader;
31 import java.util.Arrays;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.ServiceLoader;
37 import java.util.logging.Level;
38 import java.util.logging.Logger;
40 import javax.swing.SwingUtilities;
41 import javax.tools.Diagnostic;
42 import javax.tools.DiagnosticCollector;
43 import javax.tools.FileObject;
44 import javax.tools.ForwardingJavaFileManager;
45 import javax.tools.JavaCompiler;
46 import javax.tools.StandardLocation;
47 import javax.tools.JavaFileObject;
48 import javax.tools.SimpleJavaFileObject;
49 import javax.tools.StandardJavaFileManager;
50 import javax.tools.ToolProvider;
51 import javax.tools.JavaCompiler.CompilationTask;
52 import javax.tools.JavaFileObject.Kind;
54 import org.scilab.modules.commons.ScilabCommonsUtils;
57 * Class to provide a java compiler to JIMS.
58 * Try to find the compiler provide with JDK and if it is not found, use the Eclipse Compiler for Java
59 * @author Calixte DENIZET
61 @SuppressWarnings("serial")
62 public class ScilabJavaCompiler {
64 private static final String JAVACOMPILER = "javax.tools.JavaCompiler";
65 private static final String BINPATH = System.getProperty("java.io.tmpdir") + File.separator + "JIMS" + File.separator + "bin";
67 private static JavaCompiler compiler;
68 private static boolean ecjLoaded = false;
70 private static boolean isECJ;
74 new File(System.getProperty("java.io.tmpdir") + File.separator + "JIMS").mkdir();
75 new File(BINPATH).mkdir();
77 URL binURL = new File(BINPATH).toURI().toURL();
78 addURLToClassPath(binURL);
79 } catch (MalformedURLException e) {
80 System.err.println(e);
85 * Just find a compiler
87 private static void findCompiler() throws ScilabJavaException {
88 if (compiler == null) {
90 compiler = ToolProvider.getSystemJavaCompiler();
91 } catch (Exception e) { }
93 if (compiler == null) {
94 ServiceLoader<JavaCompiler> jcompilers = ServiceLoader.load(JavaCompiler.class);
95 for (JavaCompiler jc : jcompilers) {
103 if (compiler == null) {
105 throw new ScilabJavaException("No java compiler in the classpath\nCheck for tools.jar (comes from JDK) or ecj-4.4.x.jar (Eclipse Compiler for Java)");
108 // Compiler should be in thirdparty so we load it
109 ScilabCommonsUtils.loadOnUse("external_objects_java");
114 isECJ = compiler.getClass().getSimpleName().indexOf("Eclipse") != -1;
119 * Compile code got as string
120 * @param className the class name
121 * @param code the lines giving the code to compile
122 * @return an integer corresponding to the compiled and loaded class.
124 public static int compileCode(String className, String[] code) throws ScilabJavaException {
125 if (SwingUtilities.isEventDispatchThread()) {
129 SwingUtilities.invokeAndWait(new Runnable() {
134 } catch (ScilabJavaException e) {
135 // TODO Auto-generated catch block
140 } catch (InvocationTargetException e) {
141 // TODO Auto-generated catch block
143 } catch (InterruptedException e) {
144 // TODO Auto-generated catch block
149 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
150 StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);
154 // it seems that with the embedded ecj, the only way to set the cp is to use java.class.path...
156 System.setProperty("java.class.path", cp + File.pathSeparatorChar + System.getProperty("java.class.path"));
159 stdFileManager.setLocation(StandardLocation.CLASS_PATH, getClasspathFiles());
160 } catch (Exception e) { }
163 ClassFileManager manager = new ClassFileManager(stdFileManager);
164 List<SimpleJavaFileObject> compilationUnits = new ArrayList<SimpleJavaFileObject>();
165 CharArrayWriter caw = new CharArrayWriter();
166 BufferedWriter out = new BufferedWriter(caw);
167 boolean isFile = true;
168 SourceString sourceString = null;
169 for (String s : code) {
170 File f = new File(s);
171 if (!f.exists() || !f.canRead()) {
178 for (String s : code) {
179 File f = new File(s);
180 compilationUnits.add(new SourceFile(f));
183 sourceString = new SourceString(className, code);
184 compilationUnits.add(sourceString);
187 String[] compileOptions = new String[] {"-d", BINPATH};
188 Iterable<String> options = Arrays.asList(compileOptions);
191 stdFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(new File(BINPATH)));
192 } catch (IOException ex) {
193 Logger.getLogger(ScilabJavaCompiler.class.getName()).log(Level.SEVERE, null, ex);
196 CompilationTask task = compiler.getTask(out, manager, diagnostics, options, null, compilationUnits);
197 boolean success = task.call();
200 final String s = System.getProperty("java.class.path").replace(cp + File.pathSeparatorChar, "");
201 System.setProperty("java.class.path", s);
208 error = caw.toString();
209 } catch (IOException e) {
214 } catch (IOException e) { }
221 if (manager.className != null && !manager.className.isEmpty()) {
222 return ScilabClassLoader.loadJavaClass(BINPATH, manager.className);
229 error = getCompilerErrors(diagnostics);
232 throw new ScilabJavaException(error);
237 * Returns the compilation errors from the diagnostics
238 * @param diagnostics the diagnostics returned by the compiler
239 * @return a string containing the errors
241 public static String getCompilerErrors(DiagnosticCollector<JavaFileObject> diagnostics) {
242 StringBuffer buffer = new StringBuffer();
244 buffer.append("----------\n");
245 for (Diagnostic <? extends JavaFileObject > d : diagnostics.getDiagnostics()) {
246 buffer.append(Integer.toString(cpt++)).append(". ").append(d.getKind());
247 if (d.getSource() != null) {
248 buffer.append(" in ").append(d.getSource().toUri().getPath()).append(" (at line ").append(Long.toString(d.getLineNumber())).append(")\n");
250 Reader reader = null;
252 reader = d.getSource().openReader(true);
253 reader.skip(d.getStartPosition());
254 char[] data = new char[(int) (d.getEndPosition() - d.getStartPosition() + 1)];
256 buffer.append(" ").append(data).append('\n');
257 Arrays.fill(data, '^');
258 buffer.append(" ").append(data).append('\n');
259 } catch (IOException e) {
262 if (reader != null) {
265 } catch (IOException e) { }
269 // this is not a file related error
272 buffer.append(d.getMessage(Locale.getDefault())).append('\n');
275 buffer.append("----------\n");
276 return buffer.toString();
280 * Get the current classpath with the correct separator (':' under Unix and ';' under Windows)
281 * @return the classpath
283 public static String getClasspath() {
284 URLClassLoader loader = (URLClassLoader) ClassLoader.getSystemClassLoader();
285 URL[] urls = loader.getURLs();
286 StringBuffer buffer = new StringBuffer();
288 for (URL url : urls) {
289 buffer.append(url.getPath()).append(File.pathSeparatorChar);
293 return buffer.toString();
297 * Get the files in the classpath
300 public static List<File> getClasspathFiles() {
301 URLClassLoader loader = (URLClassLoader) ClassLoader.getSystemClassLoader();
302 URL[] urls = loader.getURLs();
303 List<File> files = new ArrayList<File>(urls.length);
305 for (URL url : urls) {
307 files.add(new File(url.toURI()));
308 } catch (Exception e) { }
315 * Add a class in the classpath
316 * @param url the class url
318 public static void addURLToClassPath(URL url) {
319 URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
321 final Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] {URL.class});
322 method.setAccessible(true);
323 method.invoke(sysloader , new Object[] {url});
324 } catch (NoSuchMethodException e) {
325 System.err.println("Error: Cannot find the declared method: " + e.getLocalizedMessage());
326 } catch (IllegalAccessException e) {
327 System.err.println("Error: Illegal access: " + e.getLocalizedMessage());
328 } catch (InvocationTargetException e) {
329 System.err.println("Error: Could not invocate target: " + e.getLocalizedMessage());
334 * Inner class to handle String as File
336 private static class SourceString extends SimpleJavaFileObject {
338 private final String code;
340 private SourceString(String className, String[] code) {
341 super(new File(BINPATH + "/" + className.replace('.', '/') + Kind.SOURCE.extension).toURI(), Kind.SOURCE);
343 StringBuffer buf = new StringBuffer();
344 for (String str : code) {
348 this.code = buf.toString();
351 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
357 * Inner class to handle String as File
359 private static class SourceFile extends SimpleJavaFileObject {
363 private SourceFile(File f) {
364 super(f.toURI(), Kind.SOURCE);
368 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
370 FileReader reader = new FileReader(f);
371 char[] buffer = new char[1024];
372 StringBuffer sb = new StringBuffer();
375 while ((r = reader.read(buffer, 0, 1024)) != -1) {
376 sb.append(buffer, 0, r);
382 } catch (Exception e) {
388 private static class ClassFileManager extends ForwardingJavaFileManager {
392 public ClassFileManager(StandardJavaFileManager standardManager) {
393 super(standardManager);
397 public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
398 if (sibling instanceof SourceString && className != null && className.indexOf('$') == -1) {
399 this.className = className.replace('/', '.');
402 if (ScilabJavaObject.debug) {
403 ScilabJavaObject.logger.log(Level.INFO, "Compilation of class \'" + className + "\'");
406 return super.getJavaFileForOutput(location, className, kind, sibling);