/* * Copyright 2012 EMBL - European Bioinformatics Institute * * 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 uk.ac.ebi.fg.annotare2.magetabcheck.checker; import com.google.common.base.Predicate; import uk.ac.ebi.fg.annotare2.magetabcheck.checker.annotation.Check; import uk.ac.ebi.fg.annotare2.magetabcheck.checker.annotation.Context; import uk.ac.ebi.fg.annotare2.magetabcheck.checker.annotation.MageTabCheck; import uk.ac.ebi.fg.annotare2.magetabcheck.checker.annotation.Visit; import javax.annotation.Nullable; import java.lang.reflect.*; import java.util.Arrays; import java.util.Collection; import java.util.Map; import static com.google.common.collect.Collections2.filter; import static com.google.common.collect.Maps.newHashMap; /** * @author Olga Melnichuk */ class ClassBasedCheckDefinition extends CheckDefinition { private final Class<?> clazz; private final ClassInstanceProvider instanceProvider; private final Method setContext; private final Method check; private final Map<Class, Method> visitMethods = newHashMap(); protected ClassBasedCheckDefinition(Class<?> clazz, ClassInstanceProvider instanceProvider) { super(clazz.getAnnotation(MageTabCheck.class)); this.clazz = clazz; this.instanceProvider = instanceProvider; setContext = getMethodMarkedAsContext(); check = getMethodMarkedAsCheck(); Collection<Method> allVisitMethods = getMethodsMarkedAsVisit(); for (Method m : allVisitMethods) { Class<?> paramType = null; Type type = m.getGenericParameterTypes()[0]; if (type instanceof TypeVariable) { TypeVariable typeVar = (TypeVariable) type; Class<?> superClass = m.getDeclaringClass(); TypeVariable[] vars = ((Class) superClass).getTypeParameters(); int index = -1; for (int i = 0; i < vars.length; i++) { if (vars[i].getName().equals(typeVar.getName())) { index = i; break; } } if (index >= 0) { Class[] paramTypes = findActualTypes(clazz, superClass); if (index < paramTypes.length) { paramType = paramTypes[index]; } } } else if (type instanceof Class) { paramType = (Class) type; } if (paramType != null) { visitMethods.put(paramType, m); } } } @Override public <T> CheckRunner<T> newRunner(Class<T> itemClass, Object target) { return new ClassBasedCheckRunner<T>(this, target); } @Override protected boolean isSubjectTypeAssignableFrom(Class objType) { for (Class<?> type : visitMethods.keySet()) { if (type.isAssignableFrom(objType)) { return true; } } return false; } @Override public CheckType getType() { return CheckType.CLASS_BASED; } @Override public Class<?> getCheckClass() { return clazz; } public Object getCheckInstance() { return instanceProvider.newInstance(clazz); } public void invokeSetContext(Object target, Map<Class<?>, Object> context) throws IllegalAccessException, InvocationTargetException { if (setContext != null) { setContext.invoke(target, getParams(setContext, context)); } } public void invokeCheck(Object target) throws InvocationTargetException, IllegalAccessException { if (check != null) { check.invoke(target); } } public void invokeVisit(Object target, Object subject) throws InvocationTargetException, IllegalAccessException { Method visit = getMethodMarkedAsVisit(subject.getClass()); if (visit != null) { visit.invoke(target, subject); } } final Method getMethodMarkedAsVisit(Class<?> subjectType) { Method visit = null; for (Class<?> paramType : visitMethods.keySet()) { if (paramType.isAssignableFrom(subjectType)) { visit = visitMethods.get(paramType); } } return visit; } final Method getMethodMarkedAsCheck() { Collection<Method> methods = findMethods(new Predicate<Method>() { @Override public boolean apply(@Nullable Method m) { return m != null && m.getAnnotation(Check.class) != null && m.getParameterTypes().length == 0; } }); return methods.isEmpty() ? null : methods.iterator().next(); } final Collection<Method> getMethodsMarkedAsVisit() { return findMethods(new Predicate<Method>() { @Override public boolean apply(@Nullable Method m) { return (m != null) && (m.getAnnotation(Visit.class) != null) && m.getParameterTypes().length == 1; } }); } final Method getMethodMarkedAsContext() { Collection<Method> methods = findMethods(new Predicate<Method>() { @Override public boolean apply(@Nullable Method m) { return m != null && m.getAnnotation(Context.class) != null && m.getParameterTypes().length == 0; } }); return methods.isEmpty() ? null : methods.iterator().next(); } private Collection<Method> findMethods(Predicate<Method> predicate) { return findMethods(clazz, predicate); } private static Collection<Method> findMethods(Class<?> clazz, Predicate<Method> predicate) { return filter(Arrays.asList(clazz.getMethods()), predicate); } protected static Class[] findActualTypes(Class<?> base, Class<?> superClass) { Class[] actuals = new Class[0]; for (Class clazz = base; !clazz.equals(superClass); clazz = clazz.getSuperclass()) { if (!(clazz.getGenericSuperclass() instanceof ParameterizedType)) { continue; } Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments(); Class[] nextActuals = new Class[types.length]; for (int i = 0; i < types.length; i++) { if (types[i] instanceof Class) { nextActuals[i] = (Class) types[i]; } else { nextActuals[i] = map(clazz.getTypeParameters(), types[i], actuals); } } actuals = nextActuals; } return actuals; } protected static Class map(Object[] variables, Object variable, Class[] actuals) { for (int i = 0; i < variables.length && i < actuals.length; i++) { if (variables[i].equals(variable)) { return actuals[i]; } } return null; } }