/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* 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.
*/
package org.neo4j.driver.internal.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.TypeSafeMatcher;
public abstract class MatcherFactory<T> implements SelfDescribing
{
public abstract Matcher<T> createMatcher();
/**
* Matches a collection based on the number of elements in the collection that match a given matcher.
*
* @param matcher
* The matcher used for counting matching elements.
* @param count
* The matcher used for evaluating the number of matching elements.
* @param <T>
* The type of elements in the collection.
* @return A matcher for a collection.
*/
public static <T> Matcher<? extends Iterable<T>> count( final Matcher<T> matcher, final Matcher<Integer> count )
{
return new TypeSafeDiagnosingMatcher<Iterable<T>>()
{
@Override
protected boolean matchesSafely( Iterable<T> collection, Description mismatchDescription )
{
int matches = 0;
for ( T item : collection )
{
if ( matcher.matches( item ) )
{
matches++;
}
}
if ( count.matches( matches ) )
{
return true;
}
mismatchDescription.appendText( "actual number of matches was " ).appendValue( matches )
.appendText( " in " ).appendValue( collection );
return false;
}
@Override
public void describeTo( Description description )
{
description.appendText( "collection containing " )
.appendDescriptionOf( count )
.appendText( " occurences of " )
.appendDescriptionOf( matcher );
}
};
}
/**
* Matches a collection that contains elements that match the specified matchers. The elements must be in the same
* order as the given matchers, but the collection may contain other elements in between the matching elements.
*
* @param matchers
* The matchers for the elements of the collection.
* @param <T>
* The type of the elements in the collection.
* @return A matcher for a collection.
*/
@SafeVarargs
public static <T> Matcher<? extends Iterable<T>> containsAtLeast( final Matcher<T>... matchers )
{
@SuppressWarnings( "unchecked" )
MatcherFactory<T>[] factories = new MatcherFactory[matchers.length];
for ( int i = 0; i < factories.length; i++ )
{
factories[i] = matches( matchers[i] );
}
return containsAtLeast( factories );
}
@SafeVarargs
public static <T> Matcher<? extends Iterable<T>> containsAtLeast( final MatcherFactory<T>... matcherFactories )
{
return new TypeSafeMatcher<Iterable<T>>()
{
@Override
protected boolean matchesSafely( Iterable<T> collection )
{
@SuppressWarnings( "unchecked" )
Matcher<T>[] matchers = new Matcher[matcherFactories.length];
for ( int i = 0; i < matchers.length; i++ )
{
matchers[i] = matcherFactories[i].createMatcher();
}
int i = 0;
for ( T item : collection )
{
if ( i >= matchers.length )
{
return true;
}
if ( matchers[i].matches( item ) )
{
i++;
}
}
return i == matchers.length;
}
@Override
public void describeTo( Description description )
{
description.appendText( "collection containing at least " );
for ( int i = 0; i < matcherFactories.length; i++ )
{
if ( i != 0 )
{
if ( i == matcherFactories.length - 1 )
{
description.appendText( " and " );
}
else
{
description.appendText( ", " );
}
}
description.appendDescriptionOf( matcherFactories[i] );
}
description.appendText( " (in that order) " );
}
};
}
@SafeVarargs
public static <T> MatcherFactory<T> inAnyOrder( final Matcher<? extends T>... matchers )
{
return new MatcherFactory<T>()
{
@Override
public Matcher<T> createMatcher()
{
final List<Matcher<? extends T>> remaining = new ArrayList<>( matchers.length );
Collections.addAll( remaining, matchers );
return new BaseMatcher<T>()
{
@Override
public boolean matches( Object item )
{
for ( Iterator<Matcher<? extends T>> matcher = remaining.iterator(); matcher.hasNext(); )
{
if ( matcher.next().matches( item ) )
{
matcher.remove();
return remaining.isEmpty();
}
}
return remaining.isEmpty();
}
@Override
public void describeTo( Description description )
{
describe( description );
}
};
}
@Override
public void describeTo( Description description )
{
describe( description );
}
private void describe( Description description )
{
description.appendText( "in any order" );
String sep = " {";
for ( Matcher<? extends T> matcher : matchers )
{
description.appendText( sep );
description.appendDescriptionOf( matcher );
sep = ", ";
}
description.appendText( "}" );
}
};
}
public static <T> MatcherFactory<T> matches( final Matcher<T> matcher )
{
return new MatcherFactory<T>()
{
@Override
public Matcher<T> createMatcher()
{
return matcher;
}
@Override
public void describeTo( Description description )
{
matcher.describeTo( description );
}
};
}
}