/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.parser.extwriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.RelationalGroup;
import org.pentaho.reporting.engine.classic.core.RootLevelBand;
import org.pentaho.reporting.engine.classic.core.SubReport;
import org.pentaho.reporting.engine.classic.core.filter.DataSource;
import org.pentaho.reporting.engine.classic.core.filter.EmptyDataSource;
import org.pentaho.reporting.engine.classic.core.filter.templates.Template;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.layout.BandLayoutManager;
import org.pentaho.reporting.engine.classic.core.layout.StaticLayoutManager;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.ExtParserModule;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.base.ClassFactoryCollector;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.base.ObjectDescription;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.base.ObjectFactoryException;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.datasource.DataSourceCollector;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.templates.TemplateCollector;
import org.pentaho.reporting.engine.classic.core.modules.parser.ext.factory.templates.TemplateDescription;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.StyleKey;
import org.pentaho.reporting.libraries.xmlns.common.AttributeList;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
/**
* A report description writer. The {@link ReportDefinitionWriter} class is responsible for writing the complete XML
* report definition file, but it delegates one large section (the report description) to this class.
*
* @author Thomas Morgner.
*/
public class ReportDescriptionWriter extends AbstractXMLDefinitionWriter
{
/**
* The 'band' tag.
*/
public static final String BAND_TAG = "band";
/**
* The 'element' tag.
*/
public static final String ELEMENT_TAG = "element";
/**
* The 'fields' tag name.
*/
public static final String FIELDS_TAG = "fields";
/**
* The 'field' tag name.
*/
public static final String FIELD_TAG = "field";
/**
* The 'group-header' tag name.
*/
public static final String GROUP_HEADER_TAG = "group-header";
/**
* The 'group-footer' tag name.
*/
public static final String GROUP_FOOTER_TAG = "group-footer";
/**
* The 'group' tag name.
*/
public static final String GROUP_TAG = "group";
/**
* The 'groups' tag name.
*/
public static final String GROUPS_TAG = "groups";
/**
* The 'watermark' tag name.
*/
public static final String WATERMARK_TAG = "watermark";
/**
* The report description tag name.
*/
public static final String REPORT_DESCRIPTION_TAG = "report-description";
/**
* The 'report-header' tag name.
*/
public static final String REPORT_HEADER_TAG = "report-header";
/**
* The 'report-footer' tag name.
*/
public static final String REPORT_FOOTER_TAG = "report-footer";
/**
* The 'page-header' tag name.
*/
public static final String PAGE_HEADER_TAG = "page-header";
/**
* The 'page-footer' tag name.
*/
public static final String PAGE_FOOTER_TAG = "page-footer";
/**
* The 'itemband' tag name.
*/
public static final String ITEMBAND_TAG = "itemband";
public static final String NO_DATA_BAND_TAG = "no-data-band";
/**
* Creates a new report description writer.
*
* @param reportWriter the report writer.
* @param indent the current indention level.
*/
public ReportDescriptionWriter(final ReportWriterContext reportWriter,
final XmlWriter indent)
{
super(reportWriter, indent);
}
/**
* Writes a report description element to a character stream writer.
*
* @throws IOException if there is an I/O problem.
* @throws ReportWriterException if there is a problem writing the report.
*/
public void write()
throws IOException, ReportWriterException
{
final XmlWriter writer = getXmlWriter();
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.REPORT_DESCRIPTION_TAG, XmlWriterSupport.OPEN);
writeRootBand(ReportDescriptionWriter.REPORT_HEADER_TAG, getReport().getReportHeader());
writeRootBand(ReportDescriptionWriter.REPORT_FOOTER_TAG, getReport().getReportFooter());
writeRootBand(ReportDescriptionWriter.PAGE_HEADER_TAG, getReport().getPageHeader());
writeRootBand(ReportDescriptionWriter.PAGE_FOOTER_TAG, getReport().getPageFooter());
writeRootBand(ReportDescriptionWriter.WATERMARK_TAG, getReport().getWatermark());
writeGroups();
writeRootBand(ReportDescriptionWriter.ITEMBAND_TAG, getReport().getItemBand());
writeRootBand(ReportDescriptionWriter.NO_DATA_BAND_TAG, getReport().getNoDataBand());
writer.writeCloseTag();
}
private void writeSubReports(final RootLevelBand band)
throws IOException, ReportWriterException
{
final int subReportCount = band.getSubReportCount();
for (int i = 0; i < subReportCount; i++)
{
final SubReport sreport = band.getSubReport(i);
final ReportWriterContext context =
new ReportWriterContext(sreport, getReportWriter());
final SubReportDefinitionWriter writer =
new SubReportDefinitionWriter(context, getXmlWriter());
writer.write();
}
}
/**
* Writes an element for a report band.
*
* @param tagName the tag name (for the band).
* @param band the band.
* @throws IOException if there is an I/O problem.
* @throws ReportWriterException if there is a problem writing the report.
*/
private void writeBand(final String tagName,
final Band band)
throws IOException, ReportWriterException
{
final XmlWriter writer = getXmlWriter();
if (band.getName().startsWith(Band.ANONYMOUS_BAND_PREFIX) ||
band.getName().startsWith(Element.ANONYMOUS_ELEMENT_PREFIX))
{
writer.writeTag(ExtParserModule.NAMESPACE, tagName, XmlWriterSupport.OPEN);
}
else
{
writer.writeTag(ExtParserModule.NAMESPACE, tagName,
"name", band.getName(), XmlWriterSupport.OPEN);
}
writeStyleInfo(band);
final Element[] list = band.getElementArray();
for (int i = 0; i < list.length; i++)
{
if (list[i] instanceof Band)
{
final Band b = (Band) list[i];
writeBand(ReportDescriptionWriter.BAND_TAG, b);
}
else
{
writeElement(list[i]);
}
}
if (band instanceof RootLevelBand)
{
writeSubReports((RootLevelBand) band);
}
writer.writeCloseTag();
}
private void writeStyleInfo(final Element band)
throws IOException, ReportWriterException
{
final XmlWriter writer = getXmlWriter();
final ElementStyleSheet styleSheet = band.getStyle();
if (isStyleSheetEmpty(styleSheet) == false)
{
writer.writeTag(ExtParserModule.NAMESPACE, AbstractXMLDefinitionWriter.STYLE_TAG, XmlWriterSupport.OPEN);
final StyleWriter styleWriter =
new StyleWriter(getReportWriter(), band.getStyle(), writer);
styleWriter.write();
writer.writeCloseTag();
}
final Map styleExpressions = band.getStyleExpressions();
final Iterator styleExpressionsIt = styleExpressions.entrySet().iterator();
if (styleExpressionsIt.hasNext())
{
final FunctionsWriter fnWriter =
new FunctionsWriter(getReportWriter(), writer);
while (styleExpressionsIt.hasNext())
{
final Map.Entry entry = (Map.Entry) styleExpressionsIt.next();
final StyleKey key = (StyleKey) entry.getKey();
final Expression ex = (Expression) entry.getValue();
fnWriter.writeStyleExpression(ex, key);
}
}
}
/**
* Checks whether the given stylesheet is empty and does not inherit values from modifiable or user defined parents.
*
* @param es the element stylesheet to test
* @return true, if the sheet is empty, false otherwise.
*/
private boolean isStyleSheetEmpty(final ElementStyleSheet es)
{
if (es.getParents().length > 0)
{
return false;
}
final Iterator definedPropertyNames = es.getDefinedPropertyNames();
if (definedPropertyNames.hasNext() == false)
{
return true;
}
final StyleKey name = (StyleKey) definedPropertyNames.next();
if (BandLayoutManager.LAYOUTMANAGER.equals(name) == false)
{
return false;
}
final Object styleProperty =
es.getStyleProperty(BandLayoutManager.LAYOUTMANAGER);
if (styleProperty instanceof StaticLayoutManager || styleProperty == null)
{
if (definedPropertyNames.hasNext() == false)
{
return true;
}
}
return false;
}
/**
* Writes an element to a character stream writer.
*
* @param element the element.
* @throws IOException if there is an I/O problem.
* @throws ReportWriterException if there is a problem writing the report.
*/
private void writeElement(final Element element)
throws IOException, ReportWriterException
{
final AttributeList attList = new AttributeList();
if (element.getName().startsWith(Element.ANONYMOUS_ELEMENT_PREFIX) == false)
{
attList.setAttribute(ExtParserModule.NAMESPACE, "name", element.getName());
}
final XmlWriter writer = getXmlWriter();
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.ELEMENT_TAG, attList, XmlWriterSupport.OPEN);
writeStyleInfo(element);
writeDataSourceForElement(element);
writer.writeCloseTag();
}
/**
* Writes the datasource- or template-tag for an given element.
*
* @param element the element, which should be written.
* @throws ReportWriterException if there is a problem writing the report
* @throws IOException if there is an IO error.
*/
protected void writeDataSourceForElement(final Element element)
throws ReportWriterException, IOException
{
if ((element.getDataSource() instanceof EmptyDataSource))
{
return;
}
if (element.getDataSource() instanceof Template == false)
{
writeDataSource(element.getDataSource());
return;
}
final TemplateCollector tc = getReportWriter().getTemplateCollector();
final Template template = (Template) element.getDataSource();
// the template description of the element template will get the
// template name as its name.
final TemplateDescription templateDescription =
tc.getDescription(template);
if (templateDescription == null)
{
throw new ReportWriterException("Unknown template type: " + template);
}
// create the parent description before the template description is filled.
final TemplateDescription parentTemplate = (TemplateDescription) templateDescription.getInstance();
try
{
templateDescription.setParameterFromObject(template);
}
catch (ObjectFactoryException ofe)
{
throw new ReportWriterException("Error while preparing the template", ofe);
}
final TemplateWriter templateWriter = new TemplateWriter
(getReportWriter(), getXmlWriter(), templateDescription, parentTemplate);
templateWriter.write();
}
/**
* Writes a data source to a character stream writer.
*
* @param datasource the datasource.
* @throws IOException if there is an I/O problem.
* @throws ReportWriterException if there is a problem writing the report.
*/
private void writeDataSource(final DataSource datasource)
throws IOException, ReportWriterException
{
final ReportWriterContext reportWriter = getReportWriter();
final ClassFactoryCollector classFactoryCollector =
reportWriter.getClassFactoryCollector();
ObjectDescription od =
classFactoryCollector.getDescriptionForClass(datasource.getClass());
if (od == null)
{
od = classFactoryCollector.
getSuperClassObjectDescription(datasource.getClass(), null);
}
if (od == null)
{
throw new ReportWriterException("Unable to resolve DataSource: " + datasource.getClass());
}
final DataSourceCollector dataSourceCollector =
reportWriter.getDataSourceCollector();
final String dsname = dataSourceCollector.getDataSourceName(od);
if (dsname == null)
{
throw new ReportWriterException("No name for DataSource " + datasource);
}
final XmlWriter writer = getXmlWriter();
writer.writeTag(ExtParserModule.NAMESPACE,
AbstractXMLDefinitionWriter.DATASOURCE_TAG, "type", dsname, XmlWriterSupport.OPEN);
final DataSourceWriter dsWriter =
new DataSourceWriter(reportWriter, datasource, od, writer);
dsWriter.write();
writer.writeCloseTag();
}
/**
* Writes groups to a character stream writer.
* <p/>
* 9 * @throws IOException if there is an I/O problem.
*
* @throws ReportWriterException if there is a problem writing the report.
*/
private void writeGroups()
throws IOException, ReportWriterException
{
final XmlWriter writer = getXmlWriter();
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.GROUPS_TAG, XmlWriterSupport.OPEN);
//logComment = true;
final int groupSize = getReport().getGroupCount();
for (int i = 0; i < groupSize; i++)
{
// todo: This is probably not correct.
final RelationalGroup g = (RelationalGroup) getReport().getGroup(i);
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.GROUP_TAG,
"name", g.getName(), XmlWriterSupport.OPEN);
final List fields = g.getFields();
if (fields.isEmpty() == false)
{
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.FIELDS_TAG, XmlWriterSupport.OPEN);
for (int f = 0; f < fields.size(); f++)
{
final String field = (String) fields.get(f);
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.FIELD_TAG, XmlWriterSupport.OPEN);
writer.writeTextNormalized(field, false);
writer.writeCloseTag();
}
writer.writeCloseTag();
}
else
{
writer.writeTag(ExtParserModule.NAMESPACE, ReportDescriptionWriter.FIELDS_TAG, XmlWriterSupport.CLOSE);
}
writeRootBand(ReportDescriptionWriter.GROUP_HEADER_TAG, g.getHeader());
writeRootBand(ReportDescriptionWriter.GROUP_FOOTER_TAG, g.getFooter());
writer.writeCloseTag();
}
writer.writeCloseTag();
}
private void writeRootBand(final String tag,
final Band band)
throws IOException, ReportWriterException
{
if (isEmptyRootBand(band))
{
return;
}
writeBand(tag, band);
}
private boolean isEmptyRootBand(final Band band)
{
if (band.getName().startsWith(Band.ANONYMOUS_BAND_PREFIX) == false)
{
return false;
}
if (band.getName().startsWith(Element.ANONYMOUS_ELEMENT_PREFIX) == false)
{
return false;
}
if (band.getElementCount() != 0)
{
return false;
}
if (band instanceof RootLevelBand)
{
final RootLevelBand rlb = (RootLevelBand) band;
if (rlb.getSubReportCount() > 0)
{
return false;
}
}
final ElementStyleSheet styleSheet = band.getStyle();
if (isStyleSheetEmpty(styleSheet))
{
return true;
}
return false;
}
}