summaryrefslogtreecommitdiff
path: root/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java
diff options
context:
space:
mode:
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java')
-rw-r--r--reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java1454
1 files changed, 1454 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java
new file mode 100644
index 000000000000..a66f3e8b6c4d
--- /dev/null
+++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java
@@ -0,0 +1,1454 @@
+/*************************************************************************
+ *
+ * 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.text;
+
+
+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.pentaho.OfficeNamespaces;
+import com.sun.star.report.pentaho.PentahoReportEngineMetaData;
+import com.sun.star.report.pentaho.layoutprocessor.FormatValueUtility;
+import com.sun.star.report.pentaho.model.OfficeMasterPage;
+import com.sun.star.report.pentaho.model.OfficeMasterStyles;
+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.model.PageSection;
+import com.sun.star.report.pentaho.output.OfficeDocumentReportTarget;
+import com.sun.star.report.pentaho.output.StyleUtilities;
+import com.sun.star.report.pentaho.styles.LengthCalculator;
+
+import java.io.IOException;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.DataSourceException;
+import org.jfree.report.JFreeReportInfo;
+import org.jfree.report.ReportProcessingException;
+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.pentaho.reporting.libraries.base.util.FastStack;
+import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
+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.XmlWriter;
+import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
+
+
+/**
+ * Creation-Date: 03.07.2006, 16:28:00
+ *
+ * @author Thomas Morgner
+ */
+public class TextRawReportTarget extends OfficeDocumentReportTarget
+{
+
+ private static final String ALWAYS = "always";
+ private static final String KEEP_TOGETHER = "keep-together";
+ private static final String KEEP_WITH_NEXT = "keep-with-next";
+ private static final String MAY_BREAK_BETWEEN_ROWS = "may-break-between-rows";
+ private static final String NAME = "name";
+ private static final String NONE = "none";
+ private static final String NORMAL = "normal";
+ private static final String PARAGRAPH_PROPERTIES = "paragraph-properties";
+ private static final String STANDARD = "Standard";
+ private static final String TABLE_PROPERTIES = "table-properties";
+ private static final String VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT = "variables_paragraph_with_next";
+ private static final String VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT = "variables_paragraph_without_next";
+ private static final int TABLE_LAYOUT_VARIABLES_PARAGRAPH = 0;
+ private static final int TABLE_LAYOUT_SINGLE_DETAIL_TABLE = 2;
+ private static final int CP_SETUP = 0;
+ private static final int CP_FIRST_TABLE = 1;
+ private static final int CP_NEXT_TABLE = 2;
+ // This is the initial state of the detail-band processing. It states, that we are now waiting for a
+ // detail-band to be printed.
+ private static final int DETAIL_SECTION_WAIT = 0;
+ // The first detail section has started.
+ private static final int DETAIL_SECTION_FIRST_STARTED = 1;
+ // The first detail section has been printed.
+ private static final int DETAIL_SECTION_FIRST_PRINTED = 2;
+ // An other detail section has started
+ private static final int DETAIL_SECTION_OTHER_STARTED = 3;
+ // The other detail section has been printed.
+ private static final int DETAIL_SECTION_OTHER_PRINTED = 4;
+ private boolean pageFooterOnReportFooter;
+ private boolean pageFooterOnReportHeader;
+ private boolean pageHeaderOnReportFooter;
+ private boolean pageHeaderOnReportHeader;
+ private int contentProcessingState;
+ private OfficeMasterPage currentMasterPage;
+ private final FastStack activePageContext;
+ private MasterPageFactory masterPageFactory;
+ private LengthCalculator sectionHeight;
+ private String variables;
+ private PageBreakDefinition pageBreakDefinition;
+ private VariablesDeclarations variablesDeclarations;
+ private boolean columnBreakPending;
+ private boolean sectionKeepTogether;
+ private final AttributeNameGenerator sectionNames;
+ private int detailBandProcessingState;
+ private final int tableLayoutConfig;
+ private int expectedTableRowCount;
+ private boolean firstCellSeen;
+
+ public TextRawReportTarget(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, inputRepository, outputRepository, target, imageService, datasourcefactory);
+ activePageContext = new FastStack();
+ this.sectionNames = new AttributeNameGenerator();
+
+ this.tableLayoutConfig = TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
+ }
+
+ protected String getTargetMimeType()
+ {
+ return "application/vnd.oasis.opendocument.text";
+ }
+
+ /**
+ * Checks, whether a manual page break should be inserted at the next possible location.
+ *
+ * @return true, if a pagebreak is pending, false otherwise.
+ */
+ private boolean isPagebreakPending()
+ {
+ return pageBreakDefinition != null;
+ }
+
+ private boolean isResetPageNumber()
+ {
+ return pageBreakDefinition != null && pageBreakDefinition.isResetPageNumber();
+ }
+
+ /**
+ * Defines, whether a manual pagebreak should be inserted at the next possible location.
+ *
+ * @param pageBreakDefinition the new flag value.
+ */
+ private void setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition)
+ {
+ this.pageBreakDefinition = pageBreakDefinition;
+ }
+
+ private PageBreakDefinition getPagebreakDefinition()
+ {
+ return pageBreakDefinition;
+ }
+
+ // todo
+ private boolean isKeepTableWithNext()
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP)
+ {
+ return true;
+ }
+
+ final boolean keepWithNext;
+ keepWithNext = keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL && (detailBandProcessingState == DETAIL_SECTION_WAIT);
+ return keepWithNext;
+ }
+
+ private boolean isSectionPagebreakAfter(final AttributeMap attrs)
+ {
+ final Object forceNewPage =
+ attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
+ return "after-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
+ }
+
+ private boolean isSectionPagebreakBefore(final AttributeMap attrs)
+ {
+ final Object forceNewPage =
+ attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
+ return "before-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
+ }
+
+ private PageContext getCurrentContext()
+ {
+ return (PageContext) activePageContext.peek();
+ }
+
+ private String createMasterPage(final boolean printHeader,
+ final boolean printFooter)
+ throws ReportProcessingException
+ {
+ // create the master page for the report-header.
+ // If there is a page-header or footer in the report that gets
+ // surpressed on the report-header, we have to insert a pagebreak
+ // afterwards.
+
+ final String activePageFooter;
+ // Check, whether the report header can have a page-header
+ final PageContext context = getCurrentContext();
+ if (printFooter)
+ {
+ activePageFooter = context.getPageFooterContent();
+ }
+ else
+ {
+ activePageFooter = null;
+ }
+ final String activePageHeader;
+ if (printHeader)
+ {
+ // we have to insert a manual pagebreak after the report header.
+ activePageHeader = context.getPageHeaderContent();
+ }
+ else
+ {
+ activePageHeader = null;
+ }
+
+ final String masterPageName;
+ if (currentMasterPage == null || !masterPageFactory.containsMasterPage(STANDARD, activePageHeader, activePageFooter))
+ {
+
+ final CSSNumericValue headerSize = context.getAllHeaderSize();
+ final CSSNumericValue footerSize = context.getAllFooterSize();
+
+
+ currentMasterPage = masterPageFactory.createMasterPage(STANDARD, activePageHeader, activePageFooter);
+
+// LOGGER.debug("Created a new master-page: " + currentMasterPage.getStyleName());
+
+ // todo: Store the page-layouts as well.
+ // The page layouts are derived from a common template, but as the
+ // header-heights differ, we have to derive these beasts instead
+ // of copying them
+
+ final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection();
+ final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles();
+ final String pageLayoutTemplate = currentMasterPage.getPageLayout();
+ if (pageLayoutTemplate == null)
+ {
+ // there is no pagelayout. Create one ..
+ final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
+ currentMasterPage.setPageLayout(derivedLayout);
+ }
+ else
+ {
+ final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate,
+ getPredefinedStylesCollection().getAutomaticStyles(),
+ getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
+ currentMasterPage.setPageLayout(derivedLayout);
+ }
+ officeMasterStyles.addMasterPage(currentMasterPage);
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ else
+ {
+ // retrieve the master-page.
+ final OfficeMasterPage masterPage = masterPageFactory.getMasterPage(STANDARD, activePageHeader, activePageFooter);
+ if (ObjectUtilities.equal(masterPage.getStyleName(), currentMasterPage.getStyleName()))
+ {
+ // They are the same,
+ masterPageName = null;
+ }
+ else
+ {
+ // reuse the existing one ..
+ currentMasterPage = masterPage;
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+
+ // if either the pageheader or footer are *not* printed with the
+ // report header, then this implies that we have to insert a manual
+ // pagebreak at the end of the section.
+
+ if ((!printHeader && context.getHeader() != null) || (!printFooter && context.getFooter() != null))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ return masterPageName;
+ }
+
+ private boolean isColumnBreakPending()
+ {
+ return columnBreakPending;
+ }
+
+ private void setColumnBreakPending(final boolean columnBreakPending)
+ {
+ this.columnBreakPending = columnBreakPending;
+ }
+
+ private Integer parseInt(final Object value)
+ {
+ if (value instanceof Number)
+ {
+ final Number n = (Number) value;
+ return IntegerCache.getInteger(n.intValue());
+ }
+ if (value instanceof String)
+ {
+ try
+ {
+ return IntegerCache.getInteger(Integer.parseInt((String) value));
+ }
+ catch (NumberFormatException nfe)
+ {
+ //return null; // ignore
+ }
+ }
+ return null;
+ }
+
+ private BufferState applyColumnsToPageBand(final BufferState contents,
+ final int numberOfColumns)
+ throws IOException, ReportProcessingException
+ {
+ if (numberOfColumns <= 1)
+ {
+ return contents;
+ }
+ startBuffering(getGlobalStylesCollection(), true);
+ // derive section style ..
+
+ // This is a rather cheap solution to the problem. In a sane world, we would have to feed the
+ // footer multiple times. Right now, we simply rely on the balacing, which should make sure that
+ // the column's content are evenly distributed.
+ final XmlWriter writer = getXmlWriter();
+ final AttributeList attrs = new AttributeList();
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(numberOfColumns));
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
+ for (int i = 0; i < numberOfColumns; i++)
+ {
+ writer.writeStream(contents.getXmlAsReader());
+ }
+
+ writer.writeCloseTag();
+ return finishBuffering();
+ }
+
+ private String generateSectionStyle(final int columnCount)
+ {
+ final OfficeStyles automaticStyles = getStylesCollection().getAutomaticStyles();
+ final String styleName = getAutoStyleNameGenerator().generateName("auto_section_style");
+
+ final Section sectionProperties = new Section();
+ sectionProperties.setNamespace(OfficeNamespaces.STYLE_NS);
+ sectionProperties.setType("section-properties");
+ sectionProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
+ sectionProperties.setAttribute(OfficeNamespaces.TEXT_NS, "dont-balance-text-columns", OfficeToken.FALSE);
+ sectionProperties.setAttribute(OfficeNamespaces.STYLE_NS, "editable", OfficeToken.FALSE);
+
+ if (columnCount > 1)
+ {
+ final Section columns = new Section();
+ columns.setNamespace(OfficeNamespaces.STYLE_NS);
+ columns.setType("columns");
+ columns.setAttribute(OfficeNamespaces.FO_NS, "column-count", String.valueOf(columnCount));
+ columns.setAttribute(OfficeNamespaces.STYLE_NS, "column-gap", "0cm");
+ sectionProperties.addNode(columns);
+
+// final Section columnSep = new Section();
+// columnSep.setNamespace(OfficeNamespaces.STYLE_NS);
+// columnSep.setType("column-sep");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "width", "0.035cm");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "color", "#000000");
+// columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "height", "100%");
+// columns.addNode(columnSep);
+
+ for (int i = 0; i < columnCount; i++)
+ {
+ final Section column = new Section();
+ column.setNamespace(OfficeNamespaces.STYLE_NS);
+ column.setType("column");
+ column.setAttribute(OfficeNamespaces.STYLE_NS, "rel-width", "1*");
+ column.setAttribute(OfficeNamespaces.FO_NS, "start-indent", "0cm");
+ column.setAttribute(OfficeNamespaces.FO_NS, "end-indent", "0cm");
+ columns.addNode(column);
+ }
+ }
+
+ final OfficeStyle style = new OfficeStyle();
+ style.setNamespace(OfficeNamespaces.STYLE_NS);
+ style.setType("style");
+ style.setAttribute(OfficeNamespaces.STYLE_NS, NAME, styleName);
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "family", "section");
+ style.addNode(sectionProperties);
+
+ automaticStyles.addStyle(style);
+ return styleName;
+ }
+
+ /**
+ * 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 org.jfree.report.DataSourceException
+ * if there was an error accessing the datasource
+ * @throws org.jfree.report.ReportProcessingException
+ * if some other error occured.
+ */
+ public void startReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ super.startReport(report);
+ variablesDeclarations = new VariablesDeclarations();
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ sectionNames.reset();
+
+ pageFooterOnReportFooter = false;
+ pageFooterOnReportHeader = false;
+ pageHeaderOnReportFooter = false;
+ pageHeaderOnReportHeader = false;
+ contentProcessingState = TextRawReportTarget.CP_SETUP;
+
+ activePageContext.clear();
+ activePageContext.push(new PageContext());
+
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles());
+
+ predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(true));
+ predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(false));
+ }
+
+ private OfficeStyle createVariablesStyle(final boolean keepWithNext)
+ {
+ final OfficeStyle variablesSectionStyle = new OfficeStyle();
+ variablesSectionStyle.setStyleFamily(OfficeToken.PARAGRAPH);
+ if (keepWithNext)
+ {
+ variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ }
+ else
+ {
+ variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT);
+ }
+
+ final Section paragraphProps = new Section();
+ paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ paragraphProps.setType(PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "text-align", "start");
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-align", "top");
+ variablesSectionStyle.addNode(paragraphProps);
+
+ final Section textProps = new Section();
+ textProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ textProps.setType("text-properties");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-variant", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "text-transform", NONE);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "color", "#ffffff");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-outline", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-style", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-mode", "continuous");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-position", "0% 100%");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-name", "Tahoma");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-size", "1pt");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "letter-spacing", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "letter-kerning", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-style", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "text-shadow", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-style", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-mode", "continuous");
+ textProps.setAttribute(OfficeNamespaces.FO_NS, "font-weight", NORMAL);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-rotation-angle", "0");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-emphasize", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-start-char", "");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-end-char", "");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-scale", "100%");
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-relief", NONE);
+ textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-display", NONE);
+ variablesSectionStyle.addNode(textProps);
+ return variablesSectionStyle;
+ }
+
+ protected void startContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "text", null, XmlWriterSupport.OPEN);
+
+ writeNullDate();
+
+ // now start the buffering. We have to insert the variables declaration
+ // later ..
+ startBuffering(getStylesCollection(), true);
+
+ final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
+ final Integer colCount = parseInt(columnCountRaw);
+ if (colCount != null)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setColumnCount(colCount);
+ }
+
+ }
+
+ protected void startOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+
+ if (ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace))
+ {
+ if (ObjectUtilities.equal(OfficeToken.IMAGE, elementType))
+ {
+ startImageProcessing(attrs);
+ }
+ else if (ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType) && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ startChartProcessing(attrs);
+ }
+ return;
+ }
+ else if (isFilteredNamespace(namespace))
+ {
+ throw new IllegalStateException("This element should be hidden: " + namespace + ", " + elementType);
+ }
+
+ if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace) && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
+ {
+ // Skip the columns section if the tables get merged..
+ startBuffering(getStylesCollection(), true);
+ }
+ else
+ {
+ openSection();
+
+ final boolean isTableNS = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
+ if (isTableNS)
+ {
+ if (ObjectUtilities.equal(OfficeToken.TABLE, elementType))
+ {
+ startTable(attrs);
+ return;
+ }
+
+ if (ObjectUtilities.equal(OfficeToken.TABLE_ROW, elementType))
+ {
+ startRow(attrs);
+ return;
+ }
+ }
+
+
+ if (ObjectUtilities.equal(OfficeNamespaces.TEXT_NS, namespace))
+ {
+ if (ObjectUtilities.equal("variable-set", elementType))
+ {
+ // update the variables-declaration thingie ..
+ final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
+ final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
+ final String newVarName = variablesDeclarations.produceVariable(varName, varType);
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
+ }
+ else if (ObjectUtilities.equal("variable-get", elementType))
+ {
+ final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
+ final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
+ final String newVarName = variablesDeclarations.produceVariable(varName, varType);
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
+ // this one must not be written, as the DTD does not declare it.
+ // attrs.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, null);
+ }
+ }
+
+ if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
+ {
+ // This cannot happen as long as the report sections only contain tables. But at some point in the
+ // future they will be made of paragraphs, and then we are prepared ..
+ // LOGGER.debug("Variables-Section in own paragraph " + variables);
+
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+
+ final boolean keepTogetherOnParagraph = true;
+
+ if (keepTogetherOnParagraph)
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (!firstCellSeen && (sectionKeepTogether || keepTogetherState == PageContext.KEEP_TOGETHER_GROUP))
+ {
+ OfficeStyle style = null;
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME);
+ if (styleName == null)
+ {
+ final boolean keep = (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether();
+ final ArrayList propertyNameSpaces = new ArrayList();
+ final ArrayList propertyNames = new ArrayList();
+ final ArrayList propertyValues = new ArrayList();
+
+ propertyNameSpaces.add(OfficeNamespaces.FO_NS);
+ propertyNameSpaces.add(OfficeNamespaces.FO_NS);
+ propertyNames.add(KEEP_TOGETHER);
+ propertyValues.add(ALWAYS);
+ if (keep)
+ {
+ propertyNames.add(KEEP_WITH_NEXT);
+ propertyValues.add(ALWAYS);
+ }
+ else
+ {
+ propertyNames.add(KEEP_WITH_NEXT);
+ propertyValues.add(null);
+ }
+ style = StyleUtilities.queryStyleByProperties(getStylesCollection(), OfficeToken.PARAGRAPH, PARAGRAPH_PROPERTIES, propertyNameSpaces, propertyNames, propertyValues);
+ }
+ if (style == null)
+ {
+ style = deriveStyle(OfficeToken.PARAGRAPH, styleName);
+ // Lets set the 'keep-together' flag..
+
+ Element paragraphProps = style.getParagraphProperties();
+ if (paragraphProps == null)
+ {
+ paragraphProps = new Section();
+ paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
+ paragraphProps.setType(PARAGRAPH_PROPERTIES);
+ style.addNode(paragraphProps);
+ }
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
+
+ // We prevent pagebreaks within the two adjacent rows (this one and the next one) if
+ // either a group-wide keep-together is defined or if we haven't reached the end of the
+ // current section yet.
+ if ((keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether())
+ {
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ }
+ }
+
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ }
+ }
+
+ if (ObjectUtilities.equal(OfficeNamespaces.DRAWING_NS, namespace) && ObjectUtilities.equal(OfficeToken.FRAME, elementType))
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle predefAutoStyle = getPredefinedStylesCollection().getAutomaticStyles().getStyle(OfficeToken.GRAPHIC, styleName);
+ if (predefAutoStyle != null)
+ {
+ // special ole handling
+ final Element graphicProperties = predefAutoStyle.getGraphicProperties();
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", "paragraph-content");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", "paragraph");
+ graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "flow-with-text", "false");
+ graphicProperties.setAttribute(OfficeNamespaces.DRAWING_NS, "ole-draw-aspect", "1");
+
+ // attrs.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, predefAutoStyle.getStyleName());
+ }
+ }
+
+ // process the styles as usual
+ performStyleProcessing(attrs);
+ final XmlWriter xmlWriter = getXmlWriter();
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
+
+ if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH
+ && variables != null
+ && !isRepeatingSection()
+ && ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
+ {
+ //LOGGER.debug("Variables-Section in existing cell " + variables);
+ xmlWriter.writeText(variables);
+ variables = null;
+ }
+ }
+ }
+
+ private void startRow(final AttributeMap attrs)
+ throws IOException, ReportProcessingException
+ {
+ firstCellSeen = false;
+ expectedTableRowCount -= 1;
+ final String rowStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final CSSNumericValue rowHeight = computeRowHeight(rowStyle);
+ // LOGGER.debug("Adding row-Style: " + rowStyle + " " + rowHeight);
+ sectionHeight.add(rowHeight);
+
+// if (expectedTableRowCount > 0)
+// {
+// // Some other row. Create a keep-together
+//
+// }
+// else
+// {
+// // This is the last row before the section will end.
+// // or (in some weird cases) There is no information when the row will end.
+// // Anyway, if we are here, we do not create a keep-together style on the table-row ..
+// }
+ // process the styles as usual
+ performStyleProcessing(attrs);
+
+ final AttributeList attrList = buildAttributeList(attrs);
+ getXmlWriter().writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrList, XmlWriterSupport.OPEN);
+ }
+
+ private void startTable(final AttributeMap attrs)
+ throws ReportProcessingException, IOException
+ {
+ final Integer trc = (Integer) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "table-row-count");
+ if (trc == null)
+ {
+ expectedTableRowCount = -1;
+ }
+ else
+ {
+ expectedTableRowCount = trc;
+ }
+
+ if (isSectionPagebreakBefore(attrs))
+ {
+ // force a pagebreak ..
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ // its a table. This means, it is a root-level element
+ final PageBreakDefinition breakDefinition;
+ String masterPageName = null;
+ final int currentRole = getCurrentRole();
+ if (contentProcessingState == TextRawReportTarget.CP_FIRST_TABLE)
+ {
+ contentProcessingState = TextRawReportTarget.CP_NEXT_TABLE;
+
+ // Processing the report header now.
+ if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_HEADER)
+ {
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ masterPageName = createMasterPage(pageHeaderOnReportHeader, pageFooterOnReportHeader);
+ if (masterPageName == null)
+ {
+ // we should always have a master-page ...
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+ else if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_FOOTER)
+ {
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ masterPageName = createMasterPage(pageHeaderOnReportFooter, pageFooterOnReportFooter);
+ if (masterPageName == null && isSectionPagebreakBefore(attrs))
+ {
+ // If we have a manual pagebreak, then activate the current master-page again.
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ // But we skip this (and therefore the resulting pagebreak) if there is no manual break
+ // and no other condition that would force an break.
+ }
+ else if (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ breakDefinition = null;
+ // no pagebreaks ..
+ }
+ else if (currentMasterPage == null || isPagebreakPending())
+ {
+ // Must be the first table, as we have no master-page yet.
+ masterPageName = createMasterPage(true, true);
+ setPagebreakDefinition(null);
+ if (masterPageName == null)
+ {
+ // we should always have a master-page ...
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ breakDefinition = new PageBreakDefinition(isResetPageNumber());
+ }
+ else
+ {
+ breakDefinition = null;
+ }
+ }
+ else if (isPagebreakPending() && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ // Derive an automatic style for the pagebreak.
+// LOGGER.debug("Manual pagebreak (within the section): " + getCurrentRole());
+ breakDefinition = getPagebreakDefinition();
+ setPagebreakDefinition(null);
+ masterPageName = createMasterPage(true, true);
+ if (masterPageName == null || isSectionPagebreakBefore(attrs))
+ {
+ // If we have a manual pagebreak, then activate the current master-page again.
+ masterPageName = currentMasterPage.getStyleName();
+ }
+ }
+ else
+ {
+ breakDefinition = null;
+ }
+
+ final XmlWriter xmlWriter = getXmlWriter();
+ if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && masterPageName != null)
+ {
+ // close the last table-tag, we will open a new one
+ xmlWriter.writeCloseTag();
+ // Reset the detail-state to 'started' so that the table's columns get printed now.
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+
+ if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
+ {
+ if (masterPageName != null)
+ {
+ // write a paragraph that uses the VARIABLES_HIDDEN_STYLE as
+ // primary style. Derive that one and add the manual pagebreak.
+ // The predefined style already has the 'keep-together' flags set.
+// LOGGER.debug("Variables-Section with new Master-Page " + variables + " " + masterPageName);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ if (breakDefinition.isResetPageNumber())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+ }
+ if (isColumnBreakPending())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
+ setColumnBreakPending(false);
+ }
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
+
+ masterPageName = null;
+ //breakDefinition = null;
+ }
+ else if (isColumnBreakPending())
+ {
+ setColumnBreakPending(false);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
+ }
+ else
+ {
+ // Write a paragraph without adding the pagebreak. We can reuse the global style, but we have to make
+ // sure that the style is part of the current 'auto-style' collection.
+// LOGGER.debug("Variables-Section " + variables);
+
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
+ }
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+
+ final boolean keepWithNext = isKeepTableWithNext();
+ final boolean localKeepTogether = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER));
+ final boolean tableMergeActive = isTableMergeActive();
+ this.sectionKeepTogether = tableMergeActive && localKeepTogether;
+
+ // Check, whether we have a reason to derive a style...
+ if (masterPageName != null || (!tableMergeActive && (localKeepTogether || keepWithNext)) || isColumnBreakPending())
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle style = deriveStyle("table", styleName);
+
+ if (masterPageName != null)
+ {
+// LOGGER.debug("Starting a new MasterPage: " + masterPageName);
+ // Patch the current styles.
+ // This usually only happens on Table-Styles or Paragraph-Styles
+ style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ if (breakDefinition.isResetPageNumber())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
+ }
+ }
+ if (isColumnBreakPending())
+ {
+ final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
+ paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
+ setColumnBreakPending(false);
+ }
+
+ // Inhibit breaks inside the table only if it has been defined and if we do not create one single
+ // big detail section. In that case, this flag would be invalid and would cause layout-errors.
+ if (!tableMergeActive)
+ {
+ if (localKeepTogether)
+ {
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ }
+ }
+ else
+ {
+ if (detailBandProcessingState == DETAIL_SECTION_WAIT)
+ {
+ detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+ }
+ if (keepWithNext)
+ {
+ boolean addKeepWithNext = true;
+ if (currentRole == ROLE_GROUP_FOOTER)
+ {
+ addKeepWithNext = isParentKeepTogether();
+ }
+
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ if (addKeepWithNext)
+ {
+ tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ // no need to copy the styles, this was done while deriving the
+ // style ..
+ }
+ else
+ {
+ // Check, whether we may be able to skip the table.
+ if (tableMergeActive)
+ {
+ if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
+ {
+ // Skip the whole thing ..
+ return;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_WAIT)
+ {
+ if (keepWithNext)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE, styleName);
+ final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
+ // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
+ tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
+ final String hasGroupFooter = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "has-group-footer");
+ if (hasGroupFooter != null && hasGroupFooter.equals(OfficeToken.TRUE))
+ {
+ tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
+ }
+
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
+ }
+ }
+
+ // process the styles as usual
+ performStyleProcessing(attrs);
+ }
+
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
+ }
+
+ private boolean isParentKeepTogether()
+ {
+ PageContext context = getCurrentContext();
+ if (context != null)
+ {
+ context = context.getParent();
+ if (context != null)
+ {
+ return context.getKeepTogether() == PageContext.KEEP_TOGETHER_GROUP;
+ }
+ }
+ return false;
+ }
+
+ private boolean isTableMergeActive()
+ {
+ return getCurrentRole() == ROLE_DETAIL && tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
+ }
+
+ private void openSection()
+ throws IOException
+ {
+ if (isRepeatingSection())
+ {
+ // repeating sections have other ways of defining columns ..
+ return;
+ }
+ if (getCurrentRole() == ROLE_TEMPLATE || getCurrentRole() == ROLE_SPREADSHEET_PAGE_HEADER || getCurrentRole() == ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ // the template section would break the multi-column stuff and we dont open up sections there
+ // anyway ..
+ return;
+ }
+
+ final PageContext pageContext = getCurrentContext();
+ final Integer columnCount = pageContext.getColumnCount();
+ if (columnCount != null && !pageContext.isSectionOpen())
+ {
+ final AttributeList attrs = new AttributeList();
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(columnCount));
+ attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
+ getXmlWriter().writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
+
+ pageContext.setSectionOpen(true);
+ }
+
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ sectionHeight = new LengthCalculator();
+ if (role == OfficeDocumentReportTarget.ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ // Start buffering with an dummy styles-collection, so that the global styles dont get polluted ..
+ startBuffering(new OfficeStylesCollection(), true);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ pageHeaderOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
+ pageHeaderOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ pageFooterOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
+ pageFooterOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ startBuffering(getGlobalStylesCollection(), true);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
+ {
+ startBuffering(getGlobalStylesCollection(), false);
+ }
+ else
+ {
+ contentProcessingState = TextRawReportTarget.CP_FIRST_TABLE;
+ if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
+ {
+ // if we have a repeating header, then skip the first one ..
+ // if this is a repeating footer, skip the last one. This means,
+ // we have to buffer all group footers and wait for the next section..
+ startBuffering(getContentStylesCollection(), true);
+ }
+
+ if (role != OfficeDocumentReportTarget.ROLE_DETAIL)
+ {
+ // reset the detail-state. The flag will be updated on startTable and endOther(Table) if the
+ // current role is ROLE_DETAIL
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ }
+ }
+ }
+
+ protected void startGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ super.startGroup(attrs);
+ final PageContext pageContext = new PageContext(getCurrentContext());
+ activePageContext.push(pageContext);
+
+ final Object resetPageNumber = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "reset-page-number");
+ if (OfficeToken.TRUE.equals(resetPageNumber))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(true));
+ }
+
+ final Object keepTogether = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER);
+ if ("whole-group".equals(keepTogether))
+ {
+ pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_GROUP);
+ }
+ else if ("with-first-detail".equals(keepTogether) && pageContext.getKeepTogether() != PageContext.KEEP_TOGETHER_GROUP)
+ {
+ pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_FIRST_DETAIL);
+ }
+
+ final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
+ final Integer colCount = parseInt(columnCountRaw);
+ if (colCount != null)
+ {
+ pageContext.setColumnCount(colCount);
+ }
+
+ final Object newColumn = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "start-new-column");
+ if (OfficeToken.TRUE.equals(newColumn))
+ {
+ setColumnBreakPending(true);
+ }
+ }
+
+ protected void startGroupInstance(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (getGroupContext().isGroupWithRepeatingSection())
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+ }
+
+ protected void endGroup(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (getGroupContext().isGroupWithRepeatingSection())
+ {
+ setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
+ }
+
+ super.endGroup(attrs);
+ finishSection();
+
+ activePageContext.pop();
+ }
+
+ private void finishSection()
+ throws ReportProcessingException
+ {
+ final PageContext pageContext = getCurrentContext();
+ if (pageContext.isSectionOpen())
+ {
+ pageContext.setSectionOpen(false);
+ try
+ {
+ getXmlWriter().writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException("IOError", e);
+ }
+ }
+ }
+
+ protected void endReportSection(final AttributeMap attrs, final int role)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (role == ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
+ {
+ finishBuffering();
+ return;
+ }
+
+ final CSSNumericValue result = sectionHeight.getResult();
+ if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
+ {
+ final PageContext pageContext = getCurrentContext();
+ pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
+ {
+ if (variables == null)
+ {
+ variables = finishBuffering().getXmlBuffer();
+ }
+ else
+ {
+ variables += finishBuffering().getXmlBuffer();
+ }
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER)
+ {
+ final String headerText = finishBuffering().getXmlBuffer();
+ final int iterationCount = getGroupContext().getParent().getIterationCount();
+ final boolean repeat = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "repeat-section"));
+ if (!repeat || iterationCount > 0)
+ {
+ getXmlWriter().writeText(headerText);
+ }
+ }
+ else if (role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
+ {
+ final String footerText = finishBuffering().getXmlBuffer();
+ // how do we detect whether this is the last group footer?
+ getXmlWriter().writeText(footerText);
+ }
+
+ }
+
+ public void endReport(final ReportStructureRoot report)
+ throws DataSourceException, ReportProcessingException
+ {
+ super.endReport(report);
+ variablesDeclarations = null;
+
+ try
+ {
+ // Write the settings ..
+ final AttributeList rootAttributes = new AttributeList();
+ rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS);
+ rootAttributes.addNamespaceDeclaration("config", OfficeNamespaces.CONFIG);
+ rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS);
+ rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version",
+ OfficeDocumentReportTarget.ODF_VERSION);
+ final OutputStream outputStream = getOutputRepository().createOutputStream("settings.xml", "text/xml");
+ final XmlWriter xmlWriter = new XmlWriter(new OutputStreamWriter(outputStream, "UTF-8"), createTagDescription());
+ xmlWriter.setAlwaysAddNamespace(true);
+ xmlWriter.writeXmlDeclaration("UTF-8");
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-settings", rootAttributes, XmlWriterSupport.OPEN);
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "settings", XmlWriterSupport.OPEN);
+ xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item-set", NAME, "ooo:configuration-settings", XmlWriterSupport.OPEN);
+
+ final AttributeList configAttributes = new AttributeList();
+ configAttributes.setAttribute(OfficeNamespaces.CONFIG, NAME, "TableRowKeep");
+ configAttributes.setAttribute(OfficeNamespaces.CONFIG, "type", "boolean");
+ xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item", configAttributes, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(OfficeToken.TRUE);
+ xmlWriter.writeCloseTag();
+
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ xmlWriter.close();
+
+ copyMeta();
+ }
+ catch (IOException ioe)
+ {
+ throw new ReportProcessingException("Failed to write settings document");
+ }
+ }
+
+ protected void endOther(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+
+ final boolean isInternalNS = ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace);
+ final boolean isTableNs = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
+ if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
+ {
+ finishBuffering();
+ return;
+ }
+
+ if (isInternalNS && (ObjectUtilities.equal(OfficeToken.IMAGE, elementType) || ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType)))
+ {
+ return;
+ }
+
+ final XmlWriter xmlWriter = getXmlWriter();
+ if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) && !isRepeatingSection())
+ {
+ if (variables != null)
+ {
+ // This cannot happen as long as the report sections only contain tables. But at some point in the
+ // future they will be made of paragraphs, and then we are prepared ..
+ //LOGGER.debug("Variables-Section " + variables);
+ final String tag;
+ if (sectionKeepTogether && expectedTableRowCount > 0)
+ {
+ tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT;
+ }
+ else
+ {
+ tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT;
+ }
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ tag, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ tag, XmlWriterSupport.OPEN);
+ xmlWriter.writeText(variables);
+ xmlWriter.writeCloseTag();
+ variables = null;
+ }
+ /**
+ // Only generate the empty paragraph, if we have to add the keep-together ..
+ else if (cellEmpty && expectedTableRowCount > 0 &&
+ sectionKeepTogether && !firstCellSeen)
+ {
+ // we have no variables ..
+ StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
+ getGlobalStylesCollection(), getPredefinedStylesCollection());
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
+ TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.CLOSE);
+ }
+ */
+ }
+
+ if (isTableNs && (ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) || ObjectUtilities.equal(OfficeToken.COVERED_TABLE_CELL, elementType)))
+ {
+ firstCellSeen = true;
+ }
+ if (isTableNs && ObjectUtilities.equal(OfficeToken.TABLE, elementType))
+ {
+ if (getCurrentRole() == ROLE_DETAIL)
+ {
+ if (!isTableMergeActive())
+ {
+ // We do not merge the detail bands, so an ordinary close will do.
+ xmlWriter.writeCloseTag();
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_FIRST_STARTED)
+ {
+ final int keepTogetherState = getCurrentContext().getKeepTogether();
+ if (keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL)
+ {
+ xmlWriter.writeCloseTag();
+ detailBandProcessingState = DETAIL_SECTION_FIRST_PRINTED;
+ }
+ else
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
+ }
+ }
+ else if (detailBandProcessingState == DETAIL_SECTION_OTHER_STARTED)
+ {
+ detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
+ }
+ }
+ else
+ {
+ xmlWriter.writeCloseTag();
+ }
+ if (isSectionPagebreakAfter(attrs))
+ {
+ setPagebreakDefinition(new PageBreakDefinition(false));
+ }
+ }
+ else
+ {
+ xmlWriter.writeCloseTag();
+ }
+ }
+
+ protected void endGroupBody(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ if (tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
+ {
+ // closes the table ..
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ detailBandProcessingState = DETAIL_SECTION_WAIT;
+ }
+
+ }
+
+ protected void endContent(final AttributeMap attrs)
+ throws IOException, DataSourceException, ReportProcessingException
+ {
+ finishSection();
+ final BufferState bodyText = finishBuffering();
+ final XmlWriter writer = getXmlWriter();
+
+ final Map definedMappings = variablesDeclarations.getDefinedMappings();
+ if (!definedMappings.isEmpty())
+ {
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decls", XmlWriterSupport.OPEN);
+ final Iterator mappingsIt = definedMappings.entrySet().iterator();
+ while (mappingsIt.hasNext())
+ {
+ final Map.Entry entry = (Map.Entry) mappingsIt.next();
+ final AttributeList entryList = new AttributeList();
+ entryList.setAttribute(OfficeNamespaces.TEXT_NS, NAME, (String) entry.getKey());
+ entryList.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, (String) entry.getValue());
+ writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decl", entryList, XmlWriterSupport.CLOSE);
+ }
+ writer.writeCloseTag();
+ }
+
+ writer.writeStream(bodyText.getXmlAsReader());
+ writer.setLineEmpty(true);
+ writer.writeCloseTag();
+ }
+
+ public String getExportDescriptor()
+ {
+ return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_TEXT;
+ }
+}