001/*
002 * (C) Copyright 2006-20012 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.template.processors.jod;
020
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.IOException;
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.zip.ZipEntry;
031import java.util.zip.ZipInputStream;
032
033import net.sf.jooreports.templates.DocumentTemplate;
034import net.sf.jooreports.templates.DocumentTemplateException;
035import net.sf.jooreports.templates.DocumentTemplateFactory;
036
037import org.nuxeo.ecm.core.api.Blob;
038import org.nuxeo.ecm.core.api.Blobs;
039import org.nuxeo.ecm.core.api.DocumentModel;
040import org.nuxeo.ecm.core.api.NuxeoException;
041import org.nuxeo.ecm.core.api.PropertyException;
042import org.nuxeo.ecm.core.api.model.Property;
043import org.nuxeo.ecm.core.schema.types.Type;
044import org.nuxeo.ecm.core.schema.types.primitives.BooleanType;
045import org.nuxeo.ecm.core.schema.types.primitives.DateType;
046import org.nuxeo.ecm.core.schema.types.primitives.StringType;
047import org.nuxeo.ecm.platform.rendering.fm.adapters.DocumentObjectWrapper;
048import org.nuxeo.runtime.api.Framework;
049import org.nuxeo.template.api.InputType;
050import org.nuxeo.template.api.TemplateInput;
051import org.nuxeo.template.api.TemplateProcessor;
052import org.nuxeo.template.api.adapters.TemplateBasedDocument;
053import org.nuxeo.template.api.adapters.TemplateSourceDocument;
054import org.nuxeo.template.fm.FMContextBuilder;
055import org.nuxeo.template.fm.FreeMarkerVariableExtractor;
056import org.nuxeo.template.odt.OOoArchiveModifier;
057import org.nuxeo.template.processors.AbstractTemplateProcessor;
058
059import freemarker.template.TemplateModelException;
060
061/**
062 * {@link TemplateProcessor} for ODT based templates. Using JODReports but also custom ODT hacks. May be migrated to
063 * pure ODT + Custom Freemarker soon.
064 *
065 * @author Tiry (tdelprat@nuxeo.com)
066 */
067public class JODReportTemplateProcessor extends AbstractTemplateProcessor implements TemplateProcessor {
068
069    public static final String TEMPLATE_TYPE = "JODTemplate";
070
071    protected FMContextBuilder fmContextBuilder = new FMContextBuilder();
072
073    @Override
074    public List<TemplateInput> getInitialParametersDefinition(Blob blob) throws IOException {
075
076        List<TemplateInput> params = new ArrayList<TemplateInput>();
077        String xmlContent = readXMLContent(blob);
078
079        List<String> vars = FreeMarkerVariableExtractor.extractVariables(xmlContent);
080
081        for (String var : vars) {
082            TemplateInput input = new TemplateInput(var);
083            params.add(input);
084        }
085
086        // add includes
087        // params.addAll(IncludeManager.getIncludes(xmlContent));
088
089        return params;
090    }
091
092    @Override
093    public Blob renderTemplate(TemplateBasedDocument templateBasedDocument, String templateName) throws IOException {
094
095        OOoArchiveModifier modifier = new OOoArchiveModifier();
096
097        Blob sourceTemplateBlob = templateBasedDocument.getTemplateBlob(templateName);
098        if (templateBasedDocument.getSourceTemplateDoc(templateName) != null) {
099            sourceTemplateBlob = templateBasedDocument.getSourceTemplateDoc(templateName).getAdapter(
100                    TemplateSourceDocument.class).getTemplateBlob();
101        }
102        List<TemplateInput> params = templateBasedDocument.getParams(templateName);
103
104        // init Jod template from the template DocumentModel Blob
105        DocumentTemplateFactory documentTemplateFactory = new DocumentTemplateFactory();
106        DocumentTemplate template = documentTemplateFactory.getTemplate(sourceTemplateBlob.getStream());
107
108        // build fm context
109        Map<String, Object> context = new HashMap<String, Object>();
110
111        // store Blobs to be inserted
112        List<Blob> blobsToInsert = new ArrayList<Blob>();
113
114        DocumentObjectWrapper nuxeoWrapper = new DocumentObjectWrapper(null);
115
116        for (TemplateInput param : params) {
117            if (param.isSourceValue()) {
118                Property property = null;
119                try {
120                    property = templateBasedDocument.getAdaptedDoc().getProperty(param.getSource());
121                } catch (PropertyException e) {
122                    log.warn("Unable to ready property " + param.getSource(), e);
123                }
124                if (property != null) {
125                    Serializable value = property.getValue();
126                    if (value != null) {
127                        if (Blob.class.isAssignableFrom(value.getClass())) {
128                            Blob blob = (Blob) value;
129                            if (param.getType() == InputType.PictureProperty) {
130                                if (blob.getMimeType() == null || "".equals(blob.getMimeType().trim())) {
131                                    blob.setMimeType("image/jpeg");
132                                }
133                                context.put(param.getName(), blob);
134                                blobsToInsert.add((Blob) value);
135                            }
136                        } else {
137                            try {
138                                context.put(param.getName(), nuxeoWrapper.wrap(property));
139                            } catch (TemplateModelException e) {
140                                throw new NuxeoException(e);
141                            }
142                        }
143                    } else {
144                        // no available value, try to find a default one ...
145                        Type pType = property.getType();
146                        if (pType.getName().equals(BooleanType.ID)) {
147                            context.put(param.getName(), new Boolean(false));
148                        } else if (pType.getName().equals(DateType.ID)) {
149                            context.put(param.getName(), new Date());
150                        } else if (pType.getName().equals(StringType.ID)) {
151                            context.put(param.getName(), "");
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}