File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\util\SourceCompiler.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.util;
8
9 import java.io.ByteArrayOutputStream;
10 import java.io.DataInputStream;
11 import java.io.File;
12 import java.io.FileInputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.io.PrintStream;
17 import java.io.PrintWriter;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Modifier;
20 import java.util.HashMap;
21 import org.h2.constant.ErrorCode;
22 import org.h2.message.DbException;
23 import org.h2.store.fs.FileUtils;
24
25 /**
26 * This class allows to convert source code to a class. It uses one class loader
27 * per class.
28 */
29 public class SourceCompiler {
30
31 private static final Class<?> JAVAC_SUN;
32
33 /**
34 * The class name to source code map.
35 */
36 final HashMap<String, String> sources = New.hashMap();
37
38 /**
39 * The class name to byte code map.
40 */
41 final HashMap<String, Class<?>> compiled = New.hashMap();
42
43 private final String compileDir = Utils.getProperty("java.io.tmpdir", ".");
44
45 static {
46 Class<?> clazz;
47 try {
48 clazz = Class.forName("com.sun.tools.javac.Main");
49 } catch (Exception e) {
50 clazz = null;
51 }
52 JAVAC_SUN = clazz;
53 }
54
55 /**
56 * Set the source code for the specified class.
57 * This will reset all compiled classes.
58 *
59 * @param className the class name
60 * @param source the source code
61 */
62 public void setSource(String className, String source) {
63 sources.put(className, source);
64 compiled.clear();
65 }
66
67 /**
68 * Get the class object for the given name.
69 *
70 * @param packageAndClassName the class name
71 * @return the class
72 */
73 public Class<?> getClass(String packageAndClassName) throws ClassNotFoundException {
74
75 Class<?> compiledClass = compiled.get(packageAndClassName);
76 if (compiledClass != null) {
77 return compiledClass;
78 }
79
80 ClassLoader classLoader = new ClassLoader(getClass().getClassLoader()) {
81 public Class<?> findClass(String name) throws ClassNotFoundException {
82 Class<?> classInstance = compiled.get(name);
83 if (classInstance == null) {
84 String source = sources.get(name);
85 String packageName = null;
86 int idx = name.lastIndexOf('.');
87 String className;
88 if (idx >= 0) {
89 packageName = name.substring(0, idx);
90 className = name.substring(idx + 1);
91 } else {
92 className = name;
93 }
94 byte[] data = javacCompile(packageName, className, source);
95 if (data == null) {
96 classInstance = findSystemClass(name);
97 } else {
98 classInstance = defineClass(name, data, 0, data.length);
99 compiled.put(name, classInstance);
100 }
101 }
102 return classInstance;
103 }
104 };
105 return classLoader.loadClass(packageAndClassName);
106 }
107
108 /**
109 * Get the first public static method of the given class.
110 *
111 * @param className the class name
112 * @return the method name
113 */
114 public Method getMethod(String className) throws ClassNotFoundException {
115 Class<?> clazz = getClass(className);
116 Method[] methods = clazz.getDeclaredMethods();
117 for (Method m : methods) {
118 int modifiers = m.getModifiers();
119 if (Modifier.isPublic(modifiers)) {
120 if (Modifier.isStatic(modifiers)) {
121 return m;
122 }
123 }
124 }
125 return null;
126 }
127
128 /**
129 * Compile the given class. This method tries to use the class
130 * "com.sun.tools.javac.Main" if available. If not, it tries to run "javac"
131 * in a separate process.
132 *
133 * @param packageName the package name
134 * @param className the class name
135 * @param source the source code
136 * @return the class file
137 */
138 byte[] javacCompile(String packageName, String className, String source) {
139 File dir = new File(compileDir);
140 if (packageName != null) {
141 dir = new File(dir, packageName.replace('.', '/'));
142 FileUtils.createDirectories(dir.getAbsolutePath());
143 }
144 File javaFile = new File(dir, className + ".java");
145 File classFile = new File(dir, className + ".class");
146 try {
147 OutputStream f = FileUtils.newOutputStream(javaFile.getAbsolutePath(), false);
148 PrintWriter out = new PrintWriter(IOUtils.getBufferedWriter(f));
149 classFile.delete();
150 if (source.startsWith("package ")) {
151 out.println(source);
152 } else {
153 int endImport = source.indexOf("@CODE");
154 String importCode = "import java.util.*;\n" +
155 "import java.math.*;\n" +
156 "import java.sql.*;\n";
157 if (endImport >= 0) {
158 importCode = source.substring(0, endImport);
159 source = source.substring("@CODE".length() + endImport);
160 }
161 if (packageName != null) {
162 out.println("package " + packageName + ";");
163 }
164 out.println(importCode);
165 out.println("public class "+ className +" {\n" +
166 " public static " +
167 source + "\n" +
168 "}\n");
169 }
170 out.close();
171 if (JAVAC_SUN != null) {
172 javacSun(javaFile);
173 } else {
174 javacProcess(javaFile);
175 }
176 byte[] data = new byte[(int) classFile.length()];
177 DataInputStream in = new DataInputStream(new FileInputStream(classFile));
178 in.readFully(data);
179 in.close();
180 return data;
181 } catch (Exception e) {
182 throw DbException.convert(e);
183 } finally {
184 javaFile.delete();
185 classFile.delete();
186 }
187 }
188
189 private void javacProcess(File javaFile) {
190 exec("javac",
191 "-sourcepath", compileDir,
192 "-d", compileDir,
193 "-encoding", "UTF-8",
194 javaFile.getAbsolutePath());
195 }
196
197 private int exec(String... args) {
198 ByteArrayOutputStream buff = new ByteArrayOutputStream();
199 try {
200 Process p = Runtime.getRuntime().exec(args);
201 copyInThread(p.getInputStream(), buff);
202 copyInThread(p.getErrorStream(), buff);
203 p.waitFor();
204 String err = new String(buff.toByteArray(), "UTF-8");
205 throwSyntaxError(err);
206 return p.exitValue();
207 } catch (Exception e) {
208 throw DbException.convert(e);
209 }
210 }
211
212 private static void copyInThread(final InputStream in, final OutputStream out) {
213 new Task() {
214 public void call() throws IOException {
215 IOUtils.copy(in, out);
216 }
217 }.execute();
218 }
219
220 private void javacSun(File javaFile) {
221 PrintStream old = System.err;
222 ByteArrayOutputStream buff = new ByteArrayOutputStream();
223 PrintStream temp = new PrintStream(buff);
224 try {
225 System.setErr(temp);
226 Method compile;
227 compile = JAVAC_SUN.getMethod("compile", String[].class);
228 Object javac = JAVAC_SUN.newInstance();
229 compile.invoke(javac, (Object) new String[] {
230 "-sourcepath", compileDir,
231 "-d", compileDir,
232 "-encoding", "UTF-8",
233 javaFile.getAbsolutePath() });
234 String err = new String(buff.toByteArray(), "UTF-8");
235 throwSyntaxError(err);
236 } catch (Exception e) {
237 throw DbException.convert(e);
238 } finally {
239 System.setErr(old);
240 }
241 }
242
243 private void throwSyntaxError(String err) {
244 if (err.startsWith("Note:")) {
245 // unchecked or unsafe operations - just a warning
246 } else if (err.length() > 0) {
247 err = StringUtils.replaceAll(err, compileDir, "");
248 throw DbException.get(ErrorCode.SYNTAX_ERROR_1, err);
249 }
250 }
251
252 }
253