| /* |
| * 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 |
| * |
| * https://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.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> { |
| |
| /** |
| * 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 final class NaturalOrderComparator<E extends Comparable<? super E>> implements Comparator<E> { |
| |
| /** The singleton instance. */ |
| @SuppressWarnings("rawtypes") |
| public static final NaturalOrderComparator INSTANCE = new NaturalOrderComparator(); |
| |
| /** |
| * Private constructor to prevent instantiation. Only use INSTANCE. |
| */ |
| private NaturalOrderComparator() { |
| } |
| |
| /** |
| * 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 boolean equals(final Object object) { |
| return this == object || object != null && object.getClass().equals(this.getClass()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return "NaturalOrderComparator".hashCode(); |
| } |
| } |
| |
| private static final long serialVersionUID = 1L; |
| |
| /** Property. */ |
| private String property; |
| |
| /** Comparator, untyped. */ |
| 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 {@link 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; |
| } |
| |
| /** |
| * 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 ReflectiveOperationException e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| |
| /** |
| * 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 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) { |
| return beanComparator.property == null; |
| } |
| |
| return property.equals(beanComparator.property); |
| } |
| |
| /** |
| * Gets the Comparator being used to compare beans. |
| * |
| * @return The Comparator being used to compare beans |
| */ |
| public Comparator<V> getComparator() { |
| return comparator; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * 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; |
| } |
| } |