/* TemplateProcessor.java
*
* Copyright 2010-2011 Johannes Kepler Universit�t Linz,
* Institut f�r Wissensbasierte Mathematische Systeme.
*
* This file is part of pureImage.
*
* pureImage is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 3,
* as published by the Free Software Foundation.
*
* pureImage is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pureImage. If not, see <http://www.gnu.org/licenses/>.
*/
package pI.generator;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import freemarker.cache.TemplateLoader;
import freemarker.core.Environment;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
* A class that provides an interface to the FreeMarker template engine and
* supports following features:
*
* <li>JSON data as data model <li>Properties from a map provided under variable
* 'PROPERTIES' <li>Access to the root data element under variable 'ROOT' <li>
* Control of output file through definition of variable 'output_file' in the
* template (inversion of control) <li>Scoping of sub-elements in JSON data <li>
* Iterating array types in JSON data <li>Writing to console.
*/
@SuppressWarnings("unchecked")
public class TemplateProcessor {
Configuration cfg;
private Map dataModel;
private JsonElement root;
private String defaultOutputDir = ".";
public TemplateProcessor() {
this(null, null);
}
public TemplateProcessor(String templateDir, String jsonData,
Map<String, String> properties) {
this(jsonData, properties);
try {
cfg.setDirectoryForTemplateLoading(new File(templateDir));
} catch (IOException e) {
throw new RuntimeException("Could not set template directory: "
+ templateDir);
}
this.defaultOutputDir = templateDir;
}
public TemplateProcessor(String jsonData,
Map<String, String> properties) {
if(jsonData == null || jsonData.isEmpty()) {
jsonData = "{}";
}
root = new JsonParser().parse(jsonData);
dataModel = (Map) new JsonJavaMapper().convert(root);
cfg = new Configuration();
cfg.setObjectWrapper(new DefaultObjectWrapper());
if(properties == null) {
properties = new HashMap<String, String>();
}
// register properties if given
for (String key : properties.keySet()) {
try {
cfg.setSharedVariable(key, properties.get(key));
} catch (TemplateModelException e) {
e.printStackTrace();
}
}
try {
cfg.setSharedVariable("PROPERTIES", properties);
cfg.setSharedVariable("ROOT", dataModel);
} catch (TemplateModelException e) {
e.printStackTrace();
}
}
public void setTemplateLoader(TemplateLoader loader) {
cfg.setTemplateLoader(loader);
}
public String process(String templateFile, String scope, String outFile,
boolean writeToFile, boolean iterate) {
Object scopedData;
try {
scopedData = getScopedData(dataModel, scope);
} catch (Exception e) {
throw new RuntimeException("Illegal scope expression!");
}
Template template;
try {
template = cfg.getTemplate(templateFile);
} catch (IOException e) {
System.err.println(e.getMessage());
throw new RuntimeException("Could not load template file.");
}
if (iterate && scopedData instanceof Iterable) {
StringBuffer sb = new StringBuffer();
// if an output file is specified in iteration mode
// the written data is appended to the file
boolean append = (outFile != null);
boolean isFirst = true;
for (Object item : (Iterable) scopedData) {
String s;
// don't append the first data but create new file
if (isFirst) {
s = processTemplate(template, templateFile, item, outFile,
writeToFile, false);
isFirst = false;
} else {
s = processTemplate(template, templateFile, item, outFile,
writeToFile, append);
}
sb.append(s);
}
return sb.toString();
} else {
String s = processTemplate(template, templateFile, scopedData,
outFile, writeToFile, false);
return s;
}
}
/**
* @param templateFile
* @param templateLocation
* @param template
* @param data
* @return
*/
private String processTemplate(Template template, String templateFile,
Object data, String outputFile, boolean writeToFile, boolean append) {
Environment env;
String s;
StringWriter writer = new StringWriter();
try {
env = template.createProcessingEnvironment(data, writer);
env.process();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
s = writer.toString();
if (writeToFile) {
writeFile(env, s, templateFile, outputFile, append);
}
return s;
}
/**
* @param env
* @param s
* @param templateFile
* @param outputFile
* @param append
*/
private void writeFile(Environment env, String s, String templateFile,
String outputFile, boolean append) {
boolean fromTemplate = false;
// Note: output file from command line overrides setting in template
// file
if (outputFile == null || outputFile.isEmpty()) {
TemplateModel outFileVar = null;
try {
outFileVar = env.getCurrentNamespace().get("output_file");
} catch (TemplateModelException e) {
throw new RuntimeException(
"Error while retrieving variable 'output_file' from template:"
+ templateFile);
}
// Note: in iteration mode the output file _must_ be controlled by
// template
if (outFileVar == null) {
throw new RuntimeException(
"Variable 'output_file' is not specified in template "
+ templateFile);
}
// Use setting from template file if given
outputFile = ((SimpleScalar) outFileVar).getAsString();
fromTemplate = true;
// throw if not set by template
if (outputFile == null || outputFile.isEmpty()) {
throw new RuntimeException("No output file specified!");
}
}
File _outputFile = new File(outputFile);
// if the specified file is relative and specified in template
// take it as relative to template location.
if (!_outputFile.isAbsolute() && fromTemplate) {
outputFile = defaultOutputDir + "/" + outputFile;
}
GeneratorUtils.createSourceFile(outputFile, s, append);
}
// public static void createSourceFile(File outFile, String srcCode,
// boolean append) {
// PrintWriter writer = null;
// try {
// FileOutputStream fos = new FileOutputStream(outFile, append);
// writer = new PrintWriter(fos);
// writer.write(srcCode);
// writer.flush();
// writer.close();
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } finally {
// if (writer != null)
// writer.close();
// }
// }
private Object getScopedData(Object data, String scope) {
if (scope.isEmpty())
return data;
int sepIdx = scope.indexOf('.');
String subScope;
if (sepIdx < 0) {
subScope = scope;
} else {
subScope = scope.substring(0, sepIdx);
}
Object subData;
int bracketIdx = subScope.indexOf('[');
if (bracketIdx < 0) {
subData = ((Map) data).get(subScope);
} else {
subData = ((Map) data).get(subScope.substring(0, bracketIdx));
int idx = Integer.parseInt(subScope.substring(bracketIdx + 1,
subScope.length() - 1));
subData = ((List) subData).get(idx);
}
if (sepIdx < 0) {
return subData;
} else {
return getScopedData(subData, scope.substring(sepIdx + 1));
}
}
public Configuration getConfiguration() {
return cfg;
}
public Map getDataModel() {
return dataModel;
}
public JsonElement getJsonRoot() {
return root;
}
public void addData(String key, Object data) {
this.dataModel.put(key, data);
}
}