/*
* 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.parser.extwriter;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.Element;
import org.pentaho.reporting.engine.classic.core.GroupDataBody;
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.filter.types.bands.GroupDataBodyType;
import org.pentaho.reporting.engine.classic.core.function.Expression;
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;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 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();
final GroupDataBody dataBody = (GroupDataBody) getReport().getChildElementByType( GroupDataBodyType.INSTANCE );
if ( dataBody != null ) {
writeRootBand( ReportDescriptionWriter.ITEMBAND_TAG, dataBody.getItemBand() );
writeRootBand( ReportDescriptionWriter.NO_DATA_BAND_TAG, dataBody.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<StyleKey, Expression> styleExpressions = band.getStyleExpressions();
if ( styleExpressions.isEmpty() == false ) {
final FunctionsWriter fnWriter = new FunctionsWriter( getReportWriter(), writer );
for ( final Map.Entry<StyleKey, Expression> entry : styleExpressions.entrySet() ) {
final StyleKey key = entry.getKey();
final Expression ex = 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 ) {
final StyleKey[] namesArray = es.getDefinedPropertyNamesArray();
if ( namesArray.length == 0 ) {
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++ ) {
// This will fail for crosstabs. But this code is legacy, so it is ok.
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;
}
}