001package org.nuxeo.template.processors.fm; 002 003import java.io.IOException; 004import java.io.StringWriter; 005import java.util.ArrayList; 006import java.util.List; 007import java.util.Map; 008import java.util.regex.Pattern; 009 010import org.nuxeo.common.utils.FileUtils; 011import org.nuxeo.ecm.core.api.Blob; 012import org.nuxeo.ecm.core.api.Blobs; 013import org.nuxeo.ecm.core.api.NuxeoException; 014import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 015import org.nuxeo.ecm.platform.rendering.api.RenderingException; 016import org.nuxeo.ecm.platform.rendering.fm.FreemarkerEngine; 017import org.nuxeo.runtime.api.Framework; 018import org.nuxeo.template.api.TemplateInput; 019import org.nuxeo.template.api.TemplateProcessor; 020import org.nuxeo.template.api.adapters.TemplateBasedDocument; 021import org.nuxeo.template.fm.FMContextBuilder; 022import org.nuxeo.template.fm.FreeMarkerVariableExtractor; 023import org.nuxeo.template.processors.AbstractTemplateProcessor; 024 025import freemarker.cache.StringTemplateLoader; 026 027public class FreeMarkerProcessor extends AbstractTemplateProcessor implements TemplateProcessor { 028 029 protected StringTemplateLoader loader = new StringTemplateLoader(); 030 031 protected FreemarkerEngine fmEngine = null; 032 033 protected FMContextBuilder fmContextBuilder = new FMContextBuilder(); 034 035 protected FreemarkerEngine getEngine() { 036 if (fmEngine == null) { 037 fmEngine = new FreemarkerEngine(); 038 fmEngine.getConfiguration().setTemplateLoader(loader); 039 } 040 return fmEngine; 041 } 042 043 protected final static Pattern XMLStartPattern = Pattern.compile("<\\?xml"); 044 045 protected final static Pattern HtmlTagPattern = Pattern.compile("<(\\S+?)(.*?)>(.*?)</\\1>", 046 Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE); 047 048 protected String guessMimeType(Blob result, MimetypeRegistry mreg) { 049 050 if (result == null) { 051 return null; 052 } 053 054 String content; 055 try { 056 content = result.getString(); 057 } catch (IOException e) { 058 throw new NuxeoException(e); 059 } 060 061 if (XMLStartPattern.matcher(content).find()) { 062 return "text/xml"; 063 } 064 065 if (HtmlTagPattern.matcher(content).find()) { 066 return "text/html"; 067 } 068 069 return mreg.getMimetypeFromBlobWithDefault(result, "text/plain"); 070 } 071 072 protected void setBlobAttributes(Blob result, TemplateBasedDocument templateBasedDocument) { 073 074 // try to guess mimetype and extension of the resulting Blob 075 076 MimetypeRegistry mreg = Framework.getLocalService(MimetypeRegistry.class); 077 078 String mimetype = "text/html"; 079 String extension = ".html"; 080 081 if (mreg != null) { 082 String found_mimetype = guessMimeType(result, mreg); 083 if (found_mimetype != null) { 084 mimetype = found_mimetype; 085 List<String> extensions = mreg.getExtensionsFromMimetypeName(mimetype); 086 if (extensions != null && extensions.size() > 0) { 087 extension = "." + extensions.get(0); 088 } 089 } 090 } 091 if ("text/xml".equalsIgnoreCase(mimetype)) { 092 // because MimetypeRegistry return a stupid result for XML 093 extension = ".xml"; 094 } 095 result.setMimeType(mimetype); 096 String targetFileName = FileUtils.getFileNameNoExt(templateBasedDocument.getAdaptedDoc().getTitle()); 097 result.setFilename(targetFileName + extension); 098 } 099 100 @Override 101 public Blob renderTemplate(TemplateBasedDocument templateBasedDocument, String templateName) throws IOException { 102 103 Blob sourceTemplateBlob = getSourceTemplateBlob(templateBasedDocument, templateName); 104 105 String fmTemplateKey = "main" + System.currentTimeMillis(); 106 107 String ftl = sourceTemplateBlob.getString(); 108 109 loader.putTemplate(fmTemplateKey, ftl); 110 111 Map<String, Object> ctx = fmContextBuilder.build(templateBasedDocument, templateName); 112 113 FMBindingResolver resolver = new FMBindingResolver(); 114 resolver.resolve(templateBasedDocument.getParams(templateName), ctx, templateBasedDocument); 115 116 StringWriter writer = new StringWriter(); 117 try { 118 getEngine().render(fmTemplateKey, ctx, writer); 119 } catch (RenderingException e) { 120 throw new IOException(e); 121 } 122 123 Blob result = Blobs.createBlob(writer.toString()); 124 setBlobAttributes(result, templateBasedDocument); 125 126 return result; 127 } 128 129 @Override 130 public List<TemplateInput> getInitialParametersDefinition(Blob blob) throws IOException { 131 List<TemplateInput> params = new ArrayList<TemplateInput>(); 132 133 if (blob != null) { 134 String xmlContent = blob.getString(); 135 136 if (xmlContent != null) { 137 List<String> vars = FreeMarkerVariableExtractor.extractVariables(xmlContent); 138 139 for (String var : vars) { 140 TemplateInput input = new TemplateInput(var); 141 params.add(input); 142 } 143 } 144 } 145 return params; 146 } 147 148}