org.apache.myfaces.component.html.ext.BaseSortableModel Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.component.html.ext;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.ResultDataModel;
import javax.faces.model.ResultSetDataModel;
import javax.faces.model.ScalarDataModel;
import javax.servlet.jsp.jstl.sql.Result;
/**
* BaseSortableModel provides a DataModel that is automatically sorted by the specified Comparator.
* Each time the Comparator is set, the model will be resorted.
* @since 1.1.7
* @version $Revision: 691871 $
*/
public class BaseSortableModel extends DataModel
{
private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
protected DataModel _model = null;
private Object _wrappedData = null;
private IntList _sortedIndicesList = null, // from baseIndex to sortedIndex
_baseIndicesList = null; // from sortedIndex to baseIndex
private Comparator _comparator = null;
public Comparator getComparator() {
return _comparator;
}
public void setComparator(Comparator comparator) {
this._comparator = comparator;
sort();
}
/**
* Create a new SortableModel from the given instance.
* @param model This will be converted into a {@link DataModel}
* @see #setWrappedData
*/
public BaseSortableModel(Object model)
{
setWrappedData(model);
}
/**
* No arg constructor for use as a managed-bean.
* Must call setWrappedData before using this instance.
*/
public BaseSortableModel(){}
public Object getRowData()
{
return _model.getRowData();
}
public Object getWrappedData()
{
return _wrappedData;
}
public boolean isRowAvailable()
{
return _model.isRowAvailable();
}
/**
* Sets the underlying data being managed by this instance.
* @param data This Object will be converted into a
* {@link DataModel}.
*/
public void setWrappedData(Object data)
{
_baseIndicesList = null;
_model = toDataModel(data);
_sortedIndicesList = null;
_wrappedData = data;
}
protected DataModel toDataModel(Object data)
{
if (data == null)
{
return EMPTY_DATA_MODEL;
}
else if (data instanceof DataModel)
{
return (DataModel) data;
}
else if (data instanceof List)
{
return new ListDataModel((List) data);
}
// accept a Collection is not supported in the Spec
else if (data instanceof Collection)
{
return new ListDataModel(new ArrayList((Collection) data));
}
else if (OBJECT_ARRAY_CLASS.isAssignableFrom(data.getClass()))
{
return new ArrayDataModel((Object[]) data);
}
else if (data instanceof ResultSet)
{
return new ResultSetDataModel((ResultSet) data);
}
else if (data instanceof Result)
{
return new ResultDataModel((Result) data);
}
else
{
return new ScalarDataModel(data);
}
}
public int getRowCount()
{
return _model.getRowCount();
}
public void setRowIndex(int rowIndex)
{
int baseIndex = _toBaseIndex(rowIndex);
_model.setRowIndex(baseIndex);
}
public int getRowIndex()
{
int baseIndex = _model.getRowIndex();
return _toSortedIndex(baseIndex);
}
public String toString()
{
return "BaseSortableModel[" + _model + "]";
}
/**
* Sorts the underlying collection by the Comparator.
* @todo support -1 for rowCount
*/
public void sort()
{
Comparator comparator = getComparator();
if (null == comparator)
{
// restore unsorted order:
_baseIndicesList = _sortedIndicesList = null;
return;
}
//TODO: support -1 for rowCount:
int sz = getRowCount();
if ((_baseIndicesList == null) || (_baseIndicesList.size() != sz))
{
// we do not want to mutate the original data.
// however, instead of copying the data and sorting the copy,
// we will create a list of indices into the original data, and
// sort the indices. This way, when certain rows are made current
// in this Collection, we can make them current in the underlying
// DataModel as well.
_baseIndicesList = new IntList(sz);
}
final int rowIndex = _model.getRowIndex();
_model.setRowIndex(0);
// Make sure the model has that row 0! (It could be empty.)
if (_model.isRowAvailable())
{
Collections.sort(_baseIndicesList, new RowDataComparator(comparator, _model));
_sortedIndicesList = null;
}
_model.setRowIndex(rowIndex);
}
private int _toSortedIndex(int baseIndex)
{
if ((_sortedIndicesList == null) && (_baseIndicesList != null))
{
_sortedIndicesList = (IntList) _baseIndicesList.clone();
for(int i=0; i<_baseIndicesList.size(); i++)
{
Integer base = (Integer) _baseIndicesList.get(i);
_sortedIndicesList.set(base.intValue(), new Integer(i));
}
}
return _convertIndex(baseIndex, _sortedIndicesList);
}
private int _toBaseIndex(int sortedIndex)
{
return _convertIndex(sortedIndex, _baseIndicesList);
}
private int _convertIndex(int index, List indices)
{
if (index < 0) // -1 is special
return index;
if ((indices != null) && (indices.size() > index))
{
index = ((Integer) indices.get(index)).intValue();
}
return index;
}
private static final class IntList extends ArrayList implements Cloneable
{
public IntList(int size)
{
super(size);
_expandToSize(size);
}
private void _expandToSize(int desiredSize)
{
for(int i=0; i