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