All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jasperreports.engine.fill.JRFillDataset Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see .
 */
package net.sf.jasperreports.engine.fill;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;

import net.sf.jasperreports.data.cache.CachedDataset;
import net.sf.jasperreports.data.cache.DataCacheHandler;
import net.sf.jasperreports.data.cache.DataRecorder;
import net.sf.jasperreports.data.cache.DataSnapshot;
import net.sf.jasperreports.data.cache.DataSnapshotException;
import net.sf.jasperreports.data.cache.DatasetRecorder;
import net.sf.jasperreports.engine.DatasetFilter;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.EvaluationType;
import net.sf.jasperreports.engine.JRAbstractScriptlet;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRDataset;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRGroup;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPropertiesHolder;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRQuery;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRScriptlet;
import net.sf.jasperreports.engine.JRSortField;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.ParameterContributor;
import net.sf.jasperreports.engine.ParameterContributorContext;
import net.sf.jasperreports.engine.ParameterContributorFactory;
import net.sf.jasperreports.engine.data.IndexedDataSource;
import net.sf.jasperreports.engine.design.JRDesignVariable;
import net.sf.jasperreports.engine.query.JRQueryExecuter;
import net.sf.jasperreports.engine.query.QueryExecuterFactory;
import net.sf.jasperreports.engine.scriptlets.ScriptletFactory;
import net.sf.jasperreports.engine.scriptlets.ScriptletFactoryContext;
import net.sf.jasperreports.engine.type.CalculationEnum;
import net.sf.jasperreports.engine.type.IncrementTypeEnum;
import net.sf.jasperreports.engine.type.ResetTypeEnum;
import net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum;
import net.sf.jasperreports.engine.util.DigestUtils;
import net.sf.jasperreports.engine.util.JRDataUtils;
import net.sf.jasperreports.engine.util.JRQueryExecuterUtils;
import net.sf.jasperreports.engine.util.JRResourcesUtil;
import net.sf.jasperreports.engine.util.MD5Digest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author Lucian Chirita ([email protected])
 */
public class JRFillDataset implements JRDataset, DatasetFillContext
{
	
	private static final Log log = LogFactory.getLog(JRFillDataset.class);
	
	/**
	 * The filler that created this object.
	 */
	private final BaseReportFiller filler;
	
	/**
	 *
	 */
	private JasperReportsContext jasperReportsContext;
	
	private JRPropertiesUtil propertiesUtil;
	
	/**
	 * The template dataset.
	 */
	private final JRDataset parent;
	
	/**
	 * Whether this is the main dataset of the report.
	 */
	private final boolean isMain;
	
	/**
	 * The dataset query.
	 */
	protected JRQuery query;
	
	private boolean useDatasourceParamValue;
	private boolean useConnectionParamValue;
	
	/**
	 * The dataset parameters.
	 */
	protected JRFillParameter[] parameters;

	/**
	 * The dataset parameters indexed by name.
	 */
	protected Map parametersMap;

	/**
	 * The dataset fields.
	 */
	protected JRFillField[] fields;
	
	/**
	 * The dataset fields indexed by name.
	 */
	protected Map fieldsMap;
	
	/**
	 * The dataset variables.
	 */
	protected JRFillVariable[] variables;
	
	/**
	 * The dataset variables indexed by name.
	 */
	protected Map variablesMap;
	
	/**
	 * Set of {@link VariableCalculationReq VariableCalculationReq} objects.
	 */
	protected Set variableCalculationReqs;

	/**
	 * The element datasets.
	 */
	protected JRFillElementDataset[] elementDatasets;
	
	/**
	 * Used to save the original element datasets when
	 * {@link #filterElementDatasets(JRFillElementDataset) filterElementDatasets} is called.
	 */
	protected JRFillElementDataset[] origElementDatasets;

	/**
	 * The dataset groups.
	 */
	protected JRFillGroup[] groups;

	/**
	 * The resource bundle base name.
	 */
	protected String resourceBundleBaseName;
	
	/**
	 * The resource missing handle type.
	 */
	protected WhenResourceMissingTypeEnum whenResourceMissingType;
	
	/**
	 * The scriptlet class name.
	 */
	protected String scriptletClassName;

	/**
	 * The data source. 
	 */
	protected JRDataSource dataSource;
	
	/**
	 * The {@link Locale Locale} to be used by the dataset.
	 */
	protected Locale locale;
	
	/**
	 * The loaded resource bundle.
	 */
	protected ResourceBundle resourceBundle;

	/**
	 * The {@link TimeZone TimeZone} to be used by the dataset.
	 */
	protected TimeZone timeZone;
	
	/**
	 * The cursor used when iterating the data source.
	 */
	protected int reportCount;

	/**
	 * The calculator used by the dataset.
	 */
	protected JRCalculator calculator;

	/**
	 * The scriptlets used by the dataset.
	 */
	protected List scriptlets;

	/**
	 *
	 */
	protected JRAbstractScriptlet delegateScriptlet = new JRFillDatasetScriptlet(this);

	/**
	 * The value of the {@link JRParameter#REPORT_MAX_COUNT max count} parameter.
	 */
	protected Integer reportMaxCount;

	private JRQueryExecuter queryExecuter;
	private List parameterContributors;
	
	protected DatasetFilter filter;

	protected FillDatasetPosition fillPosition;
	protected DatasetRecorder dataRecorder;
	
	private Map cacheRecordIndexCallbacks;
	private boolean cacheSkipped;
	private CachedDataset cachedDataset;
	private boolean sortedDataSource;
	
	private boolean ended;
	private int cacheRecordCount;
	private int previousCacheRecordIndex;
	private int currentCacheRecordIndex;
	
	/**
	 * Creates a fill dataset object.
	 * @param filler the filler
	 * @param dataset the template dataset
	 * @param factory the fill object factory
	 */
	public JRFillDataset(BaseReportFiller filler, JRDataset dataset, JRFillObjectFactory factory)
	{
		factory.put(dataset, this);
		
		this.filler = filler;
		this.propertiesUtil = filler == null ? JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance()) 
				: filler.getPropertiesUtil();
		this.parent = dataset;
		this.isMain = dataset.isMainDataset();
		
		scriptletClassName = dataset.getScriptletClass();
		resourceBundleBaseName = dataset.getResourceBundle();
		whenResourceMissingType = dataset.getWhenResourceMissingTypeValue();
		
		query = dataset.getQuery();
		
		setParameters(dataset, factory);

		setFields(dataset, factory);
		
		setVariables(dataset, factory);
		
		setGroups(dataset, factory);
	}


	private void setParameters(JRDataset dataset, JRFillObjectFactory factory)
	{
		JRParameter[] jrParameters = dataset.getParameters();
		if (jrParameters != null && jrParameters.length > 0)
		{
			parameters = new JRFillParameter[jrParameters.length];
			parametersMap = new HashMap();
			for (int i = 0; i < parameters.length; i++)
			{
				parameters[i] = factory.getParameter(jrParameters[i]);
				parametersMap.put(parameters[i].getName(), parameters[i]);
			}
		}
	}


	private void setGroups(JRDataset dataset, JRFillObjectFactory factory)
	{
		JRGroup[] jrGroups = dataset.getGroups();
		if (jrGroups != null && jrGroups.length > 0)
		{
			groups = new JRFillGroup[jrGroups.length];
			for (int i = 0; i < groups.length; i++)
			{
				groups[i] = factory.getGroup(jrGroups[i]);
			}
		}
	}


	private void setVariables(JRDataset dataset, JRFillObjectFactory factory)
	{
		JRVariable[] jrVariables = dataset.getVariables();
		if (jrVariables != null && jrVariables.length > 0)
		{
			List variableList = new ArrayList(jrVariables.length * 3);

			variablesMap = new HashMap();
			for (int i = 0; i < jrVariables.length; i++)
			{
				addVariable(jrVariables[i], variableList, factory);
			}

			setVariables(variableList);
		}
	}
	
	
	private JRFillVariable addVariable(JRVariable parentVariable, List variableList, JRFillObjectFactory factory)
	{
		JRFillVariable variable = factory.getVariable(parentVariable);

		CalculationEnum calculation = variable.getCalculationValue();
		switch (calculation)
		{
			case AVERAGE:
			case VARIANCE:
			{
				JRVariable countVar = createHelperVariable(parentVariable, "_COUNT", CalculationEnum.COUNT);
				JRFillVariable fillCountVar = addVariable(countVar, variableList, factory);
				variable.setHelperVariable(fillCountVar, JRCalculable.HELPER_COUNT);

				JRVariable sumVar = createHelperVariable(parentVariable, "_SUM", CalculationEnum.SUM);
				JRFillVariable fillSumVar = addVariable(sumVar, variableList, factory);
				variable.setHelperVariable(fillSumVar, JRCalculable.HELPER_SUM);

				break;
			}
			case STANDARD_DEVIATION:
			{
				JRVariable varianceVar = createHelperVariable(parentVariable, "_VARIANCE", CalculationEnum.VARIANCE);
				JRFillVariable fillVarianceVar = addVariable(varianceVar, variableList, factory);
				variable.setHelperVariable(fillVarianceVar, JRCalculable.HELPER_VARIANCE);

				break;
			}
			case DISTINCT_COUNT:
			{
				JRVariable countVar = createDistinctCountHelperVariable(parentVariable);
				JRFillVariable fillCountVar = addVariable(countVar, variableList, factory);
				variable.setHelperVariable(fillCountVar, JRCalculable.HELPER_COUNT);

				break;
			}
		}

		variableList.add(variable);
		return variable;
	}

	private JRVariable createHelperVariable(JRVariable variable, String nameSuffix, CalculationEnum calculation)
	{
		JRDesignVariable helper = new JRDesignVariable();
		helper.setName(variable.getName() + nameSuffix);
		helper.setValueClassName(variable.getValueClassName());
		helper.setIncrementerFactoryClassName(variable.getIncrementerFactoryClassName());
		helper.setResetType(variable.getResetTypeValue());
		helper.setResetGroup(variable.getResetGroup());
		helper.setIncrementType(variable.getIncrementTypeValue());
		helper.setIncrementGroup(variable.getIncrementGroup());
		helper.setCalculation(calculation);
		helper.setSystemDefined(true);
		helper.setExpression(variable.getExpression());

		return helper;
	}

	private JRVariable createDistinctCountHelperVariable(JRVariable variable)
	{
		JRDesignVariable helper = new JRDesignVariable();
		helper.setName(variable.getName() + "_DISTINCT_COUNT");
		helper.setValueClassName(variable.getValueClassName());
		helper.setIncrementerFactoryClassName(JRDistinctCountIncrementerFactory.class.getName());
		helper.setResetType(ResetTypeEnum.REPORT);

		if (variable.getIncrementTypeValue() != IncrementTypeEnum.NONE)
		{
			helper.setResetType(ResetTypeEnum.getByValue(variable.getIncrementTypeValue().getValue()));
		}
		helper.setResetGroup(variable.getIncrementGroup());
		helper.setCalculation(CalculationEnum.NOTHING);
		helper.setSystemDefined(true);
		helper.setExpression(variable.getExpression());
		
		return helper;
	}

	private void setVariables(List variableList)
	{
		variables = new JRFillVariable[variableList.size()];
		variables = variableList.toArray(variables);

		for (int i = 0; i < variables.length; i++)
		{
			variablesMap.put(variables[i].getName(), variables[i]);
		}
	}


	private void setFields(JRDataset dataset, JRFillObjectFactory factory)
	{
		JRField[] jrFields = dataset.getFields();
		if (jrFields != null && jrFields.length > 0)
		{
			fields = new JRFillField[jrFields.length];
			fieldsMap = new HashMap();
			for (int i = 0; i < fields.length; i++)
			{
				fields[i] = factory.getField(jrFields[i]);
				fieldsMap.put(fields[i].getName(), fields[i]);
			}
		}
	}


	/**
	 * Creates the calculator
	 * @param jasperReport the report
	 * @throws JRException
	 */
	public void createCalculator(JasperReport jasperReport) throws JRException
	{
		setCalculator(createCalculator(getJasperReportsContext(), jasperReport, this));
	}

	protected void setCalculator(JRCalculator calculator)
	{
		this.calculator = calculator;
	}

	protected static JRCalculator createCalculator(JasperReportsContext jasperReportsContext, JasperReport jasperReport, JRDataset dataset) throws JRException
	{
		JREvaluator evaluator = JasperCompileManager.getInstance(jasperReportsContext).getEvaluator(jasperReport, dataset);
		return new JRCalculator(evaluator);
	}


	/**
	 * Initializes the calculator.
	 * 
	 * @throws JRException
	 */
	public void initCalculator() throws JRException
	{
		calculator.init(this);
	}


	/**
	 * Inherits properties from the report.
	 */
	public void inheritFromMain()
	{
		if (resourceBundleBaseName == null && !isMain)
		{
			resourceBundleBaseName = filler.mainDataset.resourceBundleBaseName;
			whenResourceMissingType = filler.mainDataset.whenResourceMissingType;
		}
	}
	
	
	/**
	 * Creates the scriptlets.
	 * 
	 * @return the scriptlets list
	 * @throws JRException
	 */
	protected List createScriptlets(Map parameterValues) throws JRException
	{
		ScriptletFactoryContext context = new ScriptletFactoryContext(getJasperReportsContext(), this, parameterValues);
		
		scriptlets = new ArrayList();
		
		List factories = getJasperReportsContext().getExtensions(ScriptletFactory.class);
		for (Iterator it = factories.iterator(); it.hasNext();)
		{
			ScriptletFactory factory = it.next();
			List tmpScriptlets = factory.getScriplets(context);
			if (tmpScriptlets != null)
			{
				scriptlets.addAll(tmpScriptlets);
			}
		}
		
		if (scriptlets.size() == 0)
		{
			scriptlets.add(0, new JRDefaultScriptlet());
		}

		return scriptlets;
	}


	/**
	 * Initializes the element datasets.
	 * 
	 * @param factory the fill object factory used by the filler
	 */
	protected void initElementDatasets(JRFillObjectFactory factory)
	{
		elementDatasets = factory.getElementDatasets(this);
	}


	/**
	 * Filters the element datasets, leaving only one.
	 * 

* This method is used when a dataset is instantiated by a chart or crosstab. * * @param elementDataset the element dataset that should remain */ protected void filterElementDatasets(JRFillElementDataset elementDataset) { origElementDatasets = elementDatasets; elementDatasets = new JRFillElementDataset[]{elementDataset}; } /** * Restores the original element datasets. *

* This method should be called after {@link #filterElementDatasets(JRFillElementDataset) filterElementDatasets}. */ protected void restoreElementDatasets() { if (origElementDatasets != null) { elementDatasets = origElementDatasets; origElementDatasets = null; } } /** * Loads the resource bundle corresponding to the resource bundle base name and locale. */ protected ResourceBundle loadResourceBundle() { ResourceBundle loadedBundle; if (resourceBundleBaseName == null) { loadedBundle = null; } else { loadedBundle = JRResourcesUtil.loadResourceBundle(getJasperReportsContext(), resourceBundleBaseName, locale); } return loadedBundle; } /** * Reads built-in parameter values from the value map. * * @param parameterValues the parameter values * @throws JRException */ public void setParameterValues(Map parameterValues) throws JRException { parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP, parameterValues); parameterValues.put(JRParameter.JASPER_REPORTS_CONTEXT, getJasperReportsContext()); if (filler != null) { // the only case when this filler is null is when called from JRParameterDefaultValuesEvaluator // and that utility method already sets the report object in the map parameterValues.put(JRParameter.JASPER_REPORT, filler.getJasperReport()); } reportMaxCount = (Integer) parameterValues.get(JRParameter.REPORT_MAX_COUNT); locale = (Locale) parameterValues.get(JRParameter.REPORT_LOCALE); if (locale == null) { locale = defaultLocale(); parameterValues.put(JRParameter.REPORT_LOCALE, locale); } resourceBundle = (ResourceBundle) parameterValues.get(JRParameter.REPORT_RESOURCE_BUNDLE); if (resourceBundle == null) { resourceBundle = loadResourceBundle(); if (resourceBundle != null) { parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE, resourceBundle); } } timeZone = (TimeZone) parameterValues.get(JRParameter.REPORT_TIME_ZONE); if (timeZone == null) { timeZone = defaultTimeZone(); parameterValues.put(JRParameter.REPORT_TIME_ZONE, timeZone); } scriptlets = createScriptlets(parameterValues); delegateScriptlet.setData(parametersMap, fieldsMap, variablesMap, groups);//FIXMESCRIPTLET use some context contributeParameters(parameterValues); filter = (DatasetFilter) parameterValues.get(JRParameter.FILTER); // initializing cache because we need the cached parameter values cacheInit(); //FIXME do not call on default parameter value evaluation and when a data snapshot is used? setFillParameterValues(parameterValues); // after we have the parameter values, init cache recording cacheInitRecording(); // initialize the filter if (filter != null) { filter.init(this); } } protected Locale defaultLocale() { String localeCode = propertiesUtil.getProperty(this, JRFiller.PROPERTY_DEFAULT_LOCALE); Locale locale = (localeCode == null || localeCode.isEmpty()) ? Locale.getDefault() : JRDataUtils.getLocale(localeCode); return locale; } protected TimeZone defaultTimeZone() { String timezoneId = propertiesUtil.getProperty(this, JRFiller.PROPERTY_DEFAULT_TIMEZONE); TimeZone timezone = (timezoneId == null || timezoneId.isEmpty()) ? TimeZone.getDefault() : JRDataUtils.getTimeZone(timezoneId); return timezone; } /** * Initializes the data source which will be used by this dataset. * * If the dataset includes a query, this involves invoking the appropriate * query executer to execute the query and create a data source from the * results. * * @throws JRException */ public void initDatasource() throws JRException { queryExecuter = null; dataSource = null; if (cachedDataset != null) { // get the cached data source dataSource = cachedDataset.getDataSource(); } if (dataSource == null) { dataSource = (JRDataSource) getParameterValue(JRParameter.REPORT_DATA_SOURCE); if (!useDatasourceParamValue && (useConnectionParamValue || dataSource == null)) { dataSource = createQueryDatasource(); setParameter(JRParameter.REPORT_DATA_SOURCE, dataSource); } } if (DatasetSortUtil.needSorting(this)) { dataSource = DatasetSortUtil.getSortedDataSource(filler, this, locale); setParameter(JRParameter.REPORT_DATA_SOURCE, dataSource); sortedDataSource = true; } } public FillDatasetPosition getFillPosition() { return fillPosition; } public void setFillPosition(FillDatasetPosition fillPosition) { if (fillPosition != null) { // add default attributes fillPosition.addAttribute("datasetUUID", getUUID()); if (query != null) { MD5Digest queryMD5 = DigestUtils.instance().md5(query.getText()); fillPosition.addAttribute("queryMD5", queryMD5); } //FIXME optimize storage for repeating subreports/subdatasets } this.fillPosition = fillPosition; } public void setCacheSkipped(boolean cacheSkipped) { this.cacheSkipped = cacheSkipped; if (cacheSkipped && log.isDebugEnabled()) { log.debug("data cache skipped at position " + fillPosition); } } protected void cacheInit() throws DataSnapshotException { // resetting cachedDataset = null; dataRecorder = null; if (fillPosition == null) { log.debug("No fill position present, not using the data cache"); return; } if (filler != null) { // try to load a cached data snapshot cacheInitSnapshot(); } } protected void cacheInitSnapshot() throws DataSnapshotException { if (cacheSkipped) { return; } DataSnapshot dataSnapshot = filler.fillContext.getDataSnapshot(); if (dataSnapshot != null) { // using cached data cachedDataset = dataSnapshot.getCachedData(fillPosition); if (cachedDataset == null) { throw new DataSnapshotException("No snapshot data found for position " + fillPosition); } if (log.isDebugEnabled()) { log.debug("Using cached data for " + fillPosition); } } } protected void cacheInitRecording() { if (cacheSkipped) { return; } if (cachedDataset != null) { // we have a cache dataset, nothing to do return; } if (filler == null) { // not a regular report fill, nothing to do return; } // if no cached data snapshot, initialize data recording DataRecorder cacheRecorder = filler.fillContext.getDataRecorder(); if (cacheRecorder != null && cacheRecorder.isEnabled()) { // see if data recording is inhibited boolean dataRecorable = JRPropertiesUtil.getInstance(getJasperReportsContext()).getBooleanProperty( this, DataCacheHandler.PROPERTY_DATA_RECORDABLE, true); if (dataRecorable) { // check whether the data snapshot can be persisted boolean dataPersistable = JRPropertiesUtil.getInstance(getJasperReportsContext()).getBooleanProperty( this, DataCacheHandler.PROPERTY_DATA_PERSISTABLE, true); if (!dataPersistable) { if (log.isDebugEnabled()) { log.debug("data not persistable by property for " + fillPosition); } cacheRecorder.disablePersistence(); } // populating the cache dataRecorder = cacheRecorder.createRecorder(); // this will also remove existing data on rewind dataRecorder.start(parent.getFields()); if (log.isDebugEnabled()) { log.debug("Populating data cache for " + fillPosition); } // storing persisted parameter values for (JRFillParameter parameter : parameters) { if (parameter.hasProperties()) { boolean includedInCache = isIncludedInDataCache(parameter); if (includedInCache) { if (log.isDebugEnabled()) { log.debug("storing value of paramter " + parameter.getName() + " in data snapshot"); } Object value = parameter.getValue(); // we store nulls as well dataRecorder.addParameter(parameter.getName(), value); } } } cacheRecordIndexCallbacks = new HashMap(); } else { if (log.isDebugEnabled()) { log.debug("data recording inhibited by property for " + fillPosition); } // disable all recording cacheRecorder.disableRecording(); } } } protected boolean isIncludedInDataCache(JRFillParameter parameter) { String includedProp = JRPropertiesUtil.getOwnProperty(parameter, DataCacheHandler.PROPERTY_INCLUDED); return JRPropertiesUtil.asBoolean(includedProp); } protected void cacheRecord() { if (dataRecorder != null && !dataRecorder.hasEnded()) { Object[] values; if (fields == null) { values = new Object[0]; } else { values = new Object[fields.length]; for (int i = 0; i < fields.length; i++) { values[i] = fields[i].getValue(); } } dataRecorder.addRecord(values); } } protected void cacheEnd() { if (dataRecorder != null && !dataRecorder.hasEnded()) { // if we had a sorted data source, compute the original filtered record indexes if (sortedDataSource) { if (log.isDebugEnabled()) { log.debug("populating unsorted cache"); } int recordIndex = 0; // ugly cast List sortRecords = ((SortedDataSource) dataSource).getRecords(); for (SortedDataSource.SortRecord sortRecord : sortRecords) { if (sortRecord.isFiltered()) { Object[] recordValues = sortRecord.getValues(); Object[] fieldValues; // the sort record can also contain sort variable values if (fields.length == recordValues.length) { fieldValues = recordValues; } else { fieldValues = new Object[fields.length]; System.arraycopy(recordValues, 0, fieldValues, 0, fields.length); } // add the record to the data snapshot dataRecorder.addRecord(fieldValues); // current unsorted index ++recordIndex; int originalIndex = sortRecord.getRecordIndex() + 1; if (log.isDebugEnabled()) { log.debug("unsorted index " + recordIndex + " for original index " + originalIndex); } // call delayed record index callbacks CacheRecordIndexCallback recordIndexCallback = cacheRecordIndexCallbacks.get(originalIndex); if (recordIndexCallback != null) { recordIndexCallback.cacheRecordIndexAvailable(recordIndex); } } } } Object recorded = dataRecorder.end(); if (recorded != null) { // adding the recorded data to a temporary list because the fill position might not be final filler.fillContext.addDataRecordResult(fillPosition, recorded); } } } /** * Sets the parameter values from the values map. * * @param parameterValues the values map * @throws JRException */ private void setFillParameterValues(Map parameterValues) throws JRException { if (parameters != null && parameters.length > 0) { for (int i = 0; i < parameters.length; i++) { JRFillParameter parameter = parameters[i]; String paramName = parameter.getName(); Object value = null; if (parameterValues.containsKey(paramName)) { value = parameterValues.get(paramName); } else if (!parameter.isSystemDefined()) { if (isIncludedInDataCache(parameter) && cachedDataset != null) { // if it's a cache parameter and we have a cached dataset, // look for the value in the cache if (!cachedDataset.hasParameter(paramName)) { // cached data is invalid throw new DataSnapshotException("A value for parameter " + paramName + " was not found in the data snapshot"); } if (log.isDebugEnabled()) { log.debug("loading parameter " + paramName + " value from data snapshot"); } value = cachedDataset.getParameterValue(paramName); } else { value = calculator.evaluate(parameter.getDefaultValueExpression(), JRExpression.EVALUATION_DEFAULT); if (value != null) { parameterValues.put(paramName, value); } } } setParameter(parameter, value); } } } /** * */ public void contributeParameters(Map parameterValues) throws JRException { parameterContributors = getParameterContributors(new ParameterContributorContext(getJasperReportsContext(), this, parameterValues)); if (parameterContributors != null) { for(ParameterContributor contributor : parameterContributors) { contributor.contributeParameters(parameterValues); } } } public void setJasperReportsContext(JasperReportsContext jasperReportsContext) { this.jasperReportsContext = jasperReportsContext; if (jasperReportsContext != null) { this.propertiesUtil = JRPropertiesUtil.getInstance(jasperReportsContext); } } protected JasperReportsContext getJasperReportsContext() { return filler == null ? (jasperReportsContext == null ? DefaultJasperReportsContext.getInstance() : jasperReportsContext) : filler.getJasperReportsContext(); } /** * */ public void disposeParameterContributors() { if (parameterContributors != null) { for(ParameterContributor contributor : parameterContributors) { contributor.dispose(); } } } /** * */ private List getParameterContributors(ParameterContributorContext context) throws JRException { List allContributors = null; List factories = getJasperReportsContext().getExtensions(ParameterContributorFactory.class); if (factories != null && factories.size() > 0) { allContributors = new ArrayList(); for (Iterator it = factories.iterator(); it.hasNext();) { ParameterContributorFactory factory = (ParameterContributorFactory)it.next(); List contributors = factory.getContributors(context); if (contributors != null) { allContributors.addAll(contributors); } } } return allContributors; } /** * Returns the map of parameter values. * * @return the map of parameter values */ protected Map getParameterValuesMap() { JRFillParameter paramValuesParameter = parametersMap.get(JRParameter.REPORT_PARAMETERS_MAP); return (Map) paramValuesParameter.getValue(); } /** * Creates the data source from a connection. * * @return the data source to be used * @throws JRException */ private JRDataSource createQueryDatasource() throws JRException { if (query == null) { return null; } try { if (log.isDebugEnabled()) { log.debug("Fill " + filler.fillerId + ": Creating " + query.getLanguage() + " query executer"); } QueryExecuterFactory queryExecuterFactory = JRQueryExecuterUtils.getInstance(getJasperReportsContext()).getExecuterFactory(query.getLanguage()); queryExecuter = queryExecuterFactory.createQueryExecuter(getJasperReportsContext(), parent, parametersMap); filler.fillContext.setRunningQueryExecuter(queryExecuter); return queryExecuter.createDatasource(); } finally { filler.fillContext.clearRunningQueryExecuter(); } } protected void reset() { useDatasourceParamValue = false; useConnectionParamValue = false; } /** * Sets the data source to be used. * * @param parameterValues the parameter values * @param ds the data source */ public void setDatasourceParameterValue(Map parameterValues, JRDataSource ds) { useDatasourceParamValue = true; if (ds != null) { parameterValues.put(JRParameter.REPORT_DATA_SOURCE, ds); } } /** * Sets the JDBC connection to be used. * * @param parameterValues the parameter values * @param conn the connection */ public void setConnectionParameterValue(Map parameterValues, Connection conn) { useConnectionParamValue = true; if (conn != null) { parameterValues.put(JRParameter.REPORT_CONNECTION, conn); } } /** * Closes the data source used by this dataset if this data source was * obtained via a query executer. * * @see JRQueryExecuter#close() */ public void closeDatasource() { closeQueryExecuter(); reset(); if (ended) { // if the whole data source was iterated, submit the recorded data cacheEnd(); } } protected void closeQueryExecuter() { if (queryExecuter != null) { if (log.isDebugEnabled()) { log.debug("Fill " + filler.fillerId + ": closing query executer"); } queryExecuter.close(); queryExecuter = null; } } /** * Starts the iteration on the data source. */ public void start() { // resetting the variables is required for cases such as sort fields which // iterate and calculate variables. resetVariables(); reportCount = 0; ended = false; cacheRecordCount = 0; previousCacheRecordIndex = 0; currentCacheRecordIndex = 0; } protected void resetVariables() { if (variables != null) { for (JRFillVariable variable : variables) { variable.reset(); } } } /** * Moves to the next record in the data source. * * @return true if the data source was not exhausted * @throws JRException */ public boolean next() throws JRException { return next(false); } /** * Moves to the next record in the data source. * * @param sorting whether the method is called as part of the data sorting phase * @return true if the data source was not exhausted * @throws JRException */ protected boolean next(boolean sorting) throws JRException { boolean hasNext = false; if (dataSource != null) { boolean includeRow = true; do { // limits are applied after sorting to support top-N reports hasNext = advanceDataSource(!sorting); if (hasNext) { setOldValues(); calculator.estimateVariables(); // filters are applied after sorting to support top-N reports if (!sorting) { includeRow = true; // evaluate the filter expression JRExpression filterExpression = getFilterExpression(); if (filterExpression != null) { Boolean filterExprResult = (Boolean) calculator.evaluate( filterExpression, JRExpression.EVALUATION_ESTIMATED); includeRow = filterExprResult != null && filterExprResult.booleanValue(); } if (includeRow) { advanceCacheRecordIndexes(); if (sortedDataSource) { // mark the record as filtered in the sorted data source // ugly cast ((SortedDataSource) dataSource).setRecordFilteredIndex(cacheRecordCount - 1); } else { // cache the record if the filter expression evaluated to true; // dynamic filters do not exclude records from the cache. // sorted data source cache records at the end because they compute original indexes. cacheRecord(); } if (filter != null) { includeRow = filter.matches(EvaluationType.ESTIMATED); if (log.isDebugEnabled()) { log.debug("Record matched by filter: " + includeRow); } } } } if (!includeRow) { revertToOldValues(); } } } while(hasNext && !includeRow); if (hasNext) { ++reportCount; } } if (!hasNext) { ended = true; } return hasNext; } protected void advanceCacheRecordIndexes() { ++cacheRecordCount; previousCacheRecordIndex = currentCacheRecordIndex; // if we're using a cached data source, return the original record index // this covers both sorting a cached data source and filtering a cached data source if (cachedDataset != null) { // ugly cast int dataSourceIndex = ((IndexedDataSource) dataSource).getRecordIndex(); // indexes are 1-based currentCacheRecordIndex = dataSourceIndex + 1; } else { currentCacheRecordIndex = cacheRecordCount; } } protected void setOldValues() throws JRException { if (fields != null && fields.length > 0) { for (int i = 0; i < fields.length; i++) { JRFillField field = fields[i]; field.setPreviousOldValue(field.getOldValue()); field.setOldValue(field.getValue()); field.setValue(dataSource.getFieldValue(field)); } } if (variables != null && variables.length > 0) { for (int i = 0; i < variables.length; i++) { JRFillVariable variable = variables[i]; variable.setPreviousOldValue(variable.getOldValue()); variable.setOldValue(variable.getValue()); } } } protected void revertToOldValues() { if (fields != null && fields.length > 0) { for (int i = 0; i < fields.length; i++) { JRFillField field = fields[i]; field.setValue(field.getOldValue()); field.setOldValue(field.getPreviousOldValue()); } } if (variables != null && variables.length > 0) { for (int i = 0; i < variables.length; i++) { JRFillVariable variable = variables[i]; variable.setValue(variable.getOldValue()); variable.setOldValue(variable.getPreviousOldValue()); } } } protected boolean advanceDataSource(boolean limit) throws JRException { boolean hasNext; if (limit && reportMaxCount != null && reportCount >= reportMaxCount.intValue()) { hasNext = false; } else { hasNext = dataSource.next(); } return hasNext; } /** * Sets the value of a parameter. * * @param parameterName the parameter name * @param value the value * @throws JRException */ protected void setParameter(String parameterName, Object value) throws JRException { JRFillParameter parameter = parametersMap.get(parameterName); if (parameter != null) { setParameter(parameter, value); } } /** * Sets the value of the parameter. * * @param parameter the parameter * @param value the value * @throws JRException */ protected void setParameter(JRFillParameter parameter, Object value) throws JRException { // if (value != null) // { // if (parameter.getValueClass().isInstance(value)) // { // parameter.setValue(value); // } // else // { // throw new JRException( // "Incompatible " // + value.getClass().getName() // + " value assigned to parameter " // + parameter.getName() // + " in the " + getName() + " dataset." // ); // } // } // else // { parameter.setValue(value); // } } public JRFillVariable getVariable(String variableName) { return variablesMap.get(variableName); } /** * Returns the value of a variable. * * @param variableName the variable name * @return the variable value */ public Object getVariableValue(String variableName) { return getVariableValue(variableName, EvaluationType.DEFAULT); } public Object getVariableValue(String variableName, EvaluationType evaluation) { JRFillVariable var = variablesMap.get(variableName); if (var == null) { throw new JRRuntimeException("No such variable " + variableName); } return var.getValue(evaluation.getType()); } public JRFillVariable getFillVariable(String variableName) { return variablesMap.get(variableName); } /** * Returns the value of a parameter. * * @param parameterName the parameter name * @return the parameter value */ public Object getParameterValue(String parameterName) { return getParameterValue(parameterName, false); } /** * Returns the value of a parameter. * * @param parameterName the parameter name * @param ignoreMissing if set, null will be returned for inexisting parameters * @return the parameter value */ public Object getParameterValue(String parameterName, boolean ignoreMissing) { JRFillParameter param = parametersMap.get(parameterName); Object value; if (param == null) { if (!ignoreMissing) { throw new JRRuntimeException("No such parameter " + parameterName); } // look into REPORT_PARAMETERS_MAP Map valuesMap = getParameterValuesMap(); value = valuesMap == null ? null : valuesMap.get(parameterName); } else { value = param.getValue(); } return value; } /** * Returns the value of a field. * * @param fieldName the field name * @return the field value */ public Object getFieldValue(String fieldName) { return getFieldValue(fieldName, EvaluationType.DEFAULT); } public Object getFieldValue(String fieldName, EvaluationType evaluation) { JRFillField field = fieldsMap.get(fieldName); if (field == null) { throw new JRRuntimeException("No such field " + fieldName); } return field.getValue(evaluation.getType()); } public JRFillField getFillField(String fieldName) { return fieldsMap.get(fieldName); } /** * Class used to hold expression calculation requirements. */ protected static class VariableCalculationReq { String variableName; CalculationEnum calculation; VariableCalculationReq(String variableName, CalculationEnum calculation) { this.variableName = variableName; this.calculation = calculation; } public boolean equals(Object o) { if (o == null || !(o instanceof VariableCalculationReq)) { return false; } VariableCalculationReq r = (VariableCalculationReq) o; return variableName.equals(r.variableName) && calculation == r.calculation; } public int hashCode() { return 31 * calculation.ordinal() + variableName.hashCode(); } } /** * Adds a variable calculation requirement. * * @param variableName the variable name * @param calculation the required calculation */ protected void addVariableCalculationReq(String variableName, CalculationEnum calculation) { if (variableCalculationReqs == null) { variableCalculationReqs = new HashSet(); } variableCalculationReqs.add(new VariableCalculationReq(variableName, calculation)); } /** * Checks if there are variable calculation requirements and creates the required variables. * * @param factory the fill object factory */ protected void checkVariableCalculationReqs(JRFillObjectFactory factory) { if (variableCalculationReqs != null && !variableCalculationReqs.isEmpty()) { List variableList = new ArrayList(variables.length * 2); for (int i = 0; i < variables.length; i++) { JRFillVariable variable = variables[i]; checkVariableCalculationReq(variable, variableList, factory); } setVariables(variableList); } } private void checkVariableCalculationReq(JRFillVariable variable, List variableList, JRFillObjectFactory factory) { if (hasVariableCalculationReq(variable, CalculationEnum.AVERAGE) || hasVariableCalculationReq(variable, CalculationEnum.VARIANCE)) { if (variable.getHelperVariable(JRCalculable.HELPER_COUNT) == null) { JRVariable countVar = createHelperVariable(variable, "_COUNT", CalculationEnum.COUNT); JRFillVariable fillCountVar = factory.getVariable(countVar); checkVariableCalculationReq(fillCountVar, variableList, factory); variable.setHelperVariable(fillCountVar, JRCalculable.HELPER_COUNT); } if (variable.getHelperVariable(JRCalculable.HELPER_SUM) == null) { JRVariable sumVar = createHelperVariable(variable, "_SUM", CalculationEnum.SUM); JRFillVariable fillSumVar = factory.getVariable(sumVar); checkVariableCalculationReq(fillSumVar, variableList, factory); variable.setHelperVariable(fillSumVar, JRCalculable.HELPER_SUM); } } if (hasVariableCalculationReq(variable, CalculationEnum.STANDARD_DEVIATION)) { if (variable.getHelperVariable(JRCalculable.HELPER_VARIANCE) == null) { JRVariable varianceVar = createHelperVariable(variable, "_VARIANCE", CalculationEnum.VARIANCE); JRFillVariable fillVarianceVar = factory.getVariable(varianceVar); checkVariableCalculationReq(fillVarianceVar, variableList, factory); variable.setHelperVariable(fillVarianceVar, JRCalculable.HELPER_VARIANCE); } } if (hasVariableCalculationReq(variable, CalculationEnum.DISTINCT_COUNT)) { if (variable.getHelperVariable(JRCalculable.HELPER_COUNT) == null) { JRVariable countVar = createDistinctCountHelperVariable(variable); JRFillVariable fillCountVar = factory.getVariable(countVar); checkVariableCalculationReq(fillCountVar, variableList, factory); variable.setHelperVariable(fillCountVar, JRCalculable.HELPER_COUNT); } } variableList.add(variable); } private boolean hasVariableCalculationReq(JRVariable var, CalculationEnum calculation) { return variableCalculationReqs.contains(new VariableCalculationReq(var.getName(), calculation)); } @Override public UUID getUUID() { return parent.getUUID(); } public String getName() { return parent.getName(); } public String getScriptletClass() { return parent.getScriptletClass(); } public JRScriptlet[] getScriptlets() { return parent.getScriptlets(); } public JRParameter[] getParameters() { return parameters; } public Map getParametersMap() { return parametersMap; } public JRQuery getQuery() { return query; } public JRField[] getFields() { return fields; } public JRSortField[] getSortFields() { return parent.getSortFields(); } public JRVariable[] getVariables() { return variables; } public JRGroup[] getGroups() { return groups; } public boolean isMainDataset() { return isMain; } public String getResourceBundle() { return parent.getResourceBundle(); } public WhenResourceMissingTypeEnum getWhenResourceMissingTypeValue() { return whenResourceMissingType; } public void setWhenResourceMissingType(WhenResourceMissingTypeEnum whenResourceMissingType) { this.whenResourceMissingType = whenResourceMissingType; } public boolean hasProperties() { return parent.hasProperties(); } public JRPropertiesMap getPropertiesMap() { return parent.getPropertiesMap(); } public JRPropertiesHolder getParentProperties() { // report properties propagate to subdatasets return isMain ? null : filler.getJasperReport(); } public JRExpression getFilterExpression() { return parent.getFilterExpression(); } /** * */ public Object clone() { throw new UnsupportedOperationException(); } /** * Evaluates an expression * @param expression the expression * @param evaluation the evaluation type * @return the evaluation result * @throws JRException */ public Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException { return calculator.evaluate(expression, evaluation); } public Locale getLocale() { return locale; } public FillDatasetPosition getDatasetPosition() { return fillPosition; } protected static interface CacheRecordIndexCallback { void cacheRecordIndexAvailable(int recordIndex); } protected static class FillDatasetPositionRecordIndexCallback implements CacheRecordIndexCallback { protected static void setRecordIndex(FillDatasetPosition position, int recordIndex) { position.addAttribute("rowIndex", recordIndex); } private final FillDatasetPosition position; public FillDatasetPositionRecordIndexCallback(FillDatasetPosition position) { this.position = position; } @Override public void cacheRecordIndexAvailable(int recordIndex) { setRecordIndex(position, recordIndex); } } protected static class CacheRecordIndexChainedCallback implements CacheRecordIndexCallback { private final CacheRecordIndexCallback first; private final CacheRecordIndexCallback second; public CacheRecordIndexChainedCallback(CacheRecordIndexCallback first, CacheRecordIndexCallback second) { this.first = first; this.second = second; } @Override public void cacheRecordIndexAvailable(int recordIndex) { first.cacheRecordIndexAvailable(recordIndex); second.cacheRecordIndexAvailable(recordIndex); } } protected void addCacheRecordIndexCallback(int recordIndex, CacheRecordIndexCallback callback) { CacheRecordIndexCallback existingCallback = cacheRecordIndexCallbacks.get(recordIndex); if (existingCallback == null) { cacheRecordIndexCallbacks.put(recordIndex, callback); } else { CacheRecordIndexChainedCallback chainedCallback = new CacheRecordIndexChainedCallback( existingCallback, callback); cacheRecordIndexCallbacks.put(recordIndex, chainedCallback); } } public void setCacheRecordIndex(FillDatasetPosition position, byte evaluationType) { int recordIndex; switch (evaluationType) { case JRExpression.EVALUATION_OLD: recordIndex = previousCacheRecordIndex; break; default: recordIndex = currentCacheRecordIndex; break; } if (sortedDataSource && dataRecorder != null) { // when recording a sorted data source, the record indexes are computed at the end // and we need to store a callback FillDatasetPositionRecordIndexCallback callback = new FillDatasetPositionRecordIndexCallback(position); addCacheRecordIndexCallback(recordIndex, callback); if (log.isDebugEnabled()) { log.debug("registered cache callback for index " + recordIndex); } } else { FillDatasetPositionRecordIndexCallback.setRecordIndex(position, recordIndex); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy