001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.jci.compilers;
019:
020: import java.io.ByteArrayInputStream;
021: import java.io.FileNotFoundException;
022: import java.io.IOException;
023: import java.io.InputStreamReader;
024: import java.io.Reader;
025: import java.util.ArrayList;
026: import java.util.Collection;
027:
028: import org.apache.commons.jci.problems.CompilationProblem;
029: import org.apache.commons.jci.readers.ResourceReader;
030: import org.apache.commons.jci.stores.ResourceStore;
031: import org.apache.commons.jci.utils.ConversionUtils;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.mozilla.javascript.CompilerEnvirons;
035: import org.mozilla.javascript.Context;
036: import org.mozilla.javascript.ErrorReporter;
037: import org.mozilla.javascript.EvaluatorException;
038: import org.mozilla.javascript.GeneratedClassLoader;
039: import org.mozilla.javascript.ImporterTopLevel;
040: import org.mozilla.javascript.JavaScriptException;
041: import org.mozilla.javascript.NativeArray;
042: import org.mozilla.javascript.Scriptable;
043: import org.mozilla.javascript.ScriptableObject;
044: import org.mozilla.javascript.optimizer.ClassCompiler;
045:
046: /**
047: * @author tcurdt
048: */
049: public final class RhinoJavaCompiler extends AbstractJavaCompiler {
050:
051: private final Log log = LogFactory.getLog(RhinoJavaCompiler.class);
052:
053: private final JavaCompilerSettings defaultSettings;
054:
055: public RhinoJavaCompiler() {
056: defaultSettings = new RhinoJavaCompilerSettings();
057: }
058:
059: /**
060: * based on code from dev.helma.org
061: * http://dev.helma.org/source/file/helma/branches/rhinoloader/src/org/helma/javascript/RhinoLoader.java/?revision=95
062: */
063: private final class RhinoCompilingClassLoader extends ClassLoader {
064:
065: private final ScriptableObject scope;
066: private final ResourceReader reader;
067: private final ResourceStore store;
068:
069: private final Collection problems = new ArrayList();
070:
071: private final class ProblemCollector implements ErrorReporter {
072:
073: public void error(String pMessage, String pFileName,
074: int pLine, String pScript, int pColumn) {
075:
076: final CompilationProblem problem = new RhinoCompilationProblem(
077: pMessage, pFileName, pLine, pScript, pColumn,
078: true);
079:
080: if (problemHandler != null) {
081: problemHandler.handle(problem);
082: }
083:
084: problems.add(problem);
085: }
086:
087: public void warning(String pMessage, String pFileName,
088: int pLine, String pScript, int pColumn) {
089:
090: final CompilationProblem problem = new RhinoCompilationProblem(
091: pMessage, pFileName, pLine, pScript, pColumn,
092: false);
093:
094: if (problemHandler != null) {
095: problemHandler.handle(problem);
096: }
097:
098: problems.add(problem);
099: }
100:
101: public EvaluatorException runtimeError(String pMessage,
102: String pFileName, int pLine, String pScript,
103: int pColumn) {
104: return new EvaluatorException(pMessage, pFileName,
105: pLine, pScript, pColumn);
106: }
107: }
108:
109: public RhinoCompilingClassLoader(final ResourceReader pReader,
110: final ResourceStore pStore,
111: final ClassLoader pClassLoader) {
112: super (pClassLoader);
113:
114: reader = pReader;
115: store = pStore;
116:
117: final Context context = Context.enter();
118: scope = new ImporterTopLevel(context);
119: Context.exit();
120: }
121:
122: public Collection getProblems() {
123: return problems;
124: }
125:
126: protected Class findClass(final String pName)
127: throws ClassNotFoundException {
128: final Context context = Context.enter();
129: context.setErrorReporter(new ProblemCollector());
130:
131: try {
132: return compileClass(context, pName);
133: } catch (EvaluatorException e) {
134: throw new ClassNotFoundException(e.getMessage(), e);
135: } catch (IOException e) {
136: throw new ClassNotFoundException(e.getMessage(), e);
137: } finally {
138: Context.exit();
139: }
140: }
141:
142: private Class compileClass(final Context pContext,
143: final String pClassName) throws IOException,
144: ClassNotFoundException {
145:
146: Class super class = null;
147:
148: final String pSourceName = pClassName.replace('.', '/')
149: + ".js";
150:
151: final Scriptable target = evaluate(pContext, pSourceName);
152:
153: final Object baseClassName = ScriptableObject.getProperty(
154: target, "__extends__");
155:
156: if (baseClassName instanceof String) {
157: super class = Class.forName((String) baseClassName);
158: }
159:
160: final ArrayList interfaceClasses = new ArrayList();
161:
162: final Object interfaceNames = ScriptableObject.getProperty(
163: target, "__implements__");
164:
165: if (interfaceNames instanceof NativeArray) {
166:
167: final NativeArray interfaceNameArray = (NativeArray) interfaceNames;
168:
169: for (int i = 0; i < interfaceNameArray.getLength(); i++) {
170:
171: final Object obj = interfaceNameArray.get(i,
172: interfaceNameArray);
173:
174: if (obj instanceof String) {
175: interfaceClasses.add(Class
176: .forName((String) obj));
177: }
178: }
179:
180: } else if (interfaceNames instanceof String) {
181:
182: interfaceClasses.add(Class
183: .forName((String) interfaceNames));
184:
185: }
186:
187: final Class[] interfaces;
188:
189: if (!interfaceClasses.isEmpty()) {
190: interfaces = new Class[interfaceClasses.size()];
191: interfaceClasses.toArray(interfaces);
192: } else {
193: // FIXME: hm ...really no empty array good enough?
194: interfaces = null;
195: }
196:
197: return compileClass(pContext, pSourceName, pClassName,
198: super class, interfaces);
199:
200: }
201:
202: private Class compileClass(final Context pContext,
203: final String pSourceName, final String pClassName,
204: final Class pSuperClass, final Class[] pInterfaces)
205: throws IOException {
206:
207: final CompilerEnvirons environments = new CompilerEnvirons();
208: environments.initFromContext(pContext);
209: final ClassCompiler compiler = new ClassCompiler(
210: environments);
211:
212: if (pSuperClass != null) {
213: compiler.setTargetExtends(pSuperClass);
214: }
215:
216: if (pInterfaces != null) {
217: compiler.setTargetImplements(pInterfaces);
218: }
219:
220: final byte[] sourceBytes = reader.getBytes(pSourceName);
221:
222: final Object[] classes = compiler.compileToClassFiles(
223: new String(sourceBytes), getName(pSourceName), 1,
224: pClassName);
225:
226: final GeneratedClassLoader loader = pContext
227: .createClassLoader(pContext
228: .getApplicationClassLoader());
229:
230: Class clazz = null;
231:
232: for (int i = 0; i < classes.length; i += 2) {
233:
234: final String clazzName = (String) classes[i];
235: final byte[] clazzBytes = (byte[]) classes[i + 1];
236:
237: store.write(clazzName.replace('.', '/') + ".class",
238: clazzBytes);
239:
240: Class c = loader.defineClass(clazzName, clazzBytes);
241: loader.linkClass(c);
242:
243: if (i == 0) {
244: clazz = c;
245: }
246:
247: }
248:
249: return clazz;
250: }
251:
252: private String getName(String s) {
253: final int i = s.lastIndexOf('/');
254: if (i < 0) {
255: return s;
256: }
257:
258: return s.substring(i + 1);
259: }
260:
261: private Scriptable evaluate(final Context pContext,
262: final String pSourceName) throws JavaScriptException,
263: IOException {
264:
265: if (!reader.isAvailable(pSourceName)) {
266: throw new FileNotFoundException("File " + pSourceName
267: + " not found");
268: }
269:
270: final Scriptable target = pContext.newObject(scope);
271:
272: final byte[] sourceBytes = reader.getBytes(pSourceName);
273:
274: final Reader reader = new InputStreamReader(
275: new ByteArrayInputStream(sourceBytes));
276:
277: pContext.evaluateReader(target, reader,
278: getName(pSourceName), 1, null);
279:
280: return target;
281: }
282:
283: }
284:
285: public CompilationResult compile(final String[] pResourcePaths,
286: final ResourceReader pReader, final ResourceStore pStore,
287: final ClassLoader pClassLoader,
288: final JavaCompilerSettings pSettings) {
289:
290: final RhinoCompilingClassLoader cl = new RhinoCompilingClassLoader(
291: pReader, pStore, pClassLoader);
292:
293: for (int i = 0; i < pResourcePaths.length; i++) {
294: log.debug("compiling " + pResourcePaths[i]);
295:
296: final String clazzName = ConversionUtils
297: .convertResourceToClassName(pResourcePaths[i]);
298: try {
299: cl.loadClass(clazzName);
300: } catch (ClassNotFoundException e) {
301: }
302: }
303:
304: final Collection problems = cl.getProblems();
305: final CompilationProblem[] result = new CompilationProblem[problems
306: .size()];
307: problems.toArray(result);
308: return new CompilationResult(result);
309: }
310:
311: public JavaCompilerSettings createDefaultSettings() {
312: return defaultSettings;
313: }
314:
315: }
|