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.ClassArrayFormat.*;
20  import static org.apache.juneau.commons.reflect.ClassNameFormat.*;
21  import static org.apache.juneau.commons.utils.AssertionUtils.*;
22  import static org.apache.juneau.commons.utils.CollectionUtils.*;
23  import static org.apache.juneau.commons.utils.Utils.*;
24  import static java.util.stream.Collectors.*;
25  
26  import java.lang.annotation.*;
27  import java.lang.reflect.*;
28  import java.util.*;
29  import java.util.function.*;
30  import java.util.stream.*;
31  
32  import org.apache.juneau.commons.utils.*;
33  
34  /**
35   * Abstract base class containing common functionality for {@link ConstructorInfo} and {@link MethodInfo}.
36   *
37   * <p>
38   * This class provides shared functionality for both constructors and methods, which are both types of
39   * {@link Executable} in Java. It extends {@link AccessibleInfo} to provide {@link AccessibleObject}
40   * functionality for accessing private methods and constructors.
41   *
42   * <h5 class='section'>Features:</h5>
43   * <ul class='spaced-list'>
44   * 	<li>Parameter introspection - access method/constructor parameters
45   * 	<li>Exception handling - get declared exceptions
46   * 	<li>Annotation support - get annotations declared on the executable
47   * 	<li>Name formatting - get short and fully qualified names
48   * 	<li>Parameter matching - match parameters by type (strict and lenient)
49   * 	<li>Accessibility control - make private executables accessible
50   * </ul>
51   *
52   * <h5 class='section'>Use Cases:</h5>
53   * <ul class='spaced-list'>
54   * 	<li>Working with both methods and constructors in a unified way
55   * 	<li>Finding methods/constructors that match specific parameter types
56   * 	<li>Introspecting parameter and exception information
57   * 	<li>Building frameworks that need to analyze executable signatures
58   * </ul>
59   *
60   * <h5 class='section'>Usage:</h5>
61   * <p class='bjava'>
62   * 	<jc>// Get ExecutableInfo (could be MethodInfo or ConstructorInfo)</jc>
63   * 	MethodInfo <jv>mi</jv> = ...;
64   * 	ExecutableInfo <jv>ei</jv> = <jv>mi</jv>;  <jc>// MethodInfo extends ExecutableInfo</jc>
65   *
66   * 	<jc>// Get parameters</jc>
67   * 	List&lt;ParameterInfo&gt; <jv>params</jv> = <jv>ei</jv>.getParameters();
68   *
69   * 	<jc>// Get exceptions</jc>
70   * 	List&lt;ClassInfo&gt; <jv>exceptions</jv> = <jv>ei</jv>.getExceptions();
71   *
72   * 	<jc>// Check parameter matching</jc>
73   * 	<jk>boolean</jk> <jv>matches</jv> = <jv>ei</jv>.parameterMatches(String.<jk>class</jk>, Integer.<jk>class</jk>);
74   * </p>
75   *
76   * <h5 class='section'>See Also:</h5><ul>
77   * 	<li class='jc'>{@link MethodInfo} - Method introspection
78   * 	<li class='jc'>{@link ConstructorInfo} - Constructor introspection
79   * 	<li class='jc'>{@link ParameterInfo} - Parameter introspection
80   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
81   * </ul>
82   */
83  public abstract class ExecutableInfo extends AccessibleInfo {
84  
85  	protected final ClassInfo declaringClass;
86  	private final Executable inner;
87  	private final boolean isConstructor;
88  
89  	private final Supplier<List<ParameterInfo>> parameters;  // All parameters of this executable.
90  	private final Supplier<List<ClassInfo>> parameterTypes;  // All parameter types of this executable.
91  	private final Supplier<List<ClassInfo>> exceptions;  // All exceptions declared by this executable.
92  	private final Supplier<List<AnnotationInfo<Annotation>>> declaredAnnotations;  // All annotations declared directly on this executable.
93  	private final Supplier<String> shortName;  // Short name (method/constructor name with parameters).
94  	private final Supplier<String> fullName;  // Fully qualified name (declaring-class.method-name with parameters).
95  
96  	/**
97  	 * Constructor.
98  	 *
99  	 * <p>
100 	 * Creates a new ExecutableInfo wrapper for the specified executable (method or constructor).
101 	 * This constructor is protected and should not be called directly. Use the constructors
102 	 * of {@link MethodInfo} or {@link ConstructorInfo} instead.
103 	 *
104 	 * @param declaringClass The ClassInfo for the class that declares this method or constructor.
105 	 * @param inner The constructor or method that this info represents. Must not be <jk>null</jk>.
106 	 */
107 	protected ExecutableInfo(ClassInfo declaringClass, Executable inner) {
108 		super(inner, assertArgNotNull("inner", inner).getModifiers());
109 		this.declaringClass = declaringClass;
110 		this.inner = inner;
111 		this.isConstructor = inner instanceof Constructor;
112 		this.parameters = mem(this::findParameters);
113 		this.parameterTypes = mem(() -> getParameters().stream().map(ParameterInfo::getParameterType).toList());
114 		this.exceptions = mem(() -> stream(inner.getExceptionTypes()).map(ClassInfo::of).map(ClassInfo.class::cast).toList());
115 		this.declaredAnnotations = mem(() -> stream(inner.getDeclaredAnnotations()).flatMap(a -> AnnotationUtils.streamRepeated(a)).map(a -> ai((Annotatable)this, a)).toList());
116 		this.shortName = mem(() -> f("{0}({1})", getSimpleName(), getParameters().stream().map(p -> p.getParameterType().getNameSimple()).collect(joining(","))));
117 		this.fullName = mem(this::findFullName);
118 	}
119 
120 	/**
121 	 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
122 	 *
123 	 * @return This object.
124 	 */
125 	public ExecutableInfo accessible() {
126 		setAccessible();
127 		return this;
128 	}
129 
130 	/**
131 	 * Returns <jk>true</jk> if this executable can accept the specified arguments in the specified order.
132 	 *
133 	 * <p>
134 	 * This method checks if the provided arguments are compatible with the executable's parameter types
135 	 * in exact order, using {@link Class#isInstance(Object)} for type checking.
136 	 *
137 	 * <p>
138 	 * <strong>Important:</strong> For non-static inner class constructors, the first parameter is the
139 	 * implicit outer class instance (e.g., {@code Outer.this}). This method checks against the
140 	 * <em>actual</em> parameters including this implicit parameter.
141 	 *
142 	 * <h5 class='section'>Examples:</h5>
143 	 * <p class='bjava'>
144 	 * 	<jc>// Regular method</jc>
145 	 * 	<jk>public void</jk> foo(String <jv>s</jv>, Integer <jv>i</jv>);
146 	 * 	<jv>methodInfo</jv>.canAccept(<js>"hello"</js>, 42);  <jc>// true</jc>
147 	 *
148 	 * 	<jc>// Non-static inner class constructor</jc>
149 	 * 	<jk>class</jk> Outer {
150 	 * 		<jk>class</jk> Inner {
151 	 * 			Inner(String <jv>s</jv>) {}
152 	 * 		}
153 	 * 	}
154 	 * 	<jc>// Constructor actually has signature: Inner(Outer this$0, String s)</jc>
155 	 * 	Outer <jv>outer</jv> = <jk>new</jk> Outer();
156 	 * 	<jv>constructorInfo</jv>.canAccept(<js>"hello"</js>);  <jc>// false - missing outer instance</jc>
157 	 * 	<jv>constructorInfo</jv>.canAccept(<jv>outer</jv>, <js>"hello"</js>);  <jc>// true</jc>
158 	 * </p>
159 	 *
160 	 * @param args The arguments to check.
161 	 * @return <jk>true</jk> if this executable can accept the specified arguments in the specified order.
162 	 */
163 	public final boolean canAccept(Object...args) {
164 		Class<?>[] pt = inner.getParameterTypes();
165 		if (pt.length != args.length)
166 			return false;
167 		for (var i = 0; i < pt.length; i++)
168 			if (! pt[i].isInstance(args[i]))
169 				return false;
170 		return true;
171 	}
172 
173 	/**
174 	 * Returns an array of {@link AnnotatedType} objects that represent the use of types to specify the declared exceptions.
175 	 *
176 	 * <p>
177 	 * The order of the objects corresponds to the order of the exception types in the executable declaration.
178 	 *
179 	 * <p>
180 	 * Same as calling {@link Executable#getAnnotatedExceptionTypes()}.
181 	 *
182 	 * <h5 class='section'>Example:</h5>
183 	 * <p class='bjava'>
184 	 * 	<jc>// Get annotated exception types from method: void myMethod() throws @NotNull IOException</jc>
185 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>);
186 	 * 	AnnotatedType[] <jv>exTypes</jv> = <jv>mi</jv>.getAnnotatedExceptionTypes();
187 	 * </p>
188 	 *
189 	 * @return An array of {@link AnnotatedType} objects, or an empty array if the executable declares no exceptions.
190 	 * @see Executable#getAnnotatedExceptionTypes()
191 	 */
192 	public final AnnotatedType[] getAnnotatedExceptionTypes() { return inner.getAnnotatedExceptionTypes(); }
193 
194 	/**
195 	 * Returns an array of {@link AnnotatedType} objects that represent the use of types to specify formal parameter types.
196 	 *
197 	 * <p>
198 	 * The order of the objects corresponds to the order of the formal parameter types in the executable declaration.
199 	 *
200 	 * <p>
201 	 * Same as calling {@link Executable#getAnnotatedParameterTypes()}.
202 	 *
203 	 * <h5 class='section'>Example:</h5>
204 	 * <p class='bjava'>
205 	 * 	<jc>// Get annotated parameter types from method: void myMethod(@NotNull String s, @Range(min=0) int i)</jc>
206 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, String.<jk>class</jk>, <jk>int</jk>.<jk>class</jk>);
207 	 * 	AnnotatedType[] <jv>paramTypes</jv> = <jv>mi</jv>.getAnnotatedParameterTypes();
208 	 * </p>
209 	 *
210 	 * @return An array of {@link AnnotatedType} objects, or an empty array if the executable has no parameters.
211 	 * @see Executable#getAnnotatedParameterTypes()
212 	 */
213 	public final AnnotatedType[] getAnnotatedParameterTypes() { return inner.getAnnotatedParameterTypes(); }
214 
215 	/**
216 	 * Returns an {@link AnnotatedType} object that represents the use of a type to specify the receiver type of the method/constructor.
217 	 *
218 	 * <p>
219 	 * Returns <jk>null</jk> if this executable object represents a top-level type or static member.
220 	 *
221 	 * <p>
222 	 * Same as calling {@link Executable#getAnnotatedReceiverType()}.
223 	 *
224 	 * <h5 class='section'>Example:</h5>
225 	 * <p class='bjava'>
226 	 * 	<jc>// Get annotated receiver type from method: void myMethod(@MyAnnotation MyClass this)</jc>
227 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>);
228 	 * 	AnnotatedType <jv>receiverType</jv> = <jv>mi</jv>.getAnnotatedReceiverType();
229 	 * </p>
230 	 *
231 	 * @return An {@link AnnotatedType} object representing the receiver type, or <jk>null</jk> if not applicable.
232 	 * @see Executable#getAnnotatedReceiverType()
233 	 */
234 	public final AnnotatedType getAnnotatedReceiverType() { return inner.getAnnotatedReceiverType(); }
235 
236 	/**
237 	 * Returns the declared annotations on this executable.
238 	 *
239 	 * <p>
240 	 * <b>Note on Repeatable Annotations:</b>
241 	 * Repeatable annotations (those marked with {@link java.lang.annotation.Repeatable @Repeatable}) are automatically
242 	 * expanded into their individual annotation instances. For example, if a method has multiple {@code @Bean} annotations,
243 	 * this method returns each {@code @Bean} annotation separately, rather than the container annotation.
244 	 *
245 	 * @return
246 	 * 	The declared annotations on this executable as {@link AnnotationInfo} objects.
247 	 * 	<br>Repeatable annotations are expanded into individual instances.
248 	 */
249 	public final List<AnnotationInfo<Annotation>> getDeclaredAnnotations() { return declaredAnnotations.get(); }
250 
251 	/**
252 	 * Returns the declared annotations of the specified type on this executable.
253 	 *
254 	 * @param <A> The annotation type.
255 	 * @param type The annotation type.
256 	 * @return A stream of matching annotations.
257 	 */
258 	@SuppressWarnings("unchecked")
259 	public final <A extends Annotation> Stream<AnnotationInfo<A>> getDeclaredAnnotations(Class<A> type) {
260 		assertArgNotNull("type", type);
261 		// @formatter:off
262 		return declaredAnnotations.get().stream()
263 			.filter(x -> type.isInstance(x.inner()))
264 			.map(x -> (AnnotationInfo<A>)x);
265 		// @formatter:on
266 	}
267 
268 	/**
269 	 * Returns metadata about the class that declared this method or constructor.
270 	 *
271 	 * @return Metadata about the class that declared this method or constructor.
272 	 */
273 	public final ClassInfo getDeclaringClass() { return declaringClass; }
274 
275 	/**
276 	 * Returns the exception types on this executable.
277 	 *
278 	 * @return The exception types on this executable.
279 	 */
280 	public final List<ClassInfo> getExceptionTypes() { return exceptions.get(); }
281 
282 	/**
283 	 * Returns the full name of this executable.
284 	 *
285 	 * <h5 class='section'>Examples:</h5>
286 	 * <ul>
287 	 * 	<li><js>"com.foo.MyClass.get(java.util.String)"</js> - Method.
288 	 * 	<li><js>"com.foo.MyClass(java.util.String)"</js> - Constructor.
289 	 * </ul>
290 	 *
291 	 * @return The underlying executable name.
292 	 */
293 	public final String getFullName() { return fullName.get(); }
294 
295 	/**
296 	 * Returns parameter information at the specified index.
297 	 *
298 	 * @param index The parameter index.
299 	 * @return The parameter information, never <jk>null</jk>.
300 	 */
301 	public final ParameterInfo getParameter(int index) {
302 		checkIndex(index);
303 		return getParameters().get(index);
304 	}
305 
306 	/**
307 	 * Returns the number of parameters in this executable.
308 	 *
309 	 * <p>
310 	 * Same as calling {@link Executable#getParameterCount()}.
311 	 *
312 	 * @return The number of parameters in this executable.
313 	 */
314 	public final int getParameterCount() { return inner.getParameterCount(); }
315 
316 	/**
317 	 * Returns the parameters defined on this executable.
318 	 *
319 	 * <p>
320 	 * Same as calling {@link Executable#getParameters()} but wraps the results
321 	 *
322 	 * @return An array of parameter information, never <jk>null</jk>.
323 	 */
324 	public final List<ParameterInfo> getParameters() { return parameters.get(); }
325 
326 	/**
327 	 * Returns the parameter types for this executable.
328 	 *
329 	 * <p>
330 	 * This is a convenience method that extracts the parameter types from {@link #getParameters()}.
331 	 *
332 	 * <h5 class='section'>Example:</h5>
333 	 * <p class='bjava'>
334 	 * 	<jc>// Get parameter types: void myMethod(String s, int i)</jc>
335 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, String.<jk>class</jk>, <jk>int</jk>.<jk>class</jk>);
336 	 * 	List&lt;ClassInfo&gt; <jv>paramTypes</jv> = <jv>mi</jv>.getParameterTypes();
337 	 * 	<jc>// paramTypes contains ClassInfo for String and int</jc>
338 	 * </p>
339 	 *
340 	 * @return A list of parameter types, never <jk>null</jk>.
341 	 */
342 	public final List<ClassInfo> getParameterTypes() {
343 		return parameterTypes.get();
344 	}
345 
346 	/**
347 	 * Returns the short name of this executable.
348 	 *
349 	 * <h5 class='section'>Examples:</h5>
350 	 * <ul>
351 	 * 	<li><js>"MyClass.get(String)"</js> - Method.
352 	 * 	<li><js>"MyClass(String)"</js> - Constructor.
353 	 * </ul>
354 	 *
355 	 * @return The underlying executable name.
356 	 */
357 	public final String getShortName() { return shortName.get(); }
358 
359 	/**
360 	 * Returns the simple name of the underlying method.
361 	 *
362 	 * @return The simple name of the underlying method;
363 	 */
364 	public final String getSimpleName() { return isConstructor ? cns(inner.getDeclaringClass()) : inner.getName(); }
365 
366 	/**
367 	 * Returns an array of {@link TypeVariable} objects that represent the type variables declared by the generic declaration.
368 	 *
369 	 * <p>
370 	 * Returns an empty array if the generic declaration declares no type variables.
371 	 *
372 	 * <p>
373 	 * Same as calling {@link Executable#getTypeParameters()}.
374 	 *
375 	 * <h5 class='section'>Example:</h5>
376 	 * <p class='bjava'>
377 	 * 	<jc>// Get type parameters from method: &lt;T extends Number&gt; void myMethod(T value)</jc>
378 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, Number.<jk>class</jk>);
379 	 * 	TypeVariable&lt;?&gt;[] <jv>typeParams</jv> = <jv>mi</jv>.getTypeParameters();
380 	 * 	<jc>// typeParams[0].getName() returns "T"</jc>
381 	 * </p>
382 	 *
383 	 * @return An array of {@link TypeVariable} objects, or an empty array if none.
384 	 * @see Executable#getTypeParameters()
385 	 */
386 	public final TypeVariable<?>[] getTypeParameters() { return inner.getTypeParameters(); }
387 
388 	/**
389 	 * Returns <jk>true</jk> if this executable has the specified annotation.
390 	 *
391 	 * @param <A> The annotation type.
392 	 * @param type The annotation type.
393 	 * @return <jk>true</jk> if this executable has the specified annotation.
394 	 */
395 	public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
396 		return getDeclaredAnnotations(type).findFirst().isPresent();
397 	}
398 
399 	/**
400 	 * Returns <jk>true</jk> if this method has a name in the specified set.
401 	 *
402 	 * @param names The names to test for.
403 	 * @return <jk>true</jk> if this method has one of the names.
404 	 */
405 	public final boolean hasAnyName(Collection<String> names) {
406 		return names.contains(getSimpleName());
407 	}
408 
409 	/**
410 	 * Returns <jk>true</jk> if this method has a name in the specified list.
411 	 *
412 	 * @param names The names to test for.
413 	 * @return <jk>true</jk> if this method has one of the names.
414 	 */
415 	public final boolean hasAnyName(String...names) {
416 		return stream(names).anyMatch(n -> eq(n, getSimpleName()));
417 	}
418 
419 	/**
420 	 * Returns <jk>true</jk> if this executable has matching parameter types with the provided parameter list.
421 	 *
422 	 * @param params The parameters to match against.
423 	 * @return <jk>true</jk> if this executable has matching parameter types.
424 	 */
425 	public final boolean hasMatchingParameters(List<ParameterInfo> params) {
426 		var myParams = getParameters();
427 		return myParams.size() == params.size() && IntStream.range(0, params.size()).allMatch(i -> myParams.get(i).getParameterType().is(params.get(i).getParameterType()));
428 	}
429 
430 	/**
431 	 * Returns <jk>true</jk> if this method has this name.
432 	 *
433 	 * @param name The name to test for.
434 	 * @return <jk>true</jk> if this method has this name.
435 	 */
436 	public final boolean hasName(String name) {
437 		return getSimpleName().equals(name);
438 	}
439 
440 	/**
441 	 * Returns <jk>true</jk> if this executable has this number of arguments.
442 	 *
443 	 * <p>
444 	 * Same as calling {@link Executable#getParameterCount()} and comparing the count.
445 	 *
446 	 * @param number The number of expected arguments.
447 	 * @return <jk>true</jk> if this executable has this number of arguments.
448 	 */
449 	public final boolean hasNumParameters(int number) {
450 		return getParameterCount() == number;
451 	}
452 
453 	/**
454 	 * Returns <jk>true</jk> if this executable has at least one parameter.
455 	 *
456 	 * <p>
457 	 * Same as calling {@link Executable#getParameterCount()} and comparing with zero.
458 	 *
459 	 * @return <jk>true</jk> if this executable has at least one parameter.
460 	 */
461 	public final boolean hasParameters() {
462 		return getParameterCount() != 0;
463 	}
464 
465 	/**
466 	 * Returns <jk>true</jk> if this method has the specified argument parent classes.
467 	 *
468 	 * @param args The arguments to test for.
469 	 * @return <jk>true</jk> if this method has this arguments in the exact order.
470 	 */
471 	public final boolean hasParameterTypeParents(Class<?>...args) {
472 		var params = getParameters();
473 		return params.size() == args.length && params.stream().allMatch(p -> stream(args).anyMatch(a -> p.getParameterType().isParentOfLenient(a)));
474 	}
475 
476 	/**
477 	 * Returns <jk>true</jk> if this method has the specified argument parent classes.
478 	 *
479 	 * @param args The arguments to test for.
480 	 * @return <jk>true</jk> if this method has this arguments in the exact order.
481 	 */
482 	public final boolean hasParameterTypeParents(ClassInfo...args) {
483 		var params = getParameters();
484 		return params.size() == args.length && params.stream().allMatch(p -> stream(args).anyMatch(a -> p.getParameterType().isParentOfLenient(a)));
485 	}
486 
487 	/**
488 	 * Returns <jk>true</jk> if this method has the specified arguments.
489 	 *
490 	 * @param args The arguments to test for.
491 	 * @return <jk>true</jk> if this method has this arguments in the exact order.
492 	 */
493 	public final boolean hasParameterTypes(Class<?>...args) {
494 		var params = getParameters();
495 		return params.size() == args.length && IntStream.range(0, args.length).allMatch(i -> params.get(i).getParameterType().is(args[i]));
496 	}
497 
498 	/**
499 	 * Returns <jk>true</jk> if this method has the specified arguments.
500 	 *
501 	 * @param args The arguments to test for.
502 	 * @return <jk>true</jk> if this method has this arguments in the exact order.
503 	 */
504 	public final boolean hasParameterTypes(ClassInfo...args) {
505 		var params = getParameters();
506 		return params.size() == args.length && IntStream.range(0, args.length).allMatch(i -> params.get(i).getParameterType().is(args[i]));
507 	}
508 
509 	/**
510 	 * Returns <jk>true</jk> if this method has at most only these arguments using lenient matching.
511 	 *
512 	 * <p>
513 	 * Lenient matching allows arguments to be matched to parameters based on type compatibility,
514 	 * where arguments can be in any order.
515 	 *
516 	 * @param args The arguments to test for.
517 	 * @return <jk>true</jk> if this method has at most only these arguments in any order.
518 	 */
519 	public final boolean hasParameterTypesLenient(Class<?>...args) {
520 		return parameterMatchesLenientCount(args) != -1;
521 	}
522 
523 	/**
524 	 * Returns <jk>true</jk> if this method has at most only these arguments using lenient matching.
525 	 *
526 	 * <p>
527 	 * Lenient matching allows arguments to be matched to parameters based on type compatibility,
528 	 * where arguments can be in any order.
529 	 *
530 	 * @param args The arguments to test for.
531 	 * @return <jk>true</jk> if this method has at most only these arguments in any order.
532 	 */
533 	public final boolean hasParameterTypesLenient(ClassInfo...args) {
534 		return parameterMatchesLenientCount(args) != -1;
535 	}
536 
537 	/**
538 	 * Returns <jk>true</jk> if all specified flags are applicable to this method.
539 	 *
540 	 * @param flag The flag to test for.
541 	 * @return <jk>true</jk> if all specified flags are applicable to this method.
542 	 */
543 	@Override
544 	public boolean is(ElementFlag flag) {
545 		return switch (flag) {
546 			case CONSTRUCTOR -> isConstructor();
547 			case NOT_CONSTRUCTOR -> ! isConstructor();
548 			case DEPRECATED -> isDeprecated();
549 			case NOT_DEPRECATED -> isNotDeprecated();
550 			case HAS_PARAMS -> hasParameters();
551 			case HAS_NO_PARAMS -> getParameterCount() == 0;
552 			case SYNTHETIC -> isSynthetic();
553 			case NOT_SYNTHETIC -> ! isSynthetic();  // HTT
554 			case VARARGS -> isVarArgs();
555 			case NOT_VARARGS -> ! isVarArgs();
556 			default -> super.is(flag);
557 		};
558 	}
559 
560 	/**
561 	 * Returns <jk>true</jk> if this executable represents a {@link Constructor}.
562 	 *
563 	 * @return
564 	 * 	<jk>true</jk> if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}.
565 	 * 	<jk>false</jk> if this executable represents a {@link Method} and can be cast to {@link MethodInfo}.
566 	 */
567 	public final boolean isConstructor() { return isConstructor; }
568 
569 	/**
570 	 * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
571 	 *
572 	 * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
573 	 */
574 	public final boolean isDeprecated() {
575 		return inner.isAnnotationPresent(Deprecated.class);
576 
577 	}
578 
579 	/**
580 	 * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
581 	 *
582 	 * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
583 	 */
584 	public final boolean isNotDeprecated() {
585 		return ! inner.isAnnotationPresent(Deprecated.class);
586 
587 	}
588 
589 	/**
590 	 * Returns <jk>true</jk> if this executable is a synthetic construct as defined by the Java Language Specification.
591 	 *
592 	 * <p>
593 	 * Same as calling {@link Executable#isSynthetic()}.
594 	 *
595 	 * <h5 class='section'>Example:</h5>
596 	 * <p class='bjava'>
597 	 * 	<jc>// Check if method is compiler-generated</jc>
598 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"access$000"</js>);
599 	 * 	<jk>boolean</jk> <jv>isSynthetic</jv> = <jv>mi</jv>.isSynthetic();
600 	 * </p>
601 	 *
602 	 * @return <jk>true</jk> if this executable is a synthetic construct.
603 	 * @see Executable#isSynthetic()
604 	 */
605 	public final boolean isSynthetic() { return inner.isSynthetic(); }
606 
607 	/**
608 	 * Returns <jk>true</jk> if this executable was declared to take a variable number of arguments.
609 	 *
610 	 * <p>
611 	 * Same as calling {@link Executable#isVarArgs()}.
612 	 *
613 	 * <h5 class='section'>Example:</h5>
614 	 * <p class='bjava'>
615 	 * 	<jc>// Check if method accepts varargs</jc>
616 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, String[].<jk>class</jk>);
617 	 * 	<jk>boolean</jk> <jv>isVarArgs</jv> = <jv>mi</jv>.isVarArgs();
618 	 * </p>
619 	 *
620 	 * @return <jk>true</jk> if this executable was declared to take a variable number of arguments.
621 	 * @see Executable#isVarArgs()
622 	 */
623 	public final boolean isVarArgs() { return inner.isVarArgs(); }
624 
625 	/**
626 	 * Identifies if the specified visibility matches this method.
627 	 *
628 	 * @param v The visibility to validate against.
629 	 * @return <jk>true</jk> if this visibility matches the modifier attribute of this method.
630 	 */
631 	public final boolean isVisible(Visibility v) {
632 		return v.isVisible(inner);
633 	}
634 
635 	/**
636 	 * Returns how well this method matches the specified arg types using lenient matching.
637 	 *
638 	 * <p>
639 	 * Lenient matching allows arguments to be matched to parameters based on type compatibility,
640 	 * where arguments can be in any order.
641 	 *
642 	 * <p>
643 	 * The number returned is the number of method arguments that match the passed in arg types.
644 	 * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
645 	 *
646 	 * @param argTypes The arg types to check against.
647 	 * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
648 	 */
649 	public final int parameterMatchesLenientCount(Class<?>...argTypes) {
650 		int matches = 0;
651 		outer: for (var param : getParameters()) {
652 			for (var a : argTypes) {
653 				if (param.getParameterType().isParentOfLenient(a)) {
654 					matches++;
655 					continue outer;
656 				}
657 			}
658 			return -1;
659 		}
660 		return matches;
661 	}
662 
663 	/**
664 	 * Returns how well this method matches the specified arg types using lenient matching.
665 	 *
666 	 * <p>
667 	 * Lenient matching allows arguments to be matched to parameters based on type compatibility,
668 	 * where arguments can be in any order.
669 	 *
670 	 * <p>
671 	 * The number returned is the number of method arguments that match the passed in arg types.
672 	 * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
673 	 *
674 	 * @param argTypes The arg types to check against.
675 	 * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
676 	 */
677 	public final int parameterMatchesLenientCount(ClassInfo...argTypes) {
678 		int matches = 0;
679 		outer: for (var param : getParameters()) {
680 			for (var a : argTypes) {
681 				if (param.getParameterType().isParentOfLenient(a)) {
682 					matches++;
683 					continue outer;
684 				}
685 			}
686 			return -1;
687 		}
688 		return matches;
689 	}
690 
691 	/**
692 	 * Returns how well this method matches the specified arg types using lenient matching.
693 	 *
694 	 * <p>
695 	 * Lenient matching allows arguments to be matched to parameters based on type compatibility,
696 	 * where arguments can be in any order.
697 	 *
698 	 * <p>
699 	 * The number returned is the number of method arguments that match the passed in arg types.
700 	 * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
701 	 *
702 	 * @param argTypes The arg types to check against.
703 	 * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
704 	 */
705 	public final int parameterMatchesLenientCount(Object...argTypes) {
706 		int matches = 0;
707 		outer: for (var param : getParameters()) {
708 			for (var a : argTypes) {
709 				if (param.getParameterType().canAcceptArg(a)) {
710 					matches++;
711 					continue outer;
712 				}
713 			}
714 			return -1;
715 		}
716 		return matches;
717 	}
718 
719 	/**
720 	 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
721 	 *
722 	 * @return <jk>true</jk> if call was successful.
723 	 */
724 	@Override
725 	public final boolean setAccessible() {
726 		// inner can never be null - constructor asserts non-null via assertArgNotNull
727 		return safeOpt(() -> {
728 			inner.setAccessible(true);
729 			return true;
730 		}).orElse(false);
731 	}
732 
733 	/**
734 	 * Returns a string describing this executable, including type parameters.
735 	 *
736 	 * <p>
737 	 * The string includes the method/constructor name, parameter types (with generic information), and return type (for methods).
738 	 *
739 	 * <p>
740 	 * Same as calling {@link Executable#toGenericString()}.
741 	 *
742 	 * <h5 class='section'>Example:</h5>
743 	 * <p class='bjava'>
744 	 * 	<jc>// Get generic string for: public &lt;T&gt; List&lt;T&gt; myMethod(T value) throws IOException</jc>
745 	 * 	MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, Object.<jk>class</jk>);
746 	 * 	String <jv>str</jv> = <jv>mi</jv>.toGenericString();
747 	 * 	<jc>// Returns: "public &lt;T&gt; java.util.List&lt;T&gt; com.example.MyClass.myMethod(T) throws java.io.IOException"</jc>
748 	 * </p>
749 	 *
750 	 * @return A string describing this executable.
751 	 * @see Executable#toGenericString()
752 	 */
753 	public final String toGenericString() {
754 		return inner.toGenericString();
755 	}
756 
757 	@Override
758 	public String toString() {
759 		return getShortName();
760 	}
761 
762 	private void checkIndex(int index) {
763 		int pc = getParameterCount();
764 		if (pc == 0)
765 			throw new IndexOutOfBoundsException(f("Invalid index ''{0}''.  No parameters.", index));
766 		if (index < 0 || index >= pc)
767 			throw new IndexOutOfBoundsException(f("Invalid index ''{0}''.  Parameter count: {1}", index, pc));
768 	}
769 
770 	private String findFullName() {
771 		var sb = new StringBuilder(128);
772 		var dc = declaringClass;
773 		var pi = dc.getPackage();
774 		if (nn(pi))
775 			sb.append(pi.getName()).append('.');
776 		dc.appendNameFormatted(sb, SHORT, true, '$', BRACKETS);
777 		if (! isConstructor)
778 			sb.append('.').append(getSimpleName());
779 		sb.append('(');
780 		sb.append(getParameters().stream().map(p -> p.getParameterType().getNameFormatted(FULL, true, '$', BRACKETS)).collect(joining(",")));
781 		sb.append(')');
782 		return sb.toString();
783 	}
784 
785 	private List<ParameterInfo> findParameters() {
786 		var rp = inner.getParameters();
787 		var ptc = inner.getParameterTypes();  // Class<?>[] - includes all parameters (including synthetic enclosing instance)
788 		var ptt = inner.getGenericParameterTypes();  // Type[] - generic type information
789 		Type[] genericTypes;
790 
791 		if (ptt.length == ptc.length) {
792 			genericTypes = ptt;
793 		} else {
794 			var ptt2 = new Type[ptc.length];
795 			ptt2[0] = ptc[0];  // Enclosing instance type (Class<?>, not a generic Type)
796 			for (var i = 0; i < ptt.length; i++)
797 				ptt2[i + 1] = ptt[i];  // Copy remaining generic types
798 			genericTypes = ptt2;
799 		}
800 		return IntStream.range(0, rp.length).mapToObj(i -> new ParameterInfo(this, rp[i], i, ClassInfo.of(ptc[i], genericTypes[i]))).toList();
801 	}
802 }