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}