001    /*
002     * Created on Sep 17, 2010
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * the License. You may obtain a copy of the License at
006     * 
007     * http://www.apache.org/licenses/LICENSE-2.0
008     * 
009     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     * 
013     * Copyright @2010-2011 the original author or authors.
014     */
015    package org.fest.util;
016    
017    import static org.fest.util.Collections.isEmpty;
018    import static org.fest.util.Strings.quote;
019    
020    import java.util.Comparator;
021    import java.util.Iterator;
022    
023    /**
024     * Implements {@link ComparisonStrategy} contract with a comparison strategy based on a {@link Comparator}.
025     * 
026     * @author Joel Costigliola
027     */
028    public class ComparatorBasedComparisonStrategy extends AbstractComparisonStrategy {
029    
030      // A raw type is necessary because we can't make assumptions on object to be compared.
031      @SuppressWarnings("rawtypes")
032      private Comparator comparator;
033    
034      /**
035       * Creates a new </code>{@link ComparatorBasedComparisonStrategy}</code> specifying the comparison strategy with given
036       * comparator.
037       * @param comparator the comparison strategy to use.
038       */
039      public ComparatorBasedComparisonStrategy(@SuppressWarnings("rawtypes") Comparator comparator) {
040        this.comparator = comparator;
041      }
042    
043      /**
044       * Returns true if given {@link Iterable} contains given value according to {@link #comparator}, false otherwise.<br>
045       * If given {@link Iterable} is null or empty, return false.
046       * 
047       * @param iterable the {@link Iterable} to search value in
048       * @param value the object to look for in given {@link Iterable}
049       * @return true if given {@link Iterable} contains given value according to {@link #comparator}, false otherwise.
050       */
051      @SuppressWarnings("unchecked")
052      public boolean iterableContains(Iterable<?> iterable, Object value) {
053        if (isEmpty(iterable)) return false;
054        for (Object element : iterable) {
055          // avoid comparison when objects are the same or both null
056          if (element == value) return true;
057          // both objects are not null => if one is then the other is not => compare next element with value
058          if (value == null || element == null) continue;
059          if (comparator.compare(element, value) == 0) return true;
060        }
061        return false;
062      }
063    
064      /**
065       * Look for given value in given {@link Iterable} according to the {@link Comparator}, if value is found it is removed
066       * from it.<br>
067       * Does nothing if given {@link Iterable} is null (meaning no exception thrown).
068       * @param iterable the {@link Iterable} we want remove value from
069       * @param value object to remove from given {@link Iterable}
070       */
071      @SuppressWarnings("unchecked")
072      public void iterableRemoves(Iterable<?> iterable, Object value) {
073        if (iterable == null) return;
074        Iterator<?> iterator = iterable.iterator();
075        while (iterator.hasNext()) {
076          if (comparator.compare(iterator.next(), value) == 0) {
077            iterator.remove();
078          }
079        }
080      }
081    
082      /**
083       * Returns true if actual and other are equal according to {@link #comparator}, false otherwise.<br>
084       * Handles the cases where one of the parameter is null so that internal {@link #comparator} does not have too.
085       * 
086       * @param actual the object to compare to other
087       * @param other the object to compare to actual
088       * @return true if actual and other are equal according to {@link #comparator}, false otherwise.
089       */
090      @SuppressWarnings("unchecked")
091      public boolean areEqual(Object actual, Object other) {
092        if (actual == null) return other == null;
093        // actual is not null
094        if (other == null) return false;
095        // neither actual nor other are null
096        return comparator.compare(actual, other) == 0;
097      }
098    
099      /**
100       * Returns any duplicate elements from the given {@link Iterable} according to {@link #comparator}.
101       * 
102       * @param iterable the given {@link Iterable} we want to extract duplicate elements.
103       * @return an {@link Iterable} containing the duplicate elements of the given one. If no duplicates are found, an
104       *         empty {@link Iterable} is returned.
105       */
106      // overridden to write javadoc.
107      @Override
108      public Iterable<?> duplicatesFrom(Iterable<?> iterable) {
109        return super.duplicatesFrom(iterable);
110      }
111    
112      @Override
113      public String toString() {
114        String comparatorSimpleClassName = comparator.getClass().getSimpleName();
115        return quote(comparatorSimpleClassName.length() > 0 ? comparatorSimpleClassName : "anonymous comparator class");
116      }
117    
118      public Comparator<?> getComparator() {
119        return comparator;
120      }
121    
122      @SuppressWarnings("unchecked")
123      public boolean stringStartsWith(String string, String prefix) {
124        if (string.length() < prefix.length()) return false;
125        String stringPrefix = string.substring(0, prefix.length());
126        return comparator.compare(stringPrefix, prefix) == 0;
127      }
128    
129      @SuppressWarnings("unchecked")
130      public boolean stringEndsWith(String string, String suffix) {
131        if (string.length() < suffix.length()) return false;
132        String stringSuffix = string.substring(string.length() - suffix.length());
133        return comparator.compare(stringSuffix, suffix) == 0;
134      }
135    
136      public boolean stringContains(String string, String sequence) {
137        int sequenceLength = sequence.length();
138        for (int i = 0; i < string.length(); i++) {
139          String subString = string.substring(i);
140          if (subString.length() < sequenceLength) return false;
141          if (stringStartsWith(subString, sequence)) return true;
142        }
143        return false;
144      }
145    
146      @SuppressWarnings("unchecked")
147      public boolean isGreaterThan(Object actual, Object other) {
148        return comparator.compare(actual, other) > 0;
149      }
150    
151    }