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 057public abstract class ComparisonChain { 058 private ComparisonChain() {} 059 060 /** Begins a new chained comparison statement. See example in the class documentation. */ 061 public static ComparisonChain start() { 062 return ACTIVE; 063 } 064 065 private static final ComparisonChain ACTIVE = 066 new ComparisonChain() { 067 @SuppressWarnings("unchecked") // unsafe; see discussion on supertype 068 @Override 069 public ComparisonChain compare(Comparable<?> left, Comparable<?> right) { 070 return classify(((Comparable<Object>) left).compareTo(right)); 071 } 072 073 @Override 074 public <T extends @Nullable Object> ComparisonChain compare( 075 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) { 076 return classify(comparator.compare(left, right)); 077 } 078 079 @Override 080 public ComparisonChain compare(int left, int right) { 081 return classify(Integer.compare(left, right)); 082 } 083 084 @Override 085 public ComparisonChain compare(long left, long right) { 086 return classify(Long.compare(left, right)); 087 } 088 089 @Override 090 public ComparisonChain compare(float left, float right) { 091 return classify(Float.compare(left, right)); 092 } 093 094 @Override 095 public ComparisonChain compare(double left, double right) { 096 return classify(Double.compare(left, right)); 097 } 098 099 @Override 100 public ComparisonChain compareTrueFirst(boolean left, boolean right) { 101 return classify(Boolean.compare(right, left)); // reversed 102 } 103 104 @Override 105 public ComparisonChain compareFalseFirst(boolean left, boolean right) { 106 return classify(Boolean.compare(left, right)); 107 } 108 109 ComparisonChain classify(int result) { 110 return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE; 111 } 112 113 @Override 114 public int result() { 115 return 0; 116 } 117 }; 118 119 private static final ComparisonChain LESS = new InactiveComparisonChain(-1); 120 121 private static final ComparisonChain GREATER = new InactiveComparisonChain(1); 122 123 private static final class InactiveComparisonChain extends ComparisonChain { 124 final int result; 125 126 InactiveComparisonChain(int result) { 127 this.result = result; 128 } 129 130 @Override 131 public ComparisonChain compare(Comparable<?> left, Comparable<?> right) { 132 return this; 133 } 134 135 @Override 136 public <T extends @Nullable Object> ComparisonChain compare( 137 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) { 138 return this; 139 } 140 141 @Override 142 public ComparisonChain compare(int left, int right) { 143 return this; 144 } 145 146 @Override 147 public ComparisonChain compare(long left, long right) { 148 return this; 149 } 150 151 @Override 152 public ComparisonChain compare(float left, float right) { 153 return this; 154 } 155 156 @Override 157 public ComparisonChain compare(double left, double right) { 158 return this; 159 } 160 161 @Override 162 public ComparisonChain compareTrueFirst(boolean left, boolean right) { 163 return this; 164 } 165 166 @Override 167 public ComparisonChain compareFalseFirst(boolean left, boolean right) { 168 return this; 169 } 170 171 @Override 172 public int result() { 173 return result; 174 } 175 } 176 177 /** 178 * Compares two comparable objects as specified by {@link Comparable#compareTo}, <i>if</i> the 179 * result of this comparison chain has not already been determined. 180 * 181 * <p>This method is declared to accept any 2 {@code Comparable} objects, even if they are not <a 182 * href="https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html">mutually 183 * comparable</a>. If you pass objects that are not mutually comparable, this method may throw an 184 * exception. (The reason for this decision is lost to time, but the reason <i>might</i> be that 185 * we wanted to support legacy classes that implement the raw type {@code Comparable} (instead of 186 * implementing {@code Comparable<Foo>}) without producing warnings. If so, we would prefer today 187 * to produce warnings in that case, and we may change this method to do so in the future. Support 188 * for raw {@code Comparable} types in Guava in general is tracked as <a 189 * href="https://github.com/google/guava/issues/989">#989</a>.) 190 * 191 * @throws ClassCastException if the parameters are not mutually comparable 192 */ 193 public abstract ComparisonChain compare(Comparable<?> left, Comparable<?> right); 194 195 /** 196 * Compares two objects using a comparator, <i>if</i> the result of this comparison chain has not 197 * already been determined. 198 */ 199 public abstract <T extends @Nullable Object> ComparisonChain compare( 200 @ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator); 201 202 /** 203 * Compares two {@code int} values as specified by {@link Integer#compare}, <i>if</i> the result 204 * of this comparison chain has not already been determined. 205 */ 206 public abstract ComparisonChain compare(int left, int right); 207 208 /** 209 * Compares two {@code long} values as specified by {@link Long#compare}, <i>if</i> the result of 210 * this comparison chain has not already been determined. 211 */ 212 public abstract ComparisonChain compare(long left, long right); 213 214 /** 215 * Compares two {@code float} values as specified by {@link Float#compare}, <i>if</i> the result 216 * of this comparison chain has not already been determined. 217 */ 218 public abstract ComparisonChain compare(float left, float right); 219 220 /** 221 * Compares two {@code double} values as specified by {@link Double#compare}, <i>if</i> the result 222 * of this comparison chain has not already been determined. 223 */ 224 public abstract ComparisonChain compare(double left, double right); 225 226 /** 227 * Discouraged synonym for {@link #compareFalseFirst}. 228 * 229 * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed are being either 230 * negated or reversed, undo the negation or reversal and use {@link #compareTrueFirst}. 231 * @since 19.0 232 */ 233 @Deprecated 234 public final ComparisonChain compare(Boolean left, Boolean right) { 235 return compareFalseFirst(left, right); 236 } 237 238 /** 239 * Compares two {@code boolean} values, considering {@code true} to be less than {@code false}, 240 * <i>if</i> the result of this comparison chain has not already been determined. 241 * 242 * @since 12.0 243 */ 244 public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); 245 246 /** 247 * Compares two {@code boolean} values, considering {@code false} to be less than {@code true}, 248 * <i>if</i> the result of this comparison chain has not already been determined. 249 * 250 * @since 12.0 (present as {@code compare} since 2.0) 251 */ 252 public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); 253 254 /** 255 * Ends this comparison chain and returns its result: a value having the same sign as the first 256 * nonzero comparison result in the chain, or zero if every result was zero. 257 */ 258 public abstract int result(); 259}