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