package net.sourceforge.rtf.template.velocity;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.text.Format;
import java.util.Properties;
import net.sourceforge.rtf.template.AbstractTemplateEngine;
import net.sourceforge.rtf.template.IContext;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.context.Context;
public class VelocityTemplateEngineImpl extends AbstractTemplateEngine {
/**
* Velocity engine used for merge.
*/
private VelocityEngine velocityEngine;
private boolean isVelocityEngineInitialized;
public IContext newContext() {
return new VelocityContextImpl();
}
protected void mergeWithTemplateEngine(Writer writer) throws Exception {
if (velocityEngine == null)
throw new IOException(
"Velocity engine must be defined. Use setVelocityEngine method to set it.");
initializeVelocityEngine();
Context context = (VelocityContextImpl) super.getContext();
EventCartridge events = new EventCartridge();
events.addEventHandler(new InsertionHandler());
events.attachToContext(context);
Reader reader = template;
try {
velocityEngine.evaluate(context, writer, "", reader);
} finally {
reader.close();
}
}
/**
* The handler for insertion of references. It applies default formatting -
* if set with {@link #setDefaultFormat(Class, Format)} - to values
* according to their class. <br>
* <code>null</code> values are replaced with an empty string. This
* prevents keys to show up in the result of the merge if a method of a
* business object in the context returns <code>null</code> - in this case
* velocity complains about an invalid references :(.
*/
protected class InsertionHandler implements ReferenceInsertionEventHandler {
public Object referenceInsert(String reference, Object value) {
if (value == null) {
// Velocity doesn't handle NULL values as we wish ...
value = "";
} else {
boolean escaped = mustBeEscaped(value);
Format format = (Format) formats.get(value.getClass());
if (format != null) {
value = format.format(value);
if (value == null) {
// Velocity doesn't handle NULL values as we wish ...
value = "";
}
}
if (escaped)
// Value is not Inpustream (not an image), escape characters
value = escapeSpecialCharacters(value);
}
return value;
}
}
/**
* Velocity doesn't accept <code>null</code> values due to context
* chaining issues. This subclass liftens this restriction.
*/
protected class RTFContext extends VelocityContext {
public static final long serialVersionUID = 1L;
public RTFContext() {
super();
}
/**
* Chaining constructor,
*
* @param innerContext
* context impl to wrap
*/
public RTFContext(Context innerContext) {
super(innerContext);
}
/**
* Overridden so that the <code>null</code> values are accepted.
*
* @see AbstractVelocityContext#put(String,Object)
*/
public Object put(String key, Object value) {
if (key == null) {
return null;
}
return internalPut(key, value);
}
}
public VelocityEngine getVelocityEngine() {
return velocityEngine;
}
public void setVelocityEngine(VelocityEngine velocityEngine) {
this.velocityEngine = velocityEngine;
this.isVelocityEngineInitialized = false;
}
private void initializeVelocityEngine() throws Exception {
/*
* velocityEngine.setProperty( VelocityEngine.INPUT_ENCODING, "KOI8-R");
* velocityEngine.setProperty( VelocityEngine.OUTPUT_ENCODING,
* "KOI8-R");
*/
Properties p = new Properties();
/*
* p.setProperty( VelocityEngine.INPUT_ENCODING, "Windows-1251");
* p.setProperty( VelocityEngine.OUTPUT_ENCODING, "Windows-1251");
* p.setProperty(VelocityEngine.ENCODING_DEFAULT, "Windows-1251");
*/
if (!isVelocityEngineInitialized)
velocityEngine.init(p);
/*
* velocityEngine.setProperty("input.encoding", "UTF-8");
* velocityEngine.setProperty("output.encoding", "koxsi8");
* velocityEngine.setProperty("response.encoding", "xsoi8");
*/
this.isVelocityEngineInitialized = true;
}
}