/*
* 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 java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.Date;
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;
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;
}
}