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.Nullable;
027
028/**
029 * A utility for performing a "lazy" chained comparison statement, which 
030 * performs comparisons only until it finds a nonzero result. 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 * 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>Once any comparison returns a nonzero value, remaining comparisons are
046 * "short-circuited".
047 *
048 * @author Mark Davis
049 * @author Kevin Bourrillion
050 * @since 2
051 */
052@GwtCompatible
053public abstract class ComparisonChain {
054  private ComparisonChain() {}
055
056  /**
057   * Begins a new chained comparison statement. See example in the class
058   * documentation.
059   */
060  public static ComparisonChain start() {
061    return ACTIVE;
062  }
063
064  private static final ComparisonChain ACTIVE = new ComparisonChain() {
065    @SuppressWarnings("unchecked")
066    @Override public ComparisonChain compare(
067        Comparable left, Comparable right) {
068      return classify(left.compareTo(right));
069    }
070    @Override public <T> ComparisonChain compare(
071        @Nullable T left, @Nullable T right, Comparator<T> comparator) {
072      return classify(comparator.compare(left, right));
073    }
074    @Override public ComparisonChain compare(int left, int right) {
075      return classify(Ints.compare(left, right));
076    }
077    @Override public ComparisonChain compare(long left, long right) {
078      return classify(Longs.compare(left, right));
079    }
080    @Override public ComparisonChain compare(float left, float right) {
081      return classify(Float.compare(left, right));
082    }
083    @Override public ComparisonChain compare(double left, double right) {
084      return classify(Double.compare(left, right));
085    }
086    @Override public ComparisonChain compare(boolean left, boolean right) {
087      return classify(Booleans.compare(left, right));
088    }
089    ComparisonChain classify(int result) {
090      return (result < 0) ? LESS : (result > 0) ? GREATER : ACTIVE;
091    }
092    @Override public int result() {
093      return 0;
094    }
095  };
096
097  private static final ComparisonChain LESS = new InactiveComparisonChain(-1);
098
099  private static final ComparisonChain GREATER = new InactiveComparisonChain(1);
100
101  private static final class InactiveComparisonChain extends ComparisonChain {
102    final int result;
103
104    InactiveComparisonChain(int result) {
105      this.result = result;
106    }
107    @SuppressWarnings("unchecked")
108    @Override public ComparisonChain compare(
109        @Nullable Comparable left, @Nullable Comparable right) {
110      return this;
111    }
112    @Override public <T> ComparisonChain compare(@Nullable T left,
113        @Nullable T right, @Nullable Comparator<T> comparator) {
114      return this;
115    }
116    @Override public ComparisonChain compare(int left, int right) {
117      return this;
118    }
119    @Override public ComparisonChain compare(long left, long right) {
120      return this;
121    }
122    @Override public ComparisonChain compare(float left, float right) {
123      return this;
124    }
125    @Override public ComparisonChain compare(double left, double right) {
126      return this;
127    }
128    @Override public ComparisonChain compare(boolean left, boolean right) {
129      return this;
130    }
131    @Override public int result() {
132      return result;
133    }
134  }
135
136  /**
137   * Compares two comparable objects as specified by {@link
138   * Comparable#compareTo}, <i>if</i> the result of this comparison chain
139   * has not already been determined.
140   */
141  public abstract ComparisonChain compare(
142      Comparable<?> left, Comparable<?> right);
143
144  /**
145   * Compares two objects using a comparator, <i>if</i> the result of this
146   * comparison chain has not already been determined.
147   */
148  public abstract <T> ComparisonChain compare(
149      @Nullable T left, @Nullable T right, Comparator<T> comparator);
150
151  /**
152   * Compares two {@code int} values as specified by {@link Ints#compare},
153   * <i>if</i> the result of this comparison chain has not already been
154   * determined.
155   */
156  public abstract ComparisonChain compare(int left, int right);
157
158  /**
159   * Compares two {@code long} values as specified by {@link Longs#compare},
160   * <i>if</i> the result of this comparison chain has not already been
161   * determined.
162   */
163  public abstract ComparisonChain compare(long left, long right);
164
165  /**
166   * Compares two {@code float} values as specified by {@link
167   * Float#compare}, <i>if</i> the result of this comparison chain has not
168   * already been determined.
169   */
170  public abstract ComparisonChain compare(float left, float right);
171
172  /**
173   * Compares two {@code double} values as specified by {@link
174   * Double#compare}, <i>if</i> the result of this comparison chain has not
175   * already been determined.
176   */
177  public abstract ComparisonChain compare(double left, double right);
178
179  /**
180   * Compares two {@code boolean} values as specified by {@link
181   * Booleans#compare}, <i>if</i> the result of this comparison chain has not
182   * already been determined.
183   */
184  public abstract ComparisonChain compare(boolean left, boolean right);
185
186  /**
187   * Ends this comparison chain and returns its result: a value having the
188   * same sign as the first nonzero comparison result in the chain, or zero if
189   * every result was zero.
190   */
191  public abstract int result();
192}