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 }