summaryrefslogtreecommitdiff
path: root/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java
diff options
context:
space:
mode:
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java')
-rw-r--r--reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java1730
1 files changed, 1730 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java b/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java
new file mode 100644
index 000000000000..cdc8eae1b73b
--- /dev/null
+++ b/reportbuilder/java/com/sun/star/report/pentaho/output/OfficeDocumentReportTarget.java
@@ -0,0 +1,1730 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+package com.sun.star.report.pentaho.output;
+
+import com.sun.star.report.DataSourceFactory;
+import com.sun.star.report.ImageService;
+import com.sun.star.report.InputRepository;
+import com.sun.star.report.OfficeToken;
+import com.sun.star.report.OutputRepository;
+import com.sun.star.report.ReportEngineParameterNames;
+import com.sun.star.report.SDBCReportDataFactory;
+import com.sun.star.report.pentaho.OfficeNamespaces;
+import com.sun.star.report.pentaho.layoutprocessor.ImageElementContext;
+import com.sun.star.report.pentaho.model.OfficeDocument;
+import com.sun.star.report.pentaho.model.OfficeStyle;
+import com.sun.star.report.pentaho.model.OfficeStyles;
+import com.sun.star.report.pentaho.model.OfficeStylesCollection;
+import com.sun.star.report.pentaho.styles.LengthCalculator;
+import com.sun.star.report.pentaho.styles.StyleMapper;
+import com.sun.org.apache.xerces.internal.parsers.DOMParser;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.awt.Image;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.io.InputStream;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jfree.layouting.input.style.parser.CSSValueFactory;
+import org.jfree.layouting.input.style.parser.StyleSheetParserUtil;
+import org.jfree.layouting.input.style.values.CSSNumericType;
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.layouting.layouter.style.CSSValueResolverUtility;
+import org.jfree.layouting.namespace.NamespaceDefinition;
+import org.jfree.layouting.namespace.Namespaces;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.layouting.util.LazyAttributeMap;
+import org.jfree.report.DataFlags;
+import org.jfree.report.DataSourceException;
+import org.jfree.report.JFreeReportBoot;
+import org.jfree.report.JFreeReportInfo;
+import org.jfree.report.ReportProcessingException;
+import org.jfree.report.flow.AbstractReportTarget;
+import org.jfree.report.flow.ReportJob;
+import org.jfree.report.flow.ReportStructureRoot;
+import org.jfree.report.flow.ReportTargetUtil;
+import org.jfree.report.structure.Element;
+import org.jfree.report.structure.Section;
+import org.jfree.report.util.AttributeNameGenerator;
+import org.jfree.report.util.IntegerCache;
+import org.jfree.report.util.MemoryByteArrayOutputStream;
+
+import org.pentaho.reporting.libraries.base.util.FastStack;
+import org.pentaho.reporting.libraries.base.util.IOUtils;
+import org.pentaho.reporting.libraries.resourceloader.ResourceException;
+import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
+import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
+import org.pentaho.reporting.libraries.xmlns.common.AttributeList;
+import org.pentaho.reporting.libraries.xmlns.writer.DefaultTagDescription;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
+
+import org.w3c.css.sac.LexicalUnit;
+
+/**
+ * Todo: Document me!
+ *
+ * @author Thomas Morgner
+ * @since 08.03.2007
+ */
+public abstract class OfficeDocumentReportTarget extends AbstractReportTarget
+{
+
+ protected static final Log LOGGER = LogFactory.getLog(OfficeDocumentReportTarget.class);
+ public static final String HORIZONTAL_POS = "horizontal-pos";
+ public static final String TAG_DEF_PREFIX = "com.sun.star.report.pentaho.output.";
+ public static final int ROLE_NONE = 0;
+ public static final int ROLE_REPORT_HEADER = 1;
+ public static final int ROLE_REPORT_FOOTER = 2;
+ public static final int ROLE_GROUP_HEADER = 3;
+ public static final int ROLE_GROUP_FOOTER = 4;
+ public static final int ROLE_REPEATING_GROUP_HEADER = 5;
+ public static final int ROLE_REPEATING_GROUP_FOOTER = 6;
+ public static final int ROLE_PAGE_HEADER = 7;
+ public static final int ROLE_PAGE_FOOTER = 8;
+ public static final int ROLE_DETAIL = 9;
+ public static final int ROLE_VARIABLES = 10;
+ public static final int ROLE_TEMPLATE = 11;
+ public static final int ROLE_SPREADSHEET_PAGE_HEADER = 12;
+ public static final int ROLE_SPREADSHEET_PAGE_FOOTER = 13;
+ public static final int STATE_IN_DOCUMENT = 0;
+ public static final int STATE_IN_BODY = 1;
+ public static final int STATE_IN_CONTENT = 2;
+ public static final int STATE_IN_GROUP = 3;
+ public static final int STATE_IN_GROUP_BODY = 4;
+ public static final int STATE_IN_SECTION = 5;
+ public static final int STATE_IN_OTHER = 6;
+ public static final int STATE_IN_GROUP_INSTANCE = 7;
+ public static final String FAILED = "Failed";
+ public static final String VERTICAL_POS = "vertical-pos";
+ private static final String ZERO_CM = "0cm";
+ /** the verison of the ODF specification to which generated documents
+ * shall conform. */
+ public static final String ODF_VERSION = "1.2";
+
+ protected static class BufferState
+ {
+
+ private final XmlWriter xmlWriter;
+ private final MemoryByteArrayOutputStream xmlBuffer;
+ private final OfficeStylesCollection stylesCollection;
+
+ protected BufferState(final XmlWriter xmlWriter,
+ final MemoryByteArrayOutputStream xmlBuffer,
+ final OfficeStylesCollection stylesCollection)
+ {
+ this.stylesCollection = stylesCollection;
+ this.xmlWriter = xmlWriter;
+ this.xmlBuffer = xmlBuffer;
+ }
+
+ public OfficeStylesCollection getStylesCollection()
+ {
+ return stylesCollection;
+ }
+
+ public XmlWriter getXmlWriter()
+ {
+ return xmlWriter;
+ }
+
+ public String getXmlBuffer() throws ReportProcessingException
+ {
+ try
+ {
+ final byte[] zippedData = xmlBuffer.getRaw();
+ final InputStreamReader reader = new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16");
+ final StringWriter writer = new StringWriter((zippedData.length / 2) + 1);
+ IOUtils.getInstance().copyWriter(reader, writer);
+ return writer.toString();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException("Failed to copy buffer", e);
+ }
+ }
+
+ public Reader getXmlAsReader() throws ReportProcessingException
+ {
+ try
+ {
+ final byte[] zippedData = xmlBuffer.getRaw();
+ return new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(zippedData, 0, xmlBuffer.getLength())), "UTF-16");
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException("Failed to copy buffer", e);
+ }
+ }
+ }
+
+ protected static class GroupContext
+ {
+
+ private final GroupContext parent;
+ private int iterationCount;
+ private boolean groupWithRepeatingSection;
+
+ protected GroupContext(final GroupContext parent)
+ {
+ this.parent = parent;
+ }
+
+ public GroupContext getParent()
+ {
+ return parent;
+ }
+
+ public int getIterationCount()
+ {
+ return iterationCount;
+ }
+
+ public void setIterationCount(final int iterationCount)
+ {
+ this.iterationCount = iterationCount;
+ }
+
+ public boolean isGroupWithRepeatingSection()
+ {
+ return groupWithRepeatingSection;
+ }
+
+ public void setGroupWithRepeatingSection(final boolean groupWithRepeatingSection)
+ {
+ this.groupWithRepeatingSection = groupWithRepeatingSection;
+ }
+
+ public String toString()
+ {
+ return "GroupContext{" + "parent=" + parent + ", iterationCount=" + iterationCount + ", groupWithRepeatingSection=" + groupWithRepeatingSection + '}';
+ }
+ }
+ private final FastStack states;
+ private int currentRole;
+ private final FastStack xmlWriters;
+ private XmlWriter rootXmlWriter;
+ /**
+ * This styles-collection contains all styles that were predefined in the report definition file. The common styles
+ * and the master-styles will be written unmodified, the automatic styles will be ignored.
+ */
+ private OfficeStylesCollection predefinedStylesCollection;
+ /**
+ * This styles-collection contains all master-styles that have been generated by the report definition process. It
+ * also contains all automatic styles that have been generated for the page-bands (and the pagebands as well).
+ */
+ private OfficeStylesCollection globalStylesCollection;
+ /**
+ * The content styles collection contains all automatic styles that have been generated for the normal-flow content.
+ */
+ private OfficeStylesCollection contentStylesCollection;
+ private final OutputRepository outputRepository;
+ private final InputRepository inputRepository;
+ private final AttributeNameGenerator tableNameGenerator;
+ private final AttributeNameGenerator frameNameGenerator;
+ private final AttributeNameGenerator autoStyleNameGenerator;
+ private final String target;
+ private static final int INITIAL_BUFFER_SIZE = 40960;
+ private StyleMapper styleMapper;
+ private StyleSheetParserUtil styleSheetParserUtil;
+ private final AttributeNameGenerator imageNames;
+ private final ImageProducer imageProducer;
+ private final OleProducer oleProducer;
+ private GroupContext groupContext;
+ private static final boolean DEBUG_ELEMENTS =
+ JFreeReportBoot.getInstance().getExtendedConfig().getBoolProperty("com.sun.star.report.pentaho.output.DebugElements");
+
+ protected OfficeDocumentReportTarget(final ReportJob reportJob,
+ final ResourceManager resourceManager,
+ final ResourceKey baseResource,
+ final InputRepository inputRepository,
+ final OutputRepository outputRepository,
+ final String target,
+ final ImageService imageService,
+ final DataSourceFactory datasourcefactory)
+ throws ReportProcessingException
+ {
+ super(reportJob, resourceManager, baseResource);
+ if (imageService == null)
+ {
+ throw new NullPointerException("ImageService must not be null");
+ }
+ if (target == null)
+ {
+ throw new NullPointerException("Target-Name must not be null");
+ }
+
+ this.target = target;
+
+ this.tableNameGenerator = new AttributeNameGenerator();
+ this.frameNameGenerator = new AttributeNameGenerator();
+ this.autoStyleNameGenerator = new AttributeNameGenerator();
+ this.outputRepository = outputRepository;
+ this.inputRepository = inputRepository;
+ this.states = new FastStack();
+ this.xmlWriters = new FastStack();
+ this.imageNames = new AttributeNameGenerator();
+
+ this.imageProducer = new ImageProducer(inputRepository, outputRepository, imageService);
+ this.oleProducer = new OleProducer(inputRepository, outputRepository, imageService, datasourcefactory, (Integer) reportJob.getParameters().get(ReportEngineParameterNames.MAXROWS));
+
+ try
+ {
+ final ResourceManager realResourceManager = getResourceManager();
+ styleMapper = StyleMapper.loadInstance(realResourceManager);
+ }
+ catch (ResourceException e)
+ {
+ throw new ReportProcessingException("Failed to load style-mapper", e);
+ }
+ }
+
+ protected abstract String getTargetMimeType();
+
+ protected OutputRepository getOutputRepository()
+ {
+ return outputRepository;
+ }
+
+ protected InputRepository getInputRepository()
+ {
+ return inputRepository;
+ }
+
+ /**
+ * Starts the output of a new office document. This method writes the generic 'office:document-content' tag along with
+ * all known namespace declarations.
+ *
+ * @param report the report object.
+ * @throws DataSourceException if there was an error accessing the datasource
+ * @throws ReportProcessingException if some other error occured.
+ */
+ public void startReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ imageNames.reset();
+ this.groupContext = new GroupContext(null);
+
+ final DefaultTagDescription tagDescription = createTagDescription();
+ try
+ {
+ final OutputStream outputStream = outputRepository.createOutputStream(target, "text/xml");
+ final Writer writer = new OutputStreamWriter(outputStream, "UTF-8");
+
+ this.rootXmlWriter = new XmlWriter(writer, tagDescription);
+ this.rootXmlWriter.setAlwaysAddNamespace(true);
+
+ final AttributeList rootAttributes = new AttributeList();
+ rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS);
+ rootAttributes.addNamespaceDeclaration("style", OfficeNamespaces.STYLE_NS);
+ rootAttributes.addNamespaceDeclaration("text", OfficeNamespaces.TEXT_NS);
+ rootAttributes.addNamespaceDeclaration("table", OfficeNamespaces.TABLE_NS);
+ rootAttributes.addNamespaceDeclaration("draw", OfficeNamespaces.DRAWING_NS);
+ rootAttributes.addNamespaceDeclaration("fo", OfficeNamespaces.FO_NS);
+ rootAttributes.addNamespaceDeclaration("xlink", OfficeNamespaces.XLINK_NS);
+ rootAttributes.addNamespaceDeclaration("dc", OfficeNamespaces.PURL_NS);
+ rootAttributes.addNamespaceDeclaration("meta", OfficeNamespaces.META_NS);
+ rootAttributes.addNamespaceDeclaration("number", OfficeNamespaces.DATASTYLE_NS);
+ rootAttributes.addNamespaceDeclaration("svg", OfficeNamespaces.SVG_NS);
+ rootAttributes.addNamespaceDeclaration("chart", OfficeNamespaces.CHART_NS);
+ rootAttributes.addNamespaceDeclaration("chartooo", OfficeNamespaces.CHARTOOO_NS);
+ rootAttributes.addNamespaceDeclaration("dr3d", OfficeNamespaces.DR3D_NS);
+ rootAttributes.addNamespaceDeclaration("math", OfficeNamespaces.MATHML_NS);
+ rootAttributes.addNamespaceDeclaration("form", OfficeNamespaces.FORM_NS);
+ rootAttributes.addNamespaceDeclaration("script", OfficeNamespaces.SCRIPT_NS);
+ rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS);
+ rootAttributes.addNamespaceDeclaration("ooow", OfficeNamespaces.OOW2004_NS);
+ rootAttributes.addNamespaceDeclaration("oooc", OfficeNamespaces.OOC2004_NS);
+ rootAttributes.addNamespaceDeclaration("dom", OfficeNamespaces.XML_EVENT_NS);
+ rootAttributes.addNamespaceDeclaration("xforms", OfficeNamespaces.XFORMS_NS);
+ rootAttributes.addNamespaceDeclaration("xsd", OfficeNamespaces.XSD_NS);
+ rootAttributes.addNamespaceDeclaration("xsi", OfficeNamespaces.XSI_NS);
+ rootAttributes.addNamespaceDeclaration("grddl", OfficeNamespaces.GRDDL_NS);
+ rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version",
+ ODF_VERSION);
+
+ this.rootXmlWriter.writeXmlDeclaration("UTF-8");
+ this.rootXmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-content", rootAttributes, XmlWriterSupport.OPEN);
+
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_DOCUMENT));
+
+ autoStyleNameGenerator.reset();
+ tableNameGenerator.reset();
+ frameNameGenerator.reset();
+
+ final OfficeDocument reportDoc = (OfficeDocument) report;
+ predefinedStylesCollection = reportDoc.getStylesCollection();
+
+ final OfficeStyles commonStyles = predefinedStylesCollection.getCommonStyles();
+ if (!commonStyles.containsStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS))
+ {
+ final OfficeStyle graphicsDefaultStyle = new OfficeStyle();
+ graphicsDefaultStyle.setStyleFamily(OfficeToken.GRAPHIC);
+ graphicsDefaultStyle.setStyleName(OfficeToken.GRAPHICS);
+ final Element graphicProperties = produceFirstChild(graphicsDefaultStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
+ graphicProperties.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH);
+ graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "x", ZERO_CM);
+ graphicProperties.setAttribute(OfficeNamespaces.SVG_NS, "y", ZERO_CM);
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap", "dynamic");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "number-wrapped-paragraphs", "no-limit");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "wrap-contour", OfficeToken.FALSE);
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top"); // changed for chart
+
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", OfficeToken.PARAGRAPH);
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left"); // changed for chart
+
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", OfficeToken.PARAGRAPH);
+ commonStyles.addStyle(graphicsDefaultStyle);
+ }
+
+ // Make sure that later generated styles do not overwrite existing styles.
+ fillStyleNameGenerator(predefinedStylesCollection);
+
+ contentStylesCollection = new OfficeStylesCollection();
+ globalStylesCollection = new OfficeStylesCollection();
+
+ startBuffering(contentStylesCollection, true);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(FAILED, e);
+ }
+ }
+
+ protected AttributeNameGenerator getAutoStyleNameGenerator()
+ {
+ return autoStyleNameGenerator;
+ }
+
+ private void fillStyleNameGenerator(final OfficeStylesCollection stylesCollection)
+ {
+ final OfficeStyles commonStyles = stylesCollection.getCommonStyles();
+ final OfficeStyle[] allCommonStyles = commonStyles.getAllStyles();
+ for (int i = 0; i < allCommonStyles.length; i++)
+ {
+ final OfficeStyle style = allCommonStyles[i];
+ autoStyleNameGenerator.generateName(style.getStyleName());
+ }
+
+ final OfficeStyles autoStyles = stylesCollection.getAutomaticStyles();
+ final OfficeStyle[] allAutoStyles = autoStyles.getAllStyles();
+ for (int i = 0; i < allAutoStyles.length; i++)
+ {
+ final OfficeStyle style = allAutoStyles[i];
+ autoStyleNameGenerator.generateName(style.getStyleName());
+ }
+ }
+
+ public OfficeStylesCollection getPredefinedStylesCollection()
+ {
+ return predefinedStylesCollection;
+ }
+
+ public OfficeStylesCollection getGlobalStylesCollection()
+ {
+ return globalStylesCollection;
+ }
+
+ public OfficeStylesCollection getContentStylesCollection()
+ {
+ return contentStylesCollection;
+ }
+
+ /**
+ * Returns the XML-Writer tag description. This description defines whether an element can have character data inside.
+ * Such element will disable the indention, as in that case the additional whitespaces might alter the meaning of the
+ * element's contents.
+ *
+ * @return the tag description library.
+ */
+ protected DefaultTagDescription createTagDescription()
+ {
+ final DefaultTagDescription tagDescription = new DefaultTagDescription();
+ tagDescription.configure(JFreeReportBoot.getInstance().getGlobalConfig(),
+ OfficeDocumentReportTarget.TAG_DEF_PREFIX);
+ return tagDescription;
+ }
+
+ /**
+ * Returns the current processing state.
+ *
+ * @return the processing state.
+ */
+ protected int getCurrentState()
+ {
+ if (states.isEmpty())
+ {
+ throw new IllegalStateException();
+ }
+ final Integer o = (Integer) states.peek();
+ return o;
+ }
+
+ /**
+ * Starts the processing of an element and updates the processing state. This will select an apropriate handler method
+ * for the call and will call one of the start* methods.
+ *
+ * @param roAttrs the attribute map for the current element
+ * @throws DataSourceException
+ * @throws ReportProcessingException
+ */
+ public final void startElement(final AttributeMap roAttrs)
+ throws DataSourceException, ReportProcessingException
+ {
+ final AttributeMap attrs = new LazyAttributeMap(roAttrs);
+ // todo
+ if (DEBUG_ELEMENTS)
+ {
+ LOGGER.debug("Starting " + getCurrentState() + '/' + states.size() + ' ' + ReportTargetUtil.getNamespaceFromAttribute(attrs) + " -> " + ReportTargetUtil.getElemenTypeFromAttribute(attrs));
+ }
+ try
+ {
+ switch (getCurrentState())
+ {
+ case OfficeDocumentReportTarget.STATE_IN_DOCUMENT:
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "body", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_BODY));
+ startBody(attrs);
+ }
+ else
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
+ if (!isFilteredNamespace(ReportTargetUtil.getNamespaceFromAttribute(attrs)))
+ {
+ startOther(attrs);
+ }
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_BODY:
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OFFICE_NS, "report", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_CONTENT));
+ startContent(attrs);
+ }
+ else
+ {
+ throw new IllegalStateException("The 'office:body' element must have exactly one child of type 'report'");
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_CONTENT:
+ {
+ // Either a ordinary section or a group ..
+ // A group.
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "report-body", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY));
+ startGroupBody(attrs);
+ }
+ else
+ {
+ // Either a template-section, page-header, page-footer, report-header, report-footer
+ // or variables-section
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "template", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_TEMPLATE;
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-header", attrs))
+ {
+ if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role")))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER;
+ }
+ else
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_PAGE_HEADER;
+ }
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "page-footer", attrs))
+ {
+ if ("spreadsheet-section".equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "role")))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER;
+ }
+ else
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_PAGE_FOOTER;
+ }
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-header", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_REPORT_HEADER;
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "report-footer", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_REPORT_FOOTER;
+ }
+ else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
+ }
+ else
+ {
+ throw new IllegalStateException("Expected either 'template', 'report-body', " + "'report-header', 'report-footer', 'variables-section', 'page-header' or 'page-footer'");
+ }
+ startReportSection(attrs, currentRole);
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY:
+ {
+ // We now expect either an other group or a detail band.
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP));
+ groupContext = new GroupContext(groupContext);
+ startGroup(attrs);
+ }
+ else
+ {
+ // Either a variables-section, or a detail-band
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "detail", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_DETAIL;
+ }
+ else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
+ }
+ else
+ {
+ throw new IllegalStateException("Expected either 'group', 'detail' or 'variables-section'");
+ }
+ startReportSection(attrs, currentRole);
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP:
+ {
+ // A group can carry a repeating group header/footer or a group-instance section.
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-instance", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE));
+ startGroupInstance(attrs);
+ }
+ else
+ {
+ // repeating group header/footer, but *no* variables section
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section")))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER;
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs) && OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeated-section")))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER;
+ }
+ else
+ {
+ throw new IllegalStateException("Expected either 'group-instance', " + "'repeating group-header' or 'repeating group-footer'");
+ }
+ startReportSection(attrs, currentRole);
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE:
+ {
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "group-body", attrs))
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_GROUP_BODY));
+ startGroupBody(attrs);
+ }
+ else
+ {
+ // Either a group-header or group-footer or variables-section
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_SECTION));
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-header", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_GROUP_HEADER;
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.OOREPORT_NS, "group-footer", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_GROUP_FOOTER;
+ }
+ else if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, "variables-section", attrs))
+ {
+ currentRole = OfficeDocumentReportTarget.ROLE_VARIABLES;
+ }
+ else
+ {
+ throw new IllegalStateException("Expected either 'group-body', 'group-header', 'group-footer' or 'variables-section'");
+ }
+ startReportSection(attrs, currentRole);
+ }
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_SECTION:
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
+ startOther(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_OTHER:
+ {
+ states.push(IntegerCache.getInteger(OfficeDocumentReportTarget.STATE_IN_OTHER));
+ startOther(attrs);
+ break;
+ }
+ default:
+ throw new IllegalStateException("Failure: " + getCurrentState());
+ }
+ }
+ catch (IOException ioe)
+ {
+ LOGGER.error("ReportProcessing failed", ioe);
+ throw new ReportProcessingException("Failed to write content", ioe);
+ }
+// finally
+// {
+// LOGGER.debug ("Started " + getNamespaceFromAttribute(attrs) + ":" +
+// getElemenTypeFromAttribute(attrs) + " -> " + getCurrentState());
+// }
+ }
+
+ protected GroupContext getGroupContext()
+ {
+ return groupContext;
+ }
+
+ protected void performStyleProcessing(final AttributeMap attrs)
+ throws ReportProcessingException
+ {
+ final OfficeStylesCollection stylesCollection = getStylesCollection();
+ final OfficeStylesCollection predefCollection = getPredefinedStylesCollection();
+ final OfficeStylesCollection globalStylesCollection = getGlobalStylesCollection();
+
+ final String elementNamespace =
+ ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementName =
+ ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+
+ final String[] namespaces = attrs.getNameSpaces();
+ for (int i = 0; i < namespaces.length; i++)
+ {
+ final String attrNamespace = namespaces[i];
+ if (isFilteredNamespace(attrNamespace))
+ {
+ continue;
+ }
+
+ final Map attributes = attrs.getAttributes(attrNamespace);
+ final Iterator iterator = attributes.entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ final Map.Entry entry = (Map.Entry) iterator.next();
+ final String attrName = (String) entry.getKey();
+ final String attrValue = String.valueOf(entry.getValue());
+
+ final String styleFamily = styleMapper.getStyleFamilyFor(elementNamespace, elementName, attrNamespace, attrName);
+ if (styleFamily == null)
+ {
+ // None of the known style attributes.
+ continue;
+ }
+
+ if (styleMapper.isListOfStyles(elementNamespace, elementName, attrNamespace, attrName))
+ {
+ // ignored for now.
+ LOGGER.warn("List of styles is not yet implemented.");
+ continue;
+ }
+
+ // Copy styles is only called once per style.
+ StyleUtilities.copyStyle(styleFamily, attrValue, stylesCollection, globalStylesCollection, predefCollection);
+ }
+ }
+ }
+
+ protected void startBody(final AttributeMap attrs)
+ throws IOException
+ {
+ getXmlWriter().writeTag(OfficeNamespaces.OFFICE_NS, "body", XmlWriterSupport.OPEN);
+ }
+
+ private final boolean allowBuffering(final int role)
+ {
+ return (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_TEMPLATE);
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (allowBuffering(role))
+ {
+ startBuffering(new OfficeStylesCollection(), true);
+ }
+ }
+
+ protected abstract void startContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException;
+
+ protected void startGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final Object repeatingHeaderOrFooter = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "repeating-header-or-footer");
+ if (OfficeToken.TRUE.equals(repeatingHeaderOrFooter))
+ {
+ getGroupContext().setGroupWithRepeatingSection(true);
+ }
+
+ final Object iterationCount = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "iteration-count");
+ if (iterationCount instanceof Number)
+ {
+ final Number itNumber = (Number) iterationCount;
+ getGroupContext().setIterationCount(itNumber.intValue());
+ }
+ }
+
+ protected void startGroupInstance(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected void startGroupBody(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected abstract void startOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException;
+
+ public void processText(final String text)
+ throws DataSourceException, ReportProcessingException
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ final BufferedReader br = new BufferedReader(new StringReader(text));
+ String line = br.readLine();
+ while (line != null)
+ {
+ xmlWriter.writeTextNormalized(line, false);
+ line = br.readLine();
+ if (line != null)
+ {
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(FAILED, e);
+ }
+ }
+
+ protected boolean isFilteredNamespace(final String namespace)
+ {
+ if (Namespaces.LIBLAYOUT_NAMESPACE.equals(namespace))
+ {
+ return true;
+ }
+ if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace))
+ {
+ return true;
+ }
+ if (JFreeReportInfo.REPORT_NAMESPACE.equals(namespace))
+ {
+ return true;
+ }
+ if (JFreeReportInfo.COMPATIBILITY_NAMESPACE.equals(namespace))
+ {
+ return true;
+ }
+ if (OfficeNamespaces.OOREPORT_NS.equals(namespace))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void processContent(final DataFlags value)
+ throws DataSourceException, ReportProcessingException
+ {
+ final Object rawvalue = value.getValue();
+ if (rawvalue == null)
+ {
+ return;
+ }
+
+ // special handler for image (possibly also for URL ..)
+ if (rawvalue instanceof Image)
+ {
+ // do nothing yet. We should define something for that later ..
+ return;
+ }
+
+ final XmlWriter xmlWriter = getXmlWriter();
+ final String text = String.valueOf(rawvalue);
+ try
+ {
+ final BufferedReader br = new BufferedReader(new StringReader(text));
+ String line = br.readLine();
+ while (line != null)
+ {
+ xmlWriter.writeTextNormalized(line, false);
+ line = br.readLine();
+ if (line != null)
+ {
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, "line-break", XmlWriterSupport.CLOSE);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(FAILED, e);
+ }
+ }
+
+ public final void endElement(final AttributeMap roAttrs)
+ throws DataSourceException, ReportProcessingException
+ {
+ final AttributeMap attrs = new LazyAttributeMap(roAttrs);
+ // final int oldState = getCurrentState();
+ try
+ {
+
+ switch (getCurrentState())
+ {
+ case OfficeDocumentReportTarget.STATE_IN_OTHER:
+ {
+ endOther(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_SECTION:
+ {
+ endReportSection(attrs, currentRole);
+ currentRole = OfficeDocumentReportTarget.ROLE_NONE;
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP:
+ {
+ endGroup(attrs);
+ groupContext = groupContext.getParent();
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP_INSTANCE:
+ {
+ endGroupInstance(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_GROUP_BODY:
+ {
+ endGroupBody(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_CONTENT:
+ {
+ endContent(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_BODY:
+ {
+ endBody(attrs);
+ break;
+ }
+ case OfficeDocumentReportTarget.STATE_IN_DOCUMENT:
+ {
+ throw new IllegalStateException("This cannot be.");
+ }
+ default:
+ {
+ throw new IllegalStateException("Invalid state encountered.");
+ }
+ }
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException("IO Error while writing content",
+ ioe);
+ } finally
+ {
+ states.pop();
+
+ if (DEBUG_ELEMENTS)
+ {
+ LOGGER.debug("Finished " + getCurrentState() + "/" + states.size() + " " + ReportTargetUtil.getNamespaceFromAttribute(attrs) + ":" + ReportTargetUtil.getElemenTypeFromAttribute(attrs));
+ }
+
+ }
+ }
+
+ protected void endGroupBody(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected void endGroupInstance(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ public int getCurrentRole()
+ {
+ return currentRole;
+ }
+
+ protected abstract void endOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException;
+
+ protected void endReportSection(final AttributeMap attrs,
+ final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (allowBuffering(role))
+ {
+ finishBuffering();
+ }
+ }
+
+ protected void endGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ }
+
+ protected abstract void endContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException;
+
+ protected void endBody(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ getXmlWriter().writeCloseTag();
+ }
+
+ public void copyMeta()
+ {
+ // now copy the meta.xml
+ if (getInputRepository().isReadable("meta.xml"))
+ {
+ InputStream inputStream = null;
+ try
+ {
+ inputStream = getInputRepository().createInputStream("meta.xml");
+ DOMParser dOMParser = new DOMParser();
+ dOMParser.parse(new InputSource(inputStream));
+ Document document = dOMParser.getDocument();
+ NodeList nl = document.getElementsByTagName("document-meta/meta/generator");
+ Node node = document.getFirstChild().getFirstChild().getFirstChild().getFirstChild();
+ String creator = node.getNodeValue();
+ node.setNodeValue(creator + "/report_builder");
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+
+ final OutputStream outputMetaStream = getOutputRepository().createOutputStream("meta.xml", "text/xml");
+ StreamResult result = new StreamResult(outputMetaStream);
+ DOMSource source = new DOMSource(document);
+ transformer.transform(source, result);
+
+ //IOUtils.getInstance().copyStreams(inputStream, outputMetaStream);
+ outputMetaStream.flush();
+ outputMetaStream.close();
+ }
+ catch (java.lang.Exception ex)
+ {
+ } finally
+ {
+ if (inputStream != null)
+ {
+ try
+ {
+ inputStream.close();
+ }
+ catch (IOException ex)
+ {
+ Logger.getLogger(OfficeDocumentReportTarget.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+ }
+ }
+ }
+
+ public void endReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ if (xmlWriters.size() != 1)
+ {
+ throw new IllegalStateException("Invalid writer-stack state");
+ }
+
+ try
+ {
+ final StylesWriter inlineStylesWriter = new StylesWriter(rootXmlWriter);
+ inlineStylesWriter.writeContentStyles(predefinedStylesCollection, contentStylesCollection);
+
+ final BufferState state = finishBuffering();
+ this.rootXmlWriter.writeStream(state.getXmlAsReader());
+
+ final OutputStream stylesOutStream =
+ outputRepository.createOutputStream("styles.xml", "text/xml");
+ final OutputStreamWriter osw =
+ new OutputStreamWriter(stylesOutStream, "UTF-8");
+ final StylesWriter stylesWriter = new StylesWriter(osw);
+ stylesWriter.writeGlobalStyles(predefinedStylesCollection, globalStylesCollection);
+ stylesWriter.close();
+
+ this.rootXmlWriter.writeCloseTag();
+ this.rootXmlWriter.close();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(FAILED, e);
+ }
+ }
+
+ public XmlWriter getXmlWriter()
+ {
+ final BufferState bufferState = (BufferState) xmlWriters.peek();
+ return bufferState.getXmlWriter();
+ }
+
+ public OfficeStylesCollection getStylesCollection()
+ {
+ final BufferState bufferState = (BufferState) xmlWriters.peek();
+ return bufferState.getStylesCollection();
+ }
+
+ public void startBuffering(final OfficeStylesCollection stylesCollection,
+ final boolean indent) throws ReportProcessingException
+ {
+ final XmlWriter currentWriter;
+ if (xmlWriters.isEmpty())
+ {
+ currentWriter = rootXmlWriter;
+ }
+ else
+ {
+ final BufferState bufferState = (BufferState) xmlWriters.peek();
+ currentWriter = bufferState.getXmlWriter();
+ }
+
+ try
+ {
+ final MemoryByteArrayOutputStream out =
+ new MemoryByteArrayOutputStream(INITIAL_BUFFER_SIZE, 256 * INITIAL_BUFFER_SIZE);
+ final DeflaterOutputStream deflateOut = new DeflaterOutputStream(out);
+ final OutputStreamWriter xmlBuffer = new OutputStreamWriter(deflateOut, "UTF-16");
+ // final StringWriter xmlBuffer = new StringWriter
+ // (OfficeDocumentReportTarget.INITIAL_BUFFER_SIZE);
+ final XmlWriter contentXmlWriter = new XmlWriter(xmlBuffer, createTagDescription());
+ contentXmlWriter.copyNamespaces(currentWriter);
+ if (indent)
+ {
+ contentXmlWriter.setAdditionalIndent(currentWriter.getCurrentIndentLevel());
+ contentXmlWriter.setWriteFinalLinebreak(true);
+ }
+ else
+ {
+ contentXmlWriter.setWriteFinalLinebreak(false);
+ }
+ contentXmlWriter.setAlwaysAddNamespace(true);
+ xmlWriters.push(new BufferState(contentXmlWriter, out, stylesCollection));
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException("Unable to create the buffer", ioe);
+ }
+ }
+
+ public BufferState finishBuffering() throws ReportProcessingException
+ {
+ final BufferState state = (BufferState) xmlWriters.pop();
+ try
+ {
+ state.getXmlWriter().close();
+ }
+ catch (IOException e)
+ {
+ LOGGER.error("ReportProcessing failed", e);
+ }
+ return state;
+ }
+
+ public void commit()
+ throws ReportProcessingException
+ {
+ // do not call flush before the report is fully finished. Every flush
+ // causes the Office-Backend to fully ZIP all contents (it acts like a
+ // 'Save' call from the UI) and that's expensive like hell
+ }
+
+ public NamespaceDefinition getNamespaceByUri(final String uri)
+ {
+ return null;
+ }
+
+ protected AttributeList buildAttributeList(final AttributeMap attrs)
+ {
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ final AttributeList attrList = new AttributeList();
+ final String[] namespaces = attrs.getNameSpaces();
+ for (int i = 0; i < namespaces.length; i++)
+ {
+ final String attrNamespace = namespaces[i];
+ if (isFilteredNamespace(attrNamespace))
+ {
+ continue;
+ }
+
+ final Map localAttributes = attrs.getAttributes(attrNamespace);
+ final Iterator entries = localAttributes.entrySet().iterator();
+ while (entries.hasNext())
+ {
+ final Map.Entry entry = (Map.Entry) entries.next();
+ final String key = String.valueOf(entry.getKey());
+ if (OfficeNamespaces.TABLE_NS.equals(attrNamespace) && "name".equals(key))
+ {
+ final String tableName = String.valueOf(entry.getValue());
+ final String saneName = sanitizeName(tableName);
+ attrList.setAttribute(attrNamespace, key,
+ tableNameGenerator.generateName(saneName));
+ }
+ else if (OfficeNamespaces.DRAWING_NS.equals(attrNamespace) && "name".equals(key) && !"equation".equals(elementType))
+ {
+ final String objectName = String.valueOf(entry.getValue());
+ attrList.setAttribute(attrNamespace, key,
+ frameNameGenerator.generateName(objectName));
+ }
+ else
+ {
+ attrList.setAttribute(attrNamespace, key, String.valueOf(entry.getValue()));
+ }
+ }
+ }
+ return attrList;
+ }
+
+ protected String sanitizeName(final String name)
+ {
+ // A table name cannot contain spaces and should only contain
+ // ascii-characters.
+ if (name == null)
+ {
+ return "";
+ }
+ final char[] chars = name.toCharArray();
+ final StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < chars.length; i++)
+ {
+ final char aChar = chars[i];
+ if (Character.isWhitespace(aChar))
+ {
+ buffer.append('_');
+ }
+ else
+ {
+ buffer.append(aChar);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Returns the length in point. This method is f**king slow, it eats half of the processing time. I surely should
+ * replace it with something more efficient later.
+ *
+ * @param text
+ * @return
+ */
+ protected CSSNumericValue parseLength(final String text)
+ {
+ if (styleSheetParserUtil == null)
+ {
+ styleSheetParserUtil = StyleSheetParserUtil.getInstance();
+ }
+
+ final LexicalUnit cssValue = styleSheetParserUtil.parseLexicalStyleValue(
+ text);
+ return CSSValueFactory.createLengthValue(cssValue);
+ }
+
+ protected boolean isRepeatingSection()
+ {
+ return (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER || currentRole == OfficeDocumentReportTarget.ROLE_PAGE_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_VARIABLES);
+
+ }
+
+ protected OfficeStyle deriveStyle(final String styleFamily, final String styleName)
+ throws ReportProcessingException
+ {
+ // autogenerate a style. The style has already been added to the current
+ // auto-collection.
+ final OfficeStyle style = StyleUtilities.deriveStyle(styleFamily, styleName,
+ getStylesCollection(), getGlobalStylesCollection(),
+ getPredefinedStylesCollection(), getAutoStyleNameGenerator());
+ return style;
+ }
+
+ protected void startImageProcessing(final AttributeMap attrs)
+ throws ReportProcessingException
+ {
+ final Object imageData = attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.IMAGE_DATA);
+ final boolean preserveIRI = OfficeToken.TRUE.equals(attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.PRESERVE_IRI));
+
+ // for the first shot, do nothing fancy ..
+ final ImageProducer.OfficeImage image = imageProducer.produceImage(imageData, preserveIRI);
+ if (image != null)
+ {
+ final ImageElementContext imageContext = (ImageElementContext) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "image-context");
+
+ // When scaling, we have to create an image-style.
+ final CSSNumericValue width = image.getWidth(); // always in 100th of a mm
+
+ final CSSNumericValue height = image.getHeight(); // always in 100th of a mm
+
+ LOGGER.debug("Image " + imageData + " Width: " + width + ", Height: " + height);
+ if (width == null || height == null)
+ {
+ return;
+ }
+
+ CSSNumericValue imageAreaWidthVal;
+ CSSNumericValue imageAreaHeightVal;
+ CSSNumericValue posX = CSSNumericValue.createValue(CSSNumericType.CM, 0.0);
+ CSSNumericValue posY = CSSNumericValue.createValue(CSSNumericType.CM, 0.0);
+
+ String styleName = null;
+ if (imageContext != null)
+ {
+ imageAreaWidthVal = computeImageWidth(imageContext);
+ imageAreaHeightVal = computeImageHeight(imageContext);
+
+ if (imageAreaWidthVal == null || imageAreaHeightVal == null)
+ {
+ LOGGER.debug("Image data returned from context is invalid. Maybe this is not an image?");
+ return;
+ }
+ else
+ {
+ // compute the clip-area ..
+ final CSSNumericValue normalizedImageWidth =
+ CSSValueResolverUtility.convertLength(width, imageAreaWidthVal.getType());
+ final CSSNumericValue normalizedImageHeight =
+ CSSValueResolverUtility.convertLength(height, imageAreaHeightVal.getType());
+
+ final String scale = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.SCALE);
+ if (OfficeToken.NONE.equals(scale) && normalizedImageWidth.getValue() > 0 && normalizedImageHeight.getValue() > 0)
+ {
+ final double clipWidth = normalizedImageWidth.getValue() - imageAreaWidthVal.getValue();
+ final double clipHeight = normalizedImageHeight.getValue() - imageAreaHeightVal.getValue();
+ if (clipWidth > 0 && clipHeight > 0)
+ {
+ final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
+ final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append("rect(");
+ buffer.append(clipHeight / 2);
+ buffer.append(imageAreaHeightVal.getType().getType());
+ buffer.append(' ');
+ buffer.append(clipWidth / 2);
+ buffer.append(imageAreaWidthVal.getType().getType());
+ buffer.append(' ');
+ buffer.append(clipHeight / 2);
+ buffer.append(imageAreaHeightVal.getType().getType());
+ buffer.append(' ');
+ buffer.append(clipWidth / 2);
+ buffer.append(imageAreaWidthVal.getType().getType());
+ buffer.append(')');
+ graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
+
+ styleName = imageStyle.getStyleName();
+ getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
+ }
+ else if (clipWidth > 0)
+ {
+ final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
+ final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append("rect(0cm ");
+ buffer.append(clipWidth / 2);
+ buffer.append(imageAreaWidthVal.getType().getType());
+ buffer.append(" 0cm ");
+ buffer.append(clipWidth / 2);
+ buffer.append(imageAreaWidthVal.getType().getType());
+ buffer.append(')');
+ graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
+
+ styleName = imageStyle.getStyleName();
+ getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
+ imageAreaHeightVal = normalizedImageHeight;
+ }
+ else if (clipHeight > 0)
+ {
+ final OfficeStyle imageStyle = deriveStyle(OfficeToken.GRAPHIC, OfficeToken.GRAPHICS);
+ final Element graphProperties = produceFirstChild(imageStyle, OfficeNamespaces.STYLE_NS, OfficeToken.GRAPHIC_PROPERTIES);
+ final StringBuffer buffer = new StringBuffer();
+ buffer.append("rect(");
+ buffer.append(clipHeight / 2);
+ buffer.append(imageAreaHeightVal.getType().getType());
+ buffer.append(" 0cm ");
+ buffer.append(clipHeight / 2);
+ buffer.append(imageAreaHeightVal.getType().getType());
+ buffer.append(" 0cm)");
+ graphProperties.setAttribute(OfficeNamespaces.FO_NS, "clip", buffer.toString());
+
+ styleName = imageStyle.getStyleName();
+ getStylesCollection().getAutomaticStyles().addStyle(imageStyle);
+ imageAreaWidthVal = normalizedImageWidth;
+ }
+ else
+ {
+ imageAreaWidthVal = normalizedImageWidth;
+ imageAreaHeightVal = normalizedImageHeight;
+ }
+ }
+ else if (OfficeToken.ISOTROPIC.equals(scale))
+ {
+ final double[] ret = calcPaintSize(imageAreaWidthVal, imageAreaHeightVal, normalizedImageWidth, normalizedImageHeight);
+
+ posX = CSSNumericValue.createValue(imageAreaWidthVal.getType(), (imageAreaWidthVal.getValue() - ret[0]) * 0.5);
+ posY = CSSNumericValue.createValue(imageAreaHeightVal.getType(), (imageAreaHeightVal.getValue() - ret[1]) * 0.5);
+
+ imageAreaWidthVal = CSSNumericValue.createValue(imageAreaWidthVal.getType(), ret[0]);
+ imageAreaHeightVal = CSSNumericValue.createValue(imageAreaHeightVal.getType(), ret[1]);
+ }
+ }
+ // If we do scale, then we simply use the given image-area-size as valid image size and dont
+ // care about the image itself ..
+ }
+ else
+ {
+ LOGGER.debug("There is no image-context, so we have to rely on the image's natural bounds. " + "This may go awfully wrong.");
+ imageAreaWidthVal = image.getWidth();
+ imageAreaHeightVal = image.getHeight();
+ }
+
+ final AttributeList frameList = new AttributeList();
+ frameList.setAttribute(OfficeNamespaces.DRAWING_NS, "name", imageNames.generateName("Image"));
+ if (styleName != null)
+ {
+ frameList.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, styleName);
+ }
+ frameList.setAttribute(OfficeNamespaces.TEXT_NS, "anchor-type", OfficeToken.PARAGRAPH);
+ frameList.setAttribute(OfficeNamespaces.SVG_NS, "z-index", "0");
+ frameList.setAttribute(OfficeNamespaces.SVG_NS, "x", posX.getValue() + posX.getType().getType());
+ frameList.setAttribute(OfficeNamespaces.SVG_NS, "y", posY.getValue() + posY.getType().getType());
+
+
+ LOGGER.debug("Image " + imageData + " A-Width: " + imageAreaWidthVal + ", A-Height: " + imageAreaHeightVal);
+
+ if (imageAreaWidthVal != null)
+ {
+ frameList.setAttribute(OfficeNamespaces.SVG_NS,
+ "width", imageAreaWidthVal.getValue() + imageAreaWidthVal.getType().getType());
+ }
+
+ if (imageAreaHeightVal != null)
+ {
+ frameList.setAttribute(OfficeNamespaces.SVG_NS,
+ "height", imageAreaHeightVal.getValue() + imageAreaHeightVal.getType().getType());
+ }
+
+
+ final AttributeList imageList = new AttributeList();
+ imageList.setAttribute(OfficeNamespaces.XLINK_NS, "href", image.getEmbeddableLink());
+ imageList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple");
+ imageList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed");
+ imageList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad");
+
+
+ try
+ {
+ getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, "frame", frameList, XmlWriterSupport.OPEN);
+ getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.IMAGE, imageList, XmlWriterSupport.CLOSE);
+ getXmlWriter().writeCloseTag();
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException(FAILED, ioe);
+ }
+ }
+ }
+
+ private CSSNumericValue computeImageWidth(final ImageElementContext imageElementContext)
+ {
+ final LengthCalculator calculator = new LengthCalculator();
+ final String[] strings = imageElementContext.getColStyles();
+ for (int i = 0; i < strings.length; i++)
+ {
+ final String styleName = strings[i];
+ final CSSNumericValue value = computeColumnWidth(styleName);
+ if (value != null)
+ {
+ calculator.add(value);
+ }
+ }
+ return calculator.getResult();
+ }
+
+ private CSSNumericValue computeImageHeight(final ImageElementContext imageElementContext)
+ {
+ final LengthCalculator calculator = new LengthCalculator();
+ final String[] strings = imageElementContext.getRowStyles();
+ for (int i = 0; i < strings.length; i++)
+ {
+ final String styleName = strings[i];
+ final CSSNumericValue value = computeRowHeight(styleName);
+ if (value != null)
+ {
+ calculator.add(value);
+ }
+ }
+ return calculator.getResult();
+ }
+
+ protected CSSNumericValue computeRowHeight(final String rowStyle)
+ {
+ final OfficeStylesCollection contentStyles = getContentStylesCollection();
+ final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
+ if (style != null)
+ {
+ final Element element = style.getTableRowProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+
+ final String styleParent = style.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+
+ final OfficeStylesCollection globalStyles = getGlobalStylesCollection();
+ final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
+ if (globalStyle != null)
+ {
+ final Element element = globalStyle.getTableRowProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+ final String styleParent = globalStyle.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_ROW, rowStyle);
+ if (predefStyle != null)
+ {
+ final Element element = predefStyle.getTableRowProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "row-height");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+ final String styleParent = predefStyle.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+ // not found.
+ return null;
+ }
+
+ protected CSSNumericValue computeColumnWidth(final String colStyle)
+ {
+ final OfficeStylesCollection contentStyles = getContentStylesCollection();
+ final OfficeStyle style = contentStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
+ if (style != null)
+ {
+ final Element element = style.getTableColumnProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+
+ final String styleParent = style.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+
+ final OfficeStylesCollection globalStyles = getGlobalStylesCollection();
+ final OfficeStyle globalStyle = globalStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
+ if (globalStyle != null)
+ {
+ final Element element = globalStyle.getTableColumnProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+ final String styleParent = globalStyle.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final OfficeStyle predefStyle = predefStyles.getStyle(OfficeToken.TABLE_COLUMN, colStyle);
+ if (predefStyle != null)
+ {
+ final Element element = predefStyle.getTableColumnProperties();
+ if (element != null)
+ {
+ final String height = (String) element.getAttribute(OfficeNamespaces.STYLE_NS, "column-width");
+ if (height != null)
+ {
+ return parseLength(height);
+ }
+ }
+ final String styleParent = predefStyle.getStyleParent();
+ if (styleParent != null)
+ {
+ return computeRowHeight(styleParent);
+ }
+ }
+ // not found.
+ return null;
+ }
+
+ protected Element produceFirstChild(final Section style,
+ final String nameSpace,
+ final String type)
+ {
+ Element paragraphProps = style.findFirstChild(nameSpace, type);
+ if (paragraphProps == null)
+ {
+ paragraphProps = new Section();
+ paragraphProps.setNamespace(nameSpace);
+ paragraphProps.setType(type);
+ style.addNode(paragraphProps);
+ }
+ return paragraphProps;
+ }
+
+ protected void startChartProcessing(final AttributeMap attrs)
+ throws ReportProcessingException
+ {
+ final String classId = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "class-id");
+ final String chartUrl = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "href");
+ final ArrayList masterColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_COLUMNS);
+ final ArrayList masterValues = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.MASTER_VALUES);
+ final ArrayList detailColumns = (ArrayList) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, SDBCReportDataFactory.DETAIL_COLUMNS);
+ final String href = oleProducer.produceOle(chartUrl, masterColumns, masterValues, detailColumns);
+
+ final AttributeList oleList = new AttributeList();
+ oleList.setAttribute(OfficeNamespaces.DRAWING_NS, "class-id", classId);
+ oleList.setAttribute(OfficeNamespaces.XLINK_NS, "href", "./" + href);
+ oleList.setAttribute(OfficeNamespaces.XLINK_NS, "type", "simple");
+ oleList.setAttribute(OfficeNamespaces.XLINK_NS, "show", "embed");
+ oleList.setAttribute(OfficeNamespaces.XLINK_NS, "actuate", "onLoad");
+
+ try
+ {
+ getXmlWriter().writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.OBJECT_OLE, oleList, XmlWriterSupport.CLOSE);
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException(FAILED, ioe);
+ }
+ }
+
+ static private double[] calcPaintSize(final CSSNumericValue areaWidth, final CSSNumericValue areaHeight,
+ final CSSNumericValue imageWidth, final CSSNumericValue imageHeight)
+ {
+
+ final double ratioX = areaWidth.getValue() / imageWidth.getValue();
+ final double ratioY = areaHeight.getValue() / imageHeight.getValue();
+ final double ratioMin = Math.min(ratioX, ratioY);
+
+ double[] ret = new double[2];
+ ret[0] = imageWidth.getValue() * ratioMin;
+ ret[1] = imageHeight.getValue() * ratioMin;
+ return ret;
+ }
+
+ protected void writeNullDate() throws IOException
+ {
+ // write NULL DATE
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "calculation-settings", null, XmlWriterSupport.OPEN);
+ final AttributeMap nullDateAttributes = new AttributeMap();
+ nullDateAttributes.setAttribute(OfficeNamespaces.TABLE_NS, "date-value", "1900-01-01");
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, "null-date", buildAttributeList(nullDateAttributes), XmlWriterSupport.CLOSE);
+ xmlWriter.writeCloseTag();
+ }
+}