/* Copyright 2014 InterCommIT b.v.
*
* This file is part of the "Weaves" project hosted on https://github.com/intercommit/Weaves
*
* Weaves 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
* any later version.
*
* Weaves 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 Weaves. If not, see <http://www.gnu.org/licenses/>.
*
*/
package nl.intercommit.weaves.grid;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.tapestry5.PropertyConduit;
import org.apache.tapestry5.grid.ColumnSort;
import org.apache.tapestry5.grid.SortConstraint;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
public class CollectionPagedGridDataSource<T> extends PagedGridDataSource<T> {
// a filtered subset of the collection
private final List<T> list;
// an Map with hashed object keys for fast searching.
private final Map<Integer,Integer> indexedList;
public CollectionPagedGridDataSource(final Collection<T> collection,final Class<T> entityType) {
super(entityType);
assert collection != null;
// Copy the collection so that we can sort it without disturbing the original
this.list = CollectionFactory.newList(collection);
indexedList = new HashMap<Integer, Integer>();
for (int i=0;i<list.size(); i++) {
indexedList.put(list.get(i).hashCode(), i);
}
}
public List<T> fetchResult(int startIndex, int endIndexPlusOne,
List<SortConstraint> sortConstraints) {
// apply a filter first
doFilter();
for (SortConstraint constraint : sortConstraints)
{
final ColumnSort sort = constraint.getColumnSort();
if (sort == ColumnSort.UNSORTED) continue;
final PropertyConduit conduit = constraint.getPropertyModel().getConduit();
final Comparator valueComparator = new Comparator<Comparable>()
{
public int compare(Comparable o1, Comparable o2)
{
// Simplify comparison, and handle case where both are nulls.
if (o1 == o2) return 0;
if (o2 == null) return 1;
if (o1 == null) return -1;
return o1.compareTo(o2);
}
};
final Comparator rowComparator = new Comparator()
{
public int compare(Object row1, Object row2)
{
Comparable value1 = (Comparable) conduit.get(row1);
Comparable value2 = (Comparable) conduit.get(row2);
return valueComparator.compare(value1, value2);
}
};
final Comparator reverseComparator = new Comparator()
{
public int compare(Object o1, Object o2)
{
int modifier = sort == ColumnSort.ASCENDING ? 1 : -1;
return modifier * rowComparator.compare(o1, o2);
}
};
// We can freely sort this list because its just a copy.
Collections.sort(list, reverseComparator);
}
//sorted, now do the paging or not..
if (!list.isEmpty()) {
if (endIndexPlusOne >= list.size()) {
endIndexPlusOne = list.size();
} else {
endIndexPlusOne++; // sublist use endIndex as 'exclusive'!
}
if (startIndex <0) {
startIndex = 0;
}
if (startIndex > list.size()) {
startIndex = list.size();
}
return list.subList(startIndex, endIndexPlusOne);
}
return list;
}
private void doFilter() {
List<CollectionFilter> filter = createFilter();
if (filter == null) {
//some defensive actions
filter = new LinkedList<CollectionFilter>();
}
for (final CollectionFilter activeFilter:filter) {
try {
Method foundMethod = null;
for (Method method: getRowType().getMethods()) {
if (method.getName().equalsIgnoreCase("get"+activeFilter.getName())) {
if (method.getParameterTypes().length == 0) {
// get method with no params..
foundMethod = method;
break;
}
}
}
if (foundMethod != null) {
// if the method is not even available , dont bother going through the list
for (final Object element : list.toArray()) {
final Object realValue = foundMethod.invoke(element, null);
switch (activeFilter.getOp()) {
case EQ: {
if (realValue instanceof String) {
// String comparison
if (!realValue.toString().equals((activeFilter.getValue().toString()))) {
// filter it out
list.remove(element);
}
} else {
// object compare..
// TODO: workout other instances
if (!(realValue.equals(activeFilter.getValue()))) {
list.remove(element);
}
}
break;
}
case GT: {
if (realValue instanceof Date) {
if ( ((Date)realValue).before((Date)activeFilter.getValue())) {
list.remove(element);
}
}
break;
}
case LT: {
if (realValue instanceof Date) {
if ( ((Date)realValue).after((Date)activeFilter.getValue())) {
list.remove(element);
}
}
break;
}
}
}
}
} catch(Exception e) {
// TODO: logging
System.err.println("[ERROR] during collection filtering:" + e.getMessage());
}
}
}
// Override if needed
public List<CollectionFilter> createFilter() {
return new LinkedList<CollectionFilter>();
};
@Override
public Object getIdentifierForRowValue(final Object rowObject) {
if (indexedList.containsKey(rowObject.hashCode())) {
return indexedList.get(rowObject.hashCode()); // return the original index in teh given list
}
return null;
}
@Override
public Class getRowIdClass() {
return Integer.class; // the index in the original list is the row identifier type
}
}