Java example source code file (CompareToBuilder.java)
This example Java source code file (CompareToBuilder.java) is included in the alvinalexander.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
The CompareToBuilder.java Java example source code
/*
* 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.lang3.builder;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Comparator;
import org.apache.commons.lang3.ArrayUtils;
/**
* Assists in implementing {@link java.lang.Comparable#compareTo(Object)} methods.
*
* <p>It is consistent with equals(Object) and
* <code>hashcode() built with {@link EqualsBuilder} and
* {@link HashCodeBuilder}.</p>
*
* <p>Two Objects that compare equal using equals(Object) should normally
* also compare equal using <code>compareTo(Object).
*
* <p>All relevant fields should be included in the calculation of the
* comparison. Derived fields may be ignored. The same fields, in the same
* order, should be used in both <code>compareTo(Object) and
* <code>equals(Object).
*
* <p>To use this class write code as follows:
*
* <pre>
* public class MyClass {
* String field1;
* int field2;
* boolean field3;
*
* ...
*
* public int compareTo(Object o) {
* MyClass myClass = (MyClass) o;
* return new CompareToBuilder()
* .appendSuper(super.compareTo(o)
* .append(this.field1, myClass.field1)
* .append(this.field2, myClass.field2)
* .append(this.field3, myClass.field3)
* .toComparison();
* }
* }
* </pre>
*
* <p>Values are compared in the order they are appended to the builder. If any comparison returns
* a non-zero result, then that value will be the result returned by {@code toComparison()} and all
* subsequent comparisons are skipped.</p>
*
* <p>Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use
* reflection to determine the fields to append. Because fields can be private,
* <code>reflectionCompare uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to
* bypass normal access control checks. This will fail under a security manager,
* unless the appropriate permissions are set up correctly. It is also
* slower than appending explicitly.</p>
*
* <p>A typical implementation of compareTo(Object) using
* <code>reflectionCompare looks like:
* <pre>
* public int compareTo(Object o) {
* return CompareToBuilder.reflectionCompare(this, o);
* }
* </pre>
*
* <p>The reflective methods compare object fields in the order returned by
* {@link Class#getDeclaredFields()}. The fields of the class are compared first, followed by those
* of its parent classes (in order from the bottom to the top of the class hierarchy).</p>
*
* @see java.lang.Comparable
* @see java.lang.Object#equals(Object)
* @see java.lang.Object#hashCode()
* @see EqualsBuilder
* @see HashCodeBuilder
* @since 1.0
*/
public class CompareToBuilder implements Builder<Integer> {
/**
* Current state of the comparison as appended fields are checked.
*/
private int comparison;
/**
* <p>Constructor for CompareToBuilder.
*
* <p>Starts off assuming that the objects are equal. Multiple calls are
* then made to the various append methods, followed by a call to
* {@link #toComparison} to get the result.</p>
*/
public CompareToBuilder() {
super();
comparison = 0;
}
//-----------------------------------------------------------------------
/**
* <p>Compares two Objects via reflection.
*
* <p>Fields can be private, thus AccessibleObject.setAccessible
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared
* <li>Transient members will be not be compared, as they are likely derived
* fields</li>
* <li>Superclass fields will be compared
* </ul>
*
* <p>If both lhs and rhs are null,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @return a negative integer, zero, or a positive integer as <code>lhs
* is less than, equal to, or greater than <code>rhs
* @throws NullPointerException if either (but not both) parameters are
* <code>null
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
*/
public static int reflectionCompare(final Object lhs, final Object rhs) {
return reflectionCompare(lhs, rhs, false, null);
}
/**
* <p>Compares two Objects via reflection.
*
* <p>Fields can be private, thus AccessibleObject.setAccessible
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared
* <li>If compareTransients is true,
* compares transient members. Otherwise ignores them, as they
* are likely derived fields.</li>
* <li>Superclass fields will be compared
* </ul>
*
* <p>If both lhs and rhs are null,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param compareTransients whether to compare transient fields
* @return a negative integer, zero, or a positive integer as <code>lhs
* is less than, equal to, or greater than <code>rhs
* @throws NullPointerException if either <code>lhs or rhs
* (but not both) is <code>null
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
*/
public static int reflectionCompare(final Object lhs, final Object rhs, final boolean compareTransients) {
return reflectionCompare(lhs, rhs, compareTransients, null);
}
/**
* <p>Compares two Objects via reflection.
*
* <p>Fields can be private, thus AccessibleObject.setAccessible
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared
* <li>If compareTransients is true,
* compares transient members. Otherwise ignores them, as they
* are likely derived fields.</li>
* <li>Superclass fields will be compared
* </ul>
*
* <p>If both lhs and rhs are null,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param excludeFields Collection of String fields to exclude
* @return a negative integer, zero, or a positive integer as <code>lhs
* is less than, equal to, or greater than <code>rhs
* @throws NullPointerException if either <code>lhs or rhs
* (but not both) is <code>null
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
* @since 2.2
*/
public static int reflectionCompare(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
}
/**
* <p>Compares two Objects via reflection.
*
* <p>Fields can be private, thus AccessibleObject.setAccessible
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared
* <li>If compareTransients is true,
* compares transient members. Otherwise ignores them, as they
* are likely derived fields.</li>
* <li>Superclass fields will be compared
* </ul>
*
* <p>If both lhs and rhs are null,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param excludeFields array of fields to exclude
* @return a negative integer, zero, or a positive integer as <code>lhs
* is less than, equal to, or greater than <code>rhs
* @throws NullPointerException if either <code>lhs or rhs
* (but not both) is <code>null
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
* @since 2.2
*/
public static int reflectionCompare(final Object lhs, final Object rhs, final String... excludeFields) {
return reflectionCompare(lhs, rhs, false, null, excludeFields);
}
/**
* <p>Compares two Objects via reflection.
*
* <p>Fields can be private, thus AccessibleObject.setAccessible
* is used to bypass normal access control checks. This will fail under a
* security manager unless the appropriate permissions are set.</p>
*
* <ul>
* <li>Static fields will not be compared
* <li>If the compareTransients is true,
* compares transient members. Otherwise ignores them, as they
* are likely derived fields.</li>
* <li>Compares superclass fields up to and including reflectUpToClass.
* If <code>reflectUpToClass is null, compares all superclass fields.
* </ul>
*
* <p>If both lhs and rhs are null,
* they are considered equal.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param compareTransients whether to compare transient fields
* @param reflectUpToClass last superclass for which fields are compared
* @param excludeFields fields to exclude
* @return a negative integer, zero, or a positive integer as <code>lhs
* is less than, equal to, or greater than <code>rhs
* @throws NullPointerException if either <code>lhs or rhs
* (but not both) is <code>null
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
* @since 2.2 (2.0 as <code>reflectionCompare(Object, Object, boolean, Class))
*/
public static int reflectionCompare(
final Object lhs,
final Object rhs,
final boolean compareTransients,
final Class<?> reflectUpToClass,
final String... excludeFields) {
if (lhs == rhs) {
return 0;
}
if (lhs == null || rhs == null) {
throw new NullPointerException();
}
Class<?> lhsClazz = lhs.getClass();
if (!lhsClazz.isInstance(rhs)) {
throw new ClassCastException();
}
final CompareToBuilder compareToBuilder = new CompareToBuilder();
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
lhsClazz = lhsClazz.getSuperclass();
reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
}
return compareToBuilder.toComparison();
}
/**
* <p>Appends to builder the comparison of lhs
* to <code>rhs using the fields defined in clazz.
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param clazz <code>Class that defines fields to be compared
* @param builder <code>CompareToBuilder to append to
* @param useTransients whether to compare transient fields
* @param excludeFields fields to exclude
*/
private static void reflectionAppend(
final Object lhs,
final Object rhs,
final Class<?> clazz,
final CompareToBuilder builder,
final boolean useTransients,
final String[] excludeFields) {
final Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
final Field f = fields[i];
if (!ArrayUtils.contains(excludeFields, f.getName())
&& (!f.getName().contains("$"))
&& (useTransients || !Modifier.isTransient(f.getModifiers()))
&& (!Modifier.isStatic(f.getModifiers()))) {
try {
builder.append(f.get(lhs), f.get(rhs));
} catch (final IllegalAccessException e) {
// This can't happen. Would get a Security exception instead.
// Throw a runtime exception in case the impossible happens.
throw new InternalError("Unexpected IllegalAccessException");
}
}
}
}
//-----------------------------------------------------------------------
/**
* <p>Appends to the builder the compareTo(Object)
* result of the superclass.</p>
*
* @param superCompareTo result of calling <code>super.compareTo(Object)
* @return this - used to chain append calls
* @since 2.0
*/
public CompareToBuilder appendSuper(final int superCompareTo) {
if (comparison != 0) {
return this;
}
comparison = superCompareTo;
return this;
}
//-----------------------------------------------------------------------
/**
* <p>Appends to the builder the comparison of
* two <code>Objects.
*
* <ol>
* <li>Check if lhs == rhs
* <li>Check if either lhs or rhs is null,
* a <code>null object is less than a non-null object
* <li>Check the object contents
* </ol>
*
* <p>lhs must either be an array or implement {@link Comparable}.
*
* @param lhs left-hand object
* @param rhs right-hand object
* @return this - used to chain append calls
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
*/
public CompareToBuilder append(final Object lhs, final Object rhs) {
return append(lhs, rhs, null);
}
/**
* <p>Appends to the builder the comparison of
* two <code>Objects.
*
* <ol>
* <li>Check if lhs == rhs
* <li>Check if either lhs or rhs is null,
* a <code>null object is less than a non-null object
* <li>Check the object contents
* </ol>
*
* <p>If lhs is an array, array comparison methods will be used.
* Otherwise <code>comparator will be used to compare the objects.
* If <code>comparator is null, lhs must
* implement {@link Comparable} instead.</p>
*
* @param lhs left-hand object
* @param rhs right-hand object
* @param comparator <code>Comparator used to compare the objects,
* <code>null means treat lhs as Comparable
* @return this - used to chain append calls
* @throws ClassCastException if <code>rhs is not assignment-compatible
* with <code>lhs
* @since 2.0
*/
public CompareToBuilder append(final Object lhs, final Object rhs, final Comparator<?> comparator) {
if (comparison != 0) {
return this;
}
if (lhs == rhs) {
return this;
}
if (lhs == null) {
comparison = -1;
return this;
}
if (rhs == null) {
comparison = +1;
return this;
}
if (lhs.getClass().isArray()) {
// switch on type of array, to dispatch to the correct handler
// handles multi dimensional arrays
// throws a ClassCastException if rhs is not the correct array type
if (lhs instanceof long[]) {
append((long[]) lhs, (long[]) rhs);
} else if (lhs instanceof int[]) {
append((int[]) lhs, (int[]) rhs);
} else if (lhs instanceof short[]) {
append((short[]) lhs, (short[]) rhs);
} else if (lhs instanceof char[]) {
append((char[]) lhs, (char[]) rhs);
} else if (lhs instanceof byte[]) {
append((byte[]) lhs, (byte[]) rhs);
} else if (lhs instanceof double[]) {
append((double[]) lhs, (double[]) rhs);
} else if (lhs instanceof float[]) {
append((float[]) lhs, (float[]) rhs);
} else if (lhs instanceof boolean[]) {
append((boolean[]) lhs, (boolean[]) rhs);
} else {
// not an array of primitives
// throws a ClassCastException if rhs is not an array
append((Object[]) lhs, (Object[]) rhs, comparator);
}
} else {
// the simple case, not an array, just test the element
if (comparator == null) {
@SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
final Comparable<Object> comparable = (Comparable
Other Java examples (source code examples)
Here is a short list of links related to this Java CompareToBuilder.java source code file: