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.reflect; 014 015import java.lang.annotation.*; 016import java.lang.reflect.*; 017import java.util.*; 018 019import org.apache.juneau.*; 020import org.apache.juneau.annotation.*; 021import org.apache.juneau.internal.*; 022import org.apache.juneau.marshall.*; 023import org.apache.juneau.svl.*; 024 025/** 026 * Represents an annotation instance on a class and the class it was found on. 027 * 028 * @param <T> The annotation type. 029 */ 030public class AnnotationInfo<T extends Annotation> { 031 032 private final ClassInfo c; 033 private final MethodInfo m; 034 private final Package p; 035 private final T a; 036 final int rank; 037 038 /** 039 * Constructor. 040 * 041 * @param c The class where the annotation was found. 042 * @param m The method where the annotation was found. 043 * @param p The package where the annotation was found. 044 * @param a The annotation found. 045 */ 046 protected AnnotationInfo(ClassInfo c, MethodInfo m, Package p, T a) { 047 this.c = c; 048 this.m = m; 049 this.p = p; 050 this.a = a; 051 this.rank = getRank(a); 052 } 053 054 private static int getRank(Object a) { 055 ClassInfo ci = ClassInfo.ofc(a); 056 MethodInfo mi = ci.getPublicMethod("rank"); 057 if (mi != null && mi.hasReturnType(int.class)) { 058 try { 059 return (int)mi.invoke(a); 060 } catch (ExecutableException e) { 061 e.printStackTrace(); 062 } 063 } 064 return 0; 065 } 066 067 /** 068 * Convenience constructor when annotation is found on a class. 069 * 070 * @param c The class where the annotation was found. 071 * @param a The annotation found. 072 * @return A new {@link AnnotationInfo} object. 073 */ 074 public static <T extends Annotation> AnnotationInfo<T> of(ClassInfo c, T a) { 075 return new AnnotationInfo<>(c, null, null, a); 076 } 077 078 /** 079 * Convenience constructor when annotation is found on a method. 080 * 081 * @param m The method where the annotation was found. 082 * @param a The annotation found. 083 * @return A new {@link AnnotationInfo} object. 084 */ 085 public static <T extends Annotation> AnnotationInfo<T> of(MethodInfo m, T a) { 086 return new AnnotationInfo<>(null, m, null, a); 087 } 088 089 /** 090 * Convenience constructor when annotation is found on a package. 091 * 092 * @param p The package where the annotation was found. 093 * @param a The annotation found. 094 * @return A new {@link AnnotationInfo} object. 095 */ 096 public static <T extends Annotation> AnnotationInfo<T> of(Package p, T a) { 097 return new AnnotationInfo<>(null, null, p, a); 098 } 099 100 /** 101 * Returns the class where the annotation was found. 102 * 103 * @return the class where the annotation was found, or <jk>null</jk> if it wasn't found on a method. 104 */ 105 public ClassInfo getClassOn() { 106 return c; 107 } 108 109 /** 110 * Returns the method where the annotation was found. 111 * 112 * @return the method where the annotation was found, or <jk>null</jk> if it wasn't found on a method. 113 */ 114 public MethodInfo getMethodOn() { 115 return m; 116 } 117 118 /** 119 * Returns the package where the annotation was found. 120 * 121 * @return the package where the annotation was found, or <jk>null</jk> if it wasn't found on a package. 122 */ 123 public Package getPackageOn() { 124 return p; 125 } 126 127 /** 128 * Returns the annotation found. 129 * 130 * @return The annotation found. 131 */ 132 public T getAnnotation() { 133 return a; 134 } 135 136 /** 137 * Converts this object to a readable JSON object for debugging purposes. 138 * 139 * @return A new map showing the attributes of this object as a JSON object. 140 */ 141 public ObjectMap toObjectMap() { 142 ObjectMap om = new ObjectMap(); 143 if (c != null) 144 om.put("class", c.getSimpleName()); 145 if (m != null) 146 om.put("method", m.getShortName()); 147 if (p != null) 148 om.put("package", p.getName()); 149 ObjectMap oa = new ObjectMap(); 150 Class<?> ca = a.annotationType(); 151 for (Method m : ca.getDeclaredMethods()) { 152 try { 153 Object v = m.invoke(a); 154 Object d = m.getDefaultValue(); 155 if (! Objects.equals(v, d)) { 156 if (! (ArrayUtils.isArray(v) && Array.getLength(v) == 0 && Array.getLength(d) == 0)) 157 oa.put(m.getName(), v); 158 } 159 } catch (Exception e) { 160 oa.put(m.getName(), e.getLocalizedMessage()); 161 } 162 } 163 om.put("@" + ca.getSimpleName(), oa); 164 return om; 165 } 166 167 private Constructor<? extends ConfigApply<?>> configApplyConstructor; 168 169 /** 170 * If this annotation has a {@link PropertyStoreApply} annotation, returns an instance of the specified {@link ConfigApply} class. 171 * 172 * @param vrs Variable resolver passed to the {@link ConfigApply} object. 173 * @return A new {@link ConfigApply} object. Never <jk>null</jk>. 174 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 175 */ 176 @SuppressWarnings("unchecked") 177 public ConfigApply<Annotation> getConfigApply(VarResolverSession vrs) throws ExecutableException { 178 try { 179 if (configApplyConstructor == null) { 180 PropertyStoreApply psa = a.annotationType().getAnnotation(PropertyStoreApply.class); 181 if (psa != null) 182 configApplyConstructor = (Constructor<? extends ConfigApply<?>>)psa.value().getConstructor(Class.class, VarResolverSession.class); 183 if (configApplyConstructor == null) 184 throw new NoSuchFieldError("Could not find ConfigApply constructor for annotation:\n" + toString()); 185 } 186 ClassInfo ci = getClassInfo(); 187 return (ConfigApply<Annotation>) configApplyConstructor.newInstance(ci == null ? null : ci.inner(), vrs); 188 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { 189 throw new ExecutableException(e); 190 } 191 } 192 193 /** 194 * Returns the class that this annotation was found on. 195 * 196 * @return The class that this annotation was found on, or <jk>null</jk> if it was found on a package. 197 */ 198 public ClassInfo getClassInfo() { 199 if (this.c != null) 200 return this.c; 201 if (this.m != null) 202 return this.m.getDeclaringClass(); 203 return null; 204 } 205 206 /** 207 * Returns <jk>true</jk> if this annotation is the specified type. 208 * 209 * @param a The type to test against. 210 * @return <jk>true</jk> if this annotation is the specified type. 211 */ 212 public boolean isType(Class<? extends Annotation> a) { 213 Class<? extends Annotation> at = this.a.annotationType(); 214 return at == a; 215 } 216 217 /** 218 * Returns <jk>true</jk> if this annotation has the specified annotation defined on it. 219 * 220 * @param a The annotation to test for. 221 * @return <jk>true</jk> if this annotation has the specified annotation defined on it. 222 */ 223 public boolean hasAnnotation(Class<? extends Annotation> a) { 224 return this.a.annotationType().getAnnotation(a) != null; 225 } 226 227 @Override 228 public String toString() { 229 return SimpleJson.DEFAULT_READABLE.toString(toObjectMap()); 230 } 231}