001 /*
002 * Created on Nov 19, 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.assertions.internal;
016
017 import static org.fest.assertions.error.ShouldBeSorted.*;
018 import static org.fest.assertions.error.ShouldContainAtIndex.shouldContainAtIndex;
019 import static org.fest.assertions.error.ShouldNotContainAtIndex.shouldNotContainAtIndex;
020 import static org.fest.assertions.internal.CommonValidations.checkIndexValueIsValid;
021
022 import java.util.ArrayList;
023 import java.util.Comparator;
024 import java.util.List;
025
026 import org.fest.assertions.core.AssertionInfo;
027 import org.fest.assertions.data.Index;
028 import org.fest.util.ComparatorBasedComparisonStrategy;
029 import org.fest.util.ComparisonStrategy;
030 import org.fest.util.StandardComparisonStrategy;
031 import org.fest.util.VisibleForTesting;
032
033 /**
034 * Reusable assertions for <code>{@link List}</code>s.
035 *
036 * @author Alex Ruiz
037 * @author Yvonne Wang
038 * @author Joel Costigliola
039 */
040 // TODO inherits from Collections to avoid repeating comparisonStrategy ?
041 public class Lists {
042
043 private static final Lists INSTANCE = new Lists();
044
045 /**
046 * Returns the singleton instance of this class.
047 * @return the singleton instance of this class.
048 */
049 public static Lists instance() {
050 return INSTANCE;
051 }
052
053 private ComparisonStrategy comparisonStrategy;
054
055 @VisibleForTesting
056 Failures failures = Failures.instance();
057
058 @VisibleForTesting
059 Lists() {
060 this(StandardComparisonStrategy.instance());
061 }
062
063 public Lists(ComparisonStrategy comparisonStrategy) {
064 this.comparisonStrategy = comparisonStrategy;
065 }
066
067 @VisibleForTesting
068 public Comparator<?> getComparator() {
069 if (comparisonStrategy instanceof ComparatorBasedComparisonStrategy) {
070 return ((ComparatorBasedComparisonStrategy)comparisonStrategy).getComparator();
071 }
072 return null;
073 }
074
075 /**
076 * Verifies that the given {@code List} contains the given object at the given index.
077 * @param info contains information about the assertion.
078 * @param actual the given {@code List}.
079 * @param value the object to look for.
080 * @param index the index where the object should be stored in the given {@code List}.
081 * @throws AssertionError if the given {@code List} is {@code null} or empty.
082 * @throws NullPointerException if the given {@code Index} is {@code null}.
083 * @throws IndexOutOfBoundsException if the value of the given {@code Index} is equal to or greater than the size of
084 * the given {@code List}.
085 * @throws AssertionError if the given {@code List} does not contain the given object at the given index.
086 */
087 public void assertContains(AssertionInfo info, List<?> actual, Object value, Index index) {
088 assertNotNull(info, actual);
089 Iterables.instance().assertNotEmpty(info, actual);
090 checkIndexValueIsValid(index, actual.size() - 1);
091 Object actualElement = actual.get(index.value);
092 if (areEqual(actualElement, value)) return;
093 throw failures.failure(info,
094 shouldContainAtIndex(actual, value, index, actual.get(index.value), comparisonStrategy));
095 }
096
097 /**
098 * Verifies that the given {@code List} does not contain the given object at the given index.
099 * @param info contains information about the assertion.
100 * @param actual the given {@code List}.
101 * @param value the object to look for.
102 * @param index the index where the object should be stored in the given {@code List}.
103 * @throws AssertionError if the given {@code List} is {@code null}.
104 * @throws NullPointerException if the given {@code Index} is {@code null}.
105 * @throws AssertionError if the given {@code List} contains the given object at the given index.
106 */
107 public void assertDoesNotContain(AssertionInfo info, List<?> actual, Object value, Index index) {
108 assertNotNull(info, actual);
109 checkIndexValueIsValid(index, Integer.MAX_VALUE);
110 int indexValue = index.value;
111 if (indexValue >= actual.size()) return;
112 Object actualElement = actual.get(index.value);
113 if (!areEqual(actualElement, value)) return;
114 throw failures.failure(info, shouldNotContainAtIndex(actual, value, index, comparisonStrategy));
115 }
116
117 /**
118 * Verifies that the actual list is sorted into ascending order according to the natural ordering of its elements.
119 * <p>
120 * All list elements must implement the {@link Comparable} interface and must be mutually comparable (that is,
121 * e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the list), examples :
122 * <ul>
123 * <li>a list composed of {"a1", "a2", "a3"} is ok because the element type (String) is Comparable</li>
124 * <li>a list composed of Rectangle {r1, r2, r3} is <b>NOT ok</b> because Rectangle is not Comparable</li>
125 * <li>a list composed of {True, "abc", False} is <b>NOT ok</b> because elements are not mutually comparable</li>
126 * </ul>
127 * Empty lists are considered sorted.</br> Unique element lists are considered sorted unless the element type is not
128 * Comparable.
129 *
130 * @param info contains information about the assertion.
131 * @param actual the given {@code List}.
132 *
133 * @throws AssertionError if the actual list is not sorted into ascending order according to the natural ordering of
134 * its elements.
135 * @throws AssertionError if the actual list is <code>null</code>.
136 * @throws AssertionError if the actual list element type does not implement {@link Comparable}.
137 * @throws AssertionError if the actual list elements are not mutually {@link Comparable}.
138 */
139 public void assertIsSorted(AssertionInfo info, List<?> actual) {
140 assertNotNull(info, actual);
141 if (comparisonStrategy instanceof ComparatorBasedComparisonStrategy) {
142 // instead of comparing elements with their natural comparator, use the one set by client.
143 Comparator<?> comparator = ((ComparatorBasedComparisonStrategy)comparisonStrategy).getComparator();
144 assertIsSortedAccordingToComparator(info, actual, comparator);
145 return;
146 }
147 try {
148 // sorted assertion is only relevant if elements are Comparable, we assume they are
149 List<Comparable<Object>> comparableList = listOfComparableElements(actual);
150 // array with 0 or 1 element are considered sorted.
151 if (comparableList.size() <= 1) return;
152 for (int i = 0; i < comparableList.size() - 1; i++) {
153 // array is sorted in ascending order iif element i is less or equal than element i+1
154 if (comparableList.get(i).compareTo(comparableList.get(i + 1)) > 0)
155 throw failures.failure(info, shouldBeSorted(i, actual));
156 }
157 } catch (ClassCastException e) {
158 // elements are either not Comparable or not mutually Comparable (e.g. List<Object> containing String and Integer)
159 throw failures.failure(info, shouldHaveMutuallyComparableElements(actual));
160 }
161 }
162
163 /**
164 * Verifies that the actual list is sorted according to the given comparator.</br> Empty lists are considered sorted
165 * whatever the comparator is.</br> One element lists are considered sorted if element is compatible with comparator.
166 *
167 * @param info contains information about the assertion.
168 * @param actual the given {@code List}.
169 * @param comparator the {@link Comparator} used to compare list elements
170 *
171 * @throws AssertionError if the actual list is not sorted according to the given comparator.
172 * @throws AssertionError if the actual list is <code>null</code>.
173 * @throws NullPointerException if the given comparator is <code>null</code>.
174 * @throws AssertionError if the actual list elements are not mutually comparabe according to given Comparator.
175 */
176 @SuppressWarnings({ "rawtypes", "unchecked" })
177 public void assertIsSortedAccordingToComparator(AssertionInfo info, List<?> actual,
178 Comparator<? extends Object> comparator) {
179 assertNotNull(info, actual);
180 if (comparator == null) throw new NullPointerException("The given comparator should not be null");
181 try {
182 // Empty collections are considered sorted even if comparator can't be applied to their element type
183 // We can't verify that point because of erasure type at runtime.
184 if (actual.size() == 0) return;
185 Comparator rawComparator = comparator;
186 if (actual.size() == 1) {
187 // Compare unique element with itself to verify thta it is compatible with comparator (a ClassCastException is
188 // thrown if not). We have to use a raw comparator to compare the unique element of actual ... :(
189 rawComparator.compare(actual.get(0), actual.get(0));
190 return;
191 }
192 for (int i = 0; i < actual.size() - 1; i++) {
193 // List is sorted in comparator defined order iif current element is less or equal than next element
194 if (rawComparator.compare(actual.get(i), actual.get(i + 1)) > 0)
195 throw failures.failure(info, shouldBeSortedAccordingToGivenComparator(i, actual, comparator));
196 }
197 } catch (ClassCastException e) {
198 throw failures.failure(info, shouldHaveComparableElementsAccordingToGivenComparator(actual, comparator));
199 }
200 }
201
202 @SuppressWarnings("unchecked")
203 private static List<Comparable<Object>> listOfComparableElements(List<?> collection) {
204 List<Comparable<Object>> listOfComparableElements = new ArrayList<Comparable<Object>>();
205 for (Object object : collection) {
206 listOfComparableElements.add((Comparable<Object>) object);
207 }
208 return listOfComparableElements;
209 }
210
211 private void assertNotNull(AssertionInfo info, List<?> actual) {
212 Objects.instance().assertNotNull(info, actual);
213 }
214
215 /**
216 * Delegates to {@link ComparisonStrategy#areEqual(Object, Object)}
217 */
218 private boolean areEqual(Object actual, Object other) {
219 return comparisonStrategy.areEqual(actual, other);
220 }
221
222 }