ad750076001e1ea8b5b180393876ba087fc8c635
[scilab.git] / scilab / modules / external_objects_java / src / java / org / scilab / modules / external_objects_java / ScilabJavaCompiler.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
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.FileReader;
17 import java.io.IOException;
18 import java.io.StringWriter;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.net.MalformedURLException;
22 import java.net.URI;
23 import java.net.URLClassLoader;
24 import java.net.URL;
25 import java.util.Arrays;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.ServiceLoader;
30 import java.util.logging.Level;
31
32 import javax.tools.Diagnostic;
33 import javax.tools.DiagnosticCollector;
34 import javax.tools.FileObject;
35 import javax.tools.ForwardingJavaFileManager;
36 import javax.tools.JavaCompiler;
37 import javax.tools.JavaFileObject;
38 import javax.tools.SimpleJavaFileObject;
39 import javax.tools.StandardJavaFileManager;
40 import javax.tools.ToolProvider;
41 import javax.tools.JavaCompiler.CompilationTask;
42 import javax.tools.JavaFileObject.Kind;
43
44 /**
45  * Class to provide a java compiler to JIMS.
46  * Try to find the compiler provide with JDK and if it is not found, use the Eclipse Compiler for Java
47  * @author Calixte DENIZET
48  */
49 @SuppressWarnings("serial")
50 public class ScilabJavaCompiler {
51
52     private static final String JAVACOMPILER = "javax.tools.JavaCompiler";
53     private static final String BINPATH = System.getProperty("java.io.tmpdir") + File.separator + "JIMS" + File.separator + "bin";
54
55     private static JavaCompiler compiler;
56
57     static {
58         new File(System.getProperty("java.io.tmpdir") + File.separator + "JIMS").mkdir();
59         new File(BINPATH).mkdir();
60         try {
61             URL binURL = new File(BINPATH).toURI().toURL();
62             addURLToClassPath(binURL);
63         } catch (MalformedURLException e) {
64             System.err.println(e);
65         }
66     }
67
68     /**
69      * Just find a compiler
70      */
71     private static void findCompiler() throws ScilabJavaException {
72         if (compiler == null) {
73             try {
74                 compiler = ToolProvider.getSystemJavaCompiler();
75             } catch (Exception e) { }
76
77             if (compiler == null) {
78                 ServiceLoader<JavaCompiler> jcompilers = ServiceLoader.load(JavaCompiler.class);
79                 for (JavaCompiler jc : jcompilers) {
80                     if (jc != null) {
81                         compiler = jc;
82                         break;
83                     }
84                 }
85             }
86
87             if (compiler == null) {
88                 throw new ScilabJavaException("No java compiler in the classpath\nCheck for tools.jar (comes from JDK) or ecj-3.6.x.jar (Eclipse Compiler for Java)");
89             }
90         }
91     }
92
93     /**
94      * Compile code got as string
95      * @param className the class name
96      * @param code the lines giving the code to compile
97      * @return an integer corresponding to the compiled and loaded class.
98      */
99     public static int compileCode(String className, String[] code) throws ScilabJavaException {
100         findCompiler();
101
102         DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
103         StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, Locale.getDefault(), null);
104         ClassFileManager manager = new ClassFileManager(stdFileManager);
105         List<SimpleJavaFileObject> compilationUnits = new ArrayList<SimpleJavaFileObject>();
106         boolean isFile = true;
107         SourceString sourceString = null;
108         for (String s : code) {
109             File f = new File(s);
110             if (!f.exists() || !f.canRead()) {
111                 isFile = false;
112                 break;
113             }
114         }
115
116         if (isFile) {
117             for (String s : code) {
118                 File f = new File(s);
119                 compilationUnits.add(new SourceFile(f));
120             }
121         } else {
122             sourceString = new SourceString(className, code);
123             compilationUnits.add(sourceString);
124         }
125
126         String[] compileOptions = new String[] {"-d", BINPATH} ;
127         Iterable<String> options = Arrays.asList(compileOptions);
128
129         CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, compilationUnits);
130         boolean success = task.call();
131
132         if (success) {
133             if (isFile) {
134                 return -1;
135             } else {
136                 return ScilabClassLoader.loadJavaClass(manager.className, true);
137             }
138         } else {
139             StringBuffer buf = new StringBuffer();
140             for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
141                 buf.append(diagnostic.toString());
142                 buf.append("\n");
143             }
144
145             throw new ScilabJavaException(buf.toString());
146         }
147     }
148
149     /**
150      * Add a class in the classpath
151      * @param url the class url
152      */
153     public static void addURLToClassPath(URL url) {
154         URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
155         try {
156             final Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] {URL.class});
157             method.setAccessible(true);
158             method.invoke(sysloader , new Object[] {url});
159         } catch (NoSuchMethodException e) {
160             System.err.println("Error: Cannot find the declared method: " + e.getLocalizedMessage());
161         } catch (IllegalAccessException e) {
162             System.err.println("Error: Illegal access: " + e.getLocalizedMessage());
163         } catch (InvocationTargetException e) {
164             System.err.println("Error: Could not invocate target: " + e.getLocalizedMessage());
165         }
166     }
167
168     /**
169      * Inner class to handle String as File
170      */
171     private static class SourceString extends SimpleJavaFileObject {
172
173         private final String code;
174
175         private SourceString(String className, String[] code) {
176             super(new File(BINPATH + "/" + className.replace('.', '/') + Kind.SOURCE.extension).toURI(), Kind.SOURCE);
177
178             StringBuffer buf = new StringBuffer();
179             for (String str : code) {
180                 buf.append(str);
181                 buf.append("\n");
182             }
183             this.code = buf.toString();
184         }
185
186         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
187             return code;
188         }
189     }
190
191     /**
192      * Inner class to handle String as File
193      */
194     private static class SourceFile extends SimpleJavaFileObject {
195
196         final File f;
197
198         private SourceFile(File f) {
199             super(f.toURI(), Kind.SOURCE);
200             this.f = f;
201         }
202
203         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
204             try {
205                 FileReader reader = new FileReader(f);
206                 char[] buffer = new char[1024];
207                 StringBuffer sb = new StringBuffer();
208                 int r;
209
210                 while ((r = reader.read(buffer, 0, 1024)) != -1) {
211                     sb.append(buffer, 0, r);
212                 }
213
214                 reader.close();
215
216                 return sb;
217             } catch (Exception e) {
218                 return null;
219             }
220         }
221     }
222
223     private static class ClassFileManager extends ForwardingJavaFileManager {
224
225         String className;
226
227         public ClassFileManager(StandardJavaFileManager standardManager) {
228             super(standardManager);
229         }
230
231         @Override
232         public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
233             if (sibling instanceof SourceString) {
234                 this.className = className;
235             }
236
237             if (ScilabJavaObject.debug) {
238                 ScilabJavaObject.logger.log(Level.INFO, "Compilation of class \'" + className + "\'");
239             }
240
241             return super.getJavaFileForOutput(location, className, kind, sibling);
242         }
243     }
244 }