1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.report.pentaho.output.text;
24 
25 
26 import com.sun.star.report.DataSourceFactory;
27 import com.sun.star.report.ImageService;
28 import com.sun.star.report.InputRepository;
29 import com.sun.star.report.OfficeToken;
30 import com.sun.star.report.OutputRepository;
31 import com.sun.star.report.pentaho.OfficeNamespaces;
32 import com.sun.star.report.pentaho.PentahoReportEngineMetaData;
33 import com.sun.star.report.pentaho.layoutprocessor.FormatValueUtility;
34 import com.sun.star.report.pentaho.model.OfficeMasterPage;
35 import com.sun.star.report.pentaho.model.OfficeMasterStyles;
36 import com.sun.star.report.pentaho.model.OfficeStyle;
37 import com.sun.star.report.pentaho.model.OfficeStyles;
38 import com.sun.star.report.pentaho.model.OfficeStylesCollection;
39 import com.sun.star.report.pentaho.model.PageSection;
40 import com.sun.star.report.pentaho.output.OfficeDocumentReportTarget;
41 import com.sun.star.report.pentaho.output.StyleUtilities;
42 import com.sun.star.report.pentaho.styles.LengthCalculator;
43 
44 import java.io.IOException;
45 
46 import java.io.OutputStream;
47 import java.io.OutputStreamWriter;
48 
49 import java.util.ArrayList;
50 import java.util.Iterator;
51 import java.util.Map;
52 
53 
54 import org.jfree.layouting.input.style.values.CSSNumericValue;
55 import org.jfree.layouting.util.AttributeMap;
56 import org.jfree.report.DataSourceException;
57 import org.jfree.report.JFreeReportInfo;
58 import org.jfree.report.ReportProcessingException;
59 import org.jfree.report.flow.ReportJob;
60 import org.jfree.report.flow.ReportStructureRoot;
61 import org.jfree.report.flow.ReportTargetUtil;
62 import org.jfree.report.structure.Element;
63 import org.jfree.report.structure.Section;
64 import org.jfree.report.util.AttributeNameGenerator;
65 import org.jfree.report.util.IntegerCache;
66 
67 import org.pentaho.reporting.libraries.base.util.FastStack;
68 import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
69 import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
70 import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
71 import org.pentaho.reporting.libraries.xmlns.common.AttributeList;
72 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter;
73 import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
74 
75 
76 /**
77  * Creation-Date: 03.07.2006, 16:28:00
78  *
79  * @author Thomas Morgner
80  */
81 public class TextRawReportTarget extends OfficeDocumentReportTarget
82 {
83 
84     private static final String ALWAYS = "always";
85     private static final String KEEP_TOGETHER = "keep-together";
86     private static final String KEEP_WITH_NEXT = "keep-with-next";
87     private static final String MAY_BREAK_BETWEEN_ROWS = "may-break-between-rows";
88     private static final String NAME = "name";
89     private static final String NONE = "none";
90     private static final String NORMAL = "normal";
91     private static final String PARAGRAPH_PROPERTIES = "paragraph-properties";
92     private static final String STANDARD = "Standard";
93     private static final String TABLE_PROPERTIES = "table-properties";
94     private static final String VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT = "variables_paragraph_with_next";
95     private static final String VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT = "variables_paragraph_without_next";
96     private static final int TABLE_LAYOUT_VARIABLES_PARAGRAPH = 0;
97     private static final int TABLE_LAYOUT_SINGLE_DETAIL_TABLE = 2;
98     private static final int CP_SETUP = 0;
99     private static final int CP_FIRST_TABLE = 1;
100     private static final int CP_NEXT_TABLE = 2;
101     // This is the initial state of the detail-band processing. It states, that we are now waiting for a
102     // detail-band to be printed.
103     private static final int DETAIL_SECTION_WAIT = 0;
104     // The first detail section has started.
105     private static final int DETAIL_SECTION_FIRST_STARTED = 1;
106     // The first detail section has been printed.
107     private static final int DETAIL_SECTION_FIRST_PRINTED = 2;
108     // An other detail section has started
109     private static final int DETAIL_SECTION_OTHER_STARTED = 3;
110     // The other detail section has been printed.
111     private static final int DETAIL_SECTION_OTHER_PRINTED = 4;
112     private boolean pageFooterOnReportFooter;
113     private boolean pageFooterOnReportHeader;
114     private boolean pageHeaderOnReportFooter;
115     private boolean pageHeaderOnReportHeader;
116     private int contentProcessingState;
117     private OfficeMasterPage currentMasterPage;
118     private final FastStack activePageContext;
119     private MasterPageFactory masterPageFactory;
120     private LengthCalculator sectionHeight;
121     private String variables;
122     private PageBreakDefinition pageBreakDefinition;
123     private VariablesDeclarations variablesDeclarations;
124     private boolean columnBreakPending;
125     private boolean sectionKeepTogether;
126     private final AttributeNameGenerator sectionNames;
127     private int detailBandProcessingState;
128     private final int tableLayoutConfig;
129     private int expectedTableRowCount;
130     private boolean firstCellSeen;
131 
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)132     public TextRawReportTarget(final ReportJob reportJob,
133             final ResourceManager resourceManager,
134             final ResourceKey baseResource,
135             final InputRepository inputRepository,
136             final OutputRepository outputRepository,
137             final String target,
138             final ImageService imageService,
139             final DataSourceFactory datasourcefactory)
140             throws ReportProcessingException
141     {
142         super(reportJob, resourceManager, baseResource, inputRepository, outputRepository, target, imageService, datasourcefactory);
143         activePageContext = new FastStack();
144         this.sectionNames = new AttributeNameGenerator();
145 
146         this.tableLayoutConfig = TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
147     }
148 
getTargetMimeType()149     protected String getTargetMimeType()
150     {
151         return "application/vnd.oasis.opendocument.text";
152     }
153 
154     /**
155      * Checks, whether a manual page break should be inserted at the next possible location.
156      *
157      * @return true, if a pagebreak is pending, false otherwise.
158      */
isPagebreakPending()159     private boolean isPagebreakPending()
160     {
161         return pageBreakDefinition != null;
162     }
163 
isResetPageNumber()164     private boolean isResetPageNumber()
165     {
166         return pageBreakDefinition != null && pageBreakDefinition.isResetPageNumber();
167     }
168 
169     /**
170      * Defines, whether a manual pagebreak should be inserted at the next possible location.
171      *
172      * @param pageBreakDefinition the new flag value.
173      */
setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition)174     private void setPagebreakDefinition(final PageBreakDefinition pageBreakDefinition)
175     {
176         this.pageBreakDefinition = pageBreakDefinition;
177     }
178 
getPagebreakDefinition()179     private PageBreakDefinition getPagebreakDefinition()
180     {
181         return pageBreakDefinition;
182     }
183 
184     // todo
isKeepTableWithNext()185     private boolean isKeepTableWithNext()
186     {
187         final int keepTogetherState = getCurrentContext().getKeepTogether();
188         if (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP)
189         {
190             return true;
191         }
192 
193         final boolean keepWithNext;
194         keepWithNext = keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL && (detailBandProcessingState == DETAIL_SECTION_WAIT);
195         return keepWithNext;
196     }
197 
isSectionPagebreakAfter(final AttributeMap attrs)198     private boolean isSectionPagebreakAfter(final AttributeMap attrs)
199     {
200         final Object forceNewPage =
201                 attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
202         return "after-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
203     }
204 
isSectionPagebreakBefore(final AttributeMap attrs)205     private boolean isSectionPagebreakBefore(final AttributeMap attrs)
206     {
207         final Object forceNewPage =
208                 attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "force-new-page");
209         return "before-section".equals(forceNewPage) || "before-after-section".equals(forceNewPage);
210     }
211 
getCurrentContext()212     private PageContext getCurrentContext()
213     {
214         return (PageContext) activePageContext.peek();
215     }
216 
createMasterPage(final boolean printHeader, final boolean printFooter)217     private String createMasterPage(final boolean printHeader,
218             final boolean printFooter)
219             throws ReportProcessingException
220     {
221         // create the master page for the report-header.
222         // If there is a page-header or footer in the report that gets
223         // suppressed on the report-header, we have to insert a pagebreak
224         // afterwards.
225 
226         final String activePageFooter;
227         // Check, whether the report header can have a page-header
228         final PageContext context = getCurrentContext();
229         if (printFooter)
230         {
231             activePageFooter = context.getPageFooterContent();
232         }
233         else
234         {
235             activePageFooter = null;
236         }
237         final String activePageHeader;
238         if (printHeader)
239         {
240             // we have to insert a manual pagebreak after the report header.
241             activePageHeader = context.getPageHeaderContent();
242         }
243         else
244         {
245             activePageHeader = null;
246         }
247 
248         final String masterPageName;
249         if (currentMasterPage == null || !masterPageFactory.containsMasterPage(STANDARD, activePageHeader, activePageFooter))
250         {
251 
252             final CSSNumericValue headerSize = context.getAllHeaderSize();
253             final CSSNumericValue footerSize = context.getAllFooterSize();
254 
255 
256             currentMasterPage = masterPageFactory.createMasterPage(STANDARD, activePageHeader, activePageFooter);
257 
258 //      LOGGER.debug("Created a new master-page: " + currentMasterPage.getStyleName());
259 
260             // todo: Store the page-layouts as well.
261             // The page layouts are derived from a common template, but as the
262             // header-heights differ, we have to derive these beasts instead
263             // of copying them
264 
265             final OfficeStylesCollection officeStylesCollection = getGlobalStylesCollection();
266             final OfficeMasterStyles officeMasterStyles = officeStylesCollection.getMasterStyles();
267             final String pageLayoutTemplate = currentMasterPage.getPageLayout();
268             if (pageLayoutTemplate == null)
269             {
270                 // there is no pagelayout. Create one ..
271                 final String derivedLayout = masterPageFactory.createPageStyle(getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
272                 currentMasterPage.setPageLayout(derivedLayout);
273             }
274             else
275             {
276                 final String derivedLayout = masterPageFactory.derivePageStyle(pageLayoutTemplate,
277                         getPredefinedStylesCollection().getAutomaticStyles(),
278                         getGlobalStylesCollection().getAutomaticStyles(), headerSize, footerSize);
279                 currentMasterPage.setPageLayout(derivedLayout);
280             }
281             officeMasterStyles.addMasterPage(currentMasterPage);
282             masterPageName = currentMasterPage.getStyleName();
283         }
284         else
285         {
286             // retrieve the master-page.
287             final OfficeMasterPage masterPage = masterPageFactory.getMasterPage(STANDARD, activePageHeader, activePageFooter);
288             if (ObjectUtilities.equal(masterPage.getStyleName(), currentMasterPage.getStyleName()))
289             {
290                 // They are the same,
291                 masterPageName = null;
292             }
293             else
294             {
295                 // reuse the existing one ..
296                 currentMasterPage = masterPage;
297                 masterPageName = currentMasterPage.getStyleName();
298             }
299         }
300 
301         // if either the pageheader or footer are *not* printed with the
302         // report header, then this implies that we have to insert a manual
303         // pagebreak at the end of the section.
304 
305         if ((!printHeader && context.getHeader() != null) || (!printFooter && context.getFooter() != null))
306         {
307             setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
308         }
309 
310         return masterPageName;
311     }
312 
isColumnBreakPending()313     private boolean isColumnBreakPending()
314     {
315         return columnBreakPending;
316     }
317 
setColumnBreakPending(final boolean columnBreakPending)318     private void setColumnBreakPending(final boolean columnBreakPending)
319     {
320         this.columnBreakPending = columnBreakPending;
321     }
322 
parseInt(final Object value)323     private Integer parseInt(final Object value)
324     {
325         if (value instanceof Number)
326         {
327             final Number n = (Number) value;
328             return IntegerCache.getInteger(n.intValue());
329         }
330         if (value instanceof String)
331         {
332             try
333             {
334                 return IntegerCache.getInteger(Integer.parseInt((String) value));
335             }
336             catch (NumberFormatException nfe)
337             {
338                 //return null; // ignore
339             }
340         }
341         return null;
342     }
343 
applyColumnsToPageBand(final BufferState contents, final int numberOfColumns)344     private BufferState applyColumnsToPageBand(final BufferState contents,
345             final int numberOfColumns)
346             throws IOException, ReportProcessingException
347     {
348         if (numberOfColumns <= 1)
349         {
350             return contents;
351         }
352         startBuffering(getGlobalStylesCollection(), true);
353         // derive section style ..
354 
355         // This is a rather cheap solution to the problem. In a sane world, we would have to feed the
356         // footer multiple times. Right now, we simply rely on the balacing, which should make sure that
357         // the column's content are evenly distributed.
358         final XmlWriter writer = getXmlWriter();
359         final AttributeList attrs = new AttributeList();
360         attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(numberOfColumns));
361         attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
362         writer.writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
363         for (int i = 0; i < numberOfColumns; i++)
364         {
365             writer.writeStream(contents.getXmlAsReader());
366         }
367 
368         writer.writeCloseTag();
369         return finishBuffering();
370     }
371 
generateSectionStyle(final int columnCount)372     private String generateSectionStyle(final int columnCount)
373     {
374         final OfficeStyles automaticStyles = getStylesCollection().getAutomaticStyles();
375         final String styleName = getAutoStyleNameGenerator().generateName("auto_section_style");
376 
377         final Section sectionProperties = new Section();
378         sectionProperties.setNamespace(OfficeNamespaces.STYLE_NS);
379         sectionProperties.setType("section-properties");
380         sectionProperties.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
381         sectionProperties.setAttribute(OfficeNamespaces.TEXT_NS, "dont-balance-text-columns", OfficeToken.FALSE);
382         sectionProperties.setAttribute(OfficeNamespaces.STYLE_NS, "editable", OfficeToken.FALSE);
383 
384         if (columnCount > 1)
385         {
386             final Section columns = new Section();
387             columns.setNamespace(OfficeNamespaces.STYLE_NS);
388             columns.setType("columns");
389             columns.setAttribute(OfficeNamespaces.FO_NS, "column-count", String.valueOf(columnCount));
390             columns.setAttribute(OfficeNamespaces.STYLE_NS, "column-gap", "0cm");
391             sectionProperties.addNode(columns);
392 
393 //    final Section columnSep = new Section();
394 //    columnSep.setNamespace(OfficeNamespaces.STYLE_NS);
395 //    columnSep.setType("column-sep");
396 //    columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "width", "0.035cm");
397 //    columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "color", "#000000");
398 //    columnSep.setAttribute(OfficeNamespaces.STYLE_NS, "height", "100%");
399 //    columns.addNode(columnSep);
400 
401             for (int i = 0; i < columnCount; i++)
402             {
403                 final Section column = new Section();
404                 column.setNamespace(OfficeNamespaces.STYLE_NS);
405                 column.setType("column");
406                 column.setAttribute(OfficeNamespaces.STYLE_NS, "rel-width", "1*");
407                 column.setAttribute(OfficeNamespaces.FO_NS, "start-indent", "0cm");
408                 column.setAttribute(OfficeNamespaces.FO_NS, "end-indent", "0cm");
409                 columns.addNode(column);
410             }
411         }
412 
413         final OfficeStyle style = new OfficeStyle();
414         style.setNamespace(OfficeNamespaces.STYLE_NS);
415         style.setType("style");
416         style.setAttribute(OfficeNamespaces.STYLE_NS, NAME, styleName);
417         style.setAttribute(OfficeNamespaces.STYLE_NS, "family", "section");
418         style.addNode(sectionProperties);
419 
420         automaticStyles.addStyle(style);
421         return styleName;
422     }
423 
424     /**
425      * Starts the output of a new office document. This method writes the generic 'office:document-content' tag along with
426      * all known namespace declarations.
427      *
428      * @param report the report object.
429      * @throws org.jfree.report.DataSourceException
430      *          if there was an error accessing the datasource
431      * @throws org.jfree.report.ReportProcessingException
432      *          if some other error occurred.
433      */
startReport(final ReportStructureRoot report)434     public void startReport(final ReportStructureRoot report)
435             throws DataSourceException, ReportProcessingException
436     {
437         super.startReport(report);
438         variablesDeclarations = new VariablesDeclarations();
439         detailBandProcessingState = DETAIL_SECTION_WAIT;
440         sectionNames.reset();
441 
442         pageFooterOnReportFooter = false;
443         pageFooterOnReportHeader = false;
444         pageHeaderOnReportFooter = false;
445         pageHeaderOnReportHeader = false;
446         contentProcessingState = TextRawReportTarget.CP_SETUP;
447 
448         activePageContext.clear();
449         activePageContext.push(new PageContext());
450 
451         final OfficeStylesCollection predefStyles = getPredefinedStylesCollection();
452         masterPageFactory = new MasterPageFactory(predefStyles.getMasterStyles());
453 
454         predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(true));
455         predefStyles.getAutomaticStyles().addStyle(createVariablesStyle(false));
456     }
457 
createVariablesStyle(final boolean keepWithNext)458     private OfficeStyle createVariablesStyle(final boolean keepWithNext)
459     {
460         final OfficeStyle variablesSectionStyle = new OfficeStyle();
461         variablesSectionStyle.setStyleFamily(OfficeToken.PARAGRAPH);
462         if (keepWithNext)
463         {
464             variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
465         }
466         else
467         {
468             variablesSectionStyle.setStyleName(TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT);
469         }
470 
471         final Section paragraphProps = new Section();
472         paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
473         paragraphProps.setType(PARAGRAPH_PROPERTIES);
474         paragraphProps.setAttribute(OfficeNamespaces.FO_NS, OfficeToken.BACKGROUND_COLOR, "transparent");
475         paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "text-align", "start");
476         paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
477         paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
478         paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-align", "top");
479         variablesSectionStyle.addNode(paragraphProps);
480 
481         final Section textProps = new Section();
482         textProps.setNamespace(OfficeNamespaces.STYLE_NS);
483         textProps.setType("text-properties");
484         textProps.setAttribute(OfficeNamespaces.FO_NS, "font-variant", NORMAL);
485         textProps.setAttribute(OfficeNamespaces.FO_NS, "text-transform", NONE);
486         textProps.setAttribute(OfficeNamespaces.FO_NS, "color", "#ffffff");
487         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-outline", OfficeToken.FALSE);
488         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
489         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-style", NONE);
490         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-line-through-mode", "continuous");
491         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-position", "0% 100%");
492         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-name", "Tahoma");
493         textProps.setAttribute(OfficeNamespaces.FO_NS, "font-size", "1pt");
494         textProps.setAttribute(OfficeNamespaces.FO_NS, "letter-spacing", NORMAL);
495         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "letter-kerning", OfficeToken.FALSE);
496         textProps.setAttribute(OfficeNamespaces.FO_NS, "font-style", NORMAL);
497         textProps.setAttribute(OfficeNamespaces.FO_NS, "text-shadow", NONE);
498         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-style", NONE);
499         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-underline-mode", "continuous");
500         textProps.setAttribute(OfficeNamespaces.FO_NS, "font-weight", NORMAL);
501         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-rotation-angle", "0");
502         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-emphasize", NONE);
503         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine", NONE);
504         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-start-char", "");
505         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-combine-end-char", "");
506         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-blinking", OfficeToken.FALSE);
507         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-scale", "100%");
508         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "font-relief", NONE);
509         textProps.setAttribute(OfficeNamespaces.STYLE_NS, "text-display", NONE);
510         variablesSectionStyle.addNode(textProps);
511         return variablesSectionStyle;
512     }
513 
startContent(final AttributeMap attrs)514     protected void startContent(final AttributeMap attrs)
515             throws IOException, DataSourceException, ReportProcessingException
516     {
517         final XmlWriter xmlWriter = getXmlWriter();
518         xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "text", null, XmlWriterSupport.OPEN);
519 
520         writeNullDate();
521 
522         // now start the buffering. We have to insert the variables declaration
523         // later ..
524         startBuffering(getStylesCollection(), true);
525 
526         final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
527         final Integer colCount = parseInt(columnCountRaw);
528         if (colCount != null)
529         {
530             final PageContext pageContext = getCurrentContext();
531             pageContext.setColumnCount(colCount);
532         }
533 
534     }
535 
startOther(final AttributeMap attrs)536     protected void startOther(final AttributeMap attrs)
537             throws IOException, DataSourceException, ReportProcessingException
538     {
539         final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
540         final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
541 
542         if (ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace))
543         {
544             if (ObjectUtilities.equal(OfficeToken.IMAGE, elementType))
545             {
546                 startImageProcessing(attrs);
547             }
548             else if (ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType) && getCurrentRole() != ROLE_TEMPLATE)
549             {
550                 startChartProcessing(attrs);
551             }
552             return;
553         }
554         else if (isFilteredNamespace(namespace))
555         {
556             throw new IllegalStateException("This element should be hidden: " + namespace + ", " + elementType);
557         }
558 
559         if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace) && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
560         {
561             // Skip the columns section if the tables get merged..
562             startBuffering(getStylesCollection(), true);
563         }
564         else
565         {
566             openSection();
567 
568             final boolean isTableNS = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
569             if (isTableNS)
570             {
571                 if (ObjectUtilities.equal(OfficeToken.TABLE, elementType))
572                 {
573                     startTable(attrs);
574                     return;
575                 }
576 
577                 if (ObjectUtilities.equal(OfficeToken.TABLE_ROW, elementType))
578                 {
579                     startRow(attrs);
580                     return;
581                 }
582             }
583 
584 
585             if (ObjectUtilities.equal(OfficeNamespaces.TEXT_NS, namespace))
586             {
587                 if (ObjectUtilities.equal("variable-set", elementType))
588                 {
589                     // update the variables-declaration thingie ..
590                     final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
591                     final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
592                     final String newVarName = variablesDeclarations.produceVariable(varName, varType);
593                     attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
594                 }
595                 else if (ObjectUtilities.equal("variable-get", elementType))
596                 {
597                     final String varName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, NAME);
598                     final String varType = (String) attrs.getAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE);
599                     final String newVarName = variablesDeclarations.produceVariable(varName, varType);
600                     attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, newVarName);
601                     // this one must not be written, as the DTD does not declare it.
602                     // attrs.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, null);
603                 }
604             }
605 
606             if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
607             {
608                 // This cannot happen as long as the report sections only contain tables. But at some point in the
609                 // future they will be made of paragraphs, and then we are prepared ..
610                 // LOGGER.debug("Variables-Section in own paragraph " + variables);
611 
612                 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
613                         TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
614                         getGlobalStylesCollection(), getPredefinedStylesCollection());
615                 final XmlWriter xmlWriter = getXmlWriter();
616                 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
617                         TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
618                 xmlWriter.writeText(variables);
619                 xmlWriter.writeCloseTag();
620                 variables = null;
621             }
622 
623             final boolean keepTogetherOnParagraph = true;
624 
625             if (keepTogetherOnParagraph)
626             {
627                 if (ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
628                 {
629                     final int keepTogetherState = getCurrentContext().getKeepTogether();
630                     if (!firstCellSeen && (sectionKeepTogether || keepTogetherState == PageContext.KEEP_TOGETHER_GROUP))
631                     {
632                         OfficeStyle style = null;
633                         final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME);
634                         if (styleName == null)
635                         {
636                             final boolean keep = (keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether();
637                             final ArrayList propertyNameSpaces = new ArrayList();
638                             final ArrayList propertyNames = new ArrayList();
639                             final ArrayList propertyValues = new ArrayList();
640 
641                             propertyNameSpaces.add(OfficeNamespaces.FO_NS);
642                             propertyNameSpaces.add(OfficeNamespaces.FO_NS);
643                             propertyNames.add(KEEP_TOGETHER);
644                             propertyValues.add(ALWAYS);
645                             if (keep)
646                             {
647                                 propertyNames.add(KEEP_WITH_NEXT);
648                                 propertyValues.add(ALWAYS);
649                             }
650                             else
651                             {
652                                 propertyNames.add(KEEP_WITH_NEXT);
653                                 propertyValues.add(null);
654                             }
655                             style = StyleUtilities.queryStyleByProperties(getStylesCollection(), OfficeToken.PARAGRAPH, PARAGRAPH_PROPERTIES, propertyNameSpaces, propertyNames, propertyValues);
656                         }
657                         if (style == null)
658                         {
659                             style = deriveStyle(OfficeToken.PARAGRAPH, styleName);
660                             // Lets set the 'keep-together' flag..
661 
662                             Element paragraphProps = style.getParagraphProperties();
663                             if (paragraphProps == null)
664                             {
665                                 paragraphProps = new Section();
666                                 paragraphProps.setNamespace(OfficeNamespaces.STYLE_NS);
667                                 paragraphProps.setType(PARAGRAPH_PROPERTIES);
668                                 style.addNode(paragraphProps);
669                             }
670                             paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_TOGETHER, ALWAYS);
671 
672                             // We prevent pagebreaks within the two adjacent rows (this one and the next one) if
673                             // either a group-wide keep-together is defined or if we haven't reached the end of the
674                             // current section yet.
675                             if ((keepTogetherState == PageContext.KEEP_TOGETHER_GROUP || expectedTableRowCount > 0) && isParentKeepTogether())
676                             {
677                                 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
678                             }
679                         }
680 
681                         attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, style.getStyleName());
682                     }
683                 }
684             }
685 
686             if (ObjectUtilities.equal(OfficeNamespaces.DRAWING_NS, namespace) && ObjectUtilities.equal(OfficeToken.FRAME, elementType))
687             {
688                 final String styleName = (String) attrs.getAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME);
689                 final OfficeStyle predefAutoStyle = getPredefinedStylesCollection().getAutomaticStyles().getStyle(OfficeToken.GRAPHIC, styleName);
690                 if (predefAutoStyle != null)
691                 {
692                     // special ole handling
693                     final Element graphicProperties = predefAutoStyle.getGraphicProperties();
694                     graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, VERTICAL_POS, "from-top");
695                     graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, HORIZONTAL_POS, "from-left");
696                     graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "vertical-rel", "paragraph-content");
697                     graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "horizontal-rel", "paragraph");
698                     graphicProperties.setAttribute(OfficeNamespaces.STYLE_NS, "flow-with-text", "false");
699                     graphicProperties.setAttribute(OfficeNamespaces.DRAWING_NS, "ole-draw-aspect", "1");
700 
701                     // attrs.setAttribute(OfficeNamespaces.DRAWING_NS, OfficeToken.STYLE_NAME, predefAutoStyle.getStyleName());
702                 }
703             }
704 
705             // process the styles as usual
706             performStyleProcessing(attrs);
707             final XmlWriter xmlWriter = getXmlWriter();
708             final AttributeList attrList = buildAttributeList(attrs);
709             xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
710 
711             if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH
712                     && variables != null
713                     && !isRepeatingSection()
714                     && ReportTargetUtil.isElementOfType(OfficeNamespaces.TEXT_NS, OfficeToken.P, attrs))
715             {
716                 //LOGGER.debug("Variables-Section in existing cell " + variables);
717                 xmlWriter.writeText(variables);
718                 variables = null;
719             }
720         }
721     }
722 
startRow(final AttributeMap attrs)723     private void startRow(final AttributeMap attrs)
724             throws IOException, ReportProcessingException
725     {
726         firstCellSeen = false;
727         expectedTableRowCount -= 1;
728         final String rowStyle = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
729         final CSSNumericValue rowHeight = computeRowHeight(rowStyle);
730         // LOGGER.debug("Adding row-Style: " + rowStyle + " " + rowHeight);
731         sectionHeight.add(rowHeight);
732 
733 //    if (expectedTableRowCount > 0)
734 //    {
735 //      // Some other row. Create a keep-together
736 //
737 //    }
738 //    else
739 //    {
740 //      // This is the last row before the section will end.
741 //      // or (in some weird cases) There is no information when the row will end.
742 //      // Anyway, if we are here, we do not create a keep-together style on the table-row ..
743 //    }
744         // process the styles as usual
745         performStyleProcessing(attrs);
746 
747         final AttributeList attrList = buildAttributeList(attrs);
748         getXmlWriter().writeTag(OfficeNamespaces.TABLE_NS, OfficeToken.TABLE_ROW, attrList, XmlWriterSupport.OPEN);
749     }
750 
startTable(final AttributeMap attrs)751     private void startTable(final AttributeMap attrs)
752             throws ReportProcessingException, IOException
753     {
754         final Integer trc = (Integer) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "table-row-count");
755         if (trc == null)
756         {
757             expectedTableRowCount = -1;
758         }
759         else
760         {
761             expectedTableRowCount = trc;
762         }
763 
764         if (isSectionPagebreakBefore(attrs))
765         {
766             // force a pagebreak ..
767             setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
768         }
769 
770         // its a table. This means, it is a root-level element
771         final PageBreakDefinition breakDefinition;
772         String masterPageName = null;
773         final int currentRole = getCurrentRole();
774         if (contentProcessingState == TextRawReportTarget.CP_FIRST_TABLE)
775         {
776             contentProcessingState = TextRawReportTarget.CP_NEXT_TABLE;
777 
778             // Processing the report header now.
779             if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_HEADER)
780             {
781                 breakDefinition = new PageBreakDefinition(isResetPageNumber());
782                 masterPageName = createMasterPage(pageHeaderOnReportHeader, pageFooterOnReportHeader);
783                 if (masterPageName == null)
784                 {
785                     // we should always have a master-page ...
786                     masterPageName = currentMasterPage.getStyleName();
787                 }
788             }
789             else if (currentRole == OfficeDocumentReportTarget.ROLE_REPORT_FOOTER)
790             {
791                 breakDefinition = new PageBreakDefinition(isResetPageNumber());
792                 masterPageName = createMasterPage(pageHeaderOnReportFooter, pageFooterOnReportFooter);
793                 if (masterPageName == null && isSectionPagebreakBefore(attrs))
794                 {
795                     // If we have a manual pagebreak, then activate the current master-page again.
796                     masterPageName = currentMasterPage.getStyleName();
797                 }
798                 // But we skip this (and therefore the resulting pagebreak) if there is no manual break
799                 // and no other condition that would force an break.
800             }
801             else if (currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || currentRole == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
802             {
803                 breakDefinition = null;
804                 // no pagebreaks ..
805             }
806             else if (currentMasterPage == null || isPagebreakPending())
807             {
808                 // Must be the first table, as we have no master-page yet.
809                 masterPageName = createMasterPage(true, true);
810                 setPagebreakDefinition(null);
811                 if (masterPageName == null)
812                 {
813                     // we should always have a master-page ...
814                     masterPageName = currentMasterPage.getStyleName();
815                 }
816                 breakDefinition = new PageBreakDefinition(isResetPageNumber());
817             }
818             else
819             {
820                 breakDefinition = null;
821             }
822         }
823         else if (isPagebreakPending() && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER && currentRole != OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
824         {
825             // Derive an automatic style for the pagebreak.
826 //      LOGGER.debug("Manual pagebreak (within the section): " + getCurrentRole());
827             breakDefinition = getPagebreakDefinition();
828             setPagebreakDefinition(null);
829             masterPageName = createMasterPage(true, true);
830             if (masterPageName == null || isSectionPagebreakBefore(attrs))
831             {
832                 // If we have a manual pagebreak, then activate the current master-page again.
833                 masterPageName = currentMasterPage.getStyleName();
834             }
835         }
836         else
837         {
838             breakDefinition = null;
839         }
840 
841         final XmlWriter xmlWriter = getXmlWriter();
842         if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && masterPageName != null)
843         {
844             // close the last table-tag, we will open a new one
845             xmlWriter.writeCloseTag();
846             // Reset the detail-state to 'started' so that the table's columns get printed now.
847             detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
848         }
849 
850         if (tableLayoutConfig == TABLE_LAYOUT_VARIABLES_PARAGRAPH && variables != null)
851         {
852             if (masterPageName != null)
853             {
854                 // write a paragraph that uses the VARIABLES_HIDDEN_STYLE as
855                 // primary style. Derive that one and add the manual pagebreak.
856                 // The predefined style already has the 'keep-together' flags set.
857 //        LOGGER.debug("Variables-Section with new Master-Page " + variables + " " + masterPageName);
858 
859                 final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
860                 style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
861                 if (breakDefinition.isResetPageNumber())
862                 {
863                     final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
864                     paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
865                 }
866                 if (isColumnBreakPending())
867                 {
868                     final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
869                     paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
870                     setColumnBreakPending(false);
871                 }
872                 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
873 
874                 masterPageName = null;
875                 //breakDefinition = null;
876             }
877             else if (isColumnBreakPending())
878             {
879                 setColumnBreakPending(false);
880 
881                 final OfficeStyle style = deriveStyle(OfficeToken.PARAGRAPH, TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT);
882                 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
883                 paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
884 
885                 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME, style.getStyleName(), XmlWriterSupport.OPEN);
886             }
887             else
888             {
889                 // Write a paragraph without adding the pagebreak. We can reuse the global style, but we have to make
890                 // sure that the style is part of the current 'auto-style' collection.
891 //        LOGGER.debug("Variables-Section " + variables);
892 
893                 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
894                         TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
895                         getGlobalStylesCollection(), getPredefinedStylesCollection());
896                 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
897                         TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.OPEN);
898             }
899             xmlWriter.writeText(variables);
900             xmlWriter.writeCloseTag();
901             variables = null;
902         }
903 
904         final boolean keepWithNext = isKeepTableWithNext();
905         final boolean localKeepTogether = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER));
906         final boolean tableMergeActive = isTableMergeActive();
907         this.sectionKeepTogether = tableMergeActive && localKeepTogether;
908 
909         // Check, whether we have a reason to derive a style...
910         if (masterPageName != null || (!tableMergeActive && (localKeepTogether || keepWithNext)) || isColumnBreakPending())
911         {
912             final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
913             final OfficeStyle style = deriveStyle("table", styleName);
914 
915             if (masterPageName != null)
916             {
917 //        LOGGER.debug("Starting a new MasterPage: " + masterPageName);
918                 // Patch the current styles.
919                 // This usually only happens on Table-Styles or Paragraph-Styles
920                 style.setAttribute(OfficeNamespaces.STYLE_NS, "master-page-name", masterPageName);
921                 if (breakDefinition.isResetPageNumber())
922                 {
923                     final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
924                     paragraphProps.setAttribute(OfficeNamespaces.STYLE_NS, "page-number", "1");
925                 }
926             }
927             if (isColumnBreakPending())
928             {
929                 final Element paragraphProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, PARAGRAPH_PROPERTIES);
930                 paragraphProps.setAttribute(OfficeNamespaces.FO_NS, "break-before", "column");
931                 setColumnBreakPending(false);
932             }
933 
934             // Inhibit breaks inside the table only if it has been defined and if we do not create one single
935             // big detail section. In that case, this flag would be invalid and would cause layout-errors.
936             if (!tableMergeActive)
937             {
938                 if (localKeepTogether)
939                 {
940                     final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
941                     tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
942                 }
943             }
944             else
945             {
946                 if (detailBandProcessingState == DETAIL_SECTION_WAIT)
947                 {
948                     detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
949                 }
950                 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
951                 {
952                     detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
953                 }
954             }
955             if (keepWithNext)
956             {
957                 boolean addKeepWithNext = true;
958                 if (currentRole == ROLE_GROUP_FOOTER)
959                 {
960                     addKeepWithNext = isParentKeepTogether();
961                 }
962 
963                 final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
964                 tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
965                 if (addKeepWithNext)
966                 {
967                     tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
968                     // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
969                 }
970             }
971             attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
972             // no need to copy the styles, this was done while deriving the
973             // style ..
974         }
975         else
976         {
977             // Check, whether we may be able to skip the table.
978             if (tableMergeActive)
979             {
980                 if (detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
981                 {
982                     // Skip the whole thing ..
983                     return;
984                 }
985                 else if (detailBandProcessingState == DETAIL_SECTION_WAIT)
986                 {
987                     if (keepWithNext)
988                     {
989                         final String styleName = (String) attrs.getAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME);
990 
991                         final OfficeStyle style = deriveStyle(OfficeToken.TABLE, styleName);
992                         final Element tableProps = produceFirstChild(style, OfficeNamespaces.STYLE_NS, TABLE_PROPERTIES);
993                         // A keep-with-next does not work, if the may-break-betweek rows is not set to false ..
994                         tableProps.setAttribute(OfficeNamespaces.STYLE_NS, MAY_BREAK_BETWEEN_ROWS, OfficeToken.FALSE);
995                         final String hasGroupFooter = (String) attrs.getAttribute(JFreeReportInfo.REPORT_NAMESPACE, "has-group-footer");
996                         if (hasGroupFooter != null && hasGroupFooter.equals(OfficeToken.TRUE))
997                         {
998                             tableProps.setAttribute(OfficeNamespaces.FO_NS, KEEP_WITH_NEXT, ALWAYS);
999                         }
1000 
1001                         attrs.setAttribute(OfficeNamespaces.TABLE_NS, OfficeToken.STYLE_NAME, style.getStyleName());
1002                     }
1003                     detailBandProcessingState = DETAIL_SECTION_FIRST_STARTED;
1004                 }
1005                 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_PRINTED)
1006                 {
1007                     detailBandProcessingState = DETAIL_SECTION_OTHER_STARTED;
1008                 }
1009             }
1010 
1011             // process the styles as usual
1012             performStyleProcessing(attrs);
1013         }
1014 
1015         final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
1016         final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
1017         final AttributeList attrList = buildAttributeList(attrs);
1018         xmlWriter.writeTag(namespace, elementType, attrList, XmlWriterSupport.OPEN);
1019     }
1020 
isParentKeepTogether()1021     private boolean isParentKeepTogether()
1022     {
1023         PageContext context = getCurrentContext();
1024         if (context != null)
1025         {
1026             context = context.getParent();
1027             if (context != null)
1028             {
1029                 return context.getKeepTogether() == PageContext.KEEP_TOGETHER_GROUP;
1030             }
1031         }
1032         return false;
1033     }
1034 
isTableMergeActive()1035     private boolean isTableMergeActive()
1036     {
1037         return getCurrentRole() == ROLE_DETAIL && tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE;
1038     }
1039 
openSection()1040     private void openSection()
1041             throws IOException
1042     {
1043         if (isRepeatingSection())
1044         {
1045             // repeating sections have other ways of defining columns ..
1046             return;
1047         }
1048         if (getCurrentRole() == ROLE_TEMPLATE || getCurrentRole() == ROLE_SPREADSHEET_PAGE_HEADER || getCurrentRole() == ROLE_SPREADSHEET_PAGE_FOOTER)
1049         {
1050             // the template section would break the multi-column stuff and we dont open up sections there
1051             // anyway ..
1052             return;
1053         }
1054 
1055         final PageContext pageContext = getCurrentContext();
1056         final Integer columnCount = pageContext.getColumnCount();
1057         if (columnCount != null && !pageContext.isSectionOpen())
1058         {
1059             final AttributeList attrs = new AttributeList();
1060             attrs.setAttribute(OfficeNamespaces.TEXT_NS, OfficeToken.STYLE_NAME, generateSectionStyle(columnCount));
1061             attrs.setAttribute(OfficeNamespaces.TEXT_NS, NAME, sectionNames.generateName("Section"));
1062             getXmlWriter().writeTag(OfficeNamespaces.TEXT_NS, "section", attrs, XmlWriterSupport.OPEN);
1063 
1064             pageContext.setSectionOpen(true);
1065         }
1066 
1067     }
1068 
startReportSection(final AttributeMap attrs, final int role)1069     protected void startReportSection(final AttributeMap attrs, final int role)
1070             throws IOException, DataSourceException, ReportProcessingException
1071     {
1072         sectionHeight = new LengthCalculator();
1073         if (role == OfficeDocumentReportTarget.ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
1074         {
1075             // Start buffering with an dummy styles-collection, so that the global styles dont get polluted ..
1076             startBuffering(new OfficeStylesCollection(), true);
1077         }
1078         else if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
1079         {
1080             startBuffering(getGlobalStylesCollection(), true);
1081             pageHeaderOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
1082             pageHeaderOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
1083         }
1084         else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
1085         {
1086             startBuffering(getGlobalStylesCollection(), true);
1087             pageFooterOnReportHeader = PageSection.isPrintWithReportHeader(attrs);
1088             pageFooterOnReportFooter = PageSection.isPrintWithReportFooter(attrs);
1089         }
1090         else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
1091         {
1092             startBuffering(getGlobalStylesCollection(), true);
1093         }
1094         else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
1095         {
1096             startBuffering(getGlobalStylesCollection(), false);
1097         }
1098         else
1099         {
1100             contentProcessingState = TextRawReportTarget.CP_FIRST_TABLE;
1101             if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER || role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
1102             {
1103                 // if we have a repeating header, then skip the first one ..
1104                 // if this is a repeating footer, skip the last one. This means,
1105                 // we have to buffer all group footers and wait for the next section..
1106                 startBuffering(getContentStylesCollection(), true);
1107             }
1108 
1109             if (role != OfficeDocumentReportTarget.ROLE_DETAIL)
1110             {
1111                 // reset the detail-state. The flag will be updated on startTable and endOther(Table) if the
1112                 // current role is ROLE_DETAIL
1113                 detailBandProcessingState = DETAIL_SECTION_WAIT;
1114             }
1115         }
1116     }
1117 
startGroup(final AttributeMap attrs)1118     protected void startGroup(final AttributeMap attrs)
1119             throws IOException, DataSourceException, ReportProcessingException
1120     {
1121         super.startGroup(attrs);
1122         final PageContext pageContext = new PageContext(getCurrentContext());
1123         activePageContext.push(pageContext);
1124 
1125         final Object resetPageNumber = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "reset-page-number");
1126         if (OfficeToken.TRUE.equals(resetPageNumber))
1127         {
1128             setPagebreakDefinition(new PageBreakDefinition(true));
1129         }
1130 
1131         final Object keepTogether = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, KEEP_TOGETHER);
1132         if ("whole-group".equals(keepTogether))
1133         {
1134             pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_GROUP);
1135         }
1136         else if ("with-first-detail".equals(keepTogether) && pageContext.getKeepTogether() != PageContext.KEEP_TOGETHER_GROUP)
1137         {
1138             pageContext.setKeepTogether(PageContext.KEEP_TOGETHER_FIRST_DETAIL);
1139         }
1140 
1141         final Object columnCountRaw = attrs.getAttribute(OfficeNamespaces.FO_NS, "column-count");
1142         final Integer colCount = parseInt(columnCountRaw);
1143         if (colCount != null)
1144         {
1145             pageContext.setColumnCount(colCount);
1146         }
1147 
1148         final Object newColumn = attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "start-new-column");
1149         if (OfficeToken.TRUE.equals(newColumn))
1150         {
1151             setColumnBreakPending(true);
1152         }
1153     }
1154 
startGroupInstance(final AttributeMap attrs)1155     protected void startGroupInstance(final AttributeMap attrs)
1156             throws IOException, DataSourceException, ReportProcessingException
1157     {
1158         if (getGroupContext().isGroupWithRepeatingSection())
1159         {
1160             setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
1161         }
1162     }
1163 
endGroup(final AttributeMap attrs)1164     protected void endGroup(final AttributeMap attrs)
1165             throws IOException, DataSourceException, ReportProcessingException
1166     {
1167         if (getGroupContext().isGroupWithRepeatingSection())
1168         {
1169             setPagebreakDefinition(new PageBreakDefinition(isResetPageNumber()));
1170         }
1171 
1172         super.endGroup(attrs);
1173         finishSection();
1174 
1175         activePageContext.pop();
1176     }
1177 
finishSection()1178     private void finishSection()
1179             throws ReportProcessingException
1180     {
1181         final PageContext pageContext = getCurrentContext();
1182         if (pageContext.isSectionOpen())
1183         {
1184             pageContext.setSectionOpen(false);
1185             try
1186             {
1187                 getXmlWriter().writeCloseTag();
1188             }
1189             catch (IOException e)
1190             {
1191                 throw new ReportProcessingException("IOError", e);
1192             }
1193         }
1194     }
1195 
endReportSection(final AttributeMap attrs, final int role)1196     protected void endReportSection(final AttributeMap attrs, final int role)
1197             throws IOException, DataSourceException, ReportProcessingException
1198     {
1199         if (role == ROLE_TEMPLATE || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_HEADER || role == OfficeDocumentReportTarget.ROLE_SPREADSHEET_PAGE_FOOTER)
1200         {
1201             finishBuffering();
1202             return;
1203         }
1204 
1205         final CSSNumericValue result = sectionHeight.getResult();
1206         if (role == OfficeDocumentReportTarget.ROLE_PAGE_HEADER)
1207         {
1208             final PageContext pageContext = getCurrentContext();
1209             pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
1210         }
1211         else if (role == OfficeDocumentReportTarget.ROLE_PAGE_FOOTER)
1212         {
1213             final PageContext pageContext = getCurrentContext();
1214             pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
1215         }
1216         else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_HEADER)
1217         {
1218             final PageContext pageContext = getCurrentContext();
1219             pageContext.setHeader(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
1220         }
1221         else if (role == OfficeDocumentReportTarget.ROLE_REPEATING_GROUP_FOOTER)
1222         {
1223             final PageContext pageContext = getCurrentContext();
1224             pageContext.setFooter(applyColumnsToPageBand(finishBuffering(), pageContext.getActiveColumns()).getXmlBuffer(), result);
1225         }
1226         else if (role == OfficeDocumentReportTarget.ROLE_VARIABLES)
1227         {
1228             if (variables == null)
1229             {
1230                 variables = finishBuffering().getXmlBuffer();
1231             }
1232             else
1233             {
1234                 variables += finishBuffering().getXmlBuffer();
1235             }
1236         }
1237         else if (role == OfficeDocumentReportTarget.ROLE_GROUP_HEADER)
1238         {
1239             final String headerText = finishBuffering().getXmlBuffer();
1240             final int iterationCount = getGroupContext().getParent().getIterationCount();
1241             final boolean repeat = OfficeToken.TRUE.equals(attrs.getAttribute(OfficeNamespaces.OOREPORT_NS, "repeat-section"));
1242             if (!repeat || iterationCount > 0)
1243             {
1244                 getXmlWriter().writeText(headerText);
1245             }
1246         }
1247         else if (role == OfficeDocumentReportTarget.ROLE_GROUP_FOOTER)
1248         {
1249             final String footerText = finishBuffering().getXmlBuffer();
1250             // how do we detect whether this is the last group footer?
1251             getXmlWriter().writeText(footerText);
1252         }
1253 
1254     }
1255 
endReport(final ReportStructureRoot report)1256     public void endReport(final ReportStructureRoot report)
1257             throws DataSourceException, ReportProcessingException
1258     {
1259         super.endReport(report);
1260         variablesDeclarations = null;
1261 
1262         try
1263         {
1264             // Write the settings ..
1265             final AttributeList rootAttributes = new AttributeList();
1266             rootAttributes.addNamespaceDeclaration("office", OfficeNamespaces.OFFICE_NS);
1267             rootAttributes.addNamespaceDeclaration("config", OfficeNamespaces.CONFIG);
1268             rootAttributes.addNamespaceDeclaration("ooo", OfficeNamespaces.OO2004_NS);
1269             rootAttributes.setAttribute(OfficeNamespaces.OFFICE_NS, "version",
1270                     OfficeDocumentReportTarget.ODF_VERSION);
1271             final OutputStream outputStream = getOutputRepository().createOutputStream("settings.xml", "text/xml");
1272             final XmlWriter xmlWriter = new XmlWriter(new OutputStreamWriter(outputStream, "UTF-8"), createTagDescription());
1273             xmlWriter.setAlwaysAddNamespace(true);
1274             xmlWriter.writeXmlDeclaration("UTF-8");
1275             xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "document-settings", rootAttributes, XmlWriterSupport.OPEN);
1276             xmlWriter.writeTag(OfficeNamespaces.OFFICE_NS, "settings", XmlWriterSupport.OPEN);
1277             xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item-set", NAME, "ooo:configuration-settings", XmlWriterSupport.OPEN);
1278 
1279             final AttributeList configAttributes = new AttributeList();
1280             configAttributes.setAttribute(OfficeNamespaces.CONFIG, NAME, "TableRowKeep");
1281             configAttributes.setAttribute(OfficeNamespaces.CONFIG, "type", "boolean");
1282             xmlWriter.writeTag(OfficeNamespaces.CONFIG, "config-item", configAttributes, XmlWriterSupport.OPEN);
1283             xmlWriter.writeText(OfficeToken.TRUE);
1284             xmlWriter.writeCloseTag();
1285 
1286             xmlWriter.writeCloseTag();
1287             xmlWriter.writeCloseTag();
1288             xmlWriter.writeCloseTag();
1289             xmlWriter.close();
1290 
1291             copyMeta();
1292         }
1293         catch (IOException ioe)
1294         {
1295             throw new ReportProcessingException("Failed to write settings document");
1296         }
1297     }
1298 
endOther(final AttributeMap attrs)1299     protected void endOther(final AttributeMap attrs)
1300             throws IOException, DataSourceException, ReportProcessingException
1301     {
1302         final String namespace = ReportTargetUtil.getNamespaceFromAttribute(attrs);
1303         final String elementType = ReportTargetUtil.getElemenTypeFromAttribute(attrs);
1304 
1305         final boolean isInternalNS = ObjectUtilities.equal(JFreeReportInfo.REPORT_NAMESPACE, namespace);
1306         final boolean isTableNs = ObjectUtilities.equal(OfficeNamespaces.TABLE_NS, namespace);
1307         if (isTableMergeActive() && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_COLUMNS, elementType))
1308         {
1309             finishBuffering();
1310             return;
1311         }
1312 
1313         if (isInternalNS && (ObjectUtilities.equal(OfficeToken.IMAGE, elementType) || ObjectUtilities.equal(OfficeToken.OBJECT_OLE, elementType)))
1314         {
1315             return;
1316         }
1317 
1318         final XmlWriter xmlWriter = getXmlWriter();
1319         if (tableLayoutConfig != TABLE_LAYOUT_VARIABLES_PARAGRAPH && isTableNs && ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) && !isRepeatingSection())
1320         {
1321             if (variables != null)
1322             {
1323                 // This cannot happen as long as the report sections only contain tables. But at some point in the
1324                 // future they will be made of paragraphs, and then we are prepared ..
1325                 //LOGGER.debug("Variables-Section " + variables);
1326                 final String tag;
1327                 if (sectionKeepTogether && expectedTableRowCount > 0)
1328                 {
1329                     tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT;
1330                 }
1331                 else
1332                 {
1333                     tag = TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITHOUT_KEEPWNEXT;
1334                 }
1335                 StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
1336                         tag, getStylesCollection(),
1337                         getGlobalStylesCollection(), getPredefinedStylesCollection());
1338                 xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
1339                         tag, XmlWriterSupport.OPEN);
1340                 xmlWriter.writeText(variables);
1341                 xmlWriter.writeCloseTag();
1342                 variables = null;
1343             }
1344             /**
1345             // Only generate the empty paragraph, if we have to add the keep-together ..
1346             else if (cellEmpty && expectedTableRowCount > 0 &&
1347             sectionKeepTogether && !firstCellSeen)
1348             {
1349             // we have no variables ..
1350             StyleUtilities.copyStyle(OfficeToken.PARAGRAPH,
1351             TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, getStylesCollection(),
1352             getGlobalStylesCollection(), getPredefinedStylesCollection());
1353             xmlWriter.writeTag(OfficeNamespaces.TEXT_NS, OfficeToken.P, OfficeToken.STYLE_NAME,
1354             TextRawReportTarget.VARIABLES_HIDDEN_STYLE_WITH_KEEPWNEXT, XmlWriterSupport.CLOSE);
1355             }
1356              */
1357         }
1358 
1359         if (isTableNs && (ObjectUtilities.equal(OfficeToken.TABLE_CELL, elementType) || ObjectUtilities.equal(OfficeToken.COVERED_TABLE_CELL, elementType)))
1360         {
1361             firstCellSeen = true;
1362         }
1363         if (isTableNs && ObjectUtilities.equal(OfficeToken.TABLE, elementType))
1364         {
1365             if (getCurrentRole() == ROLE_DETAIL)
1366             {
1367                 if (!isTableMergeActive())
1368                 {
1369                     // We do not merge the detail bands, so an ordinary close will do.
1370                     xmlWriter.writeCloseTag();
1371                 }
1372                 else if (detailBandProcessingState == DETAIL_SECTION_FIRST_STARTED)
1373                 {
1374                     final int keepTogetherState = getCurrentContext().getKeepTogether();
1375                     if (keepTogetherState == PageContext.KEEP_TOGETHER_FIRST_DETAIL)
1376                     {
1377                         xmlWriter.writeCloseTag();
1378                         detailBandProcessingState = DETAIL_SECTION_FIRST_PRINTED;
1379                     }
1380                     else
1381                     {
1382                         detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
1383                     }
1384                 }
1385                 else if (detailBandProcessingState == DETAIL_SECTION_OTHER_STARTED)
1386                 {
1387                     detailBandProcessingState = DETAIL_SECTION_OTHER_PRINTED;
1388                 }
1389             }
1390             else
1391             {
1392                 xmlWriter.writeCloseTag();
1393             }
1394             if (isSectionPagebreakAfter(attrs))
1395             {
1396                 setPagebreakDefinition(new PageBreakDefinition(false));
1397             }
1398         }
1399         else
1400         {
1401             xmlWriter.writeCloseTag();
1402         }
1403     }
1404 
endGroupBody(final AttributeMap attrs)1405     protected void endGroupBody(final AttributeMap attrs)
1406             throws IOException, DataSourceException, ReportProcessingException
1407     {
1408         if (tableLayoutConfig == TABLE_LAYOUT_SINGLE_DETAIL_TABLE && detailBandProcessingState == DETAIL_SECTION_OTHER_PRINTED)
1409         {
1410             // closes the table ..
1411             final XmlWriter xmlWriter = getXmlWriter();
1412             xmlWriter.writeCloseTag();
1413             detailBandProcessingState = DETAIL_SECTION_WAIT;
1414         }
1415 
1416     }
1417 
endContent(final AttributeMap attrs)1418     protected void endContent(final AttributeMap attrs)
1419             throws IOException, DataSourceException, ReportProcessingException
1420     {
1421         finishSection();
1422         final BufferState bodyText = finishBuffering();
1423         final XmlWriter writer = getXmlWriter();
1424 
1425         final Map definedMappings = variablesDeclarations.getDefinedMappings();
1426         if (!definedMappings.isEmpty())
1427         {
1428             writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decls", XmlWriterSupport.OPEN);
1429             final Iterator mappingsIt = definedMappings.entrySet().iterator();
1430             while (mappingsIt.hasNext())
1431             {
1432                 final Map.Entry entry = (Map.Entry) mappingsIt.next();
1433                 final AttributeList entryList = new AttributeList();
1434                 entryList.setAttribute(OfficeNamespaces.TEXT_NS, NAME, (String) entry.getKey());
1435                 entryList.setAttribute(OfficeNamespaces.OFFICE_NS, FormatValueUtility.VALUE_TYPE, (String) entry.getValue());
1436                 writer.writeTag(OfficeNamespaces.TEXT_NS, "variable-decl", entryList, XmlWriterSupport.CLOSE);
1437             }
1438             writer.writeCloseTag();
1439         }
1440 
1441         writer.writeStream(bodyText.getXmlAsReader());
1442         writer.setLineEmpty(true);
1443         writer.writeCloseTag();
1444     }
1445 
getExportDescriptor()1446     public String getExportDescriptor()
1447     {
1448         return "raw/" + PentahoReportEngineMetaData.OPENDOCUMENT_TEXT;
1449     }
1450 }
1451