001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.collect; 018 019import com.google.common.annotations.GwtCompatible; 020import com.google.common.primitives.Booleans; 021import com.google.common.primitives.Ints; 022import com.google.common.primitives.Longs; 023import java.util.Comparator; 024import org.checkerframework.checker.nullness.qual.Nullable; 025 026/** 027 * A utility for performing a chained comparison statement. For example: 028 * 029 * <pre>{@code 030 * public int compareTo(Foo that) { 031 * return ComparisonChain.start() 032 * .compare(this.aString, that.aString) 033 * .compare(this.anInt, that.anInt) 034 * .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast()) 035 * .result(); 036 * } 037 * }</pre> 038 * 039 * <p>The value of this expression will have the same sign as the <i>first nonzero</i> comparison 040 * result in the chain, or will be zero if every comparison result was zero. 041 * 042 * <p><b>Note:</b> {@code ComparisonChain} instances are <b>immutable</b>. For this utility to work 043 * correctly, calls must be chained as illustrated above. 044 * 045 * <p>Performance note: Even though the {@code ComparisonChain} caller always invokes its {@code 046 * compare} methods unconditionally, the {@code ComparisonChain} implementation stops calling its 047 * inputs' {@link Comparable#compareTo compareTo} and {@link Comparator#compare compare} methods as 048 * soon as one of them returns a nonzero result. This optimization is typically important only in 049 * the presence of expensive {@code compareTo} and {@code compare} implementations. 050 * 051 * <p>See the Guava User Guide article on <a href= 052 * "https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained#comparecompareto"> {@code 053 * ComparisonChain}</a>. 054 * 055 * @author Mark Davis 056 * @author Kevin Bourrillion 057 * @since 2.0 058 */ 059@GwtCompatible 060@ElementTypesAreNonnullByDefault 061public abstract class ComparisonChain { 062 private ComparisonChain() {} 063 064 /** Begins a new chained comparison statement. See example in the class documentation. */ 065 public static ComparisonChain start() { 066 return ACTIVE; 067 } 068 069 private static final ComparisonChain ACTIVE = 070 new ComparisonChain() { 071 @SuppressWarnings("unchecked") // unsafe; see discussion on supertype 072 @Override 073 public ComparisonChain compare(Comparable<?> left, Comparable<?> right) { 074 return classify(((Comparable<Object>) left).compareTo(right)); 075 } 076 077 @Override 078 public <T extends @Nullable Object> ComparisonChain compare( 079 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) { 080 return classify(comparator.compare(left, right)); 081 } 082 083 @Override 084 public ComparisonChain compare(int left, int right) { 085 return classify(Ints.compare(left, right)); 086 } 087 088 @Override 089 public ComparisonChain compare(long left, long right) { 090 return classify(Longs.compare(left, right)); 091 } 092 093 @Override 094 public ComparisonChain compare(float left, float right) { 095 return classify(Float.compare(left, right)); 096 } 097 098 @Override 099 public ComparisonChain compare(double left, double right) { 100 return classify(Double.compare(left, right)); 101 } 102 103 @Override 104 public ComparisonChain compareTrueFirst(boolean left, boolean right) { 105 return classify(Booleans.compare(right, left)); // reversed 106 } 107 108 @Override 109 public ComparisonChain compareFalseFirst(boolean left, boolean right) { 110 return classify(Booleans.compare(left, right)); 111 } 112 113 ComparisonChain classify(int result) { 114 return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE; 115 } 116 117 @Override 118 public int result() { 119 return 0; 120 } 121 }; 122 123 private static final ComparisonChain LESS = new InactiveComparisonChain(-1); 124 125 private static final ComparisonChain GREATER = new InactiveComparisonChain(1); 126 127 private static final class InactiveComparisonChain extends ComparisonChain { 128 final int result; 129 130 InactiveComparisonChain(int result) { 131 this.result = result; 132 } 133 134 @Override 135 public ComparisonChain compare(Comparable<?> left, Comparable<?> right) { 136 return this; 137 } 138 139 @Override 140 public <T extends @Nullable Object> ComparisonChain compare( 141 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) { 142 return this; 143 } 144 145 @Override 146 public ComparisonChain compare(int left, int right) { 147 return this; 148 } 149 150 @Override 151 public ComparisonChain compare(long left, long right) { 152 return this; 153 } 154 155 @Override 156 public ComparisonChain compare(float left, float right) { 157 return this; 158 } 159 160 @Override 161 public ComparisonChain compare(double left, double right) { 162 return this; 163 } 164 165 @Override 166 public ComparisonChain compareTrueFirst(boolean left, boolean right) { 167 return this; 168 } 169 170 @Override 171 public ComparisonChain compareFalseFirst(boolean left, boolean right) { 172 return this; 173 } 174 175 @Override 176 public int result() { 177 return result; 178 } 179 } 180 181 /** 182 * Compares two comparable objects as specified by {@link Comparable#compareTo}, <i>if</i> the 183 * result of this comparison chain has not already been determined. 184 * 185 * <p>This method is declared to accept any 2 {@code Comparable} objects, even if they are not <a 186 * href="https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html">mutually 187 * comparable</a>. If you pass objects that are not mutually comparable, this method may throw an 188 * exception. (The reason for this decision is lost to time, but the reason <i>might</i> be that 189 * we wanted to support legacy classes that implement the raw type {@code Comparable} (instead of 190 * implementing {@code Comparable<Foo>}) without producing warnings. If so, we would prefer today 191 * to produce warnings in that case, and we may change this method to do so in the future. Support 192 * for raw {@code Comparable} types in Guava in general is tracked as <a 193 * href="https://github.com/google/guava/issues/989">#989</a>.) 194 * 195 * @throws ClassCastException if the parameters are not mutually comparable 196 */ 197 public abstract ComparisonChain compare(Comparable<?> left, Comparable<?> right); 198 199 /** 200 * Compares two objects using a comparator, <i>if</i> the result of this comparison chain has not 201 * already been determined. 202 */ 203 public abstract <T extends @Nullable Object> ComparisonChain compare( 204 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator); 205 206 /** 207 * Compares two {@code int} values as specified by {@link Ints#compare}, <i>if</i> the result of 208 * this comparison chain has not already been determined. 209 */ 210 public abstract ComparisonChain compare(int left, int right); 211 212 /** 213 * Compares two {@code long} values as specified by {@link Longs#compare}, <i>if</i> the result of 214 * this comparison chain has not already been determined. 215 */ 216 public abstract ComparisonChain compare(long left, long right); 217 218 /** 219 * Compares two {@code float} values as specified by {@link Float#compare}, <i>if</i> the result 220 * of this comparison chain has not already been determined. 221 */ 222 public abstract ComparisonChain compare(float left, float right); 223 224 /** 225 * Compares two {@code double} values as specified by {@link Double#compare}, <i>if</i> the result 226 * of this comparison chain has not already been determined. 227 */ 228 public abstract ComparisonChain compare(double left, double right); 229 230 /** 231 * Discouraged synonym for {@link #compareFalseFirst}. 232 * 233 * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed are being either 234 * negated or reversed, undo the negation or reversal and use {@link #compareTrueFirst}. 235 * @since 19.0 236 */ 237 @Deprecated 238 public final ComparisonChain compare(Boolean left, Boolean right) { 239 return compareFalseFirst(left, right); 240 } 241 242 /** 243 * Compares two {@code boolean} values, considering {@code true} to be less than {@code false}, 244 * <i>if</i> the result of this comparison chain has not already been determined. 245 * 246 * @since 12.0 247 */ 248 public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); 249 250 /** 251 * Compares two {@code boolean} values, considering {@code false} to be less than {@code true}, 252 * <i>if</i> the result of this comparison chain has not already been determined. 253 * 254 * @since 12.0 (present as {@code compare} since 2.0) 255 */ 256 public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); 257 258 /** 259 * Ends this comparison chain and returns its result: a value having the same sign as the first 260 * nonzero comparison result in the chain, or zero if every result was zero. 261 */ 262 public abstract int result(); 263}