All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.itsallcode.matcher.config.MatcherConfig Maven / Gradle / Ivy

package org.itsallcode.matcher.config;

import static java.util.stream.Collectors.toList;

import java.util.*;
import java.util.function.Function;
import java.util.stream.StreamSupport;

import org.hamcrest.*;

/**
 * Configuration for a {@link ConfigurableMatcher}.
 * 
 * @param  type of the object that is handled by the matcher
 */
public final class MatcherConfig {
    private final T expected;
    private final List> propertyConfigs;

    private MatcherConfig(final T expected, final List> propertyConfigs) {
        this.expected = expected;
        this.propertyConfigs = Collections.unmodifiableList(propertyConfigs);
    }

    T getExpected() {
        return this.expected;
    }

    /**
     * Get the property configurations.
     * 
     * @return property configurations
     */
    @SuppressWarnings("unchecked")
    public List> getPropertyConfigs() {
        return this.propertyConfigs.stream() //
                .map(c -> (PropertyConfig) c) //
                .collect(toList());
    }

    /**
     * Create a new {@link Builder} for the given expected model.
     * 
     * @param       The type of the model to compare.
     * @param expected the expected model
     * @return new builder
     */
    public static  Builder builder(final B expected) {
        return new Builder<>(expected);
    }

    /**
     * Builder for {@link MatcherConfig}.
     * 
     * @param  The type of the model to compare.
     */
    public static final class Builder {
        private final B expected;
        private final List> properties = new ArrayList<>();

        private Builder(final B expected) {
            this.expected = Objects.requireNonNull(expected);
        }

        /**
         * Add a property that can be compared with {@link Matchers#equalTo(Object)}.
         *
         * @param propertyName     name of the property.
         * @param propertyAccessor the accessor function for retrieving the property
         *                         value.
         * @param 

the type of the property. * @return the builder itself for fluent programming style. */ public

Builder addEqualsProperty(final String propertyName, final Function propertyAccessor) { return addProperty(propertyName, propertyAccessor, Matchers::equalTo); } /** * Add a property that can be compared with {@link Matchers#equalTo(Object)}. * * @param propertyName name of the property. * @param propertyAccessor the accessor function for retrieving the property * value. * @param matcherBuilder a function for creating the matcher. * @param

the type of the property. * @return the builder itself for fluent programming style. */ public

Builder addProperty(final String propertyName, final Function propertyAccessor, final Function> matcherBuilder) { final Matcher

matcher = createMatcher(propertyAccessor, matcherBuilder); return addPropertyInternal(propertyName, matcher, propertyAccessor); } @SuppressWarnings("unchecked") private

Matcher

createMatcher(final Function propertyAccessor, final Function> matcherBuilder) { final P expectedValue = propertyAccessor.apply(this.expected); if (expectedValue == null) { return (Matcher

) Matchers.nullValue(); } return matcherBuilder.apply(expectedValue); } /** * Add a property of type {@link Iterable} where the element order is relevant. * * @param propertyName name of the property. * @param propertyAccessor the accessor function for retrieving the property * value. * @param matcherBuilder a function for creating the matcher for the iterable * elements. * @param

the type of the property. * @return the builder itself for fluent programming style. */ public

Builder addIterableProperty(final String propertyName, final Function> propertyAccessor, final Function> matcherBuilder) { final Iterable expectedPropertyValue = propertyAccessor.apply(this.expected); final Matcher> listMatcher = createListMatcher(matcherBuilder, expectedPropertyValue); return addPropertyInternal(propertyName, listMatcher, propertyAccessor); } private

Matcher> createListMatcher(final Function> matcherBuilder, final Iterable expectedPropertyValue) { if (expectedPropertyValue == null) { return createNullIterableMatcher(); } if (!expectedPropertyValue.iterator().hasNext()) { return Matchers.

emptyIterable(); } final List> matchers = StreamSupport.stream(expectedPropertyValue.spliterator(), false) .map(matcherBuilder) // .collect(toList()); return Matchers.contains(matchers); } @SuppressWarnings({ "unchecked", "rawtypes" }) private

Matcher> createNullIterableMatcher() { return new NullIterableMatcher(); } private

Builder addPropertyInternal(final String propertyName, final Matcher

matcher, final Function propertyAccessor) { this.properties.add(new PropertyConfig<>(propertyName, matcher, propertyAccessor)); return this; } /** * Build a new {@link MatcherConfig}. * * @return the new {@link MatcherConfig}. */ public MatcherConfig build() { if (this.properties.isEmpty()) { throw new IllegalArgumentException("Failed to build MatcherConfig: Class " + this.expected.getClass().getName() + " has no properties."); } return new MatcherConfig<>(this.expected, new ArrayList<>(this.properties)); } } private static class NullIterableMatcher extends BaseMatcher> { @Override public boolean matches(final Object item) { return item == null; } @Override public void describeTo(final Description description) { description.appendText("null"); } } }