View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.commons.reflect;
18  
19  import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
20  import static org.apache.juneau.commons.utils.AssertionUtils.*;
21  import static org.apache.juneau.commons.utils.CollectionUtils.*;
22  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
23  import static org.apache.juneau.commons.utils.Utils.*;
24  
25  import java.lang.annotation.*;
26  import java.util.*;
27  import java.util.function.*;
28  
29  import org.apache.juneau.commons.annotation.*;
30  import org.apache.juneau.commons.collections.*;
31  
32  /**
33   * Encapsulates information about an annotation instance and the element it's declared on.
34   *
35   * <p>
36   * This class provides a convenient wrapper around Java annotations that allows you to:
37   * <ul>
38   * 	<li>Access annotation values in a type-safe manner
39   * 	<li>Query annotation properties without reflection boilerplate
40   * 	<li>Track where the annotation was found (class, method, field, etc.)
41   * 	<li>Sort annotations by precedence using ranks
42   * </ul>
43   *
44   * <h5 class='section'>Example:</h5>
45   * <p class='bjava'>
46   * 	<jc>// Get annotation info from a class</jc>
47   * 	ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
48   * 	Optional&lt;AnnotationInfo&lt;MyAnnotation&gt;&gt; <jv>ai</jv> =
49   * 		<jv>ci</jv>.getAnnotations(MyAnnotation.<jk>class</jk>).findFirst();
50   *
51   * 	<jc>// Access annotation values</jc>
52   * 	<jv>ai</jv>.ifPresent(<jv>x</jv> -&gt; {
53   * 		String <jv>value</jv> = <jv>x</jv>.getValue(String.<jk>class</jk>, <js>"value"</js>).orElse(<js>"default"</js>);
54   * 		<jk>int</jk> <jv>priority</jv> = <jv>x</jv>.getInt(<js>"priority"</js>).orElse(0);
55   * 	});
56   * </p>
57   *
58   * <h5 class='section'>See Also:</h5><ul>
59   * 	<li class='jc'>{@link ClassInfo}
60   * 	<li class='jc'>{@link MethodInfo}
61   * 	<li class='jc'>{@link FieldInfo}
62   * 	<li class='jc'>{@link ConstructorInfo}
63   * 	<li class='jc'>{@link ParameterInfo}
64   * 	<li class='jc'>{@link PackageInfo}
65   * </ul>
66   *
67   * @param <T> The annotation type.
68   */
69  public class AnnotationInfo<T extends Annotation> {
70  
71  	/**
72  	 * Creates a new annotation info object.
73  	 *
74  	 * <h5 class='section'>Example:</h5>
75  	 * <p class='bjava'>
76  	 * 	<jc>// Create annotation info for a class annotation</jc>
77  	 * 	ClassInfo <jv>ci</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>);
78  	 * 	MyAnnotation <jv>annotation</jv> = <jv>ci</jv>.inner().getAnnotation(MyAnnotation.<jk>class</jk>);
79  	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = AnnotationInfo.<jsm>of</jsm>(<jv>ci</jv>, <jv>annotation</jv>);
80  	 * </p>
81  	 *
82  	 * @param <A> The annotation type.
83  	 * @param on The annotatable object where the annotation was found (class, method, field, constructor, parameter, or package).
84  	 * @param value The annotation instance. Must not be <jk>null</jk>.
85  	 * @return A new {@link AnnotationInfo} object wrapping the annotation.
86  	 */
87  	public static <A extends Annotation> AnnotationInfo<A> of(Annotatable on, A value) {
88  		return new AnnotationInfo<>(on, value);
89  	}
90  
91  	private static int findRank(Object a) {
92  		// @formatter:off
93  		return ClassInfo.of(a).getAllMethods().stream()
94  			.filter(m -> m.hasName("rank") && m.hasReturnType(int.class))
95  			.findFirst()
96  			.map(m -> safe(() -> (int)m.invoke(a)))
97  			.orElse(0);
98  		// @formatter:on
99  	}
100 
101 	private final Annotatable annotatable;
102 	final int rank;
103 
104 	private T a;  // Effectively final
105 
106 	private final Supplier<List<MethodInfo>> methods = mem(() -> stream(a.annotationType().getMethods()).map(m -> MethodInfo.of(info(a.annotationType()), m)).toList());
107 
108 	/**
109 	 * Constructor.
110 	 *
111 	 * @param on The annotatable object where the annotation was found.
112 	 * @param a The annotation instance.
113 	 */
114 	AnnotationInfo(Annotatable on, T a) {
115 		this.annotatable = on;  // TODO - Shouldn't allow null.
116 		this.a = assertArgNotNull("a", a);
117 		this.rank = findRank(a);
118 	}
119 
120 	/**
121 	 * Returns the annotation type of this annotation.
122 	 *
123 	 * <p>
124 	 * Same as calling {@link Annotation#annotationType()}.
125 	 *
126 	 * <h5 class='section'>Example:</h5>
127 	 * <p class='bjava'>
128 	 * 	AnnotationInfo&lt;Deprecated&gt; <jv>ai</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getAnnotation(Deprecated.<jk>class</jk>);
129 	 * 	Class&lt;? <jk>extends</jk> Annotation&gt; <jv>type</jv> = <jv>ai</jv>.annotationType();  <jc>// Returns Deprecated.class</jc>
130 	 * </p>
131 	 *
132 	 * @return The annotation type of this annotation.
133 	 * @see Annotation#annotationType()
134 	 */
135 	public Class<? extends Annotation> annotationType() {
136 		return a.annotationType();
137 	}
138 
139 	/**
140 	 * Casts this annotation info to a specific annotation type.
141 	 *
142 	 * <p>
143 	 * This is useful when you have an {@code AnnotationInfo<?>} and need to narrow it to a specific type.
144 	 *
145 	 * <h5 class='section'>Example:</h5>
146 	 * <p class='bjava'>
147 	 * 	AnnotationInfo&lt;?&gt; <jv>ai</jv> = ...;
148 	 *
149 	 * 	<jc>// Safe cast</jc>
150 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>myAi</jv> = <jv>ai</jv>.cast(MyAnnotation.<jk>class</jk>);
151 	 * 	<jk>if</jk> (<jv>myAi</jv> != <jk>null</jk>) {
152 	 * 		<jc>// Use strongly-typed annotation info</jc>
153 	 * 	}
154 	 * </p>
155 	 *
156 	 * @param <A> The annotation type to cast to.
157 	 * @param type The annotation type to cast to.
158 	 * @return This annotation info cast to the specified type, or <jk>null</jk> if the annotation is not of the specified type.
159 	 */
160 	@SuppressWarnings("unchecked")
161 	public <A extends Annotation> AnnotationInfo<A> cast(Class<A> type) {
162 		return type.isInstance(a) ? (AnnotationInfo<A>)this : null;
163 	}
164 
165 	/**
166 	 * Returns true if the specified object represents an annotation that is logically equivalent to this one.
167 	 *
168 	 * <p>
169 	 * Same as calling {@link Annotation#equals(Object)} on the wrapped annotation.
170 	 *
171 	 * <p>
172 	 * Two annotations are considered equal if:
173 	 * <ul>
174 	 * 	<li>They are of the same annotation type
175 	 * 	<li>All their corresponding member values are equal
176 	 * </ul>
177 	 *
178 	 * @param o The reference object with which to compare.
179 	 * @return <jk>true</jk> if the specified object is equal to this annotation.
180 	 * @see Annotation#equals(Object)
181 	 */
182 	@Override /* Overridden from Object */
183 	public boolean equals(Object o) {
184 		if (o instanceof AnnotationInfo o2)
185 			return a.equals(o2.a);
186 		return a.equals(o);
187 	}
188 
189 	/**
190 	 * Returns the value of the specified method on this annotation as a boolean.
191 	 *
192 	 * <h5 class='section'>Example:</h5>
193 	 * <p class='bjava'>
194 	 * 	<jc>// For annotation: @MyAnnotation(enabled=true)</jc>
195 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
196 	 * 	<jk>boolean</jk> <jv>enabled</jv> = <jv>ai</jv>.getBoolean(<js>"enabled"</js>).orElse(<jk>false</jk>);  <jc>// Returns true</jc>
197 	 * </p>
198 	 *
199 	 * @param methodName The method name.
200 	 * @return An {@link Optional} containing the value as a boolean, or empty if not found or not a {@code boolean} type.
201 	 */
202 	public Optional<Boolean> getBoolean(String methodName) {
203 		return getMethod(methodName).filter(x -> x.hasReturnType(boolean.class)).map(x -> (Boolean)x.invoke(a));
204 	}
205 
206 	/**
207 	 * Returns the value of the specified method on this annotation as a class array.
208 	 *
209 	 * <p>
210 	 * For type-safe access to an array of classes of a specific supertype, use {@link #getClassArray(String, Class)}.
211 	 *
212 	 * <h5 class='section'>Example:</h5>
213 	 * <p class='bjava'>
214 	 * 	<jc>// For annotation: @MyAnnotation(types={String.class, Integer.class})</jc>
215 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
216 	 * 	Class&lt;?&gt;[] <jv>types</jv> = <jv>ai</jv>.getClassArray(<js>"types"</js>).orElse(<jk>new</jk> Class[0]);  <jc>// Returns [String.class, Integer.class]</jc>
217 	 * </p>
218 	 *
219 	 * @param methodName The method name.
220 	 * @return An {@link Optional} containing the class array value, or empty if not found or not a {@code Class[]} type.
221 	 */
222 	@SuppressWarnings("unchecked")
223 	public Optional<Class<?>[]> getClassArray(String methodName) {
224 		return (Optional<Class<?>[]>)(Optional<?>)getMethod(methodName).filter(x -> x.hasReturnType(Class[].class)).map(x -> x.invoke(a));
225 	}
226 
227 	/**
228 	 * Returns the value of the specified method on this annotation as a class array of a specific type.
229 	 *
230 	 * <h5 class='section'>Example:</h5>
231 	 * <p class='bjava'>
232 	 * 	<jc>// Get an array of serializer classes from an annotation</jc>
233 	 * 	Optional&lt;Class&lt;? <jk>extends</jk> Serializer&gt;[]&gt; <jv>serializerClasses</jv> =
234 	 * 		<jv>annotationInfo</jv>.getClassArray(<js>"serializers"</js>, Serializer.<jk>class</jk>);
235 	 * </p>
236 	 *
237 	 * @param <T> The expected supertype of the classes.
238 	 * @param methodName The method name.
239 	 * @param type The expected supertype of the class values.
240 	 * @return An optional containing the value of the specified method cast to the expected type,
241 	 *         or empty if not found, not a class array, or any element is not assignable to the expected type.
242 	 */
243 	@SuppressWarnings({ "unchecked", "hiding" })
244 	public <T> Optional<Class<? extends T>[]> getClassArray(String methodName, Class<T> type) {
245 		// @formatter:off
246 		return getMethod(methodName)
247 			.filter(x -> x.hasReturnType(Class[].class))
248 			.map(x -> (Class<?>[])x.invoke(a))
249 			.filter(arr -> {
250 				for (var c : arr) {
251 					if (!type.isAssignableFrom(c))
252 						return false;
253 				}
254 				return true;
255 			})
256 			.map(x -> (Class<? extends T>[])x);
257 		// @formatter:on
258 	}
259 
260 	/**
261 	 * Returns the value of the specified method on this annotation as a class.
262 	 *
263 	 * <p>
264 	 * For type-safe access to a class of a specific supertype, use {@link #getClassValue(String, Class)}.
265 	 *
266 	 * <h5 class='section'>Example:</h5>
267 	 * <p class='bjava'>
268 	 * 	<jc>// For annotation: @MyAnnotation(type=String.class)</jc>
269 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
270 	 * 	Class&lt;?&gt; <jv>type</jv> = <jv>ai</jv>.getClassValue(<js>"type"</js>).orElse(<jk>null</jk>);  <jc>// Returns String.class</jc>
271 	 * </p>
272 	 *
273 	 * @param methodName The method name.
274 	 * @return An {@link Optional} containing the class value, or empty if not found or not a {@link Class} type.
275 	 */
276 	@SuppressWarnings("unchecked")
277 	public Optional<Class<?>> getClassValue(String methodName) {
278 		return (Optional<Class<?>>)(Optional<?>)getMethod(methodName).filter(x -> x.hasReturnType(Class.class)).map(x -> x.invoke(a));
279 	}
280 
281 	/**
282 	 * Returns the value of the specified method on this annotation as a class of a specific type.
283 	 *
284 	 * <h5 class='section'>Example:</h5>
285 	 * <p class='bjava'>
286 	 * 	<jc>// Get a serializer class from an annotation</jc>
287 	 * 	Optional&lt;Class&lt;? <jk>extends</jk> Serializer&gt;&gt; <jv>serializerClass</jv> =
288 	 * 		<jv>annotationInfo</jv>.getClassValue(<js>"serializer"</js>, Serializer.<jk>class</jk>);
289 	 * </p>
290 	 *
291 	 * @param <T> The expected supertype of the class.
292 	 * @param methodName The method name.
293 	 * @param type The expected supertype of the class value.
294 	 * @return An optional containing the value of the specified method cast to the expected type,
295 	 *         or empty if not found, not a class, or not assignable to the expected type.
296 	 */
297 	@SuppressWarnings({ "unchecked", "hiding" })
298 	public <T> Optional<Class<? extends T>> getClassValue(String methodName, Class<T> type) {
299 		// @formatter:off
300 		return getMethod(methodName)
301 			.filter(x -> x.hasReturnType(Class.class))
302 			.map(x -> (Class<?>)x.invoke(a))
303 			.filter(type::isAssignableFrom)
304 			.map(x -> (Class<? extends T>)x);
305 		// @formatter:on
306 	}
307 
308 	//-----------------------------------------------------------------------------------------------------------------
309 	// Annotation interface methods
310 	//-----------------------------------------------------------------------------------------------------------------
311 
312 	/**
313 	 * Returns the value of the specified method on this annotation as a double.
314 	 *
315 	 * <h5 class='section'>Example:</h5>
316 	 * <p class='bjava'>
317 	 * 	<jc>// For annotation: @MyAnnotation(threshold=0.95)</jc>
318 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
319 	 * 	<jk>double</jk> <jv>threshold</jv> = <jv>ai</jv>.getDouble(<js>"threshold"</js>).orElse(0.0);  <jc>// Returns 0.95</jc>
320 	 * </p>
321 	 *
322 	 * @param methodName The method name.
323 	 * @return An {@link Optional} containing the value as a double, or empty if not found or not a {@code double} type.
324 	 */
325 	public Optional<Double> getDouble(String methodName) {
326 		return getMethod(methodName).filter(x -> x.hasReturnType(double.class)).map(x -> (Double)x.invoke(a));
327 	}
328 
329 	/**
330 	 * Returns the value of the specified method on this annotation as a float.
331 	 *
332 	 * <h5 class='section'>Example:</h5>
333 	 * <p class='bjava'>
334 	 * 	<jc>// For annotation: @MyAnnotation(weight=0.5f)</jc>
335 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
336 	 * 	<jk>float</jk> <jv>weight</jv> = <jv>ai</jv>.getFloat(<js>"weight"</js>).orElse(0.0f);  <jc>// Returns 0.5f</jc>
337 	 * </p>
338 	 *
339 	 * @param methodName The method name.
340 	 * @return An {@link Optional} containing the value as a float, or empty if not found or not a {@code float} type.
341 	 */
342 	public Optional<Float> getFloat(String methodName) {
343 		return getMethod(methodName).filter(x -> x.hasReturnType(float.class)).map(x -> (Float)x.invoke(a));
344 	}
345 
346 	/**
347 	 * Returns the value of the specified method on this annotation as an integer.
348 	 *
349 	 * <h5 class='section'>Example:</h5>
350 	 * <p class='bjava'>
351 	 * 	<jc>// For annotation: @MyAnnotation(priority=5)</jc>
352 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
353 	 * 	<jk>int</jk> <jv>priority</jv> = <jv>ai</jv>.getInt(<js>"priority"</js>).orElse(0);  <jc>// Returns 5</jc>
354 	 * </p>
355 	 *
356 	 * @param methodName The method name.
357 	 * @return An {@link Optional} containing the value as an integer, or empty if not found or not an {@code int} type.
358 	 */
359 	public Optional<Integer> getInt(String methodName) {
360 		return getMethod(methodName).filter(x -> x.hasReturnType(int.class)).map(x -> (Integer)x.invoke(a));
361 	}
362 
363 	/**
364 	 * Returns the value of the specified method on this annotation as a long.
365 	 *
366 	 * <h5 class='section'>Example:</h5>
367 	 * <p class='bjava'>
368 	 * 	<jc>// For annotation: @MyAnnotation(timestamp=1234567890L)</jc>
369 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
370 	 * 	<jk>long</jk> <jv>timestamp</jv> = <jv>ai</jv>.getLong(<js>"timestamp"</js>).orElse(0L);  <jc>// Returns 1234567890L</jc>
371 	 * </p>
372 	 *
373 	 * @param methodName The method name.
374 	 * @return An {@link Optional} containing the value as a long, or empty if not found or not a {@code long} type.
375 	 */
376 	public Optional<Long> getLong(String methodName) {
377 		return getMethod(methodName).filter(x -> x.hasReturnType(long.class)).map(x -> (Long)x.invoke(a));
378 	}
379 
380 	/**
381 	 * Returns the method with the specified name on this annotation.
382 	 *
383 	 * <h5 class='section'>Example:</h5>
384 	 * <p class='bjava'>
385 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
386 	 * 	Optional&lt;MethodInfo&gt; <jv>method</jv> = <jv>ai</jv>.getMethod(<js>"value"</js>);
387 	 * 	<jv>method</jv>.ifPresent(<jv>m</jv> -&gt; System.<jsf>out</jsf>.println(<jv>m</jv>.getReturnType()));
388 	 * </p>
389 	 *
390 	 * @param methodName The method name to look for.
391 	 * @return An {@link Optional} containing the method info, or empty if method not found.
392 	 */
393 	public Optional<MethodInfo> getMethod(String methodName) {
394 		return methods.get().stream().filter(x -> eq(methodName, x.getSimpleName())).findFirst();
395 	}
396 
397 	//-----------------------------------------------------------------------------------------------------------------
398 	// Private helper methods
399 	//-----------------------------------------------------------------------------------------------------------------
400 
401 	/**
402 	 * Returns the simple class name of this annotation.
403 	 *
404 	 * <h5 class='section'>Example:</h5>
405 	 * <p class='bjava'>
406 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
407 	 * 	String <jv>name</jv> = <jv>ai</jv>.getName();  <jc>// Returns "MyAnnotation"</jc>
408 	 * </p>
409 	 *
410 	 * @return The simple class name of the annotation (e.g., {@code "Override"} for {@code @Override}).
411 	 */
412 	public String getName() { return cns(a.annotationType()); }
413 
414 	/**
415 	 * Returns the rank of this annotation for sorting by precedence.
416 	 *
417 	 * <p>
418 	 * The rank is determined by checking if the annotation has a {@code rank()} method that returns an {@code int}.
419 	 * If found, that value is used; otherwise the rank defaults to {@code 0}.
420 	 *
421 	 * <p>
422 	 * Higher rank values indicate higher precedence when multiple annotations of the same type are present.
423 	 *
424 	 * <h5 class='section'>Example:</h5>
425 	 * <p class='bjava'>
426 	 * 	<jc>// Annotation with rank method</jc>
427 	 * 	<ja>@interface</ja> MyAnnotation {
428 	 * 		<jk>int</jk> rank() <jk>default</jk> 0;
429 	 * 	}
430 	 *
431 	 * 	<jc>// Get rank from annotation info</jc>
432 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
433 	 * 	<jk>int</jk> <jv>rank</jv> = <jv>ai</jv>.getRank();  <jc>// Returns value from rank() method</jc>
434 	 * </p>
435 	 *
436 	 * @return The rank of this annotation, or {@code 0} if no rank method exists.
437 	 */
438 	public int getRank() { return rank; }
439 
440 	/**
441 	 * Returns the return type of the specified method on this annotation.
442 	 *
443 	 * <h5 class='section'>Example:</h5>
444 	 * <p class='bjava'>
445 	 * 	Optional&lt;ClassInfo&gt; <jv>returnType</jv> = <jv>annotationInfo</jv>.getReturnType(<js>"value"</js>);
446 	 * </p>
447 	 *
448 	 * @param methodName The method name.
449 	 * @return An optional containing the return type of the specified method, or empty if method not found.
450 	 */
451 	public Optional<ClassInfo> getReturnType(String methodName) {
452 		return getMethod(methodName).map(x -> x.getReturnType());
453 	}
454 
455 	/**
456 	 * Returns the value of the specified method on this annotation as a string.
457 	 *
458 	 * <h5 class='section'>Example:</h5>
459 	 * <p class='bjava'>
460 	 * 	<jc>// For annotation: @MyAnnotation(name="John", age=30)</jc>
461 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
462 	 * 	String <jv>name</jv> = <jv>ai</jv>.getString(<js>"name"</js>).orElse(<js>"unknown"</js>);  <jc>// Returns "John"</jc>
463 	 * </p>
464 	 *
465 	 * @param methodName The method name.
466 	 * @return An {@link Optional} containing the value as a string, or empty if not found or not a string type.
467 	 */
468 	public Optional<String> getString(String methodName) {
469 		return getMethod(methodName).filter(x -> x.hasReturnType(String.class)).map(x -> s(x.invoke(a)));
470 	}
471 
472 	/**
473 	 * Returns the value of the specified method on this annotation as a string array.
474 	 *
475 	 * <h5 class='section'>Example:</h5>
476 	 * <p class='bjava'>
477 	 * 	<jc>// For annotation: @MyAnnotation(tags={"foo", "bar"})</jc>
478 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
479 	 * 	String[] <jv>tags</jv> = <jv>ai</jv>.getStringArray(<js>"tags"</js>).orElse(<jk>new</jk> String[0]);  <jc>// Returns ["foo", "bar"]</jc>
480 	 * </p>
481 	 *
482 	 * @param methodName The method name.
483 	 * @return An {@link Optional} containing the string array value, or empty if not found or not a {@code String[]} type.
484 	 */
485 	public Optional<String[]> getStringArray(String methodName) {
486 		return getMethod(methodName).filter(x -> x.hasReturnType(String[].class)).map(x -> (String[])x.invoke(a));
487 	}
488 
489 	/**
490 	 * Returns the value of the {@code value()} method on this annotation as a string.
491 	 *
492 	 * <p>
493 	 * This is a convenience method equivalent to calling {@link #getString(String) getString("value")}.
494 	 *
495 	 * <h5 class='section'>Example:</h5>
496 	 * <p class='bjava'>
497 	 * 	<jc>// For annotation: @MyAnnotation("foo")</jc>
498 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
499 	 * 	String <jv>value</jv> = <jv>ai</jv>.getValue().orElse(<js>"default"</js>);  <jc>// Returns "foo"</jc>
500 	 * </p>
501 	 *
502 	 * @return An {@link Optional} containing the value of the {@code value()} method, or empty if not found or not a string.
503 	 */
504 	public Optional<String> getValue() { return getString("value"); }
505 
506 	/**
507 	 * Returns the value of a specific annotation method.
508 	 *
509 	 * <p>
510 	 * This method provides type-safe access to annotation field values without requiring
511 	 * explicit reflection calls or casting.
512 	 *
513 	 * <h5 class='section'>Example:</h5>
514 	 * <p class='bjava'>
515 	 * 	<jc>// For annotation: @interface MyAnnotation { String value(); int priority(); }</jc>
516 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
517 	 *
518 	 * 	<jc>// Get string value</jc>
519 	 * 	Optional&lt;String&gt; <jv>value</jv> = <jv>ai</jv>.getValue(String.<jk>class</jk>, <js>"value"</js>);
520 	 *
521 	 * 	<jc>// Get int value</jc>
522 	 * 	Optional&lt;Integer&gt; <jv>priority</jv> = <jv>ai</jv>.getValue(Integer.<jk>class</jk>, <js>"priority"</js>);
523 	 * </p>
524 	 *
525 	 * @param <V> The expected type of the annotation field value.
526 	 * @param type The expected class of the annotation field value.
527 	 * @param name The name of the annotation method (field).
528 	 * @return An {@link Optional} containing the value if found and type matches, empty otherwise.
529 	 */
530 	@SuppressWarnings("unchecked")
531 	public <V> Optional<V> getValue(Class<V> type, String name) {
532 		// @formatter:off
533 		return methods.get().stream()
534 			.filter(m -> eq(m.getName(), name) && eq(m.getReturnType().inner(), type))
535 			.map(m -> safe(() -> (V)m.invoke(a)))
536 			.findFirst();
537 		// @formatter:on
538 	}
539 
540 	/**
541 	 * Returns <jk>true</jk> if this annotation is itself annotated with the specified annotation.
542 	 *
543 	 * <p>
544 	 * This checks for meta-annotations on the annotation type.
545 	 *
546 	 * <h5 class='section'>Example:</h5>
547 	 * <p class='bjava'>
548 	 * 	<jc>// Check if @MyAnnotation is annotated with @Documented</jc>
549 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
550 	 * 	<jk>boolean</jk> <jv>isDocumented</jv> = <jv>ai</jv>.hasAnnotation(Documented.<jk>class</jk>);
551 	 * </p>
552 	 *
553 	 * @param <A> The meta-annotation type.
554 	 * @param type The meta-annotation to test for.
555 	 * @return <jk>true</jk> if this annotation is annotated with the specified annotation.
556 	 */
557 	public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
558 		return nn(this.a.annotationType().getAnnotation(type));
559 	}
560 
561 	/**
562 	 * Returns the hash code of this annotation.
563 	 *
564 	 * <p>
565 	 * Same as calling {@link Annotation#hashCode()} on the wrapped annotation.
566 	 *
567 	 * <p>
568 	 * The hash code of an annotation is the sum of the hash codes of its members (including those with default values).
569 	 *
570 	 * @return The hash code of this annotation.
571 	 * @see Annotation#hashCode()
572 	 */
573 	@Override /* Overridden from Object */
574 	public int hashCode() {
575 		return a.hashCode();
576 	}
577 
578 	/**
579 	 * Returns <jk>true</jk> if this annotation has the specified fully-qualified name.
580 	 *
581 	 * <h5 class='section'>Example:</h5>
582 	 * <p class='bjava'>
583 	 * 	<jk>boolean</jk> <jv>isName</jv> = <jv>annotationInfo</jv>.hasName(<js>"org.apache.juneau.annotation.Name"</js>);
584 	 * </p>
585 	 *
586 	 * @param value The fully-qualified name to check.
587 	 * @return <jk>true</jk> if this annotation has the specified fully-qualified name.
588 	 */
589 	public boolean hasName(String value) {
590 		return eq(value, a.annotationType().getName());
591 	}
592 
593 	/**
594 	 * Returns <jk>true</jk> if this annotation has the specified simple name.
595 	 *
596 	 * <h5 class='section'>Example:</h5>
597 	 * <p class='bjava'>
598 	 * 	<jk>boolean</jk> <jv>isName</jv> = <jv>annotationInfo</jv>.hasSimpleName(<js>"Name"</js>);
599 	 * </p>
600 	 *
601 	 * @param value The simple name to check.
602 	 * @return <jk>true</jk> if this annotation has the specified simple name.
603 	 */
604 	public boolean hasSimpleName(String value) {
605 		return eq(value, a.annotationType().getSimpleName());
606 	}
607 
608 	/**
609 	 * Returns the wrapped annotation instance.
610 	 *
611 	 * <h5 class='section'>Example:</h5>
612 	 * <p class='bjava'>
613 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
614 	 * 	MyAnnotation <jv>annotation</jv> = <jv>ai</jv>.inner();
615 	 *
616 	 * 	<jc>// Access annotation methods directly</jc>
617 	 * 	String <jv>value</jv> = <jv>annotation</jv>.value();
618 	 * </p>
619 	 *
620 	 * @return The wrapped annotation instance.
621 	 */
622 	public T inner() {
623 		return a;
624 	}
625 
626 	/**
627 	 * Returns <jk>true</jk> if this annotation is in the specified {@link AnnotationGroup}.
628 	 *
629 	 * <p>
630 	 * Annotation groups are used to logically group related annotations together.
631 	 * This checks if the annotation is annotated with {@link AnnotationGroup} and if
632 	 * the group value matches the specified type.
633 	 *
634 	 * <h5 class='section'>Example:</h5>
635 	 * <p class='bjava'>
636 	 * 	<jc>// Define an annotation group</jc>
637 	 * 	<ja>@interface</ja> MyGroup {}
638 	 *
639 	 * 	<jc>// Annotation in the group</jc>
640 	 * 	<ja>@AnnotationGroup</ja>(MyGroup.<jk>class</jk>)
641 	 * 	<ja>@interface</ja> MyAnnotation {}
642 	 *
643 	 * 	<jc>// Check if annotation is in group</jc>
644 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
645 	 * 	<jk>boolean</jk> <jv>inGroup</jv> = <jv>ai</jv>.isInGroup(MyGroup.<jk>class</jk>);  <jc>// Returns true</jc>
646 	 * </p>
647 	 *
648 	 * @param <A> The group annotation type.
649 	 * @param group The group annotation class to test for.
650 	 * @return <jk>true</jk> if this annotation is in the specified group.
651 	 * @see AnnotationGroup
652 	 */
653 	public <A extends Annotation> boolean isInGroup(Class<A> group) {
654 		var x = a.annotationType().getAnnotation(AnnotationGroup.class);
655 		return (nn(x) && x.value().equals(group));
656 	}
657 
658 	/**
659 	 * Returns <jk>true</jk> if this annotation is of the specified type.
660 	 *
661 	 * <h5 class='section'>Example:</h5>
662 	 * <p class='bjava'>
663 	 * 	AnnotationInfo&lt;?&gt; <jv>ai</jv> = ...;
664 	 *
665 	 * 	<jk>if</jk> (<jv>ai</jv>.isType(MyAnnotation.<jk>class</jk>)) {
666 	 * 		<jc>// Handle MyAnnotation specifically</jc>
667 	 * 	}
668 	 * </p>
669 	 *
670 	 * @param <A> The annotation type to test for.
671 	 * @param type The annotation type to test against.
672 	 * @return <jk>true</jk> if this annotation's type is exactly the specified type.
673 	 */
674 	public <A extends Annotation> boolean isType(Class<A> type) {
675 		return this.a.annotationType() == type;
676 	}
677 
678 	/**
679 	 * Converts this annotation info to a map representation for debugging purposes.
680 	 *
681 	 * <p>
682 	 * The returned map contains:
683 	 * <ul>
684 	 * 	<li>The annotatable element's type and label (e.g., {@code "CLASS_TYPE" -> "com.example.MyClass"})
685 	 * 	<li>A nested map with the annotation's simple name as key and its non-default values
686 	 * </ul>
687 	 *
688 	 * <p>
689 	 * Only annotation values that differ from their default values are included.
690 	 *
691 	 * <h5 class='section'>Example:</h5>
692 	 * <p class='bjava'>
693 	 * 	AnnotationInfo&lt;MyAnnotation&gt; <jv>ai</jv> = ...;
694 	 * 	LinkedHashMap&lt;String,Object&gt; <jv>map</jv> = <jv>ai</jv>.toMap();
695 	 * 	<jc>// Returns: {"CLASS_TYPE": "MyClass", "@MyAnnotation": {"value": "foo", "priority": 5}}</jc>
696 	 * </p>
697 	 *
698 	 * @return A new map showing the attributes of this annotation info.
699 	 */
700 	protected FluentMap<String,Object> properties() {
701 		// @formatter:off
702 		var ca = info(a.annotationType());
703 		var ja = mapb().sorted().buildFluent();  // NOAI
704 		ca.getDeclaredMethods().stream().forEach(x -> {
705 			safeOptCatch(() -> {
706 				var val = x.invoke(a);
707 				var d = x.inner().getDefaultValue();
708 				// Add values only if they're different from the default.
709 				if (neq(val, d)) {
710 					if (! (isArray(val) && length(val) == 0 && isArray(d) && length(d) == 0))
711 						return val;
712 				}
713 				return null;
714 			}, e -> lm(e)).ifPresent(v -> ja.a(x.getName(), v));
715 		});
716 		return filteredBeanPropertyMap()
717 			.a(s(annotatable.getAnnotatableType()), annotatable.getLabel())
718 			.a("@" + ca.getNameSimple(), ja);
719 		// @formatter:on
720 	}
721 
722 	/**
723 	 * Returns a simple string representation of this annotation showing the annotation type and location.
724 	 *
725 	 * <p>
726 	 * Format: {@code @AnnotationName(on=location)}
727 	 *
728 	 * <h5 class='section'>Examples:</h5>
729 	 * <ul>
730 	 * 	<li>{@code @Rest(on=MyClass)} - Annotation on a class
731 	 * 	<li>{@code @RestGet(on=MyClass.myMethod)} - Annotation on a method
732 	 * 	<li>{@code @Inject(on=MyClass.myField)} - Annotation on a field
733 	 * 	<li>{@code @PackageAnnotation(on=my.package)} - Annotation on a package
734 	 * </ul>
735 	 *
736 	 * @return A simple string representation of this annotation.
737 	 */
738 	public String toSimpleString() {
739 		return "@" + cns(a.annotationType()) + "(on=" + annotatable.getLabel() + ")";
740 	}
741 
742 	/**
743 	 * Returns a string representation of this annotation.
744 	 *
745 	 * <p>
746 	 * Returns the map representation created by {@link #properties()}.
747 	 *
748 	 * @return A string representation of this annotation.
749 	 */
750 	@Override /* Overridden from Object */
751 	public String toString() {
752 		return r(properties());
753 	}
754 }