/* * 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) 2006 - 2009 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.sorting; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; import javax.swing.table.TableModel; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; public class TableSorter { private static final Log logger = LogFactory.getLog( TableSorter.class ); private TableModel model; private SortConstraint[] constraints; private Tuple[] sortData; private HashMap<String, Integer> columnNames; public TableSorter() { } public TableSorter init( final TableModel model, final SortConstraint[] constraints ) { this.model = model; this.constraints = constraints; return this; } private HashMap<String, Integer> createColumnNameIndex() { HashMap<String, Integer> idx = new HashMap<String, Integer>(); TableModel tableModel = getModel(); int cc = tableModel.getColumnCount(); for ( int i = 0; i < cc; i += 1 ) { idx.put( tableModel.getColumnName( i ), i ); } return idx; } public TableSorter populate() { this.columnNames = createColumnNameIndex(); this.sortData = calculateSortColumnIndex(); return this; } protected Tuple[] calculateSortColumnIndex() { ArrayList<Tuple> index = new ArrayList<Tuple>(); for ( final SortConstraint constraint : constraints ) { int idx = findIndex( constraint.getField() ); if ( idx >= 0 ) { index.add( new Tuple( idx, constraint ) ); } else { logger.debug( "Sort constraint contained references invalid column '" + constraint.getField() + "'" ); } } return index.toArray( new Tuple[index.size()] ); } private int findIndex( final String columnName ) { Integer integer = columnNames.get( columnName ); if ( integer == null ) { return -1; } return integer.intValue(); } protected TableModel getModel() { return model; } protected SortConstraint[] getConstraints() { return constraints; } public Tuple[] getSortData() { return sortData; } public int[] sortData() { if ( sortData.length > 0 ) { IndexElement[] sortableArray = createSortableData(); Arrays.sort( sortableArray ); return buildRawIndex( sortableArray ); } else { return buildIdentityMapping(); } } private int[] buildIdentityMapping() { int[] raw = new int[model.getRowCount()]; for ( int i = 0; i < raw.length; i++ ) { raw[i] = i; } return raw; } private int[] buildRawIndex( final IndexElement[] sortableArray ) { int[] raw = new int[sortableArray.length]; for ( int i = 0; i < sortableArray.length; i++ ) { IndexElement indexElement = sortableArray[i]; raw[i] = indexElement.getSourceRow(); } return raw; } private IndexElement[] createSortableData() { IndexElement[] indexElements = new IndexElement[model.getRowCount()]; for ( int i = 0; i < indexElements.length; i++ ) { indexElements[i] = createIndexElement( i ); } return indexElements; } protected IndexElement createIndexElement( final int row ) { return new IndexElement( row ); } protected Comparator<Object> getComparator() { return GenericComparator.INSTANCE; } /** * Used to cheat Java's sort method to sort an int-array with a custom handler. */ protected class IndexElement implements Comparable<IndexElement> { private int sourceRow; private WeakReference<Object[]> rowData; public IndexElement( final int sourceRow ) { this.sourceRow = sourceRow; } public int getSourceRow() { return sourceRow; } protected Object[] getRowData() { if ( rowData != null ) { Object[] objects = rowData.get(); if ( objects != null ) { return objects; } } Tuple[] sortData = getSortData(); Object[] row = new Object[sortData.length]; for ( int i = 0; i < row.length; i++ ) { row[i] = getModel().getValueAt( getSourceRow(), sortData[i].columnIndex ); } rowData = new WeakReference<Object[]>( row ); return row; } public int compareTo( final IndexElement o ) { Object[] data = getRowData(); Object[] otherData = o.getRowData(); Tuple[] sortData = getSortData(); Comparator<Object> comparator = getComparator(); for ( int i = 0; i < sortData.length; i++ ) { Tuple tuple = sortData[i]; Object rawMine = data[i]; Object rawTheirs = otherData[i]; int result = comparator.compare( rawMine, rawTheirs ) * tuple.sortOrder; if ( result != 0 ) { return result; } } return Integer.compare( sourceRow, o.sourceRow ); } } protected static class Tuple { public final int columnIndex; public final String name; public final int sortOrder; public Tuple( final int columnIndex, final SortConstraint c ) { this.columnIndex = columnIndex; this.name = c.getField(); this.sortOrder = c.isAscending() ? 1 : -1; } } public static int[] sort( final TableModel model, final SortConstraint[] constraints ) { TableSorter tableSorter = ClassicEngineBoot.getInstance().getObjectFactory().get( TableSorter.class ); return tableSorter.init( model, constraints ).populate().sortData(); } }