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<TemplateInput>();
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<String, Object>();
112
113        // store Blobs to be inserted
114        List<Blob> blobsToInsert = new ArrayList<Blob>();
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(), new 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 if (pType.getName().equals(StringType.ID)) {
155                            context.put(param.getName(), "");
156                        } else {
157                            context.put(param.getName(), new Object());
158                        }
159                    }
160                }
161            } else {
162                if (InputType.StringValue.equals(param.getType())) {
163                    context.put(param.getName(), param.getStringValue());
164                } else if (InputType.BooleanValue.equals(param.getType())) {
165                    context.put(param.getName(), param.getBooleanValue());
166                } else if (InputType.DateValue.equals(param.getType())) {
167                    context.put(param.getName(), param.getDateValue());
168                }
169            }
170        }
171
172        // add default context vars
173        DocumentModel doc = templateBasedDocument.getAdaptedDoc();
174        context.putAll(fmContextBuilder.build(doc, templateName));
175
176        File workingDir = getWorkingDir();
177        File generated = new File(workingDir, "JODReportresult");
178        generated.createNewFile();
179
180        try {
181            template.createDocument(context, new FileOutputStream(generated));
182        } catch (DocumentTemplateException e) {
183            throw new NuxeoException(e);
184        }
185
186        generated = modifier.updateArchive(workingDir, generated, blobsToInsert);
187
188        Blob newBlob = Blobs.createBlob(generated);
189        newBlob.setMimeType("application/vnd.oasis.opendocument.text");
190        if (templateBasedDocument.getTemplateBlob(templateName) != null) {
191            newBlob.setFilename(templateBasedDocument.getTemplateBlob(templateName).getFilename());
192        } else {
193            newBlob.setFilename(sourceTemplateBlob.getFilename());
194        }
195
196        // mark the file for automatic deletion on GC
197        Framework.trackFile(generated, newBlob);
198
199        return newBlob;
200    }
201
202    public String readXMLContent(Blob blob) throws IOException {
203        ZipInputStream zIn = new ZipInputStream(blob.getStream());
204        ZipEntry zipEntry = zIn.getNextEntry();
205        String xmlContent = null;
206        while (zipEntry != null) {
207            if (zipEntry.getName().equals("content.xml")) {
208                StringBuilder sb = new StringBuilder();
209                byte[] buffer = new byte[BUFFER_SIZE];
210                int read;
211                while ((read = zIn.read(buffer)) != -1) {
212                    sb.append(new String(buffer, 0, read));
213                }
214                xmlContent = sb.toString();
215                break;
216            }
217            zipEntry = zIn.getNextEntry();
218        }
219        zIn.close();
220        return xmlContent;
221    }
222
223}