package com.lexicalscope.fluentreflection; import static ch.lambdaj.Lambda.convert; import static com.lexicalscope.fluentreflection.FluentReflection.type; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.*; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import org.hamcrest.Description; import org.hamcrest.Matcher; /* * Copyright 2011 Tim Wood * * 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. */ /** * A whole bunch of matchers suitable for selecting program elements * * @author tim */ public final class ReflectionMatchers { private ReflectionMatchers() { } public static ReflectionMatcher<FluentAnnotated> annotatedWith(final Class<? extends Annotation> annotation) { return new MatcherCallableAnnotatedWith(annotation); } public static ReflectionMatcher<FluentAccess<?>> anyFluentType() { return new MatcherAnyFluentType(); } public static ReflectionMatcher<FluentAccess<?>> assignableFrom(final Class<?> klass) { return new MatcherAssignableFrom(klass); } public static ReflectionMatcher<FluentAccess<?>> assignableTo(final Class<?> klass) { return new MatcherAssignableTo(klass); } public static ReflectionMatcher<FluentAccess<?>> assignableFrom(final FluentAccess<?> klass) { return new MatcherAssignableFrom(klass); } public static ReflectionMatcher<FluentMember> canBeCalledWithArguments(final Object ... args) { final List<Matcher<? super FluentClass<?>>> types = new ArrayList<Matcher<? super FluentClass<?>>>(); for (final Object object : args) { if(object == null) { types.add(anyFluentType()); } else { final FluentClass<? extends Object> argumentType = type(object.getClass()); if(argumentType.isPrimitive()) { types.add(assignableFrom(argumentType).or(assignableFrom(argumentType.boxedType()))); } else if(argumentType.isUnboxable()) { types.add(assignableFrom(argumentType).or(assignableFrom(argumentType.unboxedType()))); } else { types.add(assignableFrom(argumentType)); } } } return hasArgumentListMatching(types); } /** * Matches the declaring class of a member * * @param declaringClass * the declaring class * * @return true iff the member is declared by the argument */ public static ReflectionMatcher<FluentMember> declaredBy(final Class<?> declaringClass) { return new MatcherDeclaredBy(reflectingOn(declaringClass)); } public static ReflectionMatcher<FluentMember> hasArgumentCount(final int argumentCount) { return new MatcherArgumentCount(argumentCount); } public static ReflectionMatcher<FluentMember> hasArgumentList(final List<Class<?>> argTypes) { return new MatcherArgumentTypes(convert(argTypes, new ConvertClassToReflectedTypeAssignableMatcher())); } public static ReflectionMatcher<FluentMember> hasArgumentListx(final List<? extends FluentAccess<?>> argTypes) { return new MatcherArgumentTypes(convert(argTypes, new ConvertFluentTypeToFluentTypeAssignableMatcher())); } public static ReflectionMatcher<FluentMember> hasArgumentListMatching( final List<Matcher<? super FluentClass<?>>> argTypes) { return new MatcherArgumentTypes(argTypes); } public static ReflectionMatcher<FluentMember> hasArguments(final Class<?>... argTypes) { return hasArgumentList(asList(argTypes)); } public static ReflectionMatcher<FluentMember> hasArgumentsMatching( final Matcher<? super FluentClass<?>>... argTypes) { return hasArgumentListMatching(asList(argTypes)); } public static ReflectionMatcher<FluentAccess<?>> hasInterface(final Class<?> interfac3) { return new MatcherHasInterface(interfac3); } /** * Matches the name of a callable * * @param name * the name * * @return true iff the argument is equal to the name of the callable */ public static ReflectionMatcher<FluentMember> hasName(final String name) { return new MatcherNamed(name); } /** * Matches a substring of the name of a callable * * @param substring * the substring * * @return true iff the argument is contained within the name of the * callable */ public static ReflectionMatcher<FluentMember> hasNameContaining(final CharSequence substring) { return new MatcherHasNameContaining(substring); } /** * Matches a suffix of the name of a callable * * @param suffix * the suffix * * @return true iff the argument is a suffix of the name of the callable */ public static ReflectionMatcher<FluentMember> hasNameEndingWith(final String suffix) { return new MatcherHasNameEndingWith(suffix); } /** * Matches a regular expression against the name of a callable * * @param regex * the regular expression * * @return true iff the argument is a regular expression matching the name * of the callable */ public static ReflectionMatcher<FluentMember> hasNameMatching(final String regex) { return new MatcherHasNameMatching(regex); } /** * Matches a prefix of the name of a callable * * @param prefix * the prefix * * @return true iff the argument is a prefix of the name of the callable */ public static ReflectionMatcher<FluentMember> hasNameStartingWith(final String prefix) { return new MatcherHasNameStartingWith(prefix); } public static ReflectionMatcher<FluentMember> hasNoArguments() { return hasArguments(); } public static ReflectionMatcher<FluentAccess<?>> hasNoInterfaces() { return new MatcherHasNoInterfaces(); } public static ReflectionMatcher<FluentMember> hasNonVoidType() { return not(hasVoidType()); } public static ReflectionMatcher<FluentAccess<?>> hasNoSuperclasses() { return new MatcherHasNoSuperclasses(); } public static ReflectionMatcher<FluentMember> hasPropertyName(final String name) { return new MatcherPropertyName(name); } public static ReflectionMatcher<FluentMember> hasReflectedArgumentList( final List<FluentClass<?>> argTypes) { return new MatcherArgumentTypes(convert(argTypes, new ConvertFluentTypeToFluentTypeAssignableMatcher())); } public static ReflectionMatcher<FluentMember> hasReflectedArguments( final FluentClass<?>... argTypes) { return hasReflectedArgumentList(asList(argTypes)); } public static ReflectionMatcher<FluentClass<?>> hasSimpleName(final String simpleName) { return new MatcherHasSimpleName(equalTo(simpleName)); } public static ReflectionMatcher<FluentMember> hasType(final Class<?> returnType) { if (returnType == null) { return hasVoidType(); } return new MatcherReturnType(new ConvertClassToReflectedTypeAssignableMatcher().convert(returnType)); } public static ReflectionMatcher<FluentMember> hasType(final FluentClass<?> returnType) { if (returnType == null) { return hasVoidType(); } return new MatcherReturnType(new ConvertFluentTypeToFluentTypeAssignableMatcher().convert(returnType)); } public static ReflectionMatcher<FluentMember> hasType(final Matcher<? super FluentClass<?>> returnType) { return new MatcherReturnType(returnType); } public static ReflectionMatcher<FluentMember> hasVisibility(final Visibility visibility) { return new MatcherVisibility(visibility); } public static ReflectionMatcher<FluentMember> hasVoidType() { return new MatcherReturnType(reflectingOn(void.class)); } public static ReflectionMatcher<FluentAccess<?>> isAnInterface() { return new MatcherIsInterface(); } public static Matcher<? super FluentMember> isDeclaredByStrictSubtypeOf(final Class<?> klass) { return new MatcherDeclaredBy(isStrictSubtypeOf(klass)); } public static ReflectionMatcher<FluentMember> isEqualsMethod() { return hasArguments(Object.class). and(hasType(boolean.class)). and(hasName("equals")); } public static Matcher<? super FluentMethod> compatibleWith(final FluentMethod method) { return hasArgumentListx(method.args()). and(hasType(method.type())). and(hasName(method.name())); } public static ReflectionMatcher<FluentMember> isExistence() { return isQuery(). and(hasType(boolean.class).or(hasType(Boolean.class))). and(hasNameStartingWith("is").or(hasNameStartingWith("has"))); } public static ReflectionMatcher<FluentMember> isFinal() { return new MatcherFinalMember(); } public static ReflectionMatcher<FluentMember> isGetter() { return hasNameStartingWith("get").and(isQuery()); } public static ReflectionMatcher<FluentMember> isHashCodeMethod() { return hasNoArguments(). and(hasType(int.class)). and(hasName("hashCode")); } public static ReflectionMatcher<FluentMember> isMutator() { return hasArgumentsMatching(anything()).and( not(hasNonVoidType())); } public static ReflectionMatcher<FluentMember> isNotStatic() { return not(isStatic()); } public static ReflectionMatcher<FluentMember> isPublicMethod() { return new MatcherPublic(); } public static ReflectionMatcher<FluentMember> isQuery() { return hasNoArguments().and(not(hasVoidType())); } public static ReflectionMatcher<FluentField> isReflectingOnField(final Field field) { return new MatcherFieldReflectingOn(field); } public static ReflectionMatcher<FluentMember> isSetter() { return hasNameStartingWith("set").and(isMutator()); } public static ReflectionMatcher<FluentMember> isStatic() { return new MatcherIsStatic(); } public static Matcher<? super FluentClass<?>> isStrictSubtypeOf(final Class<?> klass) { return new MatcherStrictSubtypeOf(klass); } public static ReflectionMatcher<FluentMember> isToStringMethod() { return hasNoArguments(). and(hasType(String.class)). and(hasName("toString")); } public static <T> ReflectionMatcher<T> not(final ReflectionMatcher<T> matcher) { return new ReflectionMatcher<T>() { @Override public void describeTo(final Description description) { description.appendText("not ").appendDescriptionOf(matcher); } @Override protected boolean matchesSafely(final T item) { return !matcher.matches(item); } }; } public static ReflectionMatcher<FluentAccess<?>> reflectingOn(final Class<?> klass) { return new MatcherReflectingOn(klass); } public static ReflectionMatcher<FluentConstructor<?>> reflectingOnConstructor( final Constructor<?> constructor) { return new MatcherConstructorReflectingOn(constructor); } }