org.mutabilitydetector.unittesting.matchers.reasons.WithAllowedReasonsMatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.mutabilitydetector.unittesting.matchers.reasons.WithAllowedReasonsMatcher.java

Source

/*
 *    Copyright (c) 2008-2011 Graham Allan
 *
 *   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.mutabilitydetector.unittesting.matchers.reasons;

import static java.lang.String.format;
import static java.util.Collections.singleton;
import static org.mutabilitydetector.unittesting.internal.ReasonsFormatter.formatReasons;
import static org.mutabilitydetector.unittesting.matchers.reasons.NoReasonsAllowed.noReasonsAllowed;

import java.util.ArrayList;
import java.util.Collection;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.mutabilitydetector.AnalysisResult;
import org.mutabilitydetector.MutableReasonDetail;

import com.google.common.collect.ImmutableList;

public final class WithAllowedReasonsMatcher extends BaseMatcher<AnalysisResult> {

    public static WithAllowedReasonsMatcher withAllowedReasons(Matcher<AnalysisResult> areImmutable,
            Iterable<Matcher<MutableReasonDetail>> allowing) {
        return new WithAllowedReasonsMatcher(areImmutable, ImmutableList.copyOf(allowing));
    }

    public static WithAllowedReasonsMatcher withNoAllowedReasons(Matcher<AnalysisResult> areImmutable) {
        Matcher<MutableReasonDetail> noReasonsAllowed = noReasonsAllowed();
        return withAllowedReasons(areImmutable, singleton(noReasonsAllowed));
    }

    private final Matcher<AnalysisResult> isImmutable;
    private final Iterable<Matcher<MutableReasonDetail>> allowedReasonMatchers;

    private WithAllowedReasonsMatcher(Matcher<AnalysisResult> isImmutable,
            Iterable<Matcher<MutableReasonDetail>> allowedReasons) {
        this.isImmutable = isImmutable;
        this.allowedReasonMatchers = allowedReasons;
    }

    @Override
    public boolean matches(Object item) {
        return matchesSafely(checkIsValidToMatchOn(item), new StringDescription());
    }

    private AnalysisResult checkIsValidToMatchOn(Object item) {
        if (item == null || !(item instanceof AnalysisResult)) {
            throw new IllegalArgumentException("Trying to pass " + item + " where an "
                    + AnalysisResult.class.getSimpleName() + " is required. "
                    + "This is probably a programmer error, not your fault. Please file an issue. ");
        } else {
            return (AnalysisResult) item;
        }
    }

    @Override
    public void describeMismatch(Object item, Description description) {
        matchesSafely(checkIsValidToMatchOn(item), description);
    }

    private boolean matchesSafely(AnalysisResult analysisResult, Description mismatchDescription) {
        boolean matches = true;
        if (!isImmutable.matches(analysisResult)) {
            matches = false;
            describeMismatchHandlingHamcrest1_1Matcher(analysisResult, mismatchDescription);
        }

        if (mutabilityReasonsHaveBeenAllowed(analysisResult.reasons, mismatchDescription)) {
            matches = true;
        }

        return matches;
    }

    private void describeMismatchHandlingHamcrest1_1Matcher(AnalysisResult analysisResult,
            Description mismatchDescription) {
        try {
            isImmutable.describeMismatch(analysisResult, mismatchDescription);
        } catch (NoSuchMethodError e) {
            mismatchDescription.appendText(
                    format("%s is actually %s%n", analysisResult.dottedClassName, analysisResult.isImmutable));
        }
    }

    private boolean mutabilityReasonsHaveBeenAllowed(Collection<MutableReasonDetail> reasons,
            Description mismatchDescription) {
        Collection<MutableReasonDetail> allowedReasons = collectAllowedReasons(reasons);
        Collection<MutableReasonDetail> unmatchedReasons = new ArrayList<MutableReasonDetail>(reasons);

        unmatchedReasons.removeAll(allowedReasons);

        boolean noAllowedReasonsProvided = allowedReasons.isEmpty();
        boolean allReasonsAllowed = unmatchedReasons.isEmpty();

        if (noAllowedReasonsProvided || !allReasonsAllowed) {
            describeMismatchedReasons(mismatchDescription, unmatchedReasons, allowedReasons);
            return false;
        }

        return true;
    }

    private Collection<MutableReasonDetail> collectAllowedReasons(Collection<MutableReasonDetail> reasons) {
        Collection<MutableReasonDetail> allowedReasons = new ArrayList<MutableReasonDetail>();
        for (MutableReasonDetail reasonDetail : reasons) {
            for (Matcher<MutableReasonDetail> allowedReasonMatcher : allowedReasonMatchers) {
                if (allowedReasonMatcher.matches(reasonDetail)) {
                    allowedReasons.add(reasonDetail);
                }
            }
        }
        return allowedReasons;
    }

    private void describeMismatchedReasons(Description mismatchDescription,
            Collection<MutableReasonDetail> unmatchedReasons, Collection<MutableReasonDetail> allowedReasons) {
        mismatchDescription.appendText(format("    Reasons:%n"));
        mismatchDescription.appendText(formatReasons(unmatchedReasons));
        mismatchDescription.appendText(format("    Allowed reasons:%n"));
        mismatchDescription.appendText(allowedReasons.isEmpty() ? "        None." : formatReasons(allowedReasons));
    }

    @Override
    public void describeTo(Description description) {
        isImmutable.describeTo(description);
    }
}