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}