/**
* Copyright 2011 meltmedia
*
* Licensed 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.xchain.framework.jsl;
import org.apache.commons.jci.compilers.JavaCompilerFactory;
import org.apache.commons.jci.compilers.JavaCompiler;
import org.apache.commons.jci.compilers.JavaCompilerSettings;
import org.apache.commons.jci.compilers.CompilationResult;
import org.apache.commons.jci.problems.CompilationProblem;
import org.apache.commons.jci.readers.ResourceReader;
import org.apache.commons.jci.readers.MemoryResourceReader;
import org.apache.commons.jci.stores.ResourceStore;
import org.apache.commons.jci.stores.MemoryResourceStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.xml.sax.SAXException;
/**
* @author Christian Trimble
* @author Josh Kennedy
*/
public class TemplateCompiler
{
private static Logger log = LoggerFactory.getLogger(TemplateCompiler.class);
public static final String SOURCE_VERSION = "1.5";
public static final String SOURCE_ENCODING = "UTF-8";
public static final String TARGET_VERSION = "1.5";
/** The class loader for the catalog. */
private TemplateClassLoader templateClassLoader;
/** The compiler that we will use for creating classes. */
private JavaCompiler compiler;
/** The resource reader where source files will be stored. */
private WrappedResourceReader resourceReader;
/** The settings for the compiler. */
private JavaCompilerSettings settings;
public void init(ClassLoader classLoader)
{
this.templateClassLoader = new TemplateClassLoader(classLoader);
resourceReader = new WrappedResourceReader(new MemoryResourceReader(), templateClassLoader);
settings = new JavaCompilerSettings();
settings.setSourceVersion(SOURCE_VERSION);
settings.setSourceEncoding(SOURCE_ENCODING);
settings.setTargetVersion(TARGET_VERSION);
compiler = new JavaCompilerFactory().createCompiler("eclipse");
}
/**
* Compiles the source result.
*
* @throws SAXException if there is a compilation error in the source file.
*/
public Class compileTemplate( SourceResult result )
throws SAXException
{
// add the resource to the reader.
try {
resourceReader.add(result.getSourceResourceName(), result.getSource().getBytes(SOURCE_ENCODING));
}
catch( UnsupportedEncodingException uee ) {
throw new SAXException("Could not build source in the encoding '"+SOURCE_ENCODING+"'.", uee);
}
// create the resource store.
ResourceStore resourceStore = new MemoryResourceStore();
// compile the result.
CompilationResult compilationResult = compiler.compile(new String[] {result.getSourceResourceName()}, resourceReader, resourceStore, templateClassLoader, settings);
// handle any errors.
if( compilationResult.getErrors().length > 0 ) {
if( log.isDebugEnabled() ) {
log.debug("Source that would not complile:\n"+result.getSource());
for( CompilationProblem error : compilationResult.getErrors() ) {
log.debug("Error ("+error.getStartLine()+", "+error.getStartColumn()+"):"+error.getMessage());
}
}
// TODO: Make this message better.
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Could not compile template source. Errors:\n");
for( CompilationProblem error : compilationResult.getErrors() ) {
stringBuilder.append("("+error.getStartLine()+", "+error.getStartColumn()+"):"+error.getMessage()+"\n");
}
throw new SAXException(stringBuilder.toString());
}
// handle any errors.
if( log.isDebugEnabled() ) {
for( CompilationProblem warning : compilationResult.getWarnings() ) {
log.debug("Compilation warning("+warning.getStartLine()+", "+warning.getStartColumn()+"):"+warning.getMessage());
}
}
byte[] compiledBytes = resourceStore.read(result.getClassResourceName());
templateClassLoader.publicDefineClass(result.getClassName(), compiledBytes, 0, compiledBytes.length);
Class templateClass = null;
try {
templateClass = templateClassLoader.loadClass(result.getClassName());
}
catch( Exception e ) {
throw new SAXException("Could not load the class '"+result.getClassName()+"'.", e);
}
return templateClass;
}
private class TemplateClassLoader
extends ClassLoader
{
public TemplateClassLoader(ClassLoader parent)
{
super(parent);
}
public void publicDefineClass( String name, byte[] b, int off, int len )
throws ClassFormatError, IndexOutOfBoundsException, SecurityException
{
this.defineClass(name, b, off, len);
}
}
public static class WrappedResourceReader
implements ResourceReader
{
protected MemoryResourceReader wrapped;
protected ClassLoader classLoader;
public WrappedResourceReader( MemoryResourceReader wrapped, ClassLoader classLoader ) { this.wrapped = wrapped; this.classLoader = classLoader; }
public void add( String name, byte[] data ) {
wrapped.add(name, data);
}
public byte[] getBytes(String name) {
byte[] result = wrapped.getBytes(name);
if( result == null ) {
try {
int bufferSize = 0;
byte[] buffer = new byte[10000];
int read = 0;
InputStream source = classLoader.getResourceAsStream(name);
while( (read = source.read(buffer, bufferSize, buffer.length)) != -1 ) {
bufferSize += read;
if( bufferSize >= buffer.length ) {
break;
}
}
result = new byte[bufferSize];
for( int i = 0; i < bufferSize; i++ ) {
result[i] = buffer[i];
}
}
catch( Exception e ) {
e.printStackTrace();
return null;
}
}
return result;
}
public boolean isAvailable( String name)
{
boolean result = wrapped.isAvailable(name);
if( !result ) {
result = (classLoader.getResource(name) != null);
}
return result;
}
}
}