/*
* Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
*
* This file is part of the Jspresso framework.
*
* Jspresso 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.
*
* Jspresso 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 Jspresso. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.util.bean;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.jspresso.framework.util.accessor.IAccessor;
import org.jspresso.framework.util.accessor.IAccessorFactory;
import org.jspresso.framework.util.collection.ESort;
/**
* A bean comparator which compare beans based on a list of properties using
* their natural order.
*
* @author Vincent Vandenschrick
*/
public class BeanComparator implements Comparator<Object> {
/**
* {@code NATURAL_COMPARATOR}.
*/
public static final Comparator<Object> NATURAL_COMPARATOR = new NaturalComparator();
private final List<IAccessor> orderingAccessors;
private final List<ESort> orderingDirections;
/**
* Constructs a new {@code BeanComparator} instance.
*
* @param orderingAccessors
* the ordering accessors.
* @param orderingDirections
* the ordering directions.
*/
public BeanComparator(List<IAccessor> orderingAccessors,
List<ESort> orderingDirections) {
this.orderingAccessors = orderingAccessors;
this.orderingDirections = orderingDirections;
}
/**
* Constructs a new {@code BeanComparator} instance.
*
* @param orderingProperties
* a map of (propertyName,direction) to define the sort.
* @param accessorFactory
* the accessor factory to access the bean instances properties.
* @param beanClass
* the bean type.
*/
public BeanComparator(Map<String, ESort> orderingProperties,
IAccessorFactory accessorFactory, Class<?> beanClass) {
orderingAccessors = new ArrayList<>();
orderingDirections = new ArrayList<>();
if (orderingProperties != null) {
for (Map.Entry<String, ESort> orderingProperty : orderingProperties
.entrySet()) {
orderingAccessors.add(accessorFactory.createPropertyAccessor(
orderingProperty.getKey(), beanClass));
orderingDirections.add(orderingProperty.getValue());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public int compare(Object o1, Object o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
for (int i = 0; i < orderingAccessors.size(); i++) {
IAccessor orderingAccessor = orderingAccessors.get(i);
ESort direction = orderingDirections.get(i);
Object o1Val;
Object o2Val;
try {
o1Val = orderingAccessor.getValue(o1);
o2Val = orderingAccessor.getValue(o2);
} catch (IllegalAccessException | NoSuchMethodException ex) {
throw new MissingPropertyException(ex.getMessage());
} catch (InvocationTargetException ex) {
if (ex.getCause() instanceof RuntimeException) {
throw (RuntimeException) ex.getCause();
}
throw new RuntimeException(ex.getCause());
}
int result = NATURAL_COMPARATOR.compare(o1Val, o2Val);
if (result != 0) {
if (direction == ESort.DESCENDING) {
result *= (-1);
}
return result;
}
}
return 0;
}
private static final class NaturalComparator implements Comparator<Object> {
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public int compare(Object o1, Object o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1 instanceof Comparable<?>) {
return ((Comparable<Object>) o1).compareTo(o2);
}
return o1.toString().compareTo(o2.toString());
}
}
}