/*!
* 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) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.build;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.Section;
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.RawDataSource;
import org.pentaho.reporting.engine.classic.core.function.ExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.layout.InlineSubreportMarker;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.richtext.RichTextConverter;
import org.pentaho.reporting.engine.classic.core.layout.richtext.RichTextConverterRegistry;
import org.pentaho.reporting.engine.classic.core.layout.style.SimpleStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import java.util.ArrayList;
import java.util.List;
public class DefaultLayoutBuilderStrategy implements LayoutBuilderStrategy {
private static final Log logger = LogFactory.getLog( DefaultLayoutBuilderStrategy.class );
private ExpressionRuntime runtime;
private ArrayList<InlineSubreportMarker> collectedReports;
private boolean designtime;
public DefaultLayoutBuilderStrategy() {
collectedReports = new ArrayList<InlineSubreportMarker>();
}
public void add( final ExpressionRuntime runtime, final LayoutModelBuilder builder, final Band band,
final List<InlineSubreportMarker> collectedSubReports ) throws ReportProcessingException {
if ( runtime == null ) {
throw new NullPointerException();
}
if ( builder == null ) {
throw new NullPointerException();
}
if ( band == null ) {
throw new NullPointerException();
}
try {
this.runtime = runtime;
final OutputProcessorMetaData outputProcessorMetaData =
runtime.getProcessingContext().getOutputProcessorMetaData();
this.designtime = outputProcessorMetaData.isFeatureSupported( OutputProcessorFeature.DESIGNTIME );
collectedReports.clear();
final SimpleStyleSheet styleSheet = band.getComputedStyle();
final boolean invConsSpace = builder.isEmptyElementsHaveSignificance();
if ( invConsSpace || isElementProcessable( band, styleSheet ) ) {
if ( addBandInternal( band, builder, true ) ) {
// when empty, add a progress marker box
builder.addProgressMarkerBox();
}
} else {
// element is not processable
builder.addProgressMarkerBox();
}
} finally {
this.runtime = null;
}
collectedSubReports.addAll( this.collectedReports );
}
private boolean addBandInternal( final Section band, final LayoutModelBuilder builder, final boolean root ) {
builder.startBox( band );
final boolean invConsSpace = builder.isEmptyElementsHaveSignificance();
for ( final ReportElement element : band ) {
final StyleSheet styleSheet = element.getComputedStyle();
if ( invConsSpace == false ) {
// pre-prune the layout model ...
if ( isElementProcessable( element, styleSheet ) == false ) {
continue;
}
}
if ( element instanceof SubReport ) {
processSubReport( (SubReport) element, builder );
continue;
}
if ( element instanceof Section ) {
addBandInternal( (Section) element, builder, false );
} else {
processContent( element, builder );
}
}
if ( root == false && builder.isEmpty() ) {
final OutputProcessorMetaData metaData = runtime.getProcessingContext().getOutputProcessorMetaData();
if ( metaData.isFeatureSupported( OutputProcessorFeature.STRICT_COMPATIBILITY ) ) {
// this is the behaviour of the old 3.9 code. It is hard to explain in sane words, just look at
// the old LayoutBuilder class for an example.
final StyleSheet computedStyle = band.getComputedStyle();
if ( invConsSpace == false && DefaultLayoutModelBuilder.isControlBand( computedStyle ) == false ) {
// creates either a block or a inline element
if ( band.getElementCount() > 0 ) {
builder.legacyFlagNotEmpty();
}
builder.finishBox();
builder.legacyAddPlaceholder( band );
return false;
}
builder.legacyFlagNotEmpty();
}
}
return builder.finishBox();
}
protected void processSubReport( final SubReport subReport, final LayoutModelBuilder builder ) {
final InlineSubreportMarker marker = builder.processSubReport( subReport );
if ( marker != null ) {
logger.debug( "Process Subreport: " + marker.getInsertationPointId() );
collectedReports.add( marker );
} else {
logger.debug( "Process Subreport: NOT returning anything." );
}
}
protected Object filterRichText( final ReportElement element, final Object initialValue ) {
final Object richTextType =
element.getAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.RICH_TEXT_TYPE );
if ( richTextType != null ) {
final RichTextConverterRegistry registry = RichTextConverterRegistry.getRegistry();
final RichTextConverter converter = registry.getConverter( String.valueOf( richTextType ) );
if ( converter != null ) {
return converter.convert( element, initialValue );
}
}
return initialValue;
}
protected void processContent( final ReportElement element, final LayoutModelBuilder builder ) {
final Object value = filterRichText( element, computeValue( runtime, element ) );
if ( value == null ) {
builder.processContent( element, null, null );
return;
}
if ( value instanceof Section ) {
final Section section = (Section) value;
RichTextStyleResolver.resolveStyle( section );
addBandInternal( section, builder, false );
return;
}
final DataSource dataSource = element.getElementType();
final Object rawValue;
if ( dataSource instanceof RawDataSource ) {
final RawDataSource rds = (RawDataSource) dataSource;
rawValue = rds.getRawValue( runtime, element );
} else {
rawValue = null;
}
builder.processContent( element, value, rawValue );
}
protected boolean isElementProcessable( final ReportElement element, final StyleSheet style ) {
if ( designtime ) {
final Object attribute =
element.getAttribute( AttributeNames.Designtime.NAMESPACE,
AttributeNames.Designtime.HIDE_IN_LAYOUT_GUI_ATTRIBUTE );
if ( Boolean.TRUE.equals( attribute ) ) {
return false;
}
return true;
}
return style.getBooleanStyleProperty( ElementStyleKeys.VISIBLE );
}
protected Object computeValue( final ExpressionRuntime runtime, final ReportElement element ) {
if ( designtime ) {
final Object value = element.getElementType().getDesignValue( runtime, element );
// should be ok for most cases ..
if ( value != null ) {
return value;
}
return element.getElementType().getMetaData().getName();
}
return element.getElementType().getValue( runtime, element );
}
}