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;
023
024import java.util.Comparator;
025
026import javax.annotation.CheckReturnValue;
027import javax.annotation.Nullable;
028
029/**
030 * A utility for performing a chained comparison statement. For example:
031 * <pre>   {@code
032 *
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 *   }}</pre>
040 *
041 * <p>The value of this expression will have the same sign as the <i>first
042 * nonzero</i> comparison result in the chain, or will be zero if every
043 * comparison result was zero.
044 *
045 * <p><b>Note:</b> {@code ComparisonChain} instances are <b>immutable</b>. For
046 * this utility to work correctly, calls must be chained as illustrated above.
047 *
048 * <p>Performance note: Even though the {@code ComparisonChain} caller always
049 * invokes its {@code compare} methods unconditionally, the {@code
050 * ComparisonChain} implementation stops calling its inputs' {@link
051 * Comparable#compareTo compareTo} and {@link Comparator#compare compare}
052 * methods as soon as one of them returns a nonzero result. This optimization is
053 * typically important only in the presence of expensive {@code compareTo} and
054 * {@code compare} implementations.
055 *
056 * <p>See the Guava User Guide article on <a href=
057 * "https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained#comparecompareto">
058 * {@code ComparisonChain}</a>.
059 *
060 * @author Mark Davis
061 * @author Kevin Bourrillion
062 * @since 2.0
063 */
064@CheckReturnValue
065@GwtCompatible
066public abstract class ComparisonChain {
067  private ComparisonChain() {}
068
069  /**
070   * Begins a new chained comparison statement. See example in the class
071   * documentation.
072   */
073  public static ComparisonChain start() {
074    return ACTIVE;
075  }
076
077  private static final ComparisonChain ACTIVE =
078      new ComparisonChain() {
079        @SuppressWarnings("unchecked")
080        @Override
081        public ComparisonChain compare(Comparable left, Comparable right) {
082          return classify(left.compareTo(right));
083        }
084
085        @Override
086        public <T> ComparisonChain compare(
087            @Nullable T left, @Nullable T right, Comparator<T> comparator) {
088          return classify(comparator.compare(left, right));
089        }
090
091        @Override
092        public ComparisonChain compare(int left, int right) {
093          return classify(Ints.compare(left, right));
094        }
095
096        @Override
097        public ComparisonChain compare(long left, long right) {
098          return classify(Longs.compare(left, right));
099        }
100
101        @Override
102        public ComparisonChain compare(float left, float right) {
103          return classify(Float.compare(left, right));
104        }
105
106        @Override
107        public ComparisonChain compare(double left, double right) {
108          return classify(Double.compare(left, right));
109        }
110
111        @Override
112        public ComparisonChain compareTrueFirst(boolean left, boolean right) {
113          return classify(Booleans.compare(right, left)); // reversed
114        }
115
116        @Override
117        public ComparisonChain compareFalseFirst(boolean left, boolean right) {
118          return classify(Booleans.compare(left, right));
119        }
120
121        ComparisonChain classify(int result) {
122          return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE;
123        }
124
125        @Override
126        public int result() {
127          return 0;
128        }
129      };
130
131  private static final ComparisonChain LESS = new InactiveComparisonChain(-1);
132
133  private static final ComparisonChain GREATER = new InactiveComparisonChain(1);
134
135  private static final class InactiveComparisonChain extends ComparisonChain {
136    final int result;
137
138    InactiveComparisonChain(int result) {
139      this.result = result;
140    }
141
142    @Override
143    public ComparisonChain compare(@Nullable Comparable left, @Nullable Comparable right) {
144      return this;
145    }
146
147    @Override
148    public <T> ComparisonChain compare(
149        @Nullable T left, @Nullable T right, @Nullable Comparator<T> comparator) {
150      return this;
151    }
152
153    @Override
154    public ComparisonChain compare(int left, int right) {
155      return this;
156    }
157
158    @Override
159    public ComparisonChain compare(long left, long right) {
160      return this;
161    }
162
163    @Override
164    public ComparisonChain compare(float left, float right) {
165      return this;
166    }
167
168    @Override
169    public ComparisonChain compare(double left, double right) {
170      return this;
171    }
172
173    @Override
174    public ComparisonChain compareTrueFirst(boolean left, boolean right) {
175      return this;
176    }
177
178    @Override
179    public ComparisonChain compareFalseFirst(boolean left, boolean right) {
180      return this;
181    }
182
183    @Override
184    public int result() {
185      return result;
186    }
187  }
188
189  /**
190   * Compares two comparable objects as specified by {@link
191   * Comparable#compareTo}, <i>if</i> the result of this comparison chain
192   * has not already been determined.
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
198   * comparison chain has not already been determined.
199   */
200  public abstract <T> ComparisonChain compare(
201      @Nullable T left, @Nullable T right, Comparator<T> comparator);
202
203  /**
204   * Compares two {@code int} values as specified by {@link Ints#compare},
205   * <i>if</i> the result of this comparison chain has not already been
206   * determined.
207   */
208  public abstract ComparisonChain compare(int left, int right);
209
210  /**
211   * Compares two {@code long} values as specified by {@link Longs#compare},
212   * <i>if</i> the result of this comparison chain has not already been
213   * determined.
214   */
215  public abstract ComparisonChain compare(long left, long right);
216
217  /**
218   * Compares two {@code float} values as specified by {@link
219   * Float#compare}, <i>if</i> the result of this comparison chain has not
220   * already been determined.
221   */
222  public abstract ComparisonChain compare(float left, float right);
223
224  /**
225   * Compares two {@code double} values as specified by {@link
226   * Double#compare}, <i>if</i> the result of this comparison chain has not
227   * already been determined.
228   */
229  public abstract ComparisonChain compare(double left, double right);
230
231  /**
232   * Discouraged synonym for {@link #compareFalseFirst}.
233   *
234   * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed
235   *     are being either negated or reversed, undo the negation or reversal and
236   *     use {@link #compareTrueFirst}.
237   * @since 19.0
238   */
239  @Deprecated
240  public final ComparisonChain compare(Boolean left, Boolean right) {
241    return compareFalseFirst(left, right);
242  }
243
244  /**
245   * Compares two {@code boolean} values, considering {@code true} to be less
246   * than {@code false}, <i>if</i> the result of this comparison chain has not
247   * already been determined.
248   *
249   * @since 12.0
250   */
251  public abstract ComparisonChain compareTrueFirst(boolean left, boolean right);
252
253  /**
254   * Compares two {@code boolean} values, considering {@code false} to be less
255   * than {@code true}, <i>if</i> the result of this comparison chain has not
256   * already been determined.
257   *
258   * @since 12.0 (present as {@code compare} since 2.0)
259   */
260  public abstract ComparisonChain compareFalseFirst(boolean left, boolean right);
261
262  /**
263   * Ends this comparison chain and returns its result: a value having the
264   * same sign as the first nonzero comparison result in the chain, or zero if
265   * every result was zero.
266   */
267  public abstract int result();
268}