/*
* Copyright 2015 Mark Michaelis
*
* 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 com.github.mmichaelis.hamcrest.nextdeed.reflect;
import com.google.common.base.MoreObjects;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import java.lang.reflect.Modifier;
/**
* Matcher for modifiers of class members such as constructors, methods and fields.
*
* @see Modifier
* @since 1.0.0
*/
public abstract class ModifierMatcherBase<T> extends TypeSafeMatcher<T> {
private final int expectedModifier;
private final String typeName;
private final boolean strict;
/**
* Creates a matcher for member modifiers.
*
* @param expectedModifier modifiers to validate
* @param typeName what type (member, class) the modifiers are evaluated from
* @param strict if {@code true}, all given modifiers must be set, if {@code false} at
* least these modifiers must be set
* @see Modifier
* @since 1.0.0
*/
public ModifierMatcherBase(int expectedModifier, String typeName, boolean strict) {
this.expectedModifier = expectedModifier;
this.typeName = typeName;
this.strict = strict;
}
@Override
public void describeTo(Description description) {
description.appendText(typeName).appendText(" with ");
describeModifier(expectedModifier, description);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("hash", Integer.toHexString(System.identityHashCode(this)))
.add("expectedModifier", expectedModifier)
.add("super", super.toString())
.toString();
}
/**
* Retrieve modifiers of the given item.
*
* @param item item to retrieve modifiers from
* @return modifiers
* @see Modifier
* @since 1.0.0
*/
protected abstract int getModifiers(T item);
@Override
protected boolean matchesSafely(T item) {
if (strict) {
return getModifiers(item) == expectedModifier;
} else {
return (getModifiers(item) & expectedModifier) == expectedModifier;
}
}
@Override
protected void describeMismatchSafely(T item, Description mismatchDescription) {
int actualModifiers = getModifiers(item);
describeModifier(actualModifiers,
mismatchDescription
.appendText("was ")
.appendText(typeName)
.appendText(" ")
.appendValue(item)
.appendText(" with ")
);
mismatchDescription.appendText(" (difference: ")
.appendValue(Modifier.toString(actualModifiers ^ expectedModifier))
.appendText(")");
}
private void describeModifier(int modifier, Description description) {
String modifierString = Modifier.toString(modifier);
String modifiersText =
(modifierString.isEmpty() || (modifierString.indexOf(' ') != -1)) ? "modifiers"
: "modifier";
description
.appendText(modifiersText)
.appendText(strict ? "" : " containing ")
.appendText(": ")
.appendValue(modifierString);
}
}