| /* |
| * 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.commons.beanutils2; |
| |
| import java.io.Serializable; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.Comparator; |
| |
| /** |
| * <p> |
| * This comparator compares two beans by the specified bean property. It is also possible to compare beans based on |
| * nested, indexed, combined, mapped bean properties. Please see the {@link PropertyUtilsBean} documentation for all |
| * property name possibilities. |
| * |
| * </p> |
| * <p> |
| * <strong>Note:</strong> The BeanComparator passes the values of the specified bean property to an internal natural |
| * order {@link Comparator}, if no comparator is specified in the constructor. If you are comparing two beans based on a |
| * property that could contain "null" values, a suitable {@code Comparator} or Apache Commons Collection |
| * {@code ComparatorChain} should be supplied in the constructor. Note that the passed in {@code Comparator} must |
| * be able to handle the passed in objects. Because the type of the property to be compared is not known at compile time |
| * no type checks can be performed by the compiler. Thus {@code ClassCastException} exceptions can be thrown if |
| * unexpected property values occur. |
| * </p> |
| * |
| * @param <T> the type of beans to be compared by this {@code Comparator} |
| * @param <V> the type of property to compare |
| */ |
| public class BeanComparator<T, V> implements Comparator<T>, Serializable { |
| |
| private static final long serialVersionUID = 1L; |
| private String property; |
| private final Comparator<V> comparator; |
| |
| /** |
| * <p> |
| * Constructs a Bean Comparator without a property set. |
| * </p> |
| * <p> |
| * <strong>Note</strong> that this is intended to be used only in bean-centric environments. |
| * </p> |
| * <p> |
| * Until {@link #setProperty} is called with a non-null value. this comparator will compare the Objects only. |
| * </p> |
| */ |
| public BeanComparator() { |
| this(null); |
| } |
| |
| /** |
| * <p> |
| * Constructs a property-based comparator for beans. This compares two beans by the property specified in the |
| * property parameter. This constructor creates a {@code BeanComparator} that uses a |
| * {@code ComparableComparator} to compare the property values. |
| * </p> |
| * |
| * <p> |
| * Passing "null" to this constructor will cause the BeanComparator to compare objects based on natural order, that |
| * is {@code java.lang.Comparable}. |
| * </p> |
| * |
| * @param property String Name of a bean property, which may contain the name of a simple, nested, indexed, mapped, |
| * or combined property. See {@link PropertyUtilsBean} for property query language syntax. If the property |
| * passed in is null then the actual objects will be compared |
| */ |
| public BeanComparator(final String property) { |
| this(property, NaturalOrderComparator.INSTANCE); |
| } |
| |
| /** |
| * Constructs a property-based comparator for beans. This constructor creates a BeanComparator that uses the |
| * supplied Comparator to compare the property values. |
| * |
| * @param property Name of a bean property, can contain the name of a simple, nested, indexed, mapped, or combined |
| * property. See {@link PropertyUtilsBean} for property query language syntax. |
| * @param comparator BeanComparator will pass the values of the specified bean property to this Comparator. If your |
| * bean property is not a comparable or contains null values, a suitable comparator may be supplied in this |
| * constructor. |
| */ |
| public BeanComparator(final String property, final Comparator<V> comparator) { |
| setProperty(property); |
| this.comparator = comparator != null ? comparator : NaturalOrderComparator.INSTANCE; |
| } |
| |
| /** |
| * Sets the method to be called to compare two JavaBeans |
| * |
| * @param property String method name to call to compare If the property passed in is null then the actual objects |
| * will be compared |
| */ |
| public void setProperty(final String property) { |
| this.property = property; |
| } |
| |
| /** |
| * Gets the property attribute of the BeanComparator |
| * |
| * @return String method name to call to compare. A null value indicates that the actual objects will be compared |
| */ |
| public String getProperty() { |
| return property; |
| } |
| |
| /** |
| * Gets the Comparator being used to compare beans. |
| * |
| * @return the Comparator being used to compare beans |
| */ |
| public Comparator<V> getComparator() { |
| return comparator; |
| } |
| |
| /** |
| * Compare two JavaBeans by their shared property. If {@link #getProperty} is null then the actual objects will be |
| * compared. |
| * |
| * @param o1 Object The first bean to get data from to compare against |
| * @param o2 Object The second bean to get data from to compare |
| * @return int negative or positive based on order |
| */ |
| @Override |
| public int compare(final T o1, final T o2) { |
| |
| if (property == null) { |
| // compare the actual objects |
| return internalCompare(o1, o2); |
| } |
| |
| try { |
| final Object value1 = PropertyUtils.getProperty(o1, property); |
| final Object value2 = PropertyUtils.getProperty(o2, property); |
| return internalCompare(value1, value2); |
| } catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { |
| throw new RuntimeException(e.getClass().getSimpleName()+": " + e.toString()); |
| } |
| } |
| |
| /** |
| * Two {@code BeanComparator}'s are equals if and only if the wrapped comparators and the property names to be |
| * compared are equal. |
| * |
| * @param o Comparator to compare to |
| * @return whether the the comparators are equal or not |
| */ |
| @Override |
| public boolean equals(final Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof BeanComparator)) { |
| return false; |
| } |
| |
| final BeanComparator<?, ?> beanComparator = (BeanComparator<?, ?>) o; |
| |
| if (!comparator.equals(beanComparator.comparator)) { |
| return false; |
| } |
| if (property != null) { |
| if (!property.equals(beanComparator.property)) { |
| return false; |
| } |
| } else { |
| return beanComparator.property == null; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Hashcode compatible with equals. |
| * |
| * @return the hash code for this comparator |
| */ |
| @Override |
| public int hashCode() { |
| return comparator.hashCode(); |
| } |
| |
| /** |
| * Compares the given values using the internal {@code Comparator}. <em>Note</em>: This comparison cannot be |
| * performed in a type-safe way; so {@code ClassCastException} exceptions may be thrown. |
| * |
| * @param val1 the first value to be compared |
| * @param val2 the second value to be compared |
| * @return the result of the comparison |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| private int internalCompare(final Object val1, final Object val2) { |
| return ((Comparator) comparator).compare(val1, val2); |
| } |
| |
| /** |
| * A {@link Comparator Comparator} that compares {@link Comparable Comparable} objects. |
| * <p> |
| * This Comparator is useful, for example, for enforcing the natural order in custom implementations of |
| * {@link java.util.SortedSet SortedSet} and {@link java.util.SortedMap SortedMap}. |
| * </p> |
| * |
| * @param <E> the type of objects compared by this comparator |
| * @see java.util.Collections#reverseOrder() |
| */ |
| private static class NaturalOrderComparator<E extends Comparable<? super E>> |
| implements Comparator<E>, Serializable { |
| |
| /** Serialization version. */ |
| private static final long serialVersionUID = -291439688585137865L; |
| |
| /** The singleton instance. */ |
| @SuppressWarnings("rawtypes") |
| public static final NaturalOrderComparator INSTANCE = new NaturalOrderComparator(); |
| |
| /** |
| * Private constructor to prevent instantiation. Only use INSTANCE. |
| */ |
| private NaturalOrderComparator() { |
| super(); |
| } |
| |
| /** |
| * Compare the two {@link Comparable Comparable} arguments. This method is equivalent to: |
| * |
| * <pre> |
| * ((Comparable) obj1).compareTo(obj2) |
| * </pre> |
| */ |
| @Override |
| public int compare(final E obj1, final E obj2) { |
| return obj1.compareTo(obj2); |
| } |
| |
| @Override |
| public int hashCode() { |
| return "NaturalOrderComparator".hashCode(); |
| } |
| |
| @Override |
| public boolean equals(final Object object) { |
| return this == object || null != object && object.getClass().equals(this.getClass()); |
| } |
| } |
| } |