/*!
* 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-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.platform.plugin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.plugin.action.jfreereport.helper.PentahoTableModel;
import org.pentaho.platform.util.messages.LocaleHelper;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.parameters.ListParameter;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterAttributeNames;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterContext;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterDefinitionEntry;
import org.pentaho.reporting.engine.classic.core.parameters.ValidationMessage;
import org.pentaho.reporting.engine.classic.core.parameters.ValidationResult;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.engine.classic.core.util.beans.BeanException;
import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry;
import org.pentaho.reporting.engine.classic.core.util.beans.ValueConverter;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.platform.plugin.messages.Messages;
import javax.swing.table.TableModel;
import java.io.IOException;
import java.lang.reflect.Array;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormatSymbols;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
public class ReportContentUtil {
/**
* Apply inputs (if any) to corresponding report parameters, care is taken when checking parameter types to perform
* any necessary casting and conversion.
*
* @param report The report to retrieve parameter definitions and values from.
* @param context a ParameterContext for which the parameters will be under
* @param validationResult the validation result that will hold the warnings. If null, a new one will be created.
* @return the validation result containing any parameter validation errors.
* @throws java.io.IOException if the report of this component could not be parsed.
* @throws ResourceException if the report of this component could not be parsed.
*/
public static ValidationResult applyInputsToReportParameters( final MasterReport report,
final ParameterContext context,
final Map<String, Object> inputs,
ValidationResult validationResult )
throws IOException, ResourceException {
if ( validationResult == null ) {
validationResult = new ValidationResult();
}
// apply inputs to report
if ( inputs != null ) {
final Log log = LogFactory.getLog( SimpleReportingComponent.class );
final ParameterDefinitionEntry[] params = report.getParameterDefinition().getParameterDefinitions();
final ReportParameterValues parameterValues = report.getParameterValues();
for ( final ParameterDefinitionEntry param : params ) {
final String paramName = param.getName();
try {
final Object computedParameter =
ReportContentUtil.computeParameterValue( context, param, inputs.get( paramName ) );
parameterValues.put( param.getName(), computedParameter );
if ( log.isInfoEnabled() ) {
log.info( Messages.getInstance().getString( "ReportPlugin.infoParameterValues", //$NON-NLS-1$
paramName, String.valueOf( inputs.get( paramName ) ), String.valueOf( computedParameter ) ) );
}
} catch ( final Exception e ) {
if ( log.isWarnEnabled() ) {
log.warn( Messages.getInstance().getString( "ReportPlugin.logErrorParametrization" ), e ); //$NON-NLS-1$
}
validationResult.addError( paramName, new ValidationMessage( e.getMessage() ) );
}
}
}
return validationResult;
}
public static Object computeParameterValue( final ParameterContext parameterContext,
final ParameterDefinitionEntry parameterDefinition, final Object value )
throws ReportProcessingException {
if ( value == null ) {
// there are still buggy report definitions out there ...
return null;
}
final Class valueType = parameterDefinition.getValueType();
final boolean allowMultiSelect = isAllowMultiSelect( parameterDefinition );
if ( allowMultiSelect && Collection.class.isInstance( value ) ) {
final Collection c = (Collection) value;
final Class componentType;
if ( valueType.isArray() ) {
componentType = valueType.getComponentType();
} else {
componentType = valueType;
}
final int length = c.size();
final Object[] sourceArray = c.toArray();
final Object array = Array.newInstance( componentType, length );
for ( int i = 0; i < length; i++ ) {
Array.set( array, i, convert( parameterContext, parameterDefinition, componentType, sourceArray[ i ] ) );
}
return array;
} else if ( value.getClass().isArray() ) {
final Class componentType;
if ( valueType.isArray() ) {
componentType = valueType.getComponentType();
} else {
componentType = valueType;
}
final int length = Array.getLength( value );
final Object array = Array.newInstance( componentType, length );
for ( int i = 0; i < length; i++ ) {
Array.set( array, i, convert( parameterContext, parameterDefinition, componentType, Array.get( value, i ) ) );
}
return array;
} else if ( allowMultiSelect ) {
// if the parameter allows multi selections, wrap this single input in an array
// and re-call addParameter with it
final Object[] array = new Object[ 1 ];
array[ 0 ] = value;
return computeParameterValue( parameterContext, parameterDefinition, array );
} else {
return convert( parameterContext, parameterDefinition, parameterDefinition.getValueType(), value );
}
}
private static boolean isAllowMultiSelect( final ParameterDefinitionEntry parameter ) {
if ( parameter instanceof ListParameter ) {
final ListParameter listParameter = (ListParameter) parameter;
return listParameter.isAllowMultiSelection();
}
return false;
}
private static Object convert( final ParameterContext context, final ParameterDefinitionEntry parameter,
final Class targetType, final Object rawValue ) throws ReportProcessingException {
if ( targetType == null ) {
throw new NullPointerException();
}
if ( rawValue == null ) {
return null;
}
if ( targetType.isInstance( rawValue ) ) {
return rawValue;
}
if ( targetType.isAssignableFrom( TableModel.class )
&& IPentahoResultSet.class.isAssignableFrom( rawValue.getClass() ) ) {
// wrap IPentahoResultSet to simulate TableModel
return new PentahoTableModel( (IPentahoResultSet) rawValue );
}
final String valueAsString = String.valueOf( rawValue );
if ( StringUtils.isEmpty( valueAsString ) ) {
// none of the converters accept empty strings as valid input. So we can return null instead.
return null;
}
if ( targetType.equals( Timestamp.class ) ) {
try {
final Date date = parseDate( parameter, context, valueAsString );
return new Timestamp( date.getTime() );
} catch ( ParseException pe ) {
// ignore, we try to parse it as real date now ..
CommonUtil.checkStyleIgnore();
}
} else if ( targetType.equals( Time.class ) ) {
try {
final Date date = parseDate( parameter, context, valueAsString );
return new Time( date.getTime() );
} catch ( ParseException pe ) {
// ignore, we try to parse it as real date now ..
CommonUtil.checkStyleIgnore();
}
} else if ( targetType.equals( java.sql.Date.class ) ) {
try {
final Date date = parseDate( parameter, context, valueAsString );
return new java.sql.Date( date.getTime() );
} catch ( ParseException pe ) {
// ignore, we try to parse it as real date now ..
CommonUtil.checkStyleIgnore();
}
} else if ( targetType.equals( Date.class ) ) {
try {
final Date date = parseDate( parameter, context, valueAsString );
return new Date( date.getTime() );
} catch ( ParseException pe ) {
// ignore, we try to parse it as real date now ..
CommonUtil.checkStyleIgnore();
}
}
final String dataFormat =
parameter.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.DATA_FORMAT, context );
if ( dataFormat != null ) {
try {
if ( Number.class.isAssignableFrom( targetType ) ) {
final DecimalFormat format =
new DecimalFormat( dataFormat, new DecimalFormatSymbols( LocaleHelper.getLocale() ) );
format.setParseBigDecimal( true );
final Number number = format.parse( valueAsString );
final String asText = ConverterRegistry.toAttributeValue( number );
return ConverterRegistry.toPropertyValue( asText, targetType );
} else if ( Date.class.isAssignableFrom( targetType ) ) {
final SimpleDateFormat format =
new SimpleDateFormat( dataFormat, new DateFormatSymbols( LocaleHelper.getLocale() ) );
format.setLenient( false );
final Date number = format.parse( valueAsString );
final String asText = ConverterRegistry.toAttributeValue( number );
return ConverterRegistry.toPropertyValue( asText, targetType );
}
} catch ( Exception e ) {
// again, ignore it .
CommonUtil.checkStyleIgnore();
}
}
final ValueConverter valueConverter = ConverterRegistry.getInstance().getValueConverter( targetType );
if ( valueConverter != null ) {
try {
return valueConverter.toPropertyValue( valueAsString );
} catch ( BeanException e ) {
throw new ReportProcessingException( Messages.getInstance().getString(
"ReportPlugin.unableToConvertParameter", parameter.getName(), valueAsString ) ); //$NON-NLS-1$
}
}
return rawValue;
}
private static Date parseDate( final ParameterDefinitionEntry parameterEntry, final ParameterContext context,
final String value ) throws ParseException {
try {
return parseDateStrict( parameterEntry, context, value );
} catch ( ParseException pe ) {
CommonUtil.checkStyleIgnore();
}
try {
// parse the legacy format that we used in 3.5.0-GA.
final Long dateAsLong = Long.parseLong( value );
return new Date( dateAsLong );
} catch ( NumberFormatException nfe ) {
// ignored
CommonUtil.checkStyleIgnore();
}
try {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd" ); // NON-NLS
return simpleDateFormat.parse( value );
} catch ( ParseException pe ) {
CommonUtil.checkStyleIgnore();
}
throw new ParseException( "Unable to parse Date", 0 );
}
private static Date parseDateStrict( final ParameterDefinitionEntry parameterEntry, final ParameterContext context,
final String value ) throws ParseException {
final String timezoneSpec =
parameterEntry.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.TIMEZONE, context );
if ( timezoneSpec == null || "server".equals( timezoneSpec ) ) { // NON-NLS
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ); // NON-NLS
return simpleDateFormat.parse( value );
} else if ( "utc".equals( timezoneSpec ) ) { // NON-NLS
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ); // NON-NLS
simpleDateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); // NON-NLS
return simpleDateFormat.parse( value );
} else if ( "client".equals( timezoneSpec ) ) { // NON-NLS
try {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ); // NON-NLS
return simpleDateFormat.parse( value );
} catch ( ParseException pe ) {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ); // NON-NLS
return simpleDateFormat.parse( value );
}
} else {
final TimeZone timeZone = TimeZone.getTimeZone( timezoneSpec );
// this never returns null, but if the timezone is not understood, we end up with GMT/UTC.
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ); // NON-NLS
simpleDateFormat.setTimeZone( timeZone );
return simpleDateFormat.parse( value );
}
}
}