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