/*
* Created on Nov 28, 2010
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* Copyright @2010-2011 the original author or authors.
*/
package org.fest.assertions.internal;
import static org.fest.assertions.error.ShouldBeEmpty.shouldBeEmpty;
import static org.fest.assertions.error.ShouldBeNullOrEmpty.shouldBeNullOrEmpty;
import static org.fest.assertions.error.ShouldContain.shouldContain;
import static org.fest.assertions.error.ShouldContainAtIndex.shouldContainAtIndex;
import static org.fest.assertions.error.ShouldContainNull.shouldContainNull;
import static org.fest.assertions.error.ShouldContainOnly.shouldContainOnly;
import static org.fest.assertions.error.ShouldContainSequence.shouldContainSequence;
import static org.fest.assertions.error.ShouldEndWith.shouldEndWith;
import static org.fest.assertions.error.ShouldHaveSize.shouldHaveSize;
import static org.fest.assertions.error.ShouldNotBeEmpty.shouldNotBeEmpty;
import static org.fest.assertions.error.ShouldNotContain.shouldNotContain;
import static org.fest.assertions.error.ShouldNotContainAtIndex.shouldNotContainAtIndex;
import static org.fest.assertions.error.ShouldNotContainNull.shouldNotContainNull;
import static org.fest.assertions.error.ShouldNotHaveDuplicates.shouldNotHaveDuplicates;
import static org.fest.assertions.error.ShouldStartWith.shouldStartWith;
import static org.fest.assertions.internal.CommonErrors.*;
import static org.fest.assertions.internal.CommonValidations.checkIndexValueIsValid;
import static org.fest.assertions.util.ArrayWrapperList.wrap;
import static org.fest.util.Collections.duplicatesFrom;
import static org.fest.util.Objects.areEqual;
import java.lang.reflect.Array;
import java.util.*;
import org.fest.assertions.core.AssertionInfo;
import org.fest.assertions.data.Index;
import org.fest.assertions.util.ArrayWrapperList;
/**
* Assertions for object and primitive arrays. It trades off performance for DRY.
*
* @author Alex Ruiz
*/
class Arrays {
private static final Arrays INSTANCE = new Arrays();
static Arrays instance() {
return INSTANCE;
}
private Arrays() {}
void assertNullOrEmpty(AssertionInfo info, Failures failures, Object array) {
if (array == null || isArrayEmpty(array)) return;
throw failures.failure(info, shouldBeNullOrEmpty(array));
}
void assertEmpty(AssertionInfo info, Failures failures, Object array) {
assertNotNull(info, array);
if (isArrayEmpty(array)) return;
throw failures.failure(info, shouldBeEmpty(array));
}
void assertHasSize(AssertionInfo info, Failures failures, Object array, int expectedSize) {
assertNotNull(info, array);
int sizeOfActual = sizeOf(array);
if (sizeOfActual == expectedSize) return;
throw failures.failure(info, shouldHaveSize(array, sizeOfActual, expectedSize));
}
void assertContains(AssertionInfo info, Failures failures, Object array, Object values) {
checkIsNotNullAndNotEmpty(values);
assertNotNull(info, array);
Set<Object> notFound = new LinkedHashSet<Object>();
int valueCount = sizeOf(values);
for (int i = 0; i < valueCount; i++) {
Object value = Array.get(values, i);
if (!arrayContains(array, value)) notFound.add(value);
}
if (notFound.isEmpty()) return;
throw failures.failure(info, shouldContain(array, values, notFound));
}
void assertContains(AssertionInfo info, Failures failures, Object array, Object value, Index index) {
assertNotNull(info, array);
assertNotEmpty(info, failures, array);
checkIndexValueIsValid(index, sizeOf(array) - 1);
Object actualElement = Array.get(array, index.value);
if (areEqual(actualElement, value)) return;
throw failures.failure(info, shouldContainAtIndex(array, value, index, Array.get(array, index.value)));
}
void assertNotEmpty(AssertionInfo info, Failures failures, Object array) {
assertNotNull(info, array);
if (!isArrayEmpty(array)) return;
throw failures.failure(info, shouldNotBeEmpty());
}
void assertDoesNotContain(AssertionInfo info, Failures failures, Object array, Object value, Index index) {
assertNotNull(info, array);
checkIndexValueIsValid(index, Integer.MAX_VALUE);
int indexValue = index.value;
if (indexValue >= sizeOf(array)) return;
Object actualElement = Array.get(array, index.value);
if (!areEqual(actualElement, value)) return;
throw failures.failure(info, shouldNotContainAtIndex(array, value, index));
}
void assertContainsOnly(AssertionInfo info, Failures failures, Object array, Object values) {
checkIsNotNullAndNotEmpty(values);
assertNotNull(info, array);
Set<Object> notExpected = asSet(array);
Set<Object> notFound = containsOnly(notExpected, values);
if (notExpected.isEmpty() && notFound.isEmpty()) return;
throw failures.failure(info, shouldContainOnly(array, values, notFound, notExpected));
}
private Set<Object> containsOnly(Set<Object> actual, Object values) {
Set<Object> notFound = new LinkedHashSet<Object>();
for (Object o : asSet(values)) {
if (actual.contains(o)) actual.remove(o);
else notFound.add(o);
}
return notFound;
}
private Set<Object> asSet(Object array) {
Set<Object> set = new LinkedHashSet<Object>();
int size = sizeOf(array);
for (int i = 0; i < size; i++) {
Object element = Array.get(array, i);
set.add(element);
}
return set;
}
void assertContainsSequence(AssertionInfo info, Failures failures, Object array, Object sequence) {
checkIsNotNullAndNotEmpty(sequence);
assertNotNull(info, array);
boolean firstAlreadyFound = false;
int i = 0;
int sequenceSize = sizeOf(sequence);
int sizeOfActual = sizeOf(array);
for (int j = 0; j < sizeOfActual; j++) {
Object o = Array.get(array, j);
if (i >= sequenceSize) break;
if (!firstAlreadyFound) {
if (!areEqual(o, Array.get(sequence, i))) continue;
firstAlreadyFound = true;
i++;
continue;
}
if (areEqual(o, Array.get(sequence, i++))) continue;
throw arrayDoesNotContainSequence(info, failures, array, sequence);
}
if (!firstAlreadyFound || i < sequenceSize) throw arrayDoesNotContainSequence(info, failures, array, sequence);
}
private AssertionError arrayDoesNotContainSequence(AssertionInfo info, Failures failures, Object array,
Object sequence) {
return failures.failure(info, shouldContainSequence(array, sequence));
}
void assertDoesNotContain(AssertionInfo info, Failures failures, Object array, Object values) {
checkIsNotNullAndNotEmpty(values);
assertNotNull(info, array);
Set<Object> found = new LinkedHashSet<Object>();
int valueCount = sizeOf(values);
for (int i = 0; i < valueCount; i++) {
Object value = Array.get(values, i);
if (arrayContains(array, value)) found.add(value);
}
if (found.isEmpty()) return;
throw failures.failure(info, shouldNotContain(array, values, found));
}
private boolean arrayContains(Object array, Object value) {
int size = sizeOf(array);
for (int i = 0; i < size; i++) {
Object element = Array.get(array, i);
if (areEqual(element, value)) return true;
}
return false;
}
void assertDoesNotHaveDuplicates(AssertionInfo info, Failures failures, Object array) {
assertNotNull(info, array);
ArrayWrapperList wrapped = wrap(array);
Collection<?> duplicates = duplicatesFrom(wrapped);
if (duplicates.isEmpty()) return;
throw failures.failure(info, shouldNotHaveDuplicates(array, duplicates));
}
void assertStartsWith(AssertionInfo info, Failures failures, Object array, Object sequence) {
checkIsNotNullAndNotEmpty(sequence);
assertNotNull(info, array);
int sequenceSize = sizeOf(sequence);
int arraySize = sizeOf(array);
if (arraySize < sequenceSize) throw arrayDoesNotStartWithSequence(info, failures, array, sequence);
for (int i = 0; i < sequenceSize; i++) {
if (areEqual(Array.get(sequence, i), Array.get(array, i))) continue;
throw arrayDoesNotStartWithSequence(info, failures, array, sequence);
}
}
private AssertionError arrayDoesNotStartWithSequence(AssertionInfo info, Failures failures, Object array,
Object sequence) {
return failures.failure(info, shouldStartWith(array, sequence));
}
void assertEndsWith(AssertionInfo info, Failures failures, Object array, Object sequence) {
checkIsNotNullAndNotEmpty(sequence);
assertNotNull(info, array);
int sequenceSize = sizeOf(sequence);
int arraySize = sizeOf(array);
if (arraySize < sequenceSize) throw arrayDoesNotEndWithSequence(info, failures, array, sequence);
for (int i = 0; i < sequenceSize; i++) {
int sequenceIndex = sequenceSize - (i + 1);
int arrayIndex = arraySize - (i + 1);
if (areEqual(Array.get(sequence, sequenceIndex), Array.get(array, arrayIndex))) continue;
throw arrayDoesNotEndWithSequence(info, failures, array, sequence);
}
}
void assertContainsNull(AssertionInfo info, Failures failures, Object array) {
assertNotNull(info, array);
if (!arrayContains(array, null)) throw failures.failure(info, shouldContainNull(array));
}
void assertDoesNotContainNull(AssertionInfo info, Failures failures, Object array) {
assertNotNull(info, array);
if (arrayContains(array, null)) throw failures.failure(info, shouldNotContainNull(array));
}
private void checkIsNotNullAndNotEmpty(Object values) {
if (values == null) throw arrayOfValuesToLookForIsNull();
if (isArrayEmpty(values)) throw arrayOfValuesToLookForIsEmpty();
}
private boolean isArrayEmpty(Object array) {
return sizeOf(array) == 0;
}
private AssertionError arrayDoesNotEndWithSequence(AssertionInfo info, Failures failures, Object array,
Object sequence) {
return failures.failure(info, shouldEndWith(array, sequence));
}
private void assertNotNull(AssertionInfo info, Object array) {
Objects.instance().assertNotNull(info, array);
}
private int sizeOf(Object array) {
return Array.getLength(array);
}
}