/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eigenbase.javac;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import org.eigenbase.util.*;
import org.codehaus.janino.*;
import org.codehaus.janino.util.*;
import org.codehaus.janino.util.resource.*;
/**
* <code>JaninoCompiler</code> implements the {@link JavaCompiler} interface by
* calling <a href="http://www.janino.net">Janino</a>.
*/
public class JaninoCompiler implements JavaCompiler {
//~ Instance fields --------------------------------------------------------
private JaninoCompilerArgs args = new JaninoCompilerArgs();
// REVIEW jvs 28-June-2004: pool this instance? Is it thread-safe?
private AccountingClassLoader classLoader;
//~ Constructors -----------------------------------------------------------
public JaninoCompiler() {
args = new JaninoCompilerArgs();
}
//~ Methods ----------------------------------------------------------------
// implement JavaCompiler
public void compile() {
// REVIEW: SWZ: 3/12/2006: When this method is invoked multiple times,
// it creates a series of AccountingClassLoader objects, each with
// the previous as its parent ClassLoader. If we refactored this
// class and its callers to specify all code to compile in one
// go, we could probably just use a single AccountingClassLoader.
assert args.destdir != null;
assert args.fullClassName != null;
assert args.source != null;
ClassLoader parentClassLoader = args.getClassLoader();
if (classLoader != null) {
parentClassLoader = classLoader;
}
Map<String, byte[]> sourceMap = new HashMap<String, byte[]>();
sourceMap.put(
ClassFile.getSourceResourceName(args.fullClassName),
args.source.getBytes());
MapResourceFinder sourceFinder = new MapResourceFinder(sourceMap);
classLoader =
new AccountingClassLoader(
parentClassLoader,
sourceFinder,
null,
args.destdir == null ? null : new File(args.destdir));
try {
classLoader.loadClass(args.fullClassName);
} catch (ClassNotFoundException ex) {
throw Util.newInternal(ex, "while compiling " + args.fullClassName);
}
}
// implement JavaCompiler
public JavaCompilerArgs getArgs() {
return args;
}
// implement JavaCompiler
public ClassLoader getClassLoader() {
return classLoader;
}
// implement JavaCompiler
public int getTotalByteCodeSize() {
return classLoader.getTotalByteCodeSize();
}
//~ Inner Classes ----------------------------------------------------------
/**
* Arguments to an invocation of the Janino compiler.
*/
private static class JaninoCompilerArgs extends JavaCompilerArgs {
String destdir;
String fullClassName;
String source;
public JaninoCompilerArgs() {
}
public boolean supportsSetSource() {
return true;
}
public void setDestdir(String destdir) {
super.setDestdir(destdir);
this.destdir = destdir;
}
public void setSource(String source, String fileName) {
this.source = source;
addFile(fileName);
}
public void setFullClassName(String fullClassName) {
this.fullClassName = fullClassName;
}
}
/**
* Refinement of JavaSourceClassLoader which keeps track of the total
* bytecode length of the classes it has compiled.
*/
private static class AccountingClassLoader extends JavaSourceClassLoader {
private final File destDir;
private int nBytes;
public AccountingClassLoader(
ClassLoader parentClassLoader,
ResourceFinder sourceFinder,
String optionalCharacterEncoding,
File destDir) {
super(
parentClassLoader,
sourceFinder,
optionalCharacterEncoding);
this.destDir = destDir;
}
int getTotalByteCodeSize() {
return nBytes;
}
// override JavaSourceClassLoader
public Map generateBytecodes(String name)
throws ClassNotFoundException {
Map<String, byte[]> map = super.generateBytecodes(name);
if (map == null) {
return map;
}
if (destDir != null) {
try {
for (Map.Entry<String, byte[]> entry : map.entrySet()) {
File file = new File(destDir, entry.getKey() + ".class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(entry.getValue());
fos.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// NOTE jvs 18-Oct-2006: Janino has actually compiled everything
// to bytecode even before all of the classes have actually
// been loaded. So we intercept their sizes here just
// after they've been compiled.
for (Object obj : map.values()) {
byte[] bytes = (byte[]) obj;
nBytes += bytes.length;
}
return map;
}
}
}
// End JaninoCompiler.java