/*
* 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.layout.process;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.CanvasRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.InlineRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ValidationResult;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableCellRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRowRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableSectionRenderBox;
@SuppressWarnings( "HardCodedStringLiteral" )
public class ValidateModelStep extends IterateStructuralProcessStep {
private static class TableValidationInfo {
private boolean needCheck;
private boolean seenBody;
private int seenRow;
private int requiredAdditionalRows;
private int currentRowMaxRowSpan;
private boolean inMainBody;
private boolean seenRowInMainBody;
private TableValidationInfo parent;
private TableValidationInfo( final TableValidationInfo parent ) {
this.parent = parent;
this.requiredAdditionalRows = 0;
}
public TableValidationInfo pop() {
return parent;
}
public void setInMainBody( final boolean inMainBody ) {
this.requiredAdditionalRows = 0;
this.inMainBody = inMainBody;
}
public boolean isRequireAdditionalRows() {
return requiredAdditionalRows > 0;
}
public boolean isNeedCheck() {
return needCheck;
}
public void setNeedCheck( final boolean needCheck ) {
this.needCheck = needCheck;
}
public boolean isSeenTableBody() {
return seenBody;
}
public void startTableSection( final boolean seenBody ) {
this.requiredAdditionalRows = 0;
this.seenBody = seenBody;
}
public boolean isSeenRowInMainBody() {
return seenRowInMainBody;
}
public void addSeenRow() {
if ( inMainBody ) {
seenRowInMainBody = true;
}
this.seenRow += 1;
this.currentRowMaxRowSpan = 0;
if ( this.requiredAdditionalRows > 0 ) {
// if we have spanned rows pending, reduce the span with each new row started, until every row is consumed.
this.requiredAdditionalRows -= 1;
}
}
public void rowFinished() {
this.requiredAdditionalRows += this.currentRowMaxRowSpan;
this.requiredAdditionalRows -= 1;
}
public void seenTableCell( final int rowSpan ) {
this.currentRowMaxRowSpan = Math.max( rowSpan, this.currentRowMaxRowSpan );
}
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "TableValidationInfo" );
sb.append( "{needCheck=" ).append( needCheck );
sb.append( ", seenBody=" ).append( seenBody );
sb.append( ", seenRow=" ).append( seenRow );
sb.append( ", inMainBody=" ).append( inMainBody );
sb.append( ", parent=" ).append( parent );
sb.append( '}' );
return sb.toString();
}
}
private static final Log logger = LogFactory.getLog( ValidateModelStep.class );
private ValidationResult result;
private TableValidationInfo validationInfo;
public ValidateModelStep() {
}
public boolean isLayoutable( final LogicalPageBox root ) {
validationInfo = null;
setResult( ValidationResult.OK );
// do not validate the header or footer or watermark sections..
processBoxChilds( root );
if ( logger.isDebugEnabled() ) {
logger.debug( "Validation result: " + getResult() );
}
return getResult() == ValidationResult.OK;
}
public void setResult( final ValidationResult result ) {
this.result = result;
}
protected boolean startCanvasBox( final CanvasRenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return false;
}
if ( box.isValidateModelCacheValid() ) {
setResult( box.isValidateModelResult() );
return false;
}
if ( box.isOpen() ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Canvas: Box is open: " + box );
}
setResult( ValidationResult.CANVAS_BOX_OPEN );
box.setValidateModelResult( getResult() );
return false;
}
if ( box.getAppliedContentRefCount() == 0 && box.getTableRefCount() == 0 ) {
return false;
}
return true;
}
protected void finishCanvasBox( final CanvasRenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
protected boolean validateBlockOrAutoBox( final RenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return false;
}
if ( box.isValidateModelCacheValid() ) {
setResult( box.isValidateModelResult() );
return false;
}
if ( box.isOpen() ) {
if ( box.getNext() != null ) {
// if this box is not the last box in a sequence of boxes, then we cannot finish the layouting
if ( logger.isDebugEnabled() ) {
logger.debug( "Block: Box is open with next element pending : " + box );
}
setResult( ValidationResult.BOX_OPEN_NEXT_PENDING );
box.setValidateModelResult( getResult() );
return false;
} else if ( box.getStaticBoxLayoutProperties().isPlaceholderBox() ) {
if ( box.getFirstChild() == null ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Block: Open Box is placeholder : " + box );
}
setResult( ValidationResult.PLACEHOLDER_BOX_OPEN );
box.setValidateModelResult( getResult() );
return false;
}
} else if ( box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Block: Paragraph is open: " + box );
}
setResult( ValidationResult.PARAGRAPH_BOX_OPEN );
box.setValidateModelResult( getResult() );
return false;
}
} else if ( box.getAppliedContentRefCount() == 0 && box.getTableRefCount() == 0 ) {
return false;
}
// pending complex content, must validate childs, but in itself it is not a indicator for invalid content.
return true;
}
protected boolean startBlockBox( final BlockRenderBox box ) {
return validateBlockOrAutoBox( box );
}
protected void finishBlockBox( final BlockRenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
protected boolean startAutoBox( final RenderBox box ) {
if ( ( box.getLayoutNodeType() & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
return validateBlockOrAutoBox( box );
}
if ( ( box.getLayoutNodeType() & LayoutNodeTypes.TYPE_BOX_TABLE ) == LayoutNodeTypes.TYPE_BOX_TABLE
|| ( box.getLayoutNodeType() & LayoutNodeTypes.TYPE_BOX_TABLE_SECTION ) == LayoutNodeTypes.TYPE_BOX_TABLE_SECTION ) {
return true;
}
return validateInlineRowOrTableCellBox( box );
}
protected void finishAutoBox( final RenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
private boolean validateInlineRowOrTableCellBox( final RenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return false;
}
if ( box.isValidateModelCacheValid() ) {
setResult( box.isValidateModelResult() );
return false;
}
if ( box.isOpen() ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Inline: Box is open : " + box );
}
setResult( ValidationResult.CELL_BOX_OPEN );
box.setValidateModelResult( getResult() );
return false;
}
if ( box.getAppliedContentRefCount() == 0 && box.getTableRefCount() == 0 ) {
return false;
}
return true;
}
protected boolean startTableCellBox( final TableCellRenderBox box ) {
int rowSpan = box.getRowSpan();
validationInfo.seenTableCell( rowSpan );
return validateInlineRowOrTableCellBox( box );
}
protected void finishTableCellBox( final TableCellRenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
protected boolean startInlineBox( final InlineRenderBox box ) {
return validateInlineRowOrTableCellBox( box );
}
protected void finishInlineBox( final InlineRenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
protected boolean startRowBox( final RenderBox box ) {
return validateInlineRowOrTableCellBox( box );
}
protected void finishRowBox( final RenderBox box ) {
if ( getResult() != ValidationResult.OK ) {
return;
}
box.setValidateModelResult( getResult() );
}
protected boolean startTableBox( final TableRenderBox table ) {
validationInfo = new TableValidationInfo( this.validationInfo );
if ( getResult() != ValidationResult.OK ) {
return false;
}
if ( table.isValidateModelCacheValid() ) {
setResult( table.isValidateModelResult() );
return true;
}
if ( table.isOpen() ) {
if ( table.isAutoLayout() ) {
// Auto-Layout means, we have to see the complete table.
// Yes, that is expensive ..
if ( logger.isDebugEnabled() ) {
logger.debug( "Table: Open Table and AutoLayout: " + table );
}
setResult( ValidationResult.TABLE_BOX_OPEN );
table.setValidateModelResult( getResult() );
return false;
} else if ( table.getColumnModel().isIncrementalModeSupported() == false ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Table: Open Table and incremental mode not supported: " + table );
}
setResult( ValidationResult.TABLE_BOX_OPEN );
table.setValidateModelResult( getResult() );
return false;
} else if ( table.isPreventPagination() ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Table: Open Table and incremental mode not supported: " + table );
}
setResult( ValidationResult.TABLE_BOX_PREVENTS_PAGINATION );
table.setValidateModelResult( getResult() );
return false;
}
}
validationInfo.setNeedCheck( true );
return true;
}
protected void finishTableBox( final TableRenderBox table ) {
try {
if ( table.isValidateModelCacheValid() ) {
return;
}
if ( getResult() != ValidationResult.OK ) {
return;
}
if ( validationInfo.isNeedCheck() == false ) {
return;
}
if ( table.isOpen() && validationInfo.isSeenTableBody() == false && validationInfo.isSeenRowInMainBody() == false ) {
// A table that is open, cannot be processed until it has at least a body and one real row of data in it.
setResult( ValidationResult.TABLE_BOX_MISSING_DATA );
table.setValidateModelResult( getResult() );
return;
}
if ( logger.isDebugEnabled() ) {
logger.debug( "Table-Box: " + validationInfo );
}
table.setValidateModelResult( getResult() );
} finally {
this.validationInfo = validationInfo.pop();
}
}
protected boolean startTableSectionBox( final TableSectionRenderBox box ) {
if ( box.getDisplayRole() == TableSectionRenderBox.Role.BODY ) {
validationInfo.setInMainBody( true );
}
validationInfo.startTableSection( true );
return true;
}
protected void finishTableSectionBox( final TableSectionRenderBox box ) {
if ( box.isOpen() && validationInfo.isRequireAdditionalRows() ) {
setResult( ValidationResult.TABLE_BODY_MISSING_ROWS );
box.setValidateModelResult( getResult() );
}
validationInfo.setInMainBody( false );
}
protected boolean startTableRowBox( final TableRowRenderBox row ) {
if ( row.isOpen() ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "Table-Row: Box is open." );
}
setResult( ValidationResult.TABLE_ROW_OPEN );
row.setValidateModelResult( getResult() );
return false;
}
validationInfo.addSeenRow();
return true;
}
protected void finishTableRowBox( final TableRowRenderBox box ) {
validationInfo.rowFinished();
}
protected boolean startOtherBox( final RenderBox box ) {
return ( getResult() == ValidationResult.OK );
}
protected ValidationResult getResult() {
return result;
}
}