/*
* 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.parameters;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.PerformanceTags;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportEnvironment;
import org.pentaho.reporting.engine.classic.core.ReportEnvironmentDataRow;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.ResourceBundleFactory;
import org.pentaho.reporting.engine.classic.core.states.PerformanceMonitorContext;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.PerformanceLoggingStopWatch;
import org.pentaho.reporting.libraries.docbundle.DocumentMetaData;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Date;
public class DefaultReportParameterValidator implements ReportParameterValidator {
private static class TrustedParameterContext implements ParameterContext {
private ParameterContext context;
private ReportEnvironmentDataRow environmentDataRow;
private ReportParameterValues trustedValues;
private TrustedParameterContext( final ParameterContext context ) {
this.context = context;
this.environmentDataRow = new ReportEnvironmentDataRow( context.getReportEnvironment() );
this.trustedValues = new ReportParameterValues();
}
public DataRow getParameterData() {
return new CompoundDataRow( environmentDataRow, trustedValues );
}
public ReportParameterValues getTrustedValues() {
return trustedValues;
}
public DocumentMetaData getDocumentMetaData() {
return context.getDocumentMetaData();
}
public ReportEnvironment getReportEnvironment() {
return context.getReportEnvironment();
}
public DataFactory getDataFactory() {
return context.getDataFactory();
}
public ResourceBundleFactory getResourceBundleFactory() {
return context.getResourceBundleFactory();
}
public ResourceKey getContentBase() {
return context.getContentBase();
}
public ResourceManager getResourceManager() {
return context.getResourceManager();
}
public Configuration getConfiguration() {
return context.getConfiguration();
}
public void close() throws ReportDataFactoryException {
// not needed..
}
public PerformanceMonitorContext getPerformanceMonitorContext() {
return context.getPerformanceMonitorContext();
}
}
private static final Log logger = LogFactory.getLog( DefaultReportParameterValidator.class );
public DefaultReportParameterValidator() {
}
public ValidationResult validate( ValidationResult result, final ReportParameterDefinition parameterDefinition,
final ParameterContext parameterContext ) throws ReportProcessingException {
if ( parameterContext == null ) {
throw new NullPointerException();
}
if ( parameterDefinition == null ) {
throw new NullPointerException();
}
if ( result == null ) {
result = new ValidationResult();
}
PerformanceLoggingStopWatch sw =
parameterContext.getPerformanceMonitorContext().createStopWatch( PerformanceTags.REPORT_PARAMETER );
try {
sw.start();
final TrustedParameterContext trustedParameterContext = new TrustedParameterContext( parameterContext );
final ParameterDefinitionEntry[] parameterDefinitionEntries = parameterDefinition.getParameterDefinitions();
for ( int i = 0; i < parameterDefinitionEntries.length; i++ ) {
final ParameterDefinitionEntry parameterDefinitionEntry = parameterDefinitionEntries[i];
final String parameterName = parameterDefinitionEntry.getName();
final Object untrustedValue = parameterContext.getParameterData().get( parameterName );
validateSingleParameter( result, trustedParameterContext, parameterDefinitionEntry, untrustedValue );
}
result.setParameterValues( trustedParameterContext.getTrustedValues() );
return result;
} finally {
sw.close();
}
}
private void validateSingleParameter( final ValidationResult result,
final TrustedParameterContext trustedParameterContext, final ParameterDefinitionEntry parameterDefinitionEntry,
Object untrustedValue ) throws ReportProcessingException {
final boolean reevaluatePossible = untrustedValue != null;
Object defaultValue = null;
if ( untrustedValue == null ) {
// compute the default value
defaultValue = parameterDefinitionEntry.getDefaultValue( trustedParameterContext );
untrustedValue = defaultValue;
}
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: " + parameterDefinitionEntry.getName() );
logger.debug( "On Validate Single Parameter: " + trustedParameterContext.getParameterData() );
logger.debug( "On Validate Single Parameter: " + untrustedValue );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
final String parameterName = parameterDefinitionEntry.getName();
final ReportParameterValues tempValue = new ReportParameterValues( trustedParameterContext.getTrustedValues() );
tempValue.put( parameterName, untrustedValue );
final Object computedValue =
FormulaParameterEvaluator.computePostProcessingValue( result, trustedParameterContext, tempValue,
parameterDefinitionEntry, untrustedValue, defaultValue );
if ( isValueMissingForMandatoryParameterCheck( parameterDefinitionEntry, computedValue ) ) {
// as the post processing expression failed or returned <null>, the computed value
// must be <null> or an error. We report an error (which stops the report processing)
// and set the default value as current value, so that the other parameters can continue.
trustedParameterContext.getTrustedValues().put( parameterName, null );
result.addError( parameterName, new ValidationMessage( Messages.getInstance().getString(
"DefaultReportParameterValidator.ParameterIsMandatory" ) ) );
return;
}
if ( parameterDefinitionEntry instanceof ListParameter == false ) {
if ( computedValue != null ) {
final Class parameterType = parameterDefinitionEntry.getValueType();
if ( parameterType.isInstance( computedValue ) == false ) {
logger.warn( "Parameter validation error: Value cannot be matched due to invalid value type '"
+ parameterDefinitionEntry.getName() + "' with value '" + computedValue + "'" );
result.addError( parameterName, new ValidationMessage( Messages.getInstance().getString(
"DefaultReportParameterValidator.ParameterIsInvalidType" ) ) );
trustedParameterContext.getTrustedValues().put( parameterName, null );
return;
}
}
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: = " + computedValue );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
trustedParameterContext.getTrustedValues().put( parameterName, computedValue );
return;
}
final ListParameter listParameter = (ListParameter) parameterDefinitionEntry;
final Object[] values;
final Class parameterType;
if ( listParameter.isAllowMultiSelection() ) {
if ( computedValue == null ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: = new Object[0]" );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
trustedParameterContext.getTrustedValues().put( parameterName, new Object[0] );
return;
}
if ( computedValue instanceof Object[] == false ) {
result.addError( parameterName, new ValidationMessage( Messages.getInstance().getString(
"DefaultReportParameterValidator.ParameterIsNotAnArray" ) ) );
trustedParameterContext.getTrustedValues().put( parameterName, null );
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: = " + null );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
return;
}
values = (Object[]) computedValue;
if ( listParameter.getValueType().isArray() ) {
parameterType = listParameter.getValueType().getComponentType();
} else {
parameterType = listParameter.getValueType();
}
} else {
values = new Object[] { computedValue };
parameterType = listParameter.getValueType();
}
final ValidationMessage message =
computeValidListValue( listParameter, trustedParameterContext, parameterType, values );
if ( message != null ) {
if ( reevaluatePossible
&& "true".equals( listParameter.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.RE_EVALUATE_ON_FAILED_VALUES, trustedParameterContext ) ) ) {
validateSingleParameter( result, trustedParameterContext, listParameter, null );
} else {
result.addError( parameterName, message );
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: = null" );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
trustedParameterContext.getTrustedValues().put( parameterName, null );
}
} else {
if ( logger.isDebugEnabled() ) {
logger.debug( "On Validate Single Parameter: = " + computedValue );
logger.debug( "On Validate Single Parameter: ------------------------------" );
}
trustedParameterContext.getTrustedValues().put( parameterName, computedValue );
}
}
private ValidationMessage computeValidListValue( final ListParameter listParameter,
final ParameterContext parameterContext, final Class parameterType, final Object[] values )
throws ReportDataFactoryException {
for ( int i = 0; i < values.length; i++ ) {
Object value = values[i];
if ( value != null ) {
if ( "".equals( value ) ) {
value = null;
} else if ( parameterType.isInstance( value ) == false ) {
logger.warn( "Parameter validation error: Value cannot be matched due to invalid value type '"
+ listParameter.getName() + "' with value '" + value + "'" );
return new ValidationMessage( Messages.getInstance().getString(
"DefaultReportParameterValidator.ParameterIsInvalidType" ) );
}
}
if ( listParameter.isStrictValueCheck() == false ) {
continue;
}
try {
final ParameterValues parameterValues = listParameter.getValues( parameterContext );
final boolean found = isValueValid( parameterValues, value );
if ( found == false ) {
logger.warn( "Parameter validation error: No such value in the result for '" + listParameter.getName()
+ "' with value '" + value + "'" );
return new ValidationMessage( Messages.getInstance().getString(
"DefaultReportParameterValidator.ParameterIsInvalidValue" ) );
}
} catch ( ReportDataFactoryException e ) {
throw e;
} catch ( Throwable e ) {
logger.warn( "Unexpected Parameter validation error", e );
// overly broad catch, I know, but some creepy code throws ClassNotDefErrors and such around ..
return new ValidationMessage( Messages.getInstance().getString( "DefaultReportParameterValidator.GlobalError" ) );
}
}
return null;
}
private boolean isValueMissingForMandatoryParameterCheck( final ParameterDefinitionEntry entry,
final Object computedValue ) {
if ( entry.isMandatory() == false ) {
return false;
}
if ( computedValue == null || "".equals( computedValue ) ) {
return true;
}
if ( entry instanceof ListParameter ) {
final ListParameter listParameter = (ListParameter) entry;
if ( listParameter.isAllowMultiSelection() ) {
if ( computedValue instanceof Object[] == false ) {
return false;
} else if ( Array.getLength( computedValue ) == 0 ) {
return true;
}
}
}
return false;
}
private boolean isValueValid( final ParameterValues parameterValues, final Object o ) {
if ( parameterValues == null ) {
throw new NullPointerException();
}
for ( int row = 0; row < parameterValues.getRowCount(); row++ ) {
final Object keyFromData = parameterValues.getKeyValue( row );
if ( o instanceof Number && keyFromData instanceof Number ) {
final BigDecimal n1 = new BigDecimal( String.valueOf( o ) );
final BigDecimal n2 = new BigDecimal( String.valueOf( keyFromData ) );
if ( n1.compareTo( n2 ) == 0 ) {
return true;
}
continue;
}
if ( o instanceof Date && keyFromData instanceof Date ) {
final Date d1 = (Date) o;
final Date d2 = (Date) keyFromData;
if ( d1.getTime() == d2.getTime() ) {
return true;
}
continue;
}
if ( "".equals( keyFromData ) ) {
if ( o == null ) {
return true;
}
continue;
}
if ( ObjectUtilities.equal( keyFromData, o ) ) {
return true;
}
}
return false;
}
}