/* 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); } }