/*!
* 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-2017 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.platform.plugin;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IParameterProvider;
import org.pentaho.platform.api.engine.IPluginManager;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.util.UUIDUtil;
import org.pentaho.plugin.jfreereport.reportcharts.ChartExpression;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.CompoundDataFactory;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.Section;
import org.pentaho.reporting.engine.classic.core.designtime.datafactory.DesignTimeDataFactoryContext;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.function.FormulaExpression;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionPropertyMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ExpressionRegistry;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlTableModule;
import org.pentaho.reporting.engine.classic.core.parameters.AbstractParameter;
import org.pentaho.reporting.engine.classic.core.parameters.DefaultListParameter;
import org.pentaho.reporting.engine.classic.core.parameters.DefaultParameterContext;
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.ParameterContextWrapper;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterDefinitionEntry;
import org.pentaho.reporting.engine.classic.core.parameters.ParameterValues;
import org.pentaho.reporting.engine.classic.core.parameters.PlainParameter;
import org.pentaho.reporting.engine.classic.core.parameters.ReportParameterDefinition;
import org.pentaho.reporting.engine.classic.core.parameters.StaticListParameter;
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.style.ElementStyleKeys;
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.engine.classic.extensions.drilldown.DrillDownProfile;
import org.pentaho.reporting.engine.classic.extensions.drilldown.DrillDownProfileMetaData;
import org.pentaho.reporting.libraries.base.util.HashNMap;
import org.pentaho.reporting.libraries.base.util.NullOutputStream;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.formula.DefaultFormulaContext;
import org.pentaho.reporting.libraries.formula.lvalues.DataTable;
import org.pentaho.reporting.libraries.formula.lvalues.FormulaFunction;
import org.pentaho.reporting.libraries.formula.lvalues.LValue;
import org.pentaho.reporting.libraries.formula.lvalues.StaticValue;
import org.pentaho.reporting.libraries.formula.parser.FormulaParser;
import org.pentaho.reporting.platform.plugin.messages.Messages;
import org.pentaho.reporting.platform.plugin.output.FastExportReportOutputHandlerFactory;
import org.pentaho.reporting.platform.plugin.output.ReportOutputHandlerFactory;
import org.springframework.web.util.HtmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.collect.Lists;
public class ParameterXmlContentHandler {
public static final String SYS_PARAM_ACCEPTED_PAGE = "accepted-page";
public static final String SYS_PARAM_RENDER_MODE = "renderMode";
public static final String SYS_PARAM_CONTENT_LINK = "::cl";
public static final String SYS_PARAM_SESSION_ID = "::session";
public static final String SYS_SERVER_NAMESPACE =
"http://reporting.pentaho.org/namespaces/engine/parameter-attributes/server";
private static final Log logger = LogFactory.getLog( ParameterXmlContentHandler.class );
private static final String SYS_PARAM_OUTPUT_TARGET = SimpleReportingComponent.OUTPUT_TARGET;
private static final String SYS_PARAM_DESTINATION = "destination";
private static final String GROUP_SYSTEM = "system";
private static final String GROUP_PARAMETERS = "parameters";
private static final String SYS_PARAM_TAB_NAME = "::TabName";
private static final String SYS_PARAM_TAB_ACTIVE = "::TabActive";
private static final String SYS_IGNORE_PARAM = "::org.pentaho.reporting";
private static final String SYS_PARAM_HTML_PROPORTIONAL_WIDTH = "htmlProportionalWidth";
private static final String CONFIG_PARAM_HTML_PROPORTIONAL_WIDTH =
"org.pentaho.reporting.engine.classic.core.modules.output.table.html.ProportionalColumnWidths";
private static final String SYS_PARAM_IS_QUERY_CONTROL_ENABLED = "query-limit-ui-enabled";
private static final String SYS_PARAM_REPORT_QUERY_LIMIT = "report-query-limit";
static final String SYS_PARAM_QUERY_LIMIT = "query-limit";
private static final String SYS_PARAM_MAX_QUERY_LIMIT = "maximum-query-limit";
// default visibility for testing purposes
Document document;
private Map<String, ParameterDefinitionEntry> systemParameter;
private final boolean paginate;
private final IParameterProvider requestParameters;
private final Map<String, Object> inputs;
public ParameterXmlContentHandler( final ParameterContentGenerator contentGenerator, final boolean paginate ) {
this.paginate = paginate;
inputs = contentGenerator.createInputs();
requestParameters = contentGenerator.getRequestParameters();
}
public static String convertParameterValueToString( final ParameterDefinitionEntry parameter,
final ParameterContext context, final Object value,
final Class<?> type ) throws BeanException {
if ( value == null ) {
return null;
}
// PIR-652
if ( value instanceof Object[] ) {
final Object[] o = (Object[]) value;
if ( o.length == 1 ) {
return String.valueOf( o[ 0 ] );
}
}
// PIR-724 - Convert String arrays to a single string with values separated by '|'
if ( value instanceof String[] ) {
final String[] o = (String[]) value;
return org.apache.commons.lang.StringUtils.join( o, '|' );
}
final ValueConverter valueConverter = ConverterRegistry.getInstance().getValueConverter( type );
if ( valueConverter == null ) {
return String.valueOf( value );
}
if ( Date.class.isAssignableFrom( type ) ) {
if ( ( value instanceof Date ) == false ) {
throw new BeanException( Messages.getInstance().getString( "ReportPlugin.errorNonDateParameterValue" ) );
}
final String timezone =
parameter.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.TIMEZONE, context );
final DateFormat dateFormat;
if ( ( timezone == null ) || "server".equals( timezone ) || //$NON-NLS-1$
"client".equals( timezone ) ) { //$NON-NLS-1$
// nothing needed ..
// for server: Just print it as it is, including the server timezone.
dateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ); //$NON-NLS-1$
} else {
// for convinience for the clients we send the date in the correct timezone.
final TimeZone timeZoneObject;
if ( "utc".equals( timezone ) ) { //$NON-NLS-1$
timeZoneObject = TimeZone.getTimeZone( "UTC" ); //$NON-NLS-1$
} else {
timeZoneObject = TimeZone.getTimeZone( timezone );
}
dateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ" ); //$NON-NLS-1$
dateFormat.setTimeZone( timeZoneObject );
}
final Date d = (Date) value;
return dateFormat.format( d );
}
if ( Number.class.isAssignableFrom( type ) ) {
final ValueConverter numConverter = ConverterRegistry.getInstance().getValueConverter( BigDecimal.class );
return numConverter.toAttributeValue( new BigDecimal( String.valueOf( value ) ) );
}
return valueConverter.toAttributeValue( value );
}
private static void appendPageCount( final SimpleReportingComponent reportComponent, final Element parameters )
throws Exception {
reportComponent.setOutputStream( new NullOutputStream() );
// so that we don't actually produce anything, we'll accept no pages in this mode
final int acceptedPage = reportComponent.getAcceptedPage();
reportComponent.setAcceptedPage( -1 );
// we can ONLY get the # of pages by asking the report to run
if ( reportComponent.validate() ) {
if ( !reportComponent.outputSupportsPagination() ) {
return;
}
final int totalPageCount = reportComponent.paginate();
parameters.setAttribute( SimpleReportingComponent.PAGINATE_OUTPUT, "true" ); //$NON-NLS-1$
parameters.setAttribute( "page-count", String.valueOf( totalPageCount ) ); //$NON-NLS-1$ //$NON-NLS-2$
// use the saved value (we changed it to -1 for performance)
parameters.setAttribute( SimpleReportingComponent.ACCEPTED_PAGE, String.valueOf( acceptedPage ) ); //$NON-NLS-1$
}
}
private IParameterProvider getRequestParameters() {
return requestParameters;
}
private Map<String, ParameterDefinitionEntry> getSystemParameter() {
if ( systemParameter == null ) {
final Map<String, ParameterDefinitionEntry> parameter = new LinkedHashMap<>();
parameter.put( SYS_PARAM_OUTPUT_TARGET, createOutputParameter() );
parameter.put( SYS_PARAM_CONTENT_LINK, createContentLinkingParameter() ); // NON-NLS
parameter.put( SYS_PARAM_TAB_NAME, createGenericSystemParameter( SYS_PARAM_TAB_NAME, false, true ) ); // NON-NLS
parameter.put( SYS_PARAM_TAB_ACTIVE,
createGenericBooleanSystemParameter( SYS_PARAM_TAB_ACTIVE, false, true ) ); // NON-NLS
// parameter.put("solution", createGenericSystemParameter("solution", false, false)); // NON-NLS
parameter.put( "yield-rate", createGenericIntSystemParameter( "yield-rate", false, false ) ); // NON-NLS
parameter.put( SYS_PARAM_ACCEPTED_PAGE,
createGenericIntSystemParameter( SYS_PARAM_ACCEPTED_PAGE, false, false ) ); // NON-NLS
parameter.put( SYS_PARAM_SESSION_ID,
createGenericSystemParameter( SYS_PARAM_SESSION_ID, false, false ) ); // NON-NLS
parameter.put( "path", createGenericSystemParameter( "path", false, false ) ); // NON-NLS
// parameter.put("name", createGenericSystemParameter("name", false, false)); // NON-NLS
// parameter.put("action", createGenericSystemParameter("action", true, false)); // NON-NLS
parameter.put( "id", createGenericSystemParameter( "id", false, false ) ); // NON-NLS
parameter.put( "output-type", createGenericSystemParameter( "output-type", true, false ) ); // NON-NLS
parameter.put( "layout", createGenericSystemParameter( "layout", true, false ) ); // NON-NLS
parameter.put( "content-handler-pattern",
createGenericSystemParameter( "content-handler-pattern", true, false ) ); // NON-NLS
parameter.put( "autoSubmit", createGenericBooleanSystemParameter( "autoSubmit", true, true ) ); // NON-NLS
parameter.put( "autoSubmitUI", createGenericBooleanSystemParameter( "autoSubmitUI", true, true ) ); // NON-NLS
parameter.put( "dashboard-mode",
createGenericBooleanSystemParameter( "dashboard-mode", false, true ) ); // NON-NLS
parameter.put( "showParameters", createGenericBooleanSystemParameter( "showParameters", true, true ) ); // NON-NLS
parameter.put( "paginate", createGenericBooleanSystemParameter( "paginate", true, false ) ); // NON-NLS
parameter.put( "ignoreDefaultDates",
createGenericBooleanSystemParameter( "ignoreDefaultDates", true, false ) ); // NON-NLS
parameter.put( "print", createGenericBooleanSystemParameter( "print", false, false ) ); // NON-NLS
parameter.put( "printer-name", createGenericSystemParameter( "printer-name", false, false ) ); // NON-NLS
parameter.put( SYS_PARAM_RENDER_MODE, createRenderModeSystemParameter() ); // NON-NLS
parameter.put( SYS_PARAM_HTML_PROPORTIONAL_WIDTH, createGenericBooleanSystemParameter(
SYS_PARAM_HTML_PROPORTIONAL_WIDTH, false, true ) );
parameter.put( SYS_PARAM_IS_QUERY_CONTROL_ENABLED, createGenericBooleanSystemParameter( SYS_PARAM_IS_QUERY_CONTROL_ENABLED, false, false ) );
parameter.put( SYS_PARAM_QUERY_LIMIT, createGenericIntSystemParameter( SYS_PARAM_QUERY_LIMIT, false, false ) );
parameter.put( SYS_PARAM_MAX_QUERY_LIMIT, createGenericIntSystemParameter( SYS_PARAM_MAX_QUERY_LIMIT, false, false ) );
systemParameter = Collections.unmodifiableMap( parameter );
}
return systemParameter;
}
/**
* Defines whether parameter with display-type "datepicker" that have no default value set shall default to "today".
* This setting generates a default value for the parameter UI, but has no effect otherwise. It is flawed from the
* very beginning and should not be used.
*
* @return whether we generate default dates.
*/
private boolean isGenerateDefaultDates() {
final Object value = inputs.get( "ignoreDefaultDates" ); // NON-NLS
if ( value == null ) {
// we do not generate default dates until it is explicitly requested.
// if the users want default values for parameters then let them define those in the parameter
return false;
}
return "true".equals( value );
}
public void createParameterContent( final OutputStream outputStream, final Serializable fileId, final String path,
boolean overrideOutputType, MasterReport report ) throws Exception {
final Object rawSessionId = inputs.get( ParameterXmlContentHandler.SYS_PARAM_SESSION_ID );
if ( ( ( rawSessionId instanceof String ) == false ) || "".equals( rawSessionId ) ) {
inputs.put( ParameterXmlContentHandler.SYS_PARAM_SESSION_ID, UUIDUtil.getUUIDAsString() );
}
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final IParameterProvider requestParams = getRequestParameters();
final SimpleReportingComponent reportComponent = new SimpleReportingComponent();
reportComponent.setReportFileId( fileId );
if ( report != null ) {
reportComponent.setReport( report );
}
reportComponent.setPaginateOutput( true );
final boolean isMobile =
"true".equals( requestParams.getStringParameter( "mobile", "false" ) ); //$NON-NLS-1$ //$NON-NLS-2$
if ( isMobile ) {
overrideOutputType = true;
reportComponent.setForceDefaultOutputTarget( true );
} else {
reportComponent.setForceDefaultOutputTarget( overrideOutputType );
}
reportComponent.setDefaultOutputTarget( HtmlTableModule.TABLE_HTML_PAGE_EXPORT_TYPE );
if ( path.endsWith( ".prpti" ) && ( requestParams.getParameter( "json" ) == null ) ) {
reportComponent.setForceUnlockPreferredOutput( true );
}
reportComponent.setInputs( inputs );
report = reportComponent.getReport();
final DefaultParameterContext parameterContext = new DefaultParameterContext( report );
final ValidationResult vr;
final Element parameters;
try {
// apply inputs to parameters
final ValidationResult validationResult =
ReportContentUtil.applyInputsToReportParameters( report, parameterContext, inputs, new ValidationResult() );
final ReportParameterDefinition reportParameterDefinition = report.getParameterDefinition();
vr =
reportParameterDefinition.getValidator().validate( validationResult, reportParameterDefinition,
parameterContext );
// determine dependent parameters
HashNMap<String, String> dependencies = null;
try {
dependencies = getDependentParameters( vr.getParameterValues(), parameterContext, report );
} catch ( final ReportDataFactoryException e ) {
logger.debug( "unable to determine dependent parameters", e );
}
parameters = document.createElement( GROUP_PARAMETERS ); //$NON-NLS-1$
parameters
.setAttribute( "is-prompt-needed", String.valueOf( vr.isEmpty() == false ) ); //$NON-NLS-1$ //$NON-NLS-2$
parameters.setAttribute( "ignore-biserver-5538", "true" );
// check if pagination is allowed and turned on
final Boolean autoSubmitFlag =
requestFlag( "autoSubmit", report, AttributeNames.Core.NAMESPACE, AttributeNames.Core.AUTO_SUBMIT_PARAMETER,
"org.pentaho.reporting.engine.classic.core.ParameterAutoSubmit" );
if ( Boolean.TRUE.equals( autoSubmitFlag ) ) {
parameters.setAttribute( "autoSubmit", "true" );
} else if ( Boolean.FALSE.equals( autoSubmitFlag ) ) {
parameters.setAttribute( "autoSubmit", "false" );
}
final Boolean autoSubmitUiFlag =
requestFlag( "autoSubmitUI",
report, // NON-NLS
AttributeNames.Core.NAMESPACE, AttributeNames.Core.AUTO_SUBMIT_DEFAULT,
"org.pentaho.reporting.engine.classic.core.ParameterAutoSubmitUI" );
if ( Boolean.FALSE.equals( autoSubmitUiFlag ) ) {
parameters.setAttribute( "autoSubmitUI", "false" ); // NON-NLS
} else {
parameters.setAttribute( "autoSubmitUI", "true" ); // NON-NLS
}
parameters.setAttribute( "layout", requestConfiguration( "layout",
report, // NON-NLS
AttributeNames.Core.NAMESPACE, AttributeNames.Core.PARAMETER_UI_LAYOUT,
"org.pentaho.reporting.engine.classic.core.ParameterUiLayout" ) );
final ParameterDefinitionEntry[] parameterDefinitions = reportParameterDefinition.getParameterDefinitions();
// Collect all parameter, but allow user-parameter to override system parameter.
// It is the user's problem if the types do not match and weird errors occur, but
// there are sensible usecases where this should be allowed.
// System parameter must come last in the list, as this is how it was done in the original
// version and this is how people expect it to be now.
final LinkedHashMap<String, ParameterDefinitionEntry> reportParameters =
new LinkedHashMap<>();
for ( final ParameterDefinitionEntry parameter : parameterDefinitions ) {
reportParameters.put( parameter.getName(), parameter );
}
for ( final Map.Entry<String, ParameterDefinitionEntry> entry : getSystemParameter().entrySet() ) {
if ( reportParameters.containsKey( entry.getKey() ) == false ) {
reportParameters.put( entry.getKey(), entry.getValue() );
}
}
if ( overrideOutputType ) {
final ParameterDefinitionEntry definitionEntry = reportParameters.get( SimpleReportingComponent.OUTPUT_TARGET );
if ( definitionEntry instanceof AbstractParameter ) {
final AbstractParameter parameter = (AbstractParameter) definitionEntry;
parameter.setHidden( true );
parameter.setMandatory( false );
}
} else {
hideOutputParameterIfLocked( report, reportParameters );
}
final Map<String, Object> inputs =
computeRealInput( parameterContext, reportParameters, reportComponent.getComputedOutputTarget(), vr );
final Boolean showParameterUI = requestFlag( "showParameters", report, // NON-NLS
AttributeNames.Core.NAMESPACE, AttributeNames.Core.SHOW_PARAMETER_UI, null );
if ( Boolean.FALSE.equals( showParameterUI ) ) {
inputs.put( "showParameters", Boolean.FALSE ); // NON-NLS
} else {
inputs.put( "showParameters", Boolean.TRUE ); // NON-NLS
}
// Adding proportional width config parameter
final String proportionalWidth =
report.getReportConfiguration().getConfigProperty( CONFIG_PARAM_HTML_PROPORTIONAL_WIDTH );
inputs.put( SYS_PARAM_HTML_PROPORTIONAL_WIDTH, Boolean.valueOf( proportionalWidth ) );
// Adding row limit config parameters
Boolean isQueryLimitControlEnabled = false;
Integer maxQueryLimit = null;
final IPluginManager pm = PentahoSystem.get( IPluginManager.class );
if ( pm != null ) {
isQueryLimitControlEnabled = Boolean.parseBoolean(
(String) pm.getPluginSetting( "reporting", "settings/query-limit-ui-enabled", "false" ) );
final Expression expression = report.getAttributeExpression( AttributeNames.Internal.NAMESPACE, AttributeNames.Internal.QUERY_LIMIT );
if ( expression != null ) {
isQueryLimitControlEnabled = false;
}
maxQueryLimit = isQueryLimitControlEnabled ? NumberUtils.toInt( (String) pm.getPluginSetting( "reporting", "settings/query-limit", "0" ), 0 ) : 0;
}
inputs.put( SYS_PARAM_IS_QUERY_CONTROL_ENABLED, isQueryLimitControlEnabled );
inputs.put( SYS_PARAM_REPORT_QUERY_LIMIT, report.getQueryLimit() );
inputs.put( SYS_PARAM_QUERY_LIMIT, report.getQueryLimit() );
inputs.put( SYS_PARAM_MAX_QUERY_LIMIT, maxQueryLimit );
//get changed parameters from request
final String[] changedParamsQuery = requestParams.getStringArrayParameter( "changedParameters", null );
appendParametersList( parameterContext, vr, parameters, dependencies, reportParameters, inputs,
changedParamsQuery );
if ( vr.isEmpty() == false ) {
parameters.appendChild( createErrorElements( vr ) );
}
final String[] outputParameter = new OutputParameterCollector().collectParameter( report );
for ( final String outputParameterName : outputParameter ) {
// <output-parameter displayName="Territory" id="[Markets].[Territory]"/>
final Element element = document.createElement( "output-parameter" ); // NON-NLS
element.setAttribute( "displayName", outputParameterName ); // NON-NLS
element.setAttribute( "id", outputParameterName ); // NON-NLS
parameters.appendChild( element );
}
if ( vr.isEmpty() && paginate
&& reportComponent.getComputedOutputTarget()
.equals( HtmlTableModule.TABLE_HTML_PAGE_EXPORT_TYPE ) ) { //$NON-NLS-1$ //$NON-NLS-2$
appendPageCount( reportComponent, parameters );
}
document.appendChild( parameters );
final DOMSource source = new DOMSource( document );
final StreamResult result = new StreamResult( outputStream );
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform( source, result );
// close parameter context
} finally {
parameterContext.close();
}
}
protected void appendParametersList( final DefaultParameterContext parameterContext,
final ValidationResult vr,
final Element parameters,
final HashNMap<String, String> dependencies,
final LinkedHashMap<String, ParameterDefinitionEntry> reportParameters,
final Map<String, Object> inputs,
final String[] changedParamsQuery )
throws CloneNotSupportedException, BeanException, ReportDataFactoryException {
if ( ( changedParamsQuery != null ) && ( changedParamsQuery.length > 0 ) ) {
final Set<Object> changedParameters = new HashSet<>( Arrays.asList( changedParamsQuery ) );
// Changed parameters and their dependencies
final Set<Object> changedWithDependencies = addAllDependencies( dependencies, changedParameters );
// Filter definitions
final List<ParameterDefinitionEntry> changedParamDefinitions =
reportParameters.values().stream().filter( p -> changedWithDependencies.contains( p.getName() ) ).collect(
Collectors
.toList() );
//We only need the top level parents in order to erase children selections in lists
final Set<Object> effectivelyChanged = filterChildren( changedParameters, dependencies );
// Filtered list without attributes
renderParameters( parameterContext, vr, parameters, new HashNMap<>(), changedParamDefinitions, effectivelyChanged,
inputs, Boolean.TRUE );
parameters.setAttribute( "minimized", "true" );
} else {
// Initial parameter call or not async mode
renderParameters( parameterContext, vr, parameters, dependencies, reportParameters.values(), null,
inputs, Boolean.FALSE );
}
}
protected void renderParameters( final DefaultParameterContext parameterContext,
final ValidationResult vr,
final Element parameters,
final HashNMap<String, String> dependencies,
final Collection<ParameterDefinitionEntry> reportParameters,
final Set<Object> changedParameters,
final Map<String, Object> inputs,
final boolean ignoreAttributes ) throws BeanException, ReportDataFactoryException {
for ( final ParameterDefinitionEntry parameter : reportParameters ) {
final Object selections = getSelections( parameter, changedParameters, inputs );
final ParameterContextWrapper wrapper =
new ParameterContextWrapper( parameterContext, vr.getParameterValues() );
final Element el = createParameterElement( parameter, wrapper, selections, dependencies, ignoreAttributes );
createParameterDependencies( el, parameter, dependencies );
parameters.appendChild( el );
}
}
protected Object getSelections( final ParameterDefinitionEntry parameter, final Set<Object> changedParameters,
final Map<String, Object> inputs ) {
final String pName = parameter.getName();
if ( null == changedParameters || changedParameters.isEmpty() ) {
return inputs.get( pName );
}
final boolean isList = parameter instanceof ListParameter;
if ( !isList ) {
//Don't mess up with plain parameters or you risk to ruin default values
return inputs.get( pName );
}
final ListParameter listParameter = (ListParameter) parameter;
// is the selections verified
final boolean isVerifiedValue = listParameter.isStrictValueCheck();
// otherwise the parameter is dependent and the selections are outdated
final boolean ifChangedParameter = ( changedParameters != null ) && changedParameters.contains( pName );
return isVerifiedValue || ifChangedParameter ? inputs.get( pName ) : null;
}
HashNMap<String, String> getDependentParameters( final ReportParameterValues computedParameterValues,
final ParameterContext parameterContext, final MasterReport report )
throws ReportDataFactoryException {
final HashNMap<String, String> downstreamParams = new HashNMap<>();
final ReportParameterDefinition reportParameterDefinition = report.getParameterDefinition();
final DesignTimeDataFactoryContext factoryContext = new DesignTimeDataFactoryContext( report );
for ( final ParameterDefinitionEntry entry : reportParameterDefinition.getParameterDefinitions() ) {
// default list parameter is only dynamic values provider now.
if ( entry instanceof DefaultListParameter ) {
final DefaultListParameter listParameter = (DefaultListParameter) entry;
// inspect for dependent fields
final String queryName = listParameter.getQueryName();
if ( queryName != null ) {
final CompoundDataFactory cdf = CompoundDataFactory.normalize( report.getDataFactory() );
final DataFactory dataFactoryForQuery = cdf.getDataFactoryForQuery( queryName );
if ( dataFactoryForQuery != null ) {
dataFactoryForQuery.initialize( factoryContext );
final String[] fields = dataFactoryForQuery.getMetaData()
.getReferencedFields( dataFactoryForQuery, queryName, computedParameterValues );
if ( fields != null ) {
for ( final String field : fields ) {
if ( !field.startsWith( SYS_IGNORE_PARAM ) ) {
downstreamParams.add( field, entry.getName() );
}
}
}
}
}
}
// inspect post proc formulas, def values, etc.
computeNormalLineage( parameterContext, entry, downstreamParams );
}
return downstreamParams;
}
private Set<Object> filterChildren( final Set<Object> changedParameters,
final HashNMap<String, String> dependencies ) {
if ( changedParameters == null || changedParameters.isEmpty() ) {
return Collections.emptySet();
}
final Set<Object> effectivelyChanged = new HashSet<>( changedParameters );
if ( ( dependencies != null ) && !dependencies.isEmpty() ) {
final Iterator<String> keyIterator = dependencies.keys();
while ( keyIterator.hasNext() ) {
final String parent = keyIterator.next();
if ( changedParameters.contains( parent ) ) {
final Iterator<String> valuesIterator = dependencies.getAll( parent );
while ( valuesIterator.hasNext() ) {
final String child = valuesIterator.next();
effectivelyChanged.remove( child );
}
}
}
}
return effectivelyChanged;
}
private Set<Object> addAllDependencies( final HashNMap<String, String> dependencies, final Set<Object> changedNames )
throws CloneNotSupportedException {
if ( ( dependencies != null ) && !dependencies.isEmpty() ) {
final Set<Object> destination = new HashSet<>( changedNames );
final Iterator<Object> changedIterator = changedNames.iterator();
while ( changedIterator.hasNext() ) {
final String nextName = String.valueOf( changedIterator.next() );
fillParams( destination, dependencies, nextName );
}
return destination;
}
return changedNames;
}
private void fillParams( final Set<Object> destination, final HashNMap<String, String> dependencies,
final String paramName ) {
final Iterator<String> all = dependencies.getAll( paramName );
if ( ( all != null ) && all.hasNext() ) {
while ( all.hasNext() ) {
final String nextName = all.next();
if ( !destination.contains( nextName ) ) {
destination.add( nextName );
fillParams( destination, dependencies, nextName );
}
}
}
}
void computeNormalLineage( final ParameterContext pc, final ParameterDefinitionEntry pe, final HashNMap<String, String> m ) {
final String name = pe.getName();
final String defValue =
pe.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.DEFAULT_VALUE_FORMULA, pc );
final String postProc =
pe.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.POST_PROCESSOR_FORMULA, pc );
final String formula =
pe.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.DISPLAY_VALUE_FORMULA, pc );
for ( final String field : analyzeFormula( defValue ) ) {
m.add( field, name );
}
for ( final String field : analyzeFormula( formula ) ) {
m.add( field, name );
}
for ( final String field : analyzeFormula( postProc ) ) {
m.add( field, name );
}
}
private String[] analyzeFormula( final String formula ) {
if ( formula == null ) {
return new String[ 0 ];
}
final FormulaExpression fe = new FormulaExpression();
fe.setFormula( formula );
final ExpressionMetaData md =
ExpressionRegistry.getInstance().getExpressionMetaData( fe.getClass().getName() );
final ExpressionPropertyMetaData pd = md.getPropertyDescription( "formula" );
return pd.getReferencedFields( fe, fe.getFormula() );
}
private Map<String, Object> computeRealInput( final ParameterContext parameterContext,
final LinkedHashMap<String, ParameterDefinitionEntry> reportParameters,
final String computedOutputTarget,
final ValidationResult result ) {
final Map<String, Object> realInputs = new HashMap<>();
realInputs.put( SYS_PARAM_DESTINATION, lookupDestination() );
final ReportParameterValues parameterValues = result.getParameterValues();
for ( final ParameterDefinitionEntry parameter : reportParameters.values() ) {
final String parameterName = parameter.getName();
final Object parameterFromReport = parameterValues.get( parameterName );
if ( parameterFromReport != null ) {
// always prefer the report parameter. The user's input has been filtered already and values
// may have been replaced by a post-processing formula.
//
realInputs.put( parameterName, parameterFromReport );
continue;
}
// the parameter values collection only contains declared parameter. So everything else will
// be handled now. This is also the time to handle rejected parameter. For these parameter,
// the calculated value for the report is <null>.
final Object value = inputs.get( parameterName );
if ( value == null ) {
// have no value, so we use the default value ..
realInputs.put( parameterName, null );
continue;
}
try {
final Object translatedValue = ReportContentUtil.computeParameterValue( parameterContext, parameter, value );
if ( translatedValue != null ) {
realInputs.put( parameterName, translatedValue );
} else {
realInputs.put( parameterName, null );
}
} catch ( final Exception be ) {
if ( logger.isDebugEnabled() ) {
logger.debug( Messages.getInstance().getString( "ReportPlugin.debugParameterCannotBeConverted",
parameter.getName(), String.valueOf( value ) ), be );
}
}
}
// thou cannot override the output target with invalid values ..
realInputs.put( SYS_PARAM_OUTPUT_TARGET, computedOutputTarget );
return realInputs;
}
private void hideOutputParameterIfLocked( final MasterReport report,
final Map<String, ParameterDefinitionEntry> reportParameters ) {
final boolean lockOutputType =
Boolean.TRUE.equals( report.getAttribute( AttributeNames.Core.NAMESPACE,
AttributeNames.Core.LOCK_PREFERRED_OUTPUT_TYPE ) );
final ParameterDefinitionEntry definitionEntry = reportParameters.get( SimpleReportingComponent.OUTPUT_TARGET );
if ( definitionEntry instanceof AbstractParameter ) {
final AbstractParameter parameter = (AbstractParameter) definitionEntry;
parameter.setHidden( lockOutputType );
parameter.setMandatory( !lockOutputType );
}
}
// default visibility for testing purposes
void createParameterDependencies( final Element element, final ParameterDefinitionEntry parameter,
final HashNMap dependencies ) {
if ( ( element == null ) || ( parameter == null ) ) {
throw new NullPointerException();
}
if ( ( dependencies == null ) || dependencies.isEmpty() || !dependencies.containsKey( parameter.getName() ) ) {
return;
}
final Element valuesElement = document.createElement( "dependencies" );
final Iterator it = dependencies.getAll( parameter.getName() );
while ( it.hasNext() ) {
final Object rName = it.next();
final String dependency = String.valueOf( rName );
final Element name = document.createElement( "name" );
name.setTextContent( dependency );
valuesElement.appendChild( name );
}
element.appendChild( valuesElement );
}
Element createParameterElement( final ParameterDefinitionEntry parameter,
final ParameterContext parameterContext, final Object selections,
final HashNMap<String, String> dependencies,
final boolean ignoreAttributes )
throws BeanException,
ReportDataFactoryException {
try {
final Element parameterElement = document.createElement( "parameter" ); //$NON-NLS-1$
parameterElement.setAttribute( "name", parameter.getName() ); //$NON-NLS-1$
final Class<?> valueType = parameter.getValueType();
parameterElement.setAttribute( "type", valueType.getName() ); //$NON-NLS-1$
parameterElement
.setAttribute( "is-mandatory", String.valueOf( parameter.isMandatory() ) ); //$NON-NLS-1$ //$NON-NLS-2$
final String[] namespaces = parameter.getParameterAttributeNamespaces();
boolean isMustValidateOnServerSet = false;
if ( !ignoreAttributes ) {
for ( final String namespace : namespaces ) {
final String[] attributeNames = parameter.getParameterAttributeNames( namespace );
for ( final String attributeName : attributeNames ) {
final String attributeValue = parameter.getTranslatedParameterAttribute( namespace, attributeName, parameterContext );
// expecting: label, parameter-render-type, parameter-layout
// but others possible as well, so we set them all
final Element attributeElement = document.createElement( "attribute" ); // NON-NLS
attributeElement.setAttribute( "namespace", namespace ); // NON-NLS
attributeElement.setAttribute( "name", attributeName ); // NON-NLS
attributeElement.setAttribute( "value", attributeValue ); // NON-NLS
parameterElement.appendChild( attributeElement );
if ( isTextFieldButNotString( valueType, attributeValue ) || ParameterAttributeNames.Core.POST_PROCESSOR_FORMULA.equals( attributeName )
|| ( ( ParameterAttributeNames.Core.RE_EVALUATE_ON_FAILED_VALUES.equals( attributeName ) || ParameterAttributeNames.Core.AUTOFILL_SELECTION.equals( attributeName ) ) && "true".equals( attributeValue ) ) ) {
// must validate on server
final Element attrElement = document.createElement( "attribute" );
attrElement.setAttribute( "namespace", SYS_SERVER_NAMESPACE );
attrElement.setAttribute( "name", "must-validate-on-server" );
attrElement.setAttribute( "value", Boolean.TRUE.toString() );
parameterElement.appendChild( attrElement );
isMustValidateOnServerSet = true;
}
}
}
}
//parameter dependencies: see backlog-7980
if ( ( dependencies != null ) && !dependencies.isEmpty() && dependencies.getAll( parameter.getName() ).hasNext() && !ignoreAttributes ) {
final List<String> deps = Lists.newArrayList( dependencies.getAll( parameter.getName() ) );
if ( !deps.isEmpty() ) {
if ( !isMustValidateOnServerSet ) {
// must validate on server
final Element attributeElement = document.createElement( "attribute" );
attributeElement.setAttribute( "namespace", SYS_SERVER_NAMESPACE );
attributeElement.setAttribute( "name", "must-validate-on-server" );
attributeElement.setAttribute( "value", Boolean.TRUE.toString() );
parameterElement.appendChild( attributeElement );
}
// and it is also has a dependencies
final Element oneMoreAttribute = document.createElement( "attribute" );
oneMoreAttribute.setAttribute( "namespace", SYS_SERVER_NAMESPACE );
oneMoreAttribute.setAttribute( "name", "has-downstream-dependent-parameter" );
oneMoreAttribute.setAttribute( "value", Boolean.TRUE.toString() );
parameterElement.appendChild( oneMoreAttribute );
}
}
final Class<?> elementValueType;
if ( valueType.isArray() ) {
elementValueType = valueType.getComponentType();
} else {
elementValueType = valueType;
}
final LinkedHashSet<Object> selectionSet = new LinkedHashSet<>();
if ( selections != null ) {
if ( selections.getClass().isArray() ) {
final int length = Array.getLength( selections );
for ( int i = 0; i < length; i++ ) {
final Object value = Array.get( selections, i );
selectionSet.add( resolveSelectionValue( value ) );
}
} else {
selectionSet.add( resolveSelectionValue( selections ) );
}
} else {
final String type =
parameter.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.TYPE,
parameterContext );
if ( ParameterAttributeNames.Core.TYPE_DATEPICKER.equals( type ) && Date.class.isAssignableFrom( valueType ) ) {
if ( isGenerateDefaultDates() ) {
selectionSet.add( new Date() );
}
}
}
if ( Date.class.isAssignableFrom( elementValueType ) ) {
parameterElement
.setAttribute( "timezone-hint",
computeTimeZoneHint( parameter, parameterContext, selectionSet ) ); //$NON-NLS-1$
}
@SuppressWarnings( "rawtypes" )
final LinkedHashSet handledValues = (LinkedHashSet) selectionSet.clone();
if ( parameter instanceof ListParameter ) {
final ListParameter asListParam = (ListParameter) parameter;
parameterElement.setAttribute( "is-multi-select",
String.valueOf( asListParam.isAllowMultiSelection() ) ); //$NON-NLS-1$ //$NON-NLS-2$
parameterElement
.setAttribute( "is-strict", String.valueOf( asListParam.isStrictValueCheck() ) ); //$NON-NLS-1$ //$NON-NLS-2$
parameterElement.setAttribute( "is-list", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
final Element valuesElement = document.createElement( "values" ); //$NON-NLS-1$
parameterElement.appendChild( valuesElement );
final ParameterValues possibleValues = asListParam.getValues( parameterContext );
for ( int i = 0; i < possibleValues.getRowCount(); i++ ) {
Object key = possibleValues.getKeyValue( i );
Object value = possibleValues.getTextValue( i );
final Element valueElement = document.createElement( "value" ); //$NON-NLS-1$
valuesElement.appendChild( valueElement );
if ( hasISOControlChars( key, elementValueType ) || hasISOControlChars( value, elementValueType ) ) {
// if either key or value have illegal chars, base64 encode them
// and set the encoded="true" flag.
key = Base64.encodeBase64String( key.toString().getBytes() );
value = Base64.encodeBase64String( value.toString().getBytes() );
valueElement.setAttribute( "encoded", "true" );
}
valueElement
.setAttribute( "label", HtmlUtils.htmlEscape( String.valueOf( value ) ) ); //$NON-NLS-1$ //$NON-NLS-2$
valueElement.setAttribute( "type", elementValueType.getName() ); //$NON-NLS-1$
if ( key instanceof Number ) {
final BigDecimal bd = new BigDecimal( String.valueOf( key ) );
valueElement.setAttribute( "selected", String.valueOf( selectionSet.contains( bd ) ) ); //$NON-NLS-1$
handledValues.remove( bd );
} else if ( key instanceof Timestamp ) {
final Timestamp origKey = (Timestamp) possibleValues.getKeyValue( i );
valueElement.setAttribute( "selected",
String.valueOf( selectionSet.contains( new Date( origKey.getTime() ) ) ) ); //$NON-NLS-1$
handledValues.remove( key );
} else if ( key == null ) {
if ( ( selections == null ) || selectionSet.contains( null ) ) {
valueElement.setAttribute( "selected", "true" ); //$NON-NLS-1$
handledValues.remove( null );
}
} else {
// key may have been encoded, we want the original raw value.
final Object origKey = possibleValues.getKeyValue( i );
valueElement.setAttribute( "selected", String.valueOf( selectionSet.contains( origKey ) ) ); //$NON-NLS-1$
handledValues.remove( key );
}
if ( key == null ) {
valueElement.setAttribute( "null", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
} else {
valueElement.setAttribute( "null", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
valueElement.setAttribute( "value", convertParameterValueToString( parameter, parameterContext, key,
elementValueType ) ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
// Only add invalid values to the selection list for non-strict parameters
if ( !asListParam.isStrictValueCheck() ) {
for ( final Object key : handledValues ) {
final Element valueElement = document.createElement( "value" ); //$NON-NLS-1$
valuesElement.appendChild( valueElement );
valueElement.setAttribute(
"label", Messages.getInstance()
.getString( "ReportPlugin.autoParameter", String.valueOf( key ) ) ); //$NON-NLS-1$ //$NON-NLS-2$
valueElement.setAttribute( "type", elementValueType.getName() ); //$NON-NLS-1$
if ( key instanceof Number ) {
final BigDecimal bd = new BigDecimal( String.valueOf( key ) );
valueElement.setAttribute( "selected", String.valueOf( selectionSet.contains( bd ) ) ); //$NON-NLS-1$
} else {
valueElement.setAttribute( "selected", String.valueOf( selectionSet.contains( key ) ) ); //$NON-NLS-1$
}
if ( key == null ) {
valueElement.setAttribute( "null", "true" ); //$NON-NLS-1$ //$NON-NLS-2$
} else {
valueElement.setAttribute( "null", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
valueElement.setAttribute( "value", convertParameterValueToString( parameter, parameterContext, key,
elementValueType ) ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
} else if ( parameter instanceof PlainParameter ) {
// apply defaults, this is the easy case
parameterElement.setAttribute( "is-multi-select", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
parameterElement.setAttribute( "is-strict", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
parameterElement.setAttribute( "is-list", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
if ( selections != null ) {
final Element valuesElement = document.createElement( "values" ); //$NON-NLS-1$
parameterElement.appendChild( valuesElement );
final Element valueElement = document.createElement( "value" ); //$NON-NLS-1$
valuesElement.appendChild( valueElement );
valueElement.setAttribute( "type", valueType.getName() ); //$NON-NLS-1$
valueElement.setAttribute( "selected", "true" ); //$NON-NLS-1$
valueElement.setAttribute( "null", "false" ); //$NON-NLS-1$ //$NON-NLS-2$
final String value = convertParameterValueToString( parameter, parameterContext, selections, valueType );
valueElement.setAttribute( "value", value ); //$NON-NLS-1$ //$NON-NLS-2$
valueElement.setAttribute( "label", HtmlUtils.htmlEscape( value ) ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
return parameterElement;
} catch ( final BeanException be ) {
logger.error( Messages.getInstance().getString( "ReportPlugin.errorFailedToGenerateParameter",
parameter.getName(), String.valueOf( selections ) ), be );
throw be;
}
}
private boolean isTextFieldButNotString( final Class<?> valueType, final String attributeValue ) {
return ( ParameterAttributeNames.Core.TYPE_TEXTBOX.equals( attributeValue )
|| ParameterAttributeNames.Core.TYPE_MULTILINE.equals( attributeValue ) )
&& !String.class.equals( valueType );
}
/**
* Determine whether value contains any ISO control characters, which are not allowed in XML.
* http://www.w3.org/TR/2006/REC-xml11-20060816/#charsets
*/
private boolean hasISOControlChars( final Object value, final Class<?> type ) {
if ( value == null ) {
return false;
}
if ( type == String.class ) {
final String string = value.toString();
for ( int i = 0; i < string.length(); i++ ) {
if ( Character.isISOControl( string.charAt( i ) ) ) {
return true;
}
}
return false;
} else if ( type == Character.class ) {
return Character.isISOControl( ( (Character) value ) );
}
return false;
}
private Object resolveSelectionValue( final Object value ) {
// convert all numerics to BigDecimals for cross-numeric-class matching
if ( value instanceof Number ) {
return new BigDecimal( String.valueOf( value.toString() ) );
} else {
return value;
}
}
private String computeTimeZoneHint( final ParameterDefinitionEntry parameter,
final ParameterContext parameterContext,
final LinkedHashSet<Object> selectionSet ) {
// add a timezone hint ..
final String timezoneSpec =
parameter.getParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.TIMEZONE,
parameterContext );
if ( "client".equals( timezoneSpec ) ) { //$NON-NLS-1$
return ( "" );
} else {
final TimeZone timeZone;
final StringBuffer value = new StringBuffer();
if ( ( timezoneSpec == null ) || "server".equals( timezoneSpec ) ) { //$NON-NLS-1$
timeZone = TimeZone.getDefault();
} else if ( "utc".equals( timezoneSpec ) ) { //$NON-NLS-1$
timeZone = TimeZone.getTimeZone( "UTC" ); //$NON-NLS-1$
} else {
timeZone = TimeZone.getTimeZone( timezoneSpec );
}
final int offset;
if ( ( selectionSet != null ) && ( selectionSet.size() > 0 ) ) {
final Date date = (Date) selectionSet.iterator().next();
offset = timeZone.getOffset( date.getTime() );
} else {
offset = timeZone.getRawOffset();
}
if ( offset < 0 ) {
value.append( "-" );
} else {
value.append( "\\+" );
}
final int seconds = Math.abs( offset / 1000 );
final int minutesRaw = seconds / 60;
final int hours = minutesRaw / 60;
final int minutes = minutesRaw % 60;
if ( hours < 10 ) {
value.append( "0" );
}
value.append( hours );
if ( minutes < 10 ) {
value.append( "0" );
}
value.append( minutes );
return value.toString();
}
}
// default visibility for testing purposes
Element createErrorElements( final ValidationResult vr ) {
final Element errors = document.createElement( "errors" ); //$NON-NLS-1$
for ( final String property : vr.getProperties() ) {
for ( final ValidationMessage message : vr.getErrors( property ) ) {
final Element error = document.createElement( "error" ); //$NON-NLS-1$
error.setAttribute( "parameter", property ); //$NON-NLS-1$
error.setAttribute( "message", message.getMessage() ); //$NON-NLS-1$
errors.appendChild( error );
}
}
final ValidationMessage[] globalMessages = vr.getErrors();
for ( final ValidationMessage globalMessage : globalMessages ) {
final Element error = document.createElement( "global-error" ); //$NON-NLS-1$
error.setAttribute( "message", globalMessage.getMessage() ); //$NON-NLS-1$
errors.appendChild( error );
}
return errors;
}
private PlainParameter createGenericSystemParameter( final String parameterName, final boolean deprecated,
final boolean preferredParameter ) {
return createGenericSystemParameter( parameterName, deprecated, preferredParameter, String.class );
}
private PlainParameter createGenericSystemParameter( final String parameterName, final boolean deprecated,
final boolean preferredParameter, final Class<?> type ) {
final PlainParameter destinationParameter = new PlainParameter( parameterName, type );
destinationParameter.setMandatory( false );
destinationParameter.setHidden( true );
destinationParameter.setRole( ParameterAttributeNames.Core.ROLE_SYSTEM_PARAMETER );
destinationParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PREFERRED, String.valueOf( preferredParameter ) );
destinationParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP, GROUP_SYSTEM );
destinationParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP_LABEL, Messages.getInstance().getString(
"ReportPlugin.SystemParameters" ) );
destinationParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.LABEL, parameterName );
destinationParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.TYPE, ParameterAttributeNames.Core.TYPE_TEXTBOX );
destinationParameter.setDeprecated( deprecated );
return destinationParameter;
}
private PlainParameter createGenericBooleanSystemParameter( final String parameterName, final boolean deprecated,
final boolean preferredParameter ) {
return createGenericSystemParameter( parameterName, deprecated, preferredParameter, Boolean.class );
}
private PlainParameter createGenericIntSystemParameter( final String parameterName, final boolean deprecated,
final boolean preferredParameter ) {
return createGenericSystemParameter( parameterName, deprecated, preferredParameter, Integer.class );
}
private StaticListParameter createContentLinkingParameter() {
final StaticListParameter parameter =
new StaticListParameter( SYS_PARAM_CONTENT_LINK, true, false, String[].class );
parameter.setMandatory( false );
parameter.setHidden( true );
parameter.setRole( ParameterAttributeNames.Core.ROLE_SYSTEM_PARAMETER );
parameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.PREFERRED,
"false" );
parameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP, GROUP_SYSTEM );
parameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP_LABEL, Messages.getInstance().getString(
"ReportPlugin.SystemParameters" ) );
parameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.LABEL,
Messages.getInstance().getString( "ReportPlugin.ContentLinking" ) );
parameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.TYPE,
ParameterAttributeNames.Core.TYPE_LIST );
return parameter;
}
private Object lookupDestination() {
return inputs.get( SYS_PARAM_DESTINATION );
}
private StaticListParameter createOutputParameter() {
final StaticListParameter listParameter =
new StaticListParameter( SYS_PARAM_OUTPUT_TARGET, false, true, String.class );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PREFERRED, String.valueOf( true ) );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP, GROUP_PARAMETERS );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP_LABEL, Messages.getInstance().getString(
"ReportPlugin.ReportParameters" ) );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.LABEL,
Messages.getInstance().getString( "ReportPlugin.OutputType" ) );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.TYPE,
ParameterAttributeNames.Core.TYPE_DROPDOWN );
listParameter.setRole( ParameterAttributeNames.Core.ROLE_SYSTEM_PARAMETER );
ReportOutputHandlerFactory handlerFactory = PentahoSystem.get( ReportOutputHandlerFactory.class );
if ( handlerFactory == null ) {
handlerFactory = new FastExportReportOutputHandlerFactory();
}
for ( final Map.Entry<String, String> entry : handlerFactory.getSupportedOutputTypes() ) {
listParameter.addValues( entry.getKey(), entry.getValue() );
}
return listParameter;
}
private ParameterDefinitionEntry createRenderModeSystemParameter() {
final StaticListParameter listParameter =
new StaticListParameter( SYS_PARAM_RENDER_MODE, false, true, String.class );
listParameter.setHidden( true );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PREFERRED, String.valueOf( false ) );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP, GROUP_SYSTEM );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE,
ParameterAttributeNames.Core.PARAMETER_GROUP_LABEL, Messages.getInstance().getString(
"ReportPlugin.SystemParameters" ) );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.LABEL,
SYS_PARAM_RENDER_MODE );
listParameter.setParameterAttribute( ParameterAttributeNames.Core.NAMESPACE, ParameterAttributeNames.Core.TYPE,
ParameterAttributeNames.Core.TYPE_DROPDOWN );
listParameter.setRole( ParameterAttributeNames.Core.ROLE_SYSTEM_PARAMETER );
listParameter.addValues( "XML", "XML" ); // NON-NLS
listParameter.addValues( "REPORT", "REPORT" ); // NON-NLS
listParameter.addValues( "DOWNLOAD", "DOWNLOAD" ); // NON-NLS
listParameter.addValues( "PARAMETER", "PARAMETER" ); // NON-NLS
return listParameter;
}
private Boolean requestFlag( final String parameter, final MasterReport report, final String attributeNamespace,
final String attributeName, final String configurationKey ) {
if ( parameter != null ) {
final IParameterProvider parameters = getRequestParameters();
final String parameterValue = parameters.getStringParameter( parameter, "" );
if ( "true".equals( parameterValue ) ) {
return Boolean.TRUE;
}
if ( "false".equals( parameterValue ) ) {
return Boolean.FALSE;
}
}
if ( ( attributeNamespace != null ) && ( attributeName != null ) ) {
final Object attr = report.getAttribute( attributeNamespace, attributeName );
if ( Boolean.TRUE.equals( attr ) || "true".equals( attr ) ) {
return Boolean.TRUE;
}
if ( Boolean.FALSE.equals( attr ) || "false".equals( attr ) ) {
return Boolean.FALSE;
}
}
if ( configurationKey != null ) {
final String configProperty = report.getConfiguration().getConfigProperty( configurationKey );
if ( "true".equals( configProperty ) ) {
return Boolean.TRUE;
}
if ( "false".equals( configProperty ) ) {
return Boolean.FALSE;
}
}
return null;
}
private String requestConfiguration( final String parameter, final MasterReport report,
final String attributeNamespace, final String attributeName,
final String configurationKey ) {
if ( parameter != null ) {
final IParameterProvider parameters = getRequestParameters();
final String parameterValue = parameters.getStringParameter( parameter, "" );
if ( StringUtils.isEmpty( parameterValue ) == false ) {
return parameterValue;
}
}
if ( ( attributeNamespace != null ) && ( attributeName != null ) ) {
final Object attr = report.getAttribute( attributeNamespace, attributeName );
if ( ( attr != null ) && ( StringUtils.isEmpty( String.valueOf( attr ) ) == false ) ) {
return String.valueOf( attr );
}
}
if ( configurationKey != null ) {
final String configProperty = report.getConfiguration().getConfigProperty( configurationKey );
if ( StringUtils.isEmpty( configProperty ) == false ) {
return configProperty;
}
}
return null;
}
private static class OutputParameterCollector {
private OutputParameterCollector() {
}
public String[] collectParameter( final MasterReport reportDefinition ) {
final LinkedHashSet<String> parameter = new LinkedHashSet<>();
inspectElement( reportDefinition, parameter );
traverseSection( reportDefinition, parameter );
return parameter.toArray( new String[ parameter.size() ] );
}
private void traverseSection( final Section section, final LinkedHashSet<String> parameter ) {
final int count = section.getElementCount();
for ( int i = 0; i < count; i++ ) {
final ReportElement element = section.getElement( i );
inspectElement( element, parameter );
if ( element instanceof Section ) {
traverseSection( (Section) element, parameter );
}
}
}
private void inspectElement( final ReportElement element, final LinkedHashSet<String> parameter ) {
try {
Expression expression = element.getStyleExpression( ElementStyleKeys.HREF_TARGET );
String[] expressionStrings;
if ( expression instanceof FormulaExpression ) {
final FormulaExpression fe = (FormulaExpression) expression;
expressionStrings = new String[] { fe.getFormulaExpression() };
} else {
expression = element.getAttributeExpression( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE );
if ( expression instanceof ChartExpression ) {
final ChartExpression ce = (ChartExpression) expression;
expressionStrings = ce.getHyperlinkFormulas();
} else {
return;
}
}
if ( ( null == expressionStrings ) && ( expressionStrings.length < 1 ) ) {
return;
}
for ( String expressionText : expressionStrings ) {
if ( StringUtils.isEmpty( expressionText ) ) {
// DrillDown only works with the formula function of the same name
continue;
}
if ( expressionText.startsWith( "=" ) ) {
expressionText = expressionText.substring( 1 );
}
if ( expressionText.startsWith( "DRILLDOWN" ) == false ) { // NON-NLS
// DrillDown only works if the function is the only element. Everything else is beyond our control.
continue;
}
final FormulaParser formulaParser = new FormulaParser();
final LValue value = formulaParser.parse( expressionText );
if ( ( value instanceof FormulaFunction ) == false ) {
// Not a valid formula or a complex term - we do not handle that
continue;
}
final DefaultFormulaContext context = new DefaultFormulaContext();
value.initialize( context );
final FormulaFunction fn = (FormulaFunction) value;
final LValue[] params = fn.getChildValues();
if ( params.length != 3 ) {
// Malformed formula: Need 3 parameter
continue;
}
final String config = extractText( params[ 0 ] );
if ( config == null ) {
// Malformed formula: No statically defined config profile
continue;
}
final DrillDownProfile profile = DrillDownProfileMetaData.getInstance().getDrillDownProfile( config );
if ( profile == null ) {
// Malformed formula: Unknown drilldown profile
continue;
}
if ( StringUtils.isEmpty( profile.getAttribute( "group" ) ) || !profile.getAttribute( "group" )
.startsWith( "pentaho" ) ) { // NON-NLS
// Only 'pentaho' and 'pentaho-sugar' drill-down profiles can be used. Filters out all other third party
// drilldowns
continue;
}
if ( ( params[ 2 ] instanceof DataTable ) == false ) {
// Malformed formula: Not a parameter table
continue;
}
final DataTable dataTable = (DataTable) params[ 2 ];
final int rowCount = dataTable.getRowCount();
final int colCount = dataTable.getColumnCount();
if ( colCount != 2 ) {
// Malformed formula: Parameter table is invalid. Must be two cols, many rows ..
continue;
}
for ( int i = 0; i < rowCount; i++ ) {
final LValue valueAt = dataTable.getValueAt( i, 0 );
final String name = extractText( valueAt );
if ( name == null ) {
continue;
}
parameter.add( name );
}
}
} catch ( final Exception e ) {
// ignore ..
CommonUtil.checkStyleIgnore();
}
}
private String extractText( final LValue value ) {
if ( value == null ) {
return null;
}
if ( value.isConstant() ) {
if ( value instanceof StaticValue ) {
final StaticValue staticValue = (StaticValue) value;
final Object o = staticValue.getValue();
if ( o == null ) {
return null; // NON-NLS
}
return String.valueOf( o );
}
}
return null; // NON-NLS
}
}
}