blob: d6b8835215e895ac7678354a747ff2611c24a897 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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";
}
}