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 }