/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.utils;
import static java.util.Collections.emptyList;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.fest.assertions.Assertions;
import org.fest.assertions.ListAssert;
/**
* Provide additional assert methods to {@link ListAssert}
*
* @author Davide D'Alto
*/
public class OrderedListAssert extends ListAssert {
private final boolean ignoringNullPosition;
protected OrderedListAssert(List<?> actual) {
this( actual, false );
}
private OrderedListAssert(List<?> actual, boolean ignoreNullPosition) {
super( actual );
this.ignoringNullPosition = ignoreNullPosition;
}
public OrderedListAssert ignoreNullOrder() {
return new OrderedListAssert( actual, true );
}
@Override
public OrderedListAssert onProperty(String propertyName) {
isNotNull();
if ( actual.isEmpty() ) {
return new OrderedListAssert( emptyList() );
}
return new OrderedListAssert( getProjection( actual, propertyName ) );
}
/**
* This methods behave like {@link ListAssert#containsExactly(Object...)} with the exception that the order of the
* null elements is ignored if {@link OrderedListAssert#ignoreNullOrder()} is called.
* Note that in this last case, the test will fail if nulls are both at the end and at the beginning.
*
* @see ListAssert#containsExactly(Object...)
*/
@Override
public OrderedListAssert containsExactly(Object... expected) {
if ( ignoringNullPosition ) {
Assertions.assertThat( actual ).hasSize( expected.length );
List<Object> actualWithoutNull = trimNull( actual );
List<Object> expectedWithoutNull = trimNull( Arrays.asList( expected ) );
Assertions.assertThat( actualWithoutNull ).isEqualTo( expectedWithoutNull );
if ( actual.size() > 1 && actualWithoutNull.size() > 0 ) {
if ( actual.get( 0 ) == null && actual.get( actual.size() - 1 ) == null ) {
throw failure( "Null order should be consistent: " + actual );
}
}
}
else {
super.containsExactly( expected );
}
return this;
}
/**
* Remove null values at the beginning and at the end of a list
*/
private static List<Object> trimNull(List<?> original) {
List<Object> trimmed = new ArrayList<Object>();
int last = findLastNonNullValuePosition( original );
int i = -1;
boolean skipNulls = true;
while ( i < last ) {
Object object = original.get( ++i );
if ( !skipNulls ) {
trimmed.add( object );
}
else if ( skipNulls && object != null ) {
skipNulls = false;
trimmed.add( object );
}
}
return trimmed;
}
private static int findLastNonNullValuePosition(List<?> actualWithNull) {
int last = actualWithNull.size() - 1;
while ( last > 0 ) {
if ( actualWithNull.get( last ) != null ) {
return last;
}
last--;
}
return -1;
}
private List<Object> getProjection(List<?> source, String propertyName) {
List<Object> projection = new ArrayList<Object>( source.size() );
for ( Object object : source ) {
projection.add( getPropertyValue( object, propertyName ) );
}
return projection;
}
private Object getPropertyValue(Object object, String propertyName) {
try {
BeanInfo info = Introspector.getBeanInfo( object.getClass() );
PropertyDescriptor[] properties = info.getPropertyDescriptors();
for ( PropertyDescriptor property : properties ) {
if ( property.getName().equals( propertyName ) ) {
return property.getReadMethod().invoke( object );
}
}
}
catch (Exception e) {
throw new RuntimeException( e );
}
throw new IllegalArgumentException( "No property " + propertyName + " exists in " + object.getClass() );
}
}