summaryrefslogtreecommitdiff
path: root/reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java
diff options
context:
space:
mode:
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java')
-rw-r--r--reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java969
1 files changed, 969 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java b/reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java
new file mode 100644
index 000000000000..baa43e687ccb
--- /dev/null
+++ b/reportbuilder/java/com/sun/star/report/pentaho/output/spreadsheet/SpreadsheetRawReportTarget.java
@@ -0,0 +1,969 @@
+/*************************************************************************
+ *
+ * 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.spreadsheet;
+
+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.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.output.text.MasterPageFactory;
+import com.sun.star.report.pentaho.styles.LengthCalculator;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jfree.layouting.input.style.values.CSSNumericType;
+import org.jfree.layouting.input.style.values.CSSNumericValue;
+import org.jfree.layouting.util.AttributeMap;
+import org.jfree.report.DataFlags;
+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.IntegerCache;
+
+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.11.2007
+ *
+ * @author Michael D'Amour
+ */
+public class SpreadsheetRawReportTarget extends OfficeDocumentReportTarget
+{
+
+ private static final String[] FOPROPS = new String[]
+ {
+ "letter-spacing", "font-variant", "text-transform"
+ };
+ private static final String NUMBERCOLUMNSSPANNED = "number-columns-spanned";
+ private static final String[] STYLEPROPS = new String[]
+ {
+ "text-combine", "font-pitch-complex", "text-rotation-angle", "font-name", "text-blinking", "letter-kerning", "text-combine-start-char", "text-combine-end-char", "text-position", "text-scale"
+ };
+ private static final int CELL_WIDTH_FACTOR = 10000;
+ private static final String TRANSPARENT = "transparent";
+ private boolean paragraphFound = false;
+ private boolean paragraphHandled = false;
+
+ /**
+ * This class represents a column boundary, not in width, but it's actual boundary location. One of the motivations
+ * for creating this class was to be able to record the boundaries for each incoming table while consuming as few
+ * objects/memory as possible.
+ */
+ private static class ColumnBoundary implements Comparable
+ {
+
+ private final Set tableIndices;
+ private final long boundary;
+
+ private ColumnBoundary(final long boundary)
+ {
+ this.tableIndices = new HashSet();
+ this.boundary = boundary;
+ }
+
+ public void addTableIndex(final int table)
+ {
+ tableIndices.add(IntegerCache.getInteger(table));
+ }
+
+ public float getBoundary()
+ {
+ return boundary;
+ }
+
+ public boolean isContainedByTable(final int table)
+ {
+ final Integer index = IntegerCache.getInteger(table);
+ return tableIndices.contains(index);
+ }
+
+ public int compareTo(final Object arg0)
+ {
+ if (arg0.equals(this))
+ {
+ return 0;
+ }
+ if (arg0 instanceof ColumnBoundary)
+ {
+ if (boundary > ((ColumnBoundary) arg0).boundary)
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ return 1;
+ }
+
+ public boolean equals(final Object obj)
+ {
+ return obj instanceof ColumnBoundary && ((ColumnBoundary) obj).boundary == boundary;
+ }
+
+ public int hashCode()
+ {
+ assert false : "hashCode not designed";
+ return 42; // any arbitrary constant will do
+ }
+ }
+ private String tableBackgroundColor; // null means transparent ...
+ private static final ColumnBoundary[] EMPTY_COLBOUNDS = new ColumnBoundary[0];
+ private boolean elementBoundaryCollectionPass;
+ private boolean oleHandled;
+ private final List columnBoundaryList;
+ private long currentRowBoundaryMarker;
+ private ColumnBoundary[] sortedBoundaryArray;
+ private ColumnBoundary[] boundariesForTableArray;
+ private int tableCounter;
+ private int columnCounter;
+ private int columnSpanCounter;
+ private int currentSpan = 0;
+ private String unitsOfMeasure;
+ final private List shapes;
+ final private List ole;
+ final private List rowHeights;
+
+ public SpreadsheetRawReportTarget(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);
+ columnBoundaryList = new ArrayList();
+ elementBoundaryCollectionPass = true;
+ rowHeights = new ArrayList();
+ shapes = new ArrayList();
+ ole = new ArrayList();
+ oleHandled = false;
+ }
+
+ public void startOther(final AttributeMap attrs) throws DataSourceException, ReportProcessingException
+ {
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.OBJECT_OLE, attrs))
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ ole.add(attrs);
+ }
+ oleHandled = true;
+ return;
+ }
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (isRepeatingSection() || isFilteredNamespace(namespace))
+ {
+ return;
+ }
+
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ if (OfficeNamespaces.TEXT_NS.equals(namespace) && OfficeToken.P.equals(elementType) && !paragraphHandled)
+ {
+ paragraphFound = true;
+ return;
+ }
+
+ if (OfficeNamespaces.DRAWING_NS.equals(namespace) && OfficeToken.FRAME.equals(elementType))
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ final LengthCalculator len = new LengthCalculator();
+ for (int i = 0; i < rowHeights.size(); i++)
+ {
+ len.add((CSSNumericValue) rowHeights.get(i));
+ // val += ((CSSNumericValue)rowHeights.get(i)).getValue();
+ }
+
+ rowHeights.clear();
+ final CSSNumericValue currentRowHeight = len.getResult();
+ rowHeights.add(currentRowHeight);
+ attrs.setAttribute(OfficeNamespaces.DRAWING_NS, "z-index", String.valueOf(shapes.size()));
+ final String y = (String) attrs.getAttribute(OfficeNamespaces.SVG_NS, "y");
+ if (y != null)
+ {
+ len.add(parseLength(y));
+ final CSSNumericValue currentY = len.getResult();
+ attrs.setAttribute(OfficeNamespaces.SVG_NS, "y", currentY.getValue() + currentY.getType().getType());
+ }
+ shapes.add(attrs);
+ }
+ return;
+ }
+ if (oleHandled)
+ {
+ if (isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ ole.add(attrs);
+ }
+ return;
+ }
+
+ // if this is the report namespace, write out a table definition ..
+ if (OfficeNamespaces.TABLE_NS.equals(namespace) && OfficeToken.TABLE.equals(elementType))
+ {
+ // whenever we see a new table, we increment our tableCounter
+ // this is used to keep tracked of the boundary conditions per table
+ tableCounter++;
+ }
+
+ if (isElementBoundaryCollectionPass())
+ {
+ collectBoundaryForElement(attrs);
+ }
+ else
+ // if (!isElementBoundaryCollectionPass())
+ {
+ try
+ {
+ processElement(attrs, namespace, elementType);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+ }
+
+ protected void startReportSection(final AttributeMap attrs, final int role) throws IOException, DataSourceException, ReportProcessingException
+ {
+ if ((role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) && (!PageSection.isPrintWithReportHeader(attrs) || !PageSection.isPrintWithReportFooter(attrs)))
+ {
+ startBuffering(new OfficeStylesCollection(), true);
+ }
+ else
+ {
+ super.startReportSection(attrs, role);
+ }
+ }
+
+ protected void endReportSection(final AttributeMap attrs, final int role) throws IOException, DataSourceException, ReportProcessingException
+ {
+ if ((role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER) && (!PageSection.isPrintWithReportHeader(attrs) || !PageSection.isPrintWithReportFooter(attrs)))
+ {
+ finishBuffering();
+ }
+ else
+ {
+ super.endReportSection(attrs, role);
+ }
+ }
+
+ private void handleParagraph()
+ {
+ if (paragraphFound)
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, null, XmlWriterSupport.OPEN);
+ paragraphHandled = true;
+ paragraphFound = false;
+ }
+ catch (IOException ex)
+ {
+ LOGGER.error("ReportProcessing failed", ex);
+ }
+ }
+ }
+
+ private void processElement(final AttributeMap attrs, final String namespace, final String elementType)
+ throws IOException, ReportProcessingException
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE, attrs))
+ {
+ // a new table means we must clear our "calculated" table boundary array cache
+ boundariesForTableArray = null;
+
+ final String tableStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (tableStyle == null)
+ {
+ tableBackgroundColor = null;
+ }
+ else
+ {
+ final Object raw = StyleUtilities.queryStyle(getPredefinedStylesCollection(), OfficeToken.TABLE, tableStyle,
+ "table-properties", OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (raw == null || TRANSPARENT.equals(raw))
+ {
+ tableBackgroundColor = null;
+ }
+ else
+ {
+ tableBackgroundColor = String.valueOf(raw);
+ }
+ }
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, attrs) || ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, attrs))
+ {
+ return;
+ }
+
+ // covered-table-cell elements may appear in the input from row or column spans. In the event that we hit a
+ // column-span we simply ignore these elements because we are going to adjust the span to fit the uniform table.
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.COVERED_TABLE_CELL, attrs))
+ {
+ if (columnSpanCounter > 0)
+ {
+ columnSpanCounter--;
+ }
+
+ if (columnSpanCounter == 0)
+ {
+ // if we weren't expecting a covered-table-cell, let's use it, it's probably from a row-span
+ columnCounter++;
+ final int span = getColumnSpanForCell(tableCounter, columnCounter, 1);
+ // use the calculated span for the column in the uniform table to create any additional covered-table-cell
+ // elements
+ for (int i = 0; i < span; i++)
+ {
+ xmlWriter.writeTag(namespace, OfficeToken.COVERED_TABLE_CELL, null, XmlWriter.CLOSE);
+ }
+ }
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrs))
+ {
+ // a new row means our column counter gets reset
+ columnCounter = 0;
+ // Lets make sure the color of the table is ok ..
+ if (tableBackgroundColor != null)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE_ROW, styleName);
+ Element tableRowProperties = style.getTableRowProperties();
+ if (tableRowProperties == null)
+ {
+ tableRowProperties = new Section();
+ tableRowProperties.setNamespace(OfficeNamespaces.STYLE_NS);
+ tableRowProperties.setType("table-row-properties");
+ tableRowProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, tableBackgroundColor);
+ style.addNode(tableRowProperties);
+ }
+ else
+ {
+ final Object oldValue = tableRowProperties.getAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (oldValue == null || TRANSPARENT.equals(oldValue))
+ {
+ tableRowProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, tableBackgroundColor);
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ }
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_CELL, attrs))
+ {
+ columnCounter++;
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (styleName != null)
+ {
+ final OfficeStyle cellStyle = getPredefinedStylesCollection().getStyle(OfficeToken.TABLE_CELL, styleName);
+ if (cellStyle != null)
+ {
+ final Section textProperties = (Section) cellStyle.getTextProperties();
+ if (textProperties != null)
+ {
+ for (String i : FOPROPS)
+ {
+ textProperties.setAttribute(OfficeNamespaces.FO_NS, i, null);
+ }
+ textProperties.setAttribute(OfficeNamespaces.TEXT_NS, "display", null);
+ for (String i : STYLEPROPS)
+ {
+ textProperties.setAttribute(OfficeNamespaces.STYLE_NS, i, null);
+ }
+ }
+ final Section props = (Section) cellStyle.getTableCellProperties();
+ if (props != null)
+ {
+ final Object raw = props.getAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR);
+ if (TRANSPARENT.equals(raw))
+ {
+ props.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, null);
+ // cellStyle.removeNode(props);
+ }
+ }
+ }
+ attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, styleName);
+ }
+
+ final String numColSpanStr = (String) attrs.getAttribute(namespace, NUMBERCOLUMNSSPANNED);
+ int initialColumnSpan = columnSpanCounter = 1;
+ if (numColSpanStr != null)
+ {
+ initialColumnSpan = Integer.parseInt(numColSpanStr);
+ columnSpanCounter = initialColumnSpan;
+ }
+ final int span = getColumnSpanForCell(tableCounter, columnCounter, initialColumnSpan);
+ if (initialColumnSpan > 1)
+ {
+ // add the initial column span to our column counter index (subtract 1, since it is counted by default)
+ columnCounter += initialColumnSpan - 1;
+ }
+
+ // if (span < initialColumnSpan)
+ // {
+ // // ColumnBoundary cbs[] = getBoundariesForTable(tableCounter);
+ // // for (int i = 0; i < cbs.length; i++)
+ // // {
+ // // System.out.print(cbs[i].getBoundary() + " ");
+ // // }
+ // // System.out.println();
+ //
+ // LOGGER.error("A cell cannot span less than the declared columns: Declared=" + initialColumnSpan + " Computed="
+ // + span);
+ // }
+
+ // there's no point to create number-columns-spanned attributes if we only span 1 column
+ if (span > 1)
+ {
+ attrs.setAttribute(namespace, NUMBERCOLUMNSSPANNED, "" + span);
+ currentSpan = span;
+ }
+ // we must also generate "covered-table-cell" elements for each column spanned
+ // but we'll do this in the endElement, after we close this OfficeToken.TABLE_CELL
+ }
+
+ // All styles have to be processed or you will loose the paragraph-styles and inline text-styles.
+ // ..
+ performStyleProcessing(attrs);
+
+ final AttributeList attrList = buildAttributeList(attrs);
+ xmlWriter.writeTag(namespace, elementType, attrList, XmlWriter.OPEN);
+ // System.out.println("elementType = " + elementType);
+ }
+
+ private void collectBoundaryForElement(final AttributeMap attrs)
+ {
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, attrs))
+ {
+ // A table row resets the column counter.
+ resetCurrentRowBoundaryMarker();
+ }
+ else if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, attrs))
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ if (styleName == null)
+ {
+ // This should not happen, but if it does, we will ignore that cell.
+ return;
+ }
+
+ final OfficeStyle style = getPredefinedStylesCollection().getStyle(OfficeToken.TABLE_COLUMN, styleName);
+ if (style == null)
+ {
+ // Now this is very bad. It means that there is no style defined with the given name.
+ return;
+ }
+
+ final Element tableColumnProperties = style.getTableColumnProperties();
+ String widthStr = (String) tableColumnProperties.getAttribute("column-width");
+ widthStr = widthStr.substring(0, widthStr.indexOf(getUnitsOfMeasure(widthStr)));
+ final float val = Float.parseFloat(widthStr) * CELL_WIDTH_FACTOR;
+ addColumnWidthToRowBoundaryMarker((long) val);
+ ColumnBoundary currentRowBoundary = new ColumnBoundary(getCurrentRowBoundaryMarker());
+ final List columnBoundaryList_ = getColumnBoundaryList();
+ final int idx = columnBoundaryList_.indexOf(currentRowBoundary);
+ if (idx == -1)
+ {
+ columnBoundaryList_.add(currentRowBoundary);
+ }
+ else
+ {
+ currentRowBoundary = (ColumnBoundary) columnBoundaryList_.get(idx);
+ }
+ currentRowBoundary.addTableIndex(tableCounter);
+ }
+ }
+
+ private String getUnitsOfMeasure(final String str)
+ {
+ if (unitsOfMeasure == null || "".equals(unitsOfMeasure))
+ {
+ if (str == null || "".equals(str))
+ {
+ unitsOfMeasure = "cm";
+ return unitsOfMeasure;
+ }
+
+ // build units of measure, set it
+ int i = str.length() - 1;
+ for (; i >= 0; i--)
+ {
+ final char c = str.charAt(i);
+ if (Character.isDigit(c) || c == '.' || c == ',')
+ {
+ break;
+ }
+ }
+ unitsOfMeasure = str.substring(i + 1);
+ }
+ return unitsOfMeasure;
+ }
+
+ private void createTableShapes() throws ReportProcessingException
+ {
+ if (!shapes.isEmpty())
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ // at this point we need to generate the table-columns section based on our boundary table
+ // <table:shapes>
+ // <draw:frame />
+ // ..
+ // </table:shapes>
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.SHAPES, null, XmlWriterSupport.OPEN);
+
+
+ for (int i = 0; i < shapes.size(); i++)
+ {
+ final AttributeMap attrs = (AttributeMap) shapes.get(i);
+ final AttributeList attrList = buildAttributeList(attrs);
+ attrList.removeAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME);
+ xmlWriter.writeTag(OfficeNamespaces.DRAWING_NS, OfficeToken.FRAME, attrList, XmlWriterSupport.OPEN);
+ startChartProcessing((AttributeMap) ole.get(i));
+
+ xmlWriter.writeCloseTag();
+ }
+ xmlWriter.writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+ }
+
+ private void createTableColumns() throws ReportProcessingException
+ {
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ // at this point we need to generate the table-columns section based on our boundary table
+ // <table-columns>
+ // <table-column style-name="coX"/>
+ // ..
+ // </table-columns>
+ // the first boundary is '0' which is a placeholder so we will ignore it
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMNS, null, XmlWriterSupport.OPEN);
+
+ // blow away current column styles
+ // start processing at i=1 because we added a boundary for "0" which is virtual
+ final ColumnBoundary[] cba = getSortedColumnBoundaryArray();
+ for (int i = 1; i < cba.length; i++)
+ {
+ final ColumnBoundary cb = cba[i];
+ float columnWidth = cb.getBoundary();
+ if (i > 1)
+ {
+ columnWidth -= cba[i - 1].getBoundary();
+ }
+ columnWidth = columnWidth / CELL_WIDTH_FACTOR;
+ final OfficeStyle style = deriveStyle(OfficeToken.TABLE_COLUMN, ("co" + i + "_"));
+ final Section tableColumnProperties = new Section();
+ tableColumnProperties.setType("table-column-properties");
+ tableColumnProperties.setNamespace(style.getNamespace());
+ final String width = String.format("%f", columnWidth);
+ tableColumnProperties.setAttribute(style.getNamespace(),
+ "column-width", width + getUnitsOfMeasure(null));
+ style.addNode(tableColumnProperties);
+
+ final AttributeList myAttrList = new AttributeList();
+ myAttrList.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_COLUMN, myAttrList, XmlWriterSupport.CLOSE);
+ }
+ xmlWriter.writeCloseTag();
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+
+ protected void endOther(final AttributeMap attrs) throws DataSourceException, ReportProcessingException
+ {
+ if (ReportTargetUtil.isElementOfType(JFreeReportInfo.REPORT_NAMESPACE, OfficeToken.OBJECT_OLE, attrs) || oleHandled)
+ {
+ oleHandled = false;
+ return;
+ }
+
+ if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrs) && isElementBoundaryCollectionPass() && getCurrentRole() != ROLE_TEMPLATE)
+ {
+ final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
+ rowHeights.add(computeRowHeight(styleName));
+ }
+
+ if (isRepeatingSection() || isElementBoundaryCollectionPass())
+ {
+ return;
+ }
+
+ final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
+ if (isFilteredNamespace(namespace))
+ {
+ return;
+ }
+ final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
+ if (OfficeNamespaces.DRAWING_NS.equals(namespace) && OfficeToken.FRAME.equals(elementType))
+ {
+ return;
+ }
+
+ // if this is the report namespace, write out a table definition ..
+ if (OfficeNamespaces.TABLE_NS.equals(namespace) && (OfficeToken.TABLE.equals(elementType) || OfficeToken.COVERED_TABLE_CELL.equals(elementType) || OfficeToken.TABLE_COLUMN.equals(elementType) || OfficeToken.TABLE_COLUMNS.equals(elementType)))
+ {
+ return;
+ }
+
+ if (!paragraphHandled && OfficeNamespaces.TEXT_NS.equals(namespace) && OfficeToken.P.equals(elementType))
+ {
+ if (!paragraphHandled)
+ {
+ return;
+ }
+
+ paragraphHandled = false;
+ }
+ try
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ // table-cell elements may have a number-columns-spanned attribute which indicates how many
+ // 'covered-table-cell' elements we need to generate
+ generateCoveredTableCells(attrs);
+ }
+ catch (IOException e)
+ {
+ throw new ReportProcessingException(OfficeDocumentReportTarget.FAILED, e);
+ }
+ }
+
+ private void generateCoveredTableCells(final AttributeMap attrs) throws IOException
+ {
+ if (!ReportTargetUtil.isElementOfType(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_CELL, attrs))
+ {
+ return;
+ }
+
+ // do this after we close the tag
+ final XmlWriter xmlWriter = getXmlWriter();
+ // final Object attribute = attrs.getAttribute(OfficeNamespaces.TABLE_NS,NUMBERCOLUMNSSPANNED);
+ // final int span = TextUtilities.parseInt((String) attribute, 0);
+ final int span = currentSpan;
+ currentSpan = 0;
+ for (int i = 1; i < span; i++)
+ {
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.COVERED_TABLE_CELL, null, XmlWriter.CLOSE);
+ }
+ }
+
+ public String getExportDescriptor()
+ {
+ return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_SPREADSHEET;
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+ public void processText(final String text) throws DataSourceException, ReportProcessingException
+ {
+ if (!(isRepeatingSection() || isElementBoundaryCollectionPass()))
+ {
+ handleParagraph();
+ super.processText(text);
+ }
+ }
+
+ public void processContent(final DataFlags value) throws DataSourceException, ReportProcessingException
+ {
+ if (!(isRepeatingSection() || isElementBoundaryCollectionPass()))
+ {
+ handleParagraph();
+ super.processContent(value);
+ }
+ }
+
+ protected String getStartContent()
+ {
+ return "spreadsheet";
+ }
+
+ protected void startContent(final AttributeMap attrs) throws IOException, DataSourceException,
+ ReportProcessingException
+ {
+ if (!isElementBoundaryCollectionPass())
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, getStartContent(), null, XmlWriterSupport.OPEN);
+
+ writeNullDate();
+
+ final AttributeMap tableAttributes = new AttributeMap();
+ tableAttributes.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.NAMESPACE_ATTRIBUTE, OfficeNamespaces.TABLE_NS);
+ tableAttributes.setAttribute(JFreeReportInfo.REPORT_NAMESPACE, Element.TYPE_ATTRIBUTE, OfficeToken.TABLE);
+ tableAttributes.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, generateInitialTableStyle());
+ tableAttributes.setAttribute(OfficeNamespaces.TABLE_NS, "name", "Report");
+
+ performStyleProcessing(tableAttributes);
+
+ xmlWriter.writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE, buildAttributeList(tableAttributes), XmlWriterSupport.OPEN);
+ createTableShapes();
+ createTableColumns();
+ }
+ }
+
+ private String generateInitialTableStyle() throws ReportProcessingException
+ {
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final OfficeStyles commonStyles = predefStyles.getAutomaticStyles();
+ if (!commonStyles.containsStyle(OfficeToken.TABLE, "Initial_Table"))
+ {
+ final String masterPageName = createMasterPage();
+
+ final OfficeStyle tableStyle = new OfficeStyle();
+ tableStyle.setStyleFamily(OfficeToken.TABLE);
+ tableStyle.setStyleName("Initial_Table");
+ tableStyle.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
+ final Element tableProperties = produceFirstChild(tableStyle, OfficeNamespaces.STYLE_NS, "table-properties");
+ tableProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, TRANSPARENT);
+ commonStyles.addStyle(tableStyle);
+ }
+ return "Initial_Table";
+ }
+
+ private String createMasterPage() throws ReportProcessingException
+ {
+ final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
+ final MasterPageFactory masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles());
+ final OfficeMasterPage masterPage;
+ if (!masterPageFactory.containsMasterPage("Standard", null, null))
+ {
+ masterPage = masterPageFactory.createMasterPage("Standard", null, null);
+
+ final CSSNumericValue zeroLength = CSSNumericValue.createValue(CSSNumericType.CM, 0);
+ final String pageLayoutTemplate = masterPage.getPageLayout();
+ if (pageLayoutTemplate == null)
+ {
+ // there is no pagelayout. Create one ..
+ final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), zeroLength, zeroLength);
+ masterPage.setPageLayout(derivedLayout);
+ }
+ else
+ {
+ final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate,
+ getPredefinedStylesCollection().getAutomaticStyles(),
+ getGlobalStylesCollection().getAutomaticStyles(), zeroLength, zeroLength);
+ masterPage.setPageLayout(derivedLayout);
+ }
+
+ final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection();
+ final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles();
+ officeMasterStyles.addMasterPage(masterPage);
+ }
+ else
+ {
+ masterPage = masterPageFactory.getMasterPage("Standard", null, null);
+ }
+ return masterPage.getStyleName();
+ }
+
+ protected void endContent(final AttributeMap attrs) throws IOException, DataSourceException,
+ ReportProcessingException
+ {
+ // todo
+ if (!isElementBoundaryCollectionPass())
+ {
+ final XmlWriter xmlWriter = getXmlWriter();
+ xmlWriter.writeCloseTag();
+ xmlWriter.writeCloseTag();
+ }
+ }
+
+ public void endReport(final ReportStructureRoot report) throws DataSourceException, ReportProcessingException
+ {
+ super.endReport(report);
+ setElementBoundaryCollectionPass(false);
+ resetTableCounter();
+ columnCounter = 0;
+ copyMeta();
+ }
+
+ private boolean isElementBoundaryCollectionPass()
+ {
+ return elementBoundaryCollectionPass;
+ }
+
+ private void setElementBoundaryCollectionPass(final boolean elementBoundaryCollectionPass)
+ {
+ this.elementBoundaryCollectionPass = elementBoundaryCollectionPass;
+ }
+
+ private ColumnBoundary[] getSortedColumnBoundaryArray()
+ {
+ if (sortedBoundaryArray == null)
+ {
+ getColumnBoundaryList().add(new ColumnBoundary(0));
+ sortedBoundaryArray = (ColumnBoundary[]) getColumnBoundaryList().toArray(new ColumnBoundary[getColumnBoundaryList().size()]);
+ Arrays.sort(sortedBoundaryArray);
+ }
+ return sortedBoundaryArray;
+ }
+
+ private List getColumnBoundaryList()
+ {
+ return columnBoundaryList;
+ }
+
+ private void addColumnWidthToRowBoundaryMarker(final long width)
+ {
+ currentRowBoundaryMarker += width;
+ }
+
+ private long getCurrentRowBoundaryMarker()
+ {
+ return currentRowBoundaryMarker;
+ }
+
+ private void resetTableCounter()
+ {
+ tableCounter = 0;
+ }
+
+ private void resetCurrentRowBoundaryMarker()
+ {
+ currentRowBoundaryMarker = 0;
+ }
+
+ private ColumnBoundary[] getBoundariesForTable(final int table)
+ {
+ if (boundariesForTableArray == null)
+ {
+ final List boundariesForTable = new ArrayList();
+ final List boundaryList = getColumnBoundaryList();
+ for (int i = 0; i < boundaryList.size(); i++)
+ {
+ final ColumnBoundary b = (ColumnBoundary) boundaryList.get(i);
+ if (b.isContainedByTable(table))
+ {
+ boundariesForTable.add(b);
+ }
+ }
+ boundariesForTableArray = (ColumnBoundary[]) boundariesForTable.toArray(new ColumnBoundary[boundariesForTable.size()]);
+ Arrays.sort(boundariesForTableArray);
+ }
+ return boundariesForTableArray;
+ }
+
+ private int getColumnSpanForCell(final int table, final int col, final int initialColumnSpan)
+ {
+ final ColumnBoundary[] globalBoundaries = getSortedColumnBoundaryArray();
+ final ColumnBoundary[] tableBoundaries = getBoundariesForTable(table);
+ // how many column boundaries in the globalBoundaries list fall between the currentRowWidth and the next boundary
+ // for the current row
+
+ float cellBoundary = tableBoundaries[col - 1].getBoundary();
+ float cellWidth = tableBoundaries[col - 1].getBoundary();
+
+ if (col > 1)
+ {
+ cellWidth = cellWidth - tableBoundaries[col - 2].getBoundary();
+ }
+
+ if (initialColumnSpan > 1)
+ {
+ // ok we've got some additional spanning specified on the input
+ final int index = (col - 1) + (initialColumnSpan - 1);
+ cellWidth += tableBoundaries[index].getBoundary() - tableBoundaries[col - 1].getBoundary();
+ cellBoundary = tableBoundaries[index].getBoundary();
+ }
+
+ int beginBoundaryIndex = 0;
+ int endBoundaryIndex = globalBoundaries.length - 1;
+ for (int i = 0; i < globalBoundaries.length; i++)
+ {
+ // find beginning boundary
+ if (globalBoundaries[i].getBoundary() <= cellBoundary - cellWidth)
+ {
+ beginBoundaryIndex = i;
+ }
+ if (globalBoundaries[i].getBoundary() <= cellBoundary)
+ {
+ endBoundaryIndex = i;
+ }
+ }
+ final int span = endBoundaryIndex - beginBoundaryIndex;
+ // span will be zero for the first column, so we adjust it to 1
+ if (span == 0)
+ {
+ return 1;
+ }
+ // System.out.println("table = " + table + " col = " + col + " rowBoundaries.length = " + tableBoundaries.length + "
+ // cellWidth = " + cellWidth + " span = " + span);
+ return span;
+ }
+
+ protected String getTargetMimeType()
+ {
+ return "application/vnd.oasis.opendocument.spreadsheet";
+ }
+}