001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016 017import java.io.*; 018import java.lang.annotation.*; 019import java.util.*; 020 021/** 022 * Reflection utilities. 023 */ 024public final class ReflectionUtils { 025 026 /** 027 * Similar to {@link Class#getAnnotation(Class)} except also searches annotations on interfaces. 028 * 029 * @param <T> The annotation class type. 030 * @param a The annotation class. 031 * @param c The annotated class. 032 * @return The annotation, or <jk>null</jk> if not found. 033 */ 034 public static <T extends Annotation> T getAnnotation(Class<T> a, Class<?> c) { 035 if (c == null) 036 return null; 037 038 T t = getDeclaredAnnotation(a, c); 039 if (t != null) 040 return t; 041 042 t = getAnnotation(a, c.getSuperclass()); 043 if (t != null) 044 return t; 045 046 for (Class<?> c2 : c.getInterfaces()) { 047 t = getAnnotation(a, c2); 048 if (t != null) 049 return t; 050 } 051 return null; 052 } 053 054 /** 055 * Returns the specified annotation only if it's been declared on the specified class. 056 * 057 * <p> 058 * More efficient than calling {@link Class#getAnnotation(Class)} since it doesn't recursively look for the class 059 * up the parent chain. 060 * 061 * @param <T> The annotation class type. 062 * @param a The annotation class. 063 * @param c The annotated class. 064 * @return The annotation, or <jk>null</jk> if not found. 065 */ 066 @SuppressWarnings("unchecked") 067 public static <T extends Annotation> T getDeclaredAnnotation(Class<T> a, Class<?> c) { 068 for (Annotation a2 : c.getDeclaredAnnotations()) 069 if (a2.annotationType() == a) 070 return (T)a2; 071 return null; 072 } 073 074 /** 075 * Returns all instances of the specified annotation on the specified class. 076 * 077 * <p> 078 * Searches all superclasses and superinterfaces. 079 * Results are ordered child-to-parent. 080 * 081 * @param <T> The annotation class type. 082 * @param a The annotation class type. 083 * @param c The class being searched. 084 * @return The found matches, or an empty array if annotation was not found. 085 */ 086 public static <T extends Annotation> List<T> findAnnotations(Class<T> a, Class<?> c) { 087 List<T> l = new LinkedList<>(); 088 appendAnnotations(a, c, l); 089 return l; 090 } 091 092 /** 093 * Same as {@link #findAnnotations(Class, Class)} but returns the list in parent-to-child order. 094 * 095 * @param a The annotation class type. 096 * @param c The class being searched. 097 * @return The found matches, or an empty array if annotation was not found. 098 */ 099 public static <T extends Annotation> List<T> findAnnotationsParentFirst(Class<T> a, Class<?> c) { 100 List<T> l = findAnnotations(a, c); 101 Collections.reverse(l); 102 return l; 103 } 104 105 /** 106 * Same as {@link #findAnnotations(Class, Class)} except returns the annotations as a map with the keys being the 107 * class on which the annotation was found. 108 * 109 * <p> 110 * Results are ordered child-to-parent. 111 * 112 * @param <T> The annotation class type. 113 * @param a The annotation class type. 114 * @param c The class being searched. 115 * @return The found matches, or an empty map if annotation was not found. 116 */ 117 public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMap(Class<T> a, Class<?> c) { 118 LinkedHashMap<Class<?>,T> m = new LinkedHashMap<>(); 119 findAnnotationsMap(a, c, m); 120 return m; 121 } 122 123 /** 124 * Same as {@link #findAnnotationsMap(Class, Class)} except returns results in parent-to-child order. 125 * 126 * @param <T> The annotation class type. 127 * @param a The annotation class type. 128 * @param c The class being searched. 129 * @return The found matches, or an empty map if annotation was not found. 130 */ 131 public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMapParentFirst(Class<T> a, Class<?> c) { 132 return CollectionUtils.reverse(findAnnotationsMap(a, c)); 133 } 134 135 private static <T extends Annotation> void findAnnotationsMap(Class<T> a, Class<?> c, Map<Class<?>,T> m) { 136 if (c == null) 137 return; 138 139 T t = getDeclaredAnnotation(a, c); 140 if (t != null) 141 m.put(c, t); 142 143 findAnnotationsMap(a, c.getSuperclass(), m); 144 145 for (Class<?> c2 : c.getInterfaces()) 146 findAnnotationsMap(a, c2, m); 147 } 148 149 /** 150 * Finds and appends the specified annotation on the specified class and superclasses/interfaces to the specified 151 * list. 152 * 153 * @param a The annotation. 154 * @param c The class. 155 * @param l The list of annotations. 156 */ 157 public static <T extends Annotation> void appendAnnotations(Class<T> a, Class<?> c, List<T> l) { 158 if (c == null) 159 return; 160 161 addIfNotNull(l, getDeclaredAnnotation(a, c)); 162 163 if (c.getPackage() != null) 164 addIfNotNull(l, c.getPackage().getAnnotation(a)); 165 166 appendAnnotations(a, c.getSuperclass(), l); 167 168 for (Class<?> c2 : c.getInterfaces()) 169 appendAnnotations(a, c2, l); 170 } 171 172 /** 173 * Similar to {@link Class#getResourceAsStream(String)} except looks up the parent hierarchy for the existence of 174 * the specified resource. 175 * 176 * @param c The class to return the resource on. 177 * @param name The resource name. 178 * @return An input stream on the specified resource, or <jk>null</jk> if the resource could not be found. 179 */ 180 public static InputStream getResource(Class<?> c, String name) { 181 if (name == null) 182 return null; 183 while (c != null) { 184 InputStream is = c.getResourceAsStream(name); 185 if (is != null) 186 return is; 187 c = c.getSuperclass(); 188 } 189 return null; 190 } 191} 192