| /************************************************************** |
| * |
| * 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"; |
| } |
| } |