001/*
002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 */
020
021package org.nuxeo.template.processors.jod;
022
023import java.io.File;
024import java.io.FileOutputStream;
025import java.io.IOException;
026import java.io.Serializable;
027import java.util.ArrayList;
028import java.util.Date;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.zip.ZipEntry;
033import java.util.zip.ZipInputStream;
034
035import net.sf.jooreports.templates.DocumentTemplate;
036import net.sf.jooreports.templates.DocumentTemplateException;
037import net.sf.jooreports.templates.DocumentTemplateFactory;
038
039import org.nuxeo.ecm.core.api.Blob;
040import org.nuxeo.ecm.core.api.Blobs;
041import org.nuxeo.ecm.core.api.DocumentModel;
042import org.nuxeo.ecm.core.api.NuxeoException;
043import org.nuxeo.ecm.core.api.PropertyException;
044import org.nuxeo.ecm.core.api.model.Property;
045import org.nuxeo.ecm.core.schema.types.Type;
046import org.nuxeo.ecm.core.schema.types.primitives.BooleanType;
047import org.nuxeo.ecm.core.schema.types.primitives.DateType;
048import org.nuxeo.ecm.core.schema.types.primitives.StringType;
049import org.nuxeo.ecm.platform.rendering.fm.adapters.DocumentObjectWrapper;
050import org.nuxeo.runtime.api.Framework;
051import org.nuxeo.template.api.InputType;
052import org.nuxeo.template.api.TemplateInput;
053import org.nuxeo.template.api.TemplateProcessor;
054import org.nuxeo.template.api.adapters.TemplateBasedDocument;
055import org.nuxeo.template.api.adapters.TemplateSourceDocument;
056import org.nuxeo.template.fm.FMContextBuilder;
057import org.nuxeo.template.fm.FreeMarkerVariableExtractor;
058import org.nuxeo.template.odt.OOoArchiveModifier;
059import org.nuxeo.template.processors.AbstractTemplateProcessor;
060
061import freemarker.template.TemplateModelException;
062
063/**
064 * {@link TemplateProcessor} for ODT based templates. Using JODReports but also custom ODT hacks. May be migrated to
065 * pure ODT + Custom Freemarker soon.
066 *
067 * @author Tiry (tdelprat@nuxeo.com)
068 */
069public class JODReportTemplateProcessor extends AbstractTemplateProcessor implements TemplateProcessor {
070
071    public static final String TEMPLATE_TYPE = "JODTemplate";
072
073    protected FMContextBuilder fmContextBuilder = new FMContextBuilder();
074
075    @Override
076    public List<TemplateInput> getInitialParametersDefinition(Blob blob) throws IOException {
077
078        List<TemplateInput> params = new ArrayList<>();
079        String xmlContent = readXMLContent(blob);
080
081        List<String> vars = FreeMarkerVariableExtractor.extractVariables(xmlContent);
082
083        for (String var : vars) {
084            TemplateInput input = new TemplateInput(var);
085            params.add(input);
086        }
087
088        // add includes
089        // params.addAll(IncludeManager.getIncludes(xmlContent));
090
091        return params;
092    }
093
094    @Override
095    public Blob renderTemplate(TemplateBasedDocument templateBasedDocument, String templateName) throws IOException {
096
097        OOoArchiveModifier modifier = new OOoArchiveModifier();
098
099        Blob sourceTemplateBlob = templateBasedDocument.getTemplateBlob(templateName);
100        if (templateBasedDocument.getSourceTemplateDoc(templateName) != null) {
101            sourceTemplateBlob = templateBasedDocument.getSourceTemplateDoc(templateName).getAdapter(
102                    TemplateSourceDocument.class).getTemplateBlob();
103        }
104        List<TemplateInput> params = templateBasedDocument.getParams(templateName);
105
106        // init Jod template from the template DocumentModel Blob
107        DocumentTemplateFactory documentTemplateFactory = new DocumentTemplateFactory();
108        DocumentTemplate template = documentTemplateFactory.getTemplate(sourceTemplateBlob.getStream());
109
110        // build fm context
111        Map<String, Object> context = new HashMap<>();
112
113        // store Blobs to be inserted
114        List<Blob> blobsToInsert = new ArrayList<>();
115
116        DocumentObjectWrapper nuxeoWrapper = new DocumentObjectWrapper(null);
117
118        for (TemplateInput param : params) {
119            if (param.isSourceValue()) {
120                Property property = null;
121                try {
122                    property = templateBasedDocument.getAdaptedDoc().getProperty(param.getSource());
123                } catch (PropertyException e) {
124                    log.warn("Unable to ready property " + param.getSource(), e);
125                }
126                if (property != null) {
127                    Serializable value = property.getValue();
128                    if (value != null) {
129                        if (Blob.class.isAssignableFrom(value.getClass())) {
130                            Blob blob = (Blob) value;
131                            if (param.getType() == InputType.PictureProperty) {
132                                if (blob.getMimeType() == null || "".equals(blob.getMimeType().trim())) {
133                                    blob.setMimeType("image/jpeg");
134                                }
135                                context.put(param.getName(), blob);
136                                blobsToInsert.add((Blob) value);
137                            }
138                        } else {
139                            try {
140                                context.put(param.getName(), nuxeoWrapper.wrap(property));
141                            } catch (TemplateModelException e) {
142                                throw new NuxeoException(e);
143                            }
144                        }
145                    } else {
146                        // no available value, try to find a default one ...
147                        Type pType = property.getType();
148                        if (pType.getName().equals(BooleanType.ID)) {
149                            context.put(param.getName(), Boolean.FALSE);
150                        } else if (pType.getName().equals(DateType.ID)) {
151                            context.put(param.getName(), new Date());
152                        } else if (pType.getName().equals(StringType.ID)) {
153                            context.put(param.getName(), "");
154                        } else {
155                            context.put(param.getName(), new Object());
156                        }
157                    }
158                }
159            } else {
160                if (InputType.StringValue.equals(param.getType())) {
161                    context.put(param.getName(), param.getStringValue());
162                } else if (InputType.BooleanValue.equals(param.getType())) {
163                    context.put(param.getName(), param.getBooleanValue());
164                } else if (InputType.DateValue.equals(param.getType())) {
165                    context.put(param.getName(), param.getDateValue());
166                }
167            }
168        }
169
170        // add default context vars
171        DocumentModel doc = templateBasedDocument.getAdaptedDoc();
172        context.putAll(fmContextBuilder.build(doc, templateName));
173
174        File workingDir = getWorkingDir();
175        File generated = new File(workingDir, "JODReportresult");
176        generated.createNewFile();
177
178        try {
179            template.createDocument(context, new FileOutputStream(generated));
180        } catch (DocumentTemplateException e) {
181            throw new NuxeoException(e);
182        }
183
184        generated = modifier.updateArchive(workingDir, generated, blobsToInsert);
185
186        Blob newBlob = Blobs.createBlob(generated);
187        newBlob.setMimeType("application/vnd.oasis.opendocument.text");
188        if (templateBasedDocument.getTemplateBlob(templateName) != null) {
189            newBlob.setFilename(templateBasedDocument.getTemplateBlob(templateName).getFilename());
190        } else {
191            newBlob.setFilename(sourceTemplateBlob.getFilename());
192        }
193
194        // mark the file for automatic deletion on GC
195        Framework.trackFile(generated, newBlob);
196
197        return newBlob;
198    }
199
200    public String readXMLContent(Blob blob) throws IOException {
201        ZipInputStream zIn = new ZipInputStream(blob.getStream());
202        ZipEntry zipEntry = zIn.getNextEntry();
203        String xmlContent = null;
204        while (zipEntry != null) {
205            if (zipEntry.getName().equals("content.xml")) {
206                StringBuilder sb = new StringBuilder();
207                byte[] buffer = new byte[BUFFER_SIZE];
208                int read;
209                while ((read = zIn.read(buffer)) != -1) {
210                    sb.append(new String(buffer, 0, read));
211                }
212                xmlContent = sb.toString();
213                break;
214            }
215            zipEntry = zIn.getNextEntry();
216        }
217        zIn.close();
218        return xmlContent;
219    }
220
221}