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