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}