diff options
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/text')
6 files changed, 2384 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/MasterPageFactory.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/MasterPageFactory.java new file mode 100644 index 000000000000..ff680e14657a --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/MasterPageFactory.java @@ -0,0 +1,417 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +import com.sun.star.report.pentaho.OfficeNamespaces; +import com.sun.star.report.pentaho.model.OfficeMasterPage; +import com.sun.star.report.pentaho.model.OfficeMasterStyles; +import com.sun.star.report.pentaho.model.OfficeStyles; +import com.sun.star.report.pentaho.model.PageLayout; +import com.sun.star.report.pentaho.model.RawText; + +import java.util.HashMap; +import java.util.Map; + +import org.jfree.layouting.input.style.values.CSSNumericValue; +import org.jfree.report.ReportProcessingException; +import org.jfree.report.structure.Element; +import org.jfree.report.structure.Section; +import org.jfree.report.util.AttributeNameGenerator; + + +/** + * Todo: Document me! + * + * @author Thomas Morgner + * @since 14.03.2007 + */ +public class MasterPageFactory +{ + + private static class MasterPageFactoryKey + { + + private final String template; + private final String pageHeader; + private final String pageFooter; + + public MasterPageFactoryKey(final String template, + final String pageHeader, + final String pageFooter) + { + this.template = template; + this.pageHeader = pageHeader; + this.pageFooter = pageFooter; + } + + public boolean equals(final Object o) + { + if (this != o) + { + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final MasterPageFactoryKey that = (MasterPageFactoryKey) o; + + if (pageFooter != null ? !pageFooter.equals( + that.pageFooter) : that.pageFooter != null) + { + return false; + } + if (pageHeader != null ? !pageHeader.equals( + that.pageHeader) : that.pageHeader != null) + { + return false; + } + if (template != null ? !template.equals( + that.template) : that.template != null) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + int result = (template != null ? template.hashCode() : 0); + result = 31 * result + (pageHeader != null ? pageHeader.hashCode() : 0); + result = 31 * result + (pageFooter != null ? pageFooter.hashCode() : 0); + return result; + } + + public String getTemplate() + { + return template; + } + + public String getPageHeader() + { + return pageHeader; + } + + public String getPageFooter() + { + return pageFooter; + } + } + + private static class PageLayoutKey + { + + private final String templateName; + private final CSSNumericValue headerHeight; + private final CSSNumericValue footerHeight; + + public PageLayoutKey(final String templateName, + final CSSNumericValue headerHeight, + final CSSNumericValue footerHeight) + { + this.templateName = templateName; + this.headerHeight = headerHeight; + this.footerHeight = footerHeight; + } + + public String getTemplateName() + { + return templateName; + } + + public CSSNumericValue getHeaderHeight() + { + return headerHeight; + } + + public CSSNumericValue getFooterHeight() + { + return footerHeight; + } + + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final PageLayoutKey key = (PageLayoutKey) o; + + if (footerHeight != null ? !footerHeight.equals( + key.footerHeight) : key.footerHeight != null) + { + return false; + } + if (headerHeight != null ? !headerHeight.equals( + key.headerHeight) : key.headerHeight != null) + { + return false; + } + return !(templateName != null ? !templateName.equals( + key.templateName) : key.templateName != null); + + } + + public int hashCode() + { + int result; + result = (templateName != null ? templateName.hashCode() : 0); + result = 31 * result + (headerHeight != null ? headerHeight.hashCode() : 0); + result = 31 * result + (footerHeight != null ? footerHeight.hashCode() : 0); + return result; + } + } + // todo: Patch the page-layout ... + private static final String DEFAULT_PAGE_NAME = "Default"; + private final OfficeMasterStyles predefinedStyles; + private final AttributeNameGenerator masterPageNameGenerator; + private final Map masterPages; + private final AttributeNameGenerator pageLayoutNameGenerator; + private final Map pageLayouts; + + public MasterPageFactory(final OfficeMasterStyles predefinedStyles) + { + this.predefinedStyles = predefinedStyles; + this.masterPages = new HashMap(); + this.masterPageNameGenerator = new AttributeNameGenerator(); + this.pageLayouts = new HashMap(); + this.pageLayoutNameGenerator = new AttributeNameGenerator(); + } + + public OfficeMasterPage getMasterPage(final String template, + final String pageHeader, + final String pageFooter) + { + final MasterPageFactoryKey key = + new MasterPageFactoryKey(template, pageHeader, pageFooter); + return (OfficeMasterPage) masterPages.get(key); + } + + public boolean containsMasterPage(final String template, + final String pageHeader, + final String pageFooter) + { + final MasterPageFactoryKey key = + new MasterPageFactoryKey(template, pageHeader, pageFooter); + return masterPages.containsKey(key); + } + + public OfficeMasterPage createMasterPage(final String template, + final String pageHeader, + final String pageFooter) + { + final MasterPageFactoryKey key = + new MasterPageFactoryKey(template, pageHeader, pageFooter); + final OfficeMasterPage cached = (OfficeMasterPage) masterPages.get(key); + if (cached != null) + { + return cached; + } + + final String targetName = (masterPages.isEmpty()) ? "Standard" : template; + + OfficeMasterPage predef = predefinedStyles.getMasterPage(template); + if (predef == null) + { + // This is a 'magic' name .. + // todo: It could be that this should be called 'Standard' instead + predef = predefinedStyles.getMasterPage(MasterPageFactory.DEFAULT_PAGE_NAME); + } + + if (predef != null) + { + try + { + // derive + final OfficeMasterPage derived = (OfficeMasterPage) predef.clone(); + return setupMasterPage(derived, targetName, pageHeader, pageFooter, + key); + } + catch (CloneNotSupportedException cne) + { + throw new IllegalStateException("Implementation error: Unable to derive page", cne); + } + } + + final OfficeMasterPage masterPage = new OfficeMasterPage(); + masterPage.setNamespace(OfficeNamespaces.STYLE_NS); + masterPage.setType("master-page"); + return setupMasterPage(masterPage, targetName, pageHeader, pageFooter, key); + } + + private OfficeMasterPage setupMasterPage(final OfficeMasterPage derived, + final String targetName, + final String pageHeader, + final String pageFooter, + final MasterPageFactoryKey key) + { + derived.setStyleName(masterPageNameGenerator.generateName(targetName)); + masterPages.put(key, derived); + + if (pageHeader != null) + { + final Section header = new Section(); + header.setNamespace(OfficeNamespaces.STYLE_NS); + header.setType("header"); + header.addNode(new RawText(pageHeader)); + derived.addNode(header); + } + + if (pageFooter != null) + { + final Section footer = new Section(); + footer.setNamespace(OfficeNamespaces.STYLE_NS); + footer.setType("footer"); + footer.addNode(new RawText(pageFooter)); + derived.addNode(footer); + } + + return derived; + } + + public String createPageStyle(final OfficeStyles commonStyles, + final CSSNumericValue headerHeight, + final CSSNumericValue footerHeight) + { + final PageLayoutKey key = + new PageLayoutKey(null, headerHeight, footerHeight); + final PageLayout derived = new PageLayout(); + final String name = pageLayoutNameGenerator.generateName("autogenerated"); + derived.setStyleName(name); + commonStyles.addPageStyle(derived); + + if (headerHeight != null) + { + final Section headerStyle = new Section(); + headerStyle.setNamespace(OfficeNamespaces.STYLE_NS); + headerStyle.setType("header-style"); + derived.addNode(headerStyle); + MasterPageFactory.applyHeaderFooterHeight(headerStyle, headerHeight); + } + + if (footerHeight != null) + { + final Section footerStyle = new Section(); + footerStyle.setNamespace(OfficeNamespaces.STYLE_NS); + footerStyle.setType("footer-style"); + derived.addNode(footerStyle); + MasterPageFactory.applyHeaderFooterHeight(footerStyle, footerHeight); + } + pageLayouts.put(key, name); + return name; + } + + public String derivePageStyle(final String pageStyleTemplate, + final OfficeStyles predefined, + final OfficeStyles commonStyles, + final CSSNumericValue headerHeight, + final CSSNumericValue footerHeight) + throws ReportProcessingException + { + if (pageStyleTemplate == null) + { + throw new NullPointerException("A style-name must be given"); + } + + final PageLayoutKey key = + new PageLayoutKey(pageStyleTemplate, headerHeight, footerHeight); + final String pageLayoutName = (String) pageLayouts.get(key); + if (pageLayoutName != null) + { + // there's already a suitable version included. + return pageLayoutName; + } + + final PageLayout original = predefined.getPageStyle(pageStyleTemplate); + if (original == null) + { + throw new ReportProcessingException("Invalid page-layout '" + pageStyleTemplate + "', will not continue."); + } + + try + { + final PageLayout derived = (PageLayout) original.clone(); + final String name = pageLayoutNameGenerator.generateName( + pageStyleTemplate); + derived.setStyleName(name); + commonStyles.addPageStyle(derived); + + if (headerHeight != null) + { + Section headerStyle = derived.getHeaderStyle(); + if (headerStyle == null) + { + headerStyle = new Section(); + headerStyle.setNamespace(OfficeNamespaces.STYLE_NS); + headerStyle.setType("header-style"); + derived.addNode(headerStyle); + } + MasterPageFactory.applyHeaderFooterHeight(headerStyle, headerHeight); + } + + if (footerHeight != null) + { + Section footerStyle = derived.getFooterStyle(); + if (footerStyle == null) + { + footerStyle = new Section(); + footerStyle.setNamespace(OfficeNamespaces.STYLE_NS); + footerStyle.setType("footer-style"); + derived.addNode(footerStyle); + } + + MasterPageFactory.applyHeaderFooterHeight(footerStyle, footerHeight); + } + pageLayouts.put(key, name); + return name; + } + catch (CloneNotSupportedException e) + { + throw new IllegalStateException("Clone failed.", e); + } + } + + private static void applyHeaderFooterHeight(final Section headerFooterStyle, + final CSSNumericValue style) + { + Element headerFooterProps = headerFooterStyle.findFirstChild(OfficeNamespaces.STYLE_NS, "header-footer-properties"); + if (headerFooterProps == null) + { + headerFooterProps = new Section(); + headerFooterProps.setNamespace(OfficeNamespaces.STYLE_NS); + headerFooterProps.setType("header-footer-properties"); + headerFooterStyle.addNode(headerFooterProps); + } + headerFooterProps.setAttribute(OfficeNamespaces.SVG_NS, "height", style.getValue() + style.getType().getType()); + } +} diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageBreakDefinition.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageBreakDefinition.java new file mode 100644 index 000000000000..4632d81b8970 --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageBreakDefinition.java @@ -0,0 +1,49 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +/** + * Todo: Document me! + * + * @author Thomas Morgner + * @since 24.03.2007 + */ +public class PageBreakDefinition +{ + + private final boolean resetPageNumber; + + public PageBreakDefinition(final boolean resetPageNumber) + { + this.resetPageNumber = resetPageNumber; + } + + public boolean isResetPageNumber() + { + return resetPageNumber; + } +} diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageContext.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageContext.java new file mode 100644 index 000000000000..8aa0bb09a1f1 --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/PageContext.java @@ -0,0 +1,228 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +import com.sun.star.report.pentaho.styles.LengthCalculator; + +import org.jfree.layouting.input.style.values.CSSNumericValue; + +/** + * Todo: Document me! + * + * @author Thomas Morgner + * @since 24.03.2007 + */ +public class PageContext +{ + + public static final int KEEP_TOGETHER_OFF = 0; + public static final int KEEP_TOGETHER_GROUP = 1; + public static final int KEEP_TOGETHER_FIRST_DETAIL = 2; + private PageContext parent; + private String header; + private CSSNumericValue headerHeight; + private String footer; + private CSSNumericValue footerHeight; + private int keepTogether; + private Integer columnCount = null; + private boolean sectionOpen; + + public PageContext() + { + this(null); + } + + public PageContext(final PageContext parent) + { + this.parent = parent; + if (parent != null) + { + this.keepTogether = parent.getKeepTogether(); + } + } + + public int getActiveColumns() + { + PageContext pc = this; + while (pc != null) + { + // TODO: IS this code correct? Why not columnCount = pc.getColumnCount(); ? + if (columnCount != null) + { + return columnCount; + } + pc = pc.getParent(); + } + return 1; + } + + public void setColumnCount(final Integer columnCount) + { + this.columnCount = columnCount; + } + + public Integer getColumnCount() + { + return columnCount; + } + + public String getHeader() + { + return header; + } + + public void setHeader(final String header, final CSSNumericValue height) + { + this.header = header; + this.headerHeight = height; + } + + public String getFooter() + { + return footer; + } + + public CSSNumericValue getHeaderHeight() + { + return headerHeight; + } + + public CSSNumericValue getFooterHeight() + { + return footerHeight; + } + + public void setFooter(final String footer, final CSSNumericValue height) + { + this.footer = footer; + this.footerHeight = height; + } + + public int getKeepTogether() + { + return keepTogether; + } + + public void setKeepTogether(final int keepTogether) + { + this.keepTogether = keepTogether; + } + + public PageContext getParent() + { + return parent; + } + + public CSSNumericValue getAllFooterSize() + { + if (parent == null) + { + return footerHeight; + } + + final LengthCalculator lnc = new LengthCalculator(); + PageContext pc = this; + while (pc != null) + { + lnc.add(pc.getFooterHeight()); + pc = pc.getParent(); + } + return lnc.getResult(); + } + + public CSSNumericValue getAllHeaderSize() + { + if (parent == null) + { + return headerHeight; + } + + final LengthCalculator lnc = new LengthCalculator(); + PageContext pc = this; + while (pc != null) + { + lnc.add(pc.getHeaderHeight()); + pc = pc.getParent(); + } + return lnc.getResult(); + } + + public String getPageFooterContent() + { + if (parent == null) + { + return getFooter(); + } + + final StringBuffer b = new StringBuffer(); + + PageContext pc = this; + while (pc != null) + { + final String footer_ = pc.getFooter(); + if (footer_ != null) + { + b.append(footer_); + } + pc = pc.getParent(); + } + + if (b.length() != 0) + { + return b.toString(); + } + return null; + } + + public String getPageHeaderContent() + { + if (parent == null) + { + return getHeader(); + } + + final StringBuffer b = new StringBuffer(); + b.append(parent.getPageHeaderContent()); + b.append(getHeader()); + + if (b.length() != 0) + { + return b.toString(); + } + return null; + } + + public boolean isSectionOpen() + { + return sectionOpen; + } + + public void setSectionOpen(final boolean sectionOpen) + { + this.sectionOpen = sectionOpen; + } +} diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportProcessor.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportProcessor.java new file mode 100644 index 000000000000..00d4aff11de9 --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportProcessor.java @@ -0,0 +1,116 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +import com.sun.star.report.DataSourceFactory; +import com.sun.star.report.ImageService; +import com.sun.star.report.InputRepository; +import com.sun.star.report.OutputRepository; +import com.sun.star.report.pentaho.PentahoFormulaContext; + +import org.jfree.report.ReportProcessingException; +import org.jfree.report.data.ReportContextImpl; +import org.jfree.report.flow.ReportContext; +import org.jfree.report.flow.ReportJob; +import org.jfree.report.flow.ReportStructureRoot; +import org.jfree.report.flow.ReportTarget; +import org.jfree.report.flow.SinglePassReportProcessor; + +import org.pentaho.reporting.libraries.resourceloader.ResourceManager; + +/** + * Creation-Date: 03.07.2006, 17:08:25 + * + * @author Thomas Morgner + */ +public class TextRawReportProcessor extends SinglePassReportProcessor +{ + + private final OutputRepository outputRepository; + private final String targetName; + private final InputRepository inputRepository; + private final ImageService imageService; + private final DataSourceFactory dataSourceFactory; + + public TextRawReportProcessor(final InputRepository inputRepository, + final OutputRepository outputRepository, + final String targetName, + final ImageService imageService, + final DataSourceFactory dataSourceFactory) + { + if (inputRepository == null) + { + throw new NullPointerException(); + } + if (outputRepository == null) + { + throw new NullPointerException(); + } + if (targetName == null) + { + throw new NullPointerException(); + } + if (imageService == null) + { + throw new NullPointerException(); + } + if (dataSourceFactory == null) + { + throw new NullPointerException(); + } + + this.targetName = targetName; + this.inputRepository = inputRepository; + this.outputRepository = outputRepository; + this.imageService = imageService; + this.dataSourceFactory = dataSourceFactory; + } + + protected ReportTarget createReportTarget(final ReportJob job) + throws ReportProcessingException + { + final ReportStructureRoot report = job.getReportStructureRoot(); + final ResourceManager resourceManager = report.getResourceManager(); + + return new TextRawReportTarget(job, resourceManager, report.getBaseResource(), + inputRepository, outputRepository, targetName, imageService, dataSourceFactory); + } + + protected ReportContext createReportContext(final ReportJob job, + final ReportTarget target) + { + final ReportContext context = super.createReportContext(job, target); + if (context instanceof ReportContextImpl) + { + final ReportContextImpl impl = (ReportContextImpl) context; + impl.setFormulaContext(new PentahoFormulaContext(impl.getFormulaContext(), job.getConfiguration())); + } + return context; + } +} + + diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java new file mode 100644 index 000000000000..9f3746086b87 --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/TextRawReportTarget.java @@ -0,0 +1,1466 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +import com.sun.star.report.DataSourceFactory; +import com.sun.star.report.ImageService; +import com.sun.star.report.InputRepository; +import com.sun.star.report.OfficeToken; +import com.sun.star.report.OutputRepository; +import com.sun.star.report.pentaho.OfficeNamespaces; +import com.sun.star.report.pentaho.PentahoReportEngineMetaData; +import com.sun.star.report.pentaho.layoutprocessor.FormatValueUtility; +import com.sun.star.report.pentaho.model.OfficeMasterPage; +import com.sun.star.report.pentaho.model.OfficeMasterStyles; +import com.sun.star.report.pentaho.model.OfficeStyle; +import com.sun.star.report.pentaho.model.OfficeStyles; +import com.sun.star.report.pentaho.model.OfficeStylesCollection; +import com.sun.star.report.pentaho.model.PageSection; +import com.sun.star.report.pentaho.output.OfficeDocumentReportTarget; +import com.sun.star.report.pentaho.output.StyleUtilities; +import com.sun.star.report.pentaho.styles.LengthCalculator; + +import java.io.IOException; +import java.io.InputStream; +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.IOUtils; +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(); + + // now copy the meta.xml + if (getInputRepository().isReadable("meta.xml")) + { + final InputStream inputStream = getInputRepository().createInputStream("meta.xml"); + try + { + final OutputStream outputMetaStream = getOutputRepository().createOutputStream("meta.xml", "text/xml"); + IOUtils.getInstance().copyStreams(inputStream, outputMetaStream); + outputMetaStream.close(); + } finally + { + inputStream.close(); + } + } + } + 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; + } +} diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/text/VariablesDeclarations.java b/reportbuilder/java/com/sun/star/report/pentaho/output/text/VariablesDeclarations.java new file mode 100644 index 000000000000..b79d18b56a0b --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/text/VariablesDeclarations.java @@ -0,0 +1,108 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output.text; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.jfree.report.util.AttributeNameGenerator; + + +/** + * A collection that holds all used variables. A variable is primarily keyed by + * its original name. If a variable contains more than one type, it is also + * keyed by the type. + * + * @author Thomas Morgner + * @since 26.03.2007 + */ +public class VariablesDeclarations +{ + + private final AttributeNameGenerator nameGenerator; + private final Map variables; + + public VariablesDeclarations() + { + variables = new HashMap(); + nameGenerator = new AttributeNameGenerator(); + } + + public String produceVariable(final String name, + final String type) + { + HashMap holder = (HashMap) variables.get(name); + if (holder == null) + { + holder = new HashMap(); + variables.put(name, holder); + } + + final String mapping = (String) holder.get(type); + if (mapping != null) + { + return mapping; + } + final String result = nameGenerator.generateName(name); + if (holder.isEmpty()) + { + // create the default mapping as well.. + holder.put(null, name); + holder.put("time", name); + holder.put("date", name); + holder.put("datetime", name); + holder.put("float", name); + holder.put("string", name); + holder.put("boolean", name); + } + holder.put(type, name); + return result; + } + + public Map getDefinedMappings() + { + final HashMap mappings = new HashMap(); + final Iterator vars = variables.values().iterator(); + while (vars.hasNext()) + { + final HashMap types = (HashMap) vars.next(); + final Iterator varsByType = types.entrySet().iterator(); + while (varsByType.hasNext()) + { + final Map.Entry entry = (Map.Entry) varsByType.next(); + final String type = (String) entry.getKey(); + if (type != null) + { + final String varName = (String) entry.getValue(); + mappings.put(varName, type); + } + } + } + return mappings; + } +} |