net.sf.jasperreports.engine.fill.DatasetSortUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 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.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRScriptletException;
import net.sf.jasperreports.engine.JRSortField;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.design.JRDesignDatasetRun;
import net.sf.jasperreports.engine.fill.DatasetSortInfo.RecordField;
import net.sf.jasperreports.engine.fill.DatasetSortInfo.SortFieldInfo;
import net.sf.jasperreports.engine.fill.SortedDataSource.SortRecord;
import net.sf.jasperreports.engine.type.SortFieldTypeEnum;
/**
* @author Teodor Danciu ([email protected])
*/
public class DatasetSortUtil
{
public static final String EXCEPTION_MESSAGE_KEY_SORT_FIELD_NOT_FOUND = "fill.dataset.sort.field.not.found";
public static final String EXCEPTION_MESSAGE_KEY_SORT_VARIABLE_NOT_FOUND = "fill.dataset.sort.variable.not.found";
/**
* Returns all current sort field criteria, including the dynamic ones provided as report parameter.
*/
public static JRSortField[] getAllSortFields(JRFillDataset dataset)
{
List allSortFields = new ArrayList();
JRSortField[] staticSortFields = dataset.getSortFields();
if (staticSortFields != null)
{
allSortFields.addAll(Arrays.asList(staticSortFields));
}
@SuppressWarnings("unchecked")
List dynamicSortFields = (List)dataset.getParameterValue(JRParameter.SORT_FIELDS, true);
if (dynamicSortFields != null)
{
allSortFields.addAll(dynamicSortFields);
}
return allSortFields.toArray(new JRSortField[allSortFields.size()]);
}
/**
*
*/
public static boolean needSorting(JRFillDataset dataset)
{
JRSortField[] staticSortFields = dataset.getSortFields();
@SuppressWarnings("unchecked")
List dynamicSortFields = (List)dataset.getParameterValue(JRParameter.SORT_FIELDS, true);
return
(staticSortFields != null
&& staticSortFields.length > 0)
|| (dynamicSortFields != null
&& dynamicSortFields.size() > 0);
}
/**
*
*/
public static SortedDataSource getSortedDataSource(
BaseReportFiller filler,
JRFillDataset dataset,
Locale locale
) throws JRException
{
DatasetSortInfo sortInfo = createSortInfo(dataset);
SortFillDatasetRun sortDatasetRun = new SortFillDatasetRun(filler, dataset, sortInfo);
List records = sortDatasetRun.sort();
// using indirect sorting in order to also preserve the original record order for data caching
int recordCount = records.size();
// we need wrapper objects for Arrays.sort with comparator
Integer[] indexes = new Integer[recordCount];
for (int i = 0; i < recordCount; i++) {
indexes[i] = i;
}
/* */
Arrays.sort(
indexes,
new DataSourceComparator(
sortInfo,
locale,
records
)
);
return new SortedDataSource(sortInfo, records, indexes);
}
/**
*
*/
private static DatasetSortInfo createSortInfo(JRFillDataset dataset) throws JRException
{
DatasetSortInfo sortInfo = new DatasetSortInfo();
sortInfo.setOriginalDataSource(dataset.dataSource);
Map fieldsMap = new HashMap();
Map fieldIndexMap = new HashMap();
JRField[] fields = dataset.getFields();
if (fields != null)
{
for (int i = 0; i < fields.length; i++)
{
JRField field = fields[i];
fieldsMap.put(field.getName(), field);
fieldIndexMap.put(field.getName(), i);
sortInfo.addRecordField(field.getName());
}
}
Map variablesMap = new HashMap();
JRVariable[] variables = dataset.getVariables();
if (variables != null)
{
for (int i = 0; i < variables.length; i++)
{
variablesMap.put(variables[i].getName(), variables[i]);
}
}
JRSortField[] sortFields = getAllSortFields(dataset);
if (sortFields != null)
{
for (int i = 0; i < sortFields.length; i++)
{
JRSortField sortField = sortFields[i];
String sortFieldName = sortField.getName();
boolean collatorFlag;
int recordIndex;
if (sortField.getType() == SortFieldTypeEnum.VARIABLE)
{
JRVariable variable = variablesMap.get(sortFieldName);
if (variable == null)
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_SORT_VARIABLE_NOT_FOUND,
new Object[]{sortFieldName});
}
recordIndex = sortInfo.addRecordVariable(variable.getName());
collatorFlag = String.class.getName().equals(variable.getValueClassName());
}
else
{
JRField field = fieldsMap.get(sortFieldName);
if (field == null)
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_SORT_FIELD_NOT_FOUND,
new Object[]{sortFieldName});
}
recordIndex = fieldIndexMap.get(sortField.getName());
collatorFlag = String.class.getName().equals(field.getValueClassName());
}
sortInfo.addSortField(sortField, recordIndex, collatorFlag);
}
}
return sortInfo;
}
}
/**
*
*/
class DataSourceComparator implements Comparator
{
private final Collator collator;
private final List sortFields;
private final List records;
public DataSourceComparator(DatasetSortInfo sortFieldInfo, Locale locale,
List records)
{
this.collator = Collator.getInstance(locale);
this.sortFields = sortFieldInfo.getSortFields();
this.records = records;
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public int compare(Integer idx1, Integer idx2)
{
// assuming random access records list
Object[] record1 = records.get(idx1).getValues();
Object[] record2 = records.get(idx2).getValues();
int ret = 0;
for (SortFieldInfo info : sortFields)
{
Comparable field1 = (Comparable)record1[info.getRecordIndex()];
Comparable field2 = (Comparable)record2[info.getRecordIndex()];
if (field1 == null)
{
ret = (field2 == null) ? 0 : -1;
}
else if (field2 == null)
{
ret = 1;
}
else
{
if (info.useCollator())
{
ret = collator.compare(field1, field2);
}
else
{
ret = field1.compareTo(field2);
}
}
if (ret != 0)
{
if (info.isDescending())
{
ret = -ret;
}
return ret;
}
}
return ret;
}
}
/**
* Used to iterate on a subdataset and create a sorted data source.
*
* @author Teodor Danciu ([email protected])
*/
class SortFillDatasetRun extends JRFillDatasetRun
{
private DatasetSortInfo sortInfo;
private int recordIndex;
private List records;
public SortFillDatasetRun(BaseReportFiller filler, JRFillDataset dataset, DatasetSortInfo sortInfo) throws JRException
{
super(
filler,
new JRDesignDatasetRun(), //we don't need anything from a dataset run. just avoid NPEs down the call
dataset
);
this.sortInfo = sortInfo;
}
public List sort() throws JRException
{
recordIndex = 0;
records = new ArrayList();
try
{
//all parameters are already set onto the dataset by the main fill process
iterate();
}
finally
{
dataset.closeQueryExecuter();
dataset.reset();
}
return records;
}
@Override
protected void detail() throws JRScriptletException, JRException
{
super.detail();
List recordFields = sortInfo.getRecordFields();
int fieldCount = recordFields.size();
Object[] record = new Object[fieldCount];
int index = 0;
for (RecordField recordField : recordFields)
{
Object value;
if (recordField.isVariable())
{
value = dataset.getVariableValue(recordField.getName());
}
else
{
value = dataset.getFieldValue(recordField.getName());
}
record[index] = value;
++index;
}
// also store the original record index
SortRecord sortRecord = new SortedDataSource.SortRecord(record, recordIndex);
++recordIndex;
records.add(sortRecord);
}
@Override
protected boolean advanceDataset() throws JRException
{
// do not filter records before sorting in order to support "Top 5 sorted" cases.
// FIXME optimize by filtering when the filters are on fields (and other cases).
return dataset.next(true);
}
@Override
protected boolean toStartWhenNoData()
{
//not needed for sorting
return false;
}
}