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