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