/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.eclipse.ecr.web.rendering.fm;
import java.io.Writer;
import java.util.ResourceBundle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.web.rendering.api.RenderingEngine;
import org.eclipse.ecr.web.rendering.api.RenderingException;
import org.eclipse.ecr.web.rendering.api.ResourceLocator;
import org.eclipse.ecr.web.rendering.api.View;
import org.eclipse.ecr.web.rendering.fm.adapters.DocumentObjectWrapper;
import org.eclipse.ecr.web.rendering.fm.extensions.BlockDirective;
import org.eclipse.ecr.web.rendering.fm.extensions.BlockWriter;
import org.eclipse.ecr.web.rendering.fm.extensions.BlockWriterRegistry;
import org.eclipse.ecr.web.rendering.fm.extensions.DocRefMethod;
import org.eclipse.ecr.web.rendering.fm.extensions.ExtendsDirective;
import org.eclipse.ecr.web.rendering.fm.extensions.FormatDate;
import org.eclipse.ecr.web.rendering.fm.extensions.LocaleMessagesMethod;
import org.eclipse.ecr.web.rendering.fm.extensions.MessagesMethod;
import org.eclipse.ecr.web.rendering.fm.extensions.NewMethod;
import org.eclipse.ecr.web.rendering.fm.extensions.SuperBlockDirective;
import org.eclipse.ecr.web.rendering.fm.i18n.ResourceComposite;
import freemarker.core.Environment;
import freemarker.template.Configuration;
import freemarker.template.Template;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*
*/
public class FreemarkerEngine implements RenderingEngine {
private static final Log log = LogFactory.getLog(FreemarkerEngine.class);
public static final String RENDERING_ENGINE_KEY = "NX_RENDERING_ENGINE";
protected final Configuration cfg;
// the wrapper is not a singleton since it contains some info about the
// engine instance
// so we will have one wrapper per engine instance
protected final DocumentObjectWrapper wrapper;
protected final MessagesMethod messages = new MessagesMethod(null);
protected final LocaleMessagesMethod localeMessages = new LocaleMessagesMethod(
null);
protected ResourceTemplateLoader loader;
public FreemarkerEngine() {
this(null, null);
}
public FreemarkerEngine(Configuration cfg, ResourceLocator locator) {
wrapper = new DocumentObjectWrapper(this);
this.cfg = cfg == null ? new Configuration() : cfg;
this.cfg.setWhitespaceStripping(true);
this.cfg.setLocalizedLookup(false);
this.cfg.setClassicCompatible(true);
this.cfg.setObjectWrapper(wrapper);
// custom directives goes here
this.cfg.setSharedVariable("block", new BlockDirective());
this.cfg.setSharedVariable("superBlock", new SuperBlockDirective());
this.cfg.setSharedVariable("extends", new ExtendsDirective());
this.cfg.setSharedVariable("docRef", new DocRefMethod());
this.cfg.setSharedVariable("new", new NewMethod());
this.cfg.setSharedVariable("message", messages);
this.cfg.setSharedVariable("lmessage", localeMessages);
this.cfg.setSharedVariable("formatDate", new FormatDate());
this.cfg.setCustomAttribute(RENDERING_ENGINE_KEY, this);
setResourceLocator(locator);
}
/**
* set the resource bundle to be used with method message and lmessage. If
* the resourcebundle is not of the type ResourceComposite, lmessage will
* create a default ResourceComposite.
*/
@Override
public void setMessageBundle(ResourceBundle messages) {
this.messages.setBundle(messages);
if (messages instanceof ResourceComposite) {
localeMessages.setBundle((ResourceComposite) messages);
}
}
@Override
public ResourceBundle getMessageBundle() {
return messages.getBundle();
}
@Override
public void setResourceLocator(ResourceLocator locator) {
loader = new ResourceTemplateLoader(locator);
cfg.setTemplateLoader(loader);
}
@Override
public ResourceLocator getResourceLocator() {
return loader.getLocator();
}
public ResourceTemplateLoader getLoader() {
return loader;
}
@Override
public void setSharedVariable(String key, Object value) {
try {
cfg.setSharedVariable(key, value);
} catch (Exception e) {
log.error(e, e);
}
}
public DocumentObjectWrapper getObjectWrapper() {
return wrapper;
}
public Configuration getConfiguration() {
return cfg;
}
@Override
public View getView(String path) {
return new View(this, path);
}
@Override
public View getView(String path, Object object) {
return new View(this, path, object);
}
/**
*
* @param template
* @param input
* @param writer
* @param baseUrl a base URL used for resolving referenced files in extends directive.
* @throws RenderingException
*/
@Override
public void render(String template, Object input, Writer writer)
throws RenderingException {
try {
/*
* A special method to get the absolute path as an URI to be used
* with freemarker since freemarker removes the leading / from the
* absolute path and the file cannot be resolved anymore In the case
* of URI like path freemarker is not modifying the path <p>
*
* @see TemplateCache#normalizeName()
* @see ResourceTemplateLoader#findTemplateSource()
*/
if (template.startsWith("/")) {
template = "fs://" + template;
}
Template temp = cfg.getTemplate(template);
BlockWriter bw = new BlockWriter(temp.getName(), "",
new BlockWriterRegistry());
Environment env = temp.createProcessingEnvironment(input, bw,
wrapper);
env.process();
bw.copyTo(writer);
} catch (Exception e) {
throw new RenderingException(e);
}
}
@Override
public void flushCache() {
cfg.clearTemplateCache();
}
}