| // Copyright 2016 The Bazel Authors. All rights reserved. | |
| // | |
| // 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.google.devtools.build.lib.skylarkinterface; | |
| import com.google.devtools.build.lib.util.Pair; | |
| import java.lang.reflect.Method; | |
| import javax.annotation.Nullable; | |
| /** | |
| * Helpers for accessing Skylark interface annotations. | |
| */ | |
| public class SkylarkInterfaceUtils { | |
| @Nullable | |
| private static Pair<Class<?>, SkylarkModule> searchForSkylarkModule(Class<?> classObj) { | |
| if (classObj.isAnnotationPresent(SkylarkModule.class)) { | |
| return new Pair<Class<?>, SkylarkModule>( | |
| classObj, classObj.getAnnotation(SkylarkModule.class)); | |
| } | |
| Class<?> superclass = classObj.getSuperclass(); | |
| if (superclass != null) { | |
| Pair<Class<?>, SkylarkModule> result = searchForSkylarkModule(superclass); | |
| if (result != null) { | |
| return result; | |
| } | |
| } | |
| for (Class<?> interfaceObj : classObj.getInterfaces()) { | |
| Pair<Class<?>, SkylarkModule> result = searchForSkylarkModule(interfaceObj); | |
| if (result != null) { | |
| return result; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns the {@link SkylarkModule} annotation for the given class, if it exists, and | |
| * null otherwise. The first annotation found will be returned, starting with {@code classObj} | |
| * and following its base classes and interfaces recursively. | |
| */ | |
| @Nullable | |
| public static SkylarkModule getSkylarkModule(Class<?> classObj) { | |
| Pair<Class<?>, SkylarkModule> result = searchForSkylarkModule(classObj); | |
| return result == null ? null : result.second; | |
| } | |
| /** | |
| * Searches {@code classObj}'s class hierarchy and returns the first superclass or interface that | |
| * is annotated with {@link SkylarkModule} (including possibly {@code classObj} itself), or null | |
| * if none is found. | |
| */ | |
| @Nullable | |
| public static Class<?> getParentWithSkylarkModule(Class<?> classObj) { | |
| Pair<Class<?>, SkylarkModule> result = searchForSkylarkModule(classObj); | |
| return result == null ? null : result.first; | |
| } | |
| /** | |
| * Returns the {@link SkylarkCallable} annotation for the given method, if it exists, and | |
| * null otherwise. The first annotation of an overridden version of the method that is found | |
| * will be returned, starting with {@code classObj} and following its base classes and | |
| * interfaces recursively, skipping any annotation inside a class not marked | |
| * {@link SkylarkModule}. | |
| */ | |
| @Nullable | |
| public static SkylarkCallable getSkylarkCallable(Class<?> classObj, Method method) { | |
| try { | |
| Method superMethod = classObj.getMethod(method.getName(), method.getParameterTypes()); | |
| if (classObj.isAnnotationPresent(SkylarkModule.class) | |
| && superMethod.isAnnotationPresent(SkylarkCallable.class)) { | |
| return superMethod.getAnnotation(SkylarkCallable.class); | |
| } | |
| } catch (NoSuchMethodException e) { | |
| // The class might not have the specified method, so an exception is OK. | |
| } | |
| if (classObj.getSuperclass() != null) { | |
| SkylarkCallable annotation = getSkylarkCallable(classObj.getSuperclass(), method); | |
| if (annotation != null) { | |
| return annotation; | |
| } | |
| } | |
| for (Class<?> interfaceObj : classObj.getInterfaces()) { | |
| SkylarkCallable annotation = getSkylarkCallable(interfaceObj, method); | |
| if (annotation != null) { | |
| return annotation; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Convenience version of {@code getAnnotationsFromParentClass(Class, Method)} that uses | |
| * the declaring class of the method. | |
| */ | |
| @Nullable | |
| public static SkylarkCallable getSkylarkCallable(Method method) { | |
| return getSkylarkCallable(method.getDeclaringClass(), method); | |
| } | |
| } |