001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.reflect;
018
019import static java.util.stream.Collectors.*;
020import static org.apache.juneau.commons.function.Suppliers.*;
021import static org.apache.juneau.commons.reflect.ClassArrayFormat.*;
022import static org.apache.juneau.commons.reflect.ClassNameFormat.*;
023import static org.apache.juneau.commons.utils.CollectionUtils.*;
024import static org.apache.juneau.commons.utils.Shorts.*;
025import static org.apache.juneau.commons.utils.ThrowableUtils.*;
026
027import java.lang.annotation.*;
028import java.lang.reflect.*;
029import java.util.*;
030import java.util.function.*;
031import java.util.stream.*;
032
033import org.apache.juneau.commons.utils.*;
034
035/**
036 * Abstract base class containing common functionality for {@link ConstructorInfo} and {@link MethodInfo}.
037 *
038 * <p>
039 * This class provides shared functionality for both constructors and methods, which are both types of
040 * {@link Executable} in Java. It extends {@link AccessibleInfo} to provide {@link AccessibleObject}
041 * functionality for accessing private methods and constructors.
042 *
043 * <h5 class='section'>Features:</h5>
044 * <ul class='spaced-list'>
045 *    <li>Parameter introspection - access method/constructor parameters
046 *    <li>Exception handling - get declared exceptions
047 *    <li>Annotation support - get annotations declared on the executable
048 *    <li>Name formatting - get short and fully qualified names
049 *    <li>Parameter matching - match parameters by type (strict and lenient)
050 *    <li>Accessibility control - make private executables accessible
051 * </ul>
052 *
053 * <h5 class='section'>Use Cases:</h5>
054 * <ul class='spaced-list'>
055 *    <li>Working with both methods and constructors in a unified way
056 *    <li>Finding methods/constructors that match specific parameter types
057 *    <li>Introspecting parameter and exception information
058 *    <li>Building frameworks that need to analyze executable signatures
059 * </ul>
060 *
061 * <h5 class='section'>Usage:</h5>
062 * <p class='bjava'>
063 *    <jc>// Get ExecutableInfo (could be MethodInfo or ConstructorInfo)</jc>
064 *    MethodInfo <jv>mi</jv> = ...;
065 *    ExecutableInfo <jv>ei</jv> = <jv>mi</jv>;  <jc>// MethodInfo extends ExecutableInfo</jc>
066 *
067 *    <jc>// Get parameters</jc>
068 *    List&lt;ParameterInfo&gt; <jv>params</jv> = <jv>ei</jv>.getParameters();
069 *
070 *    <jc>// Get exceptions</jc>
071 *    List&lt;ClassInfo&gt; <jv>exceptions</jv> = <jv>ei</jv>.getExceptions();
072 *
073 *    <jc>// Check parameter matching</jc>
074 *    <jk>boolean</jk> <jv>matches</jv> = <jv>ei</jv>.parameterMatches(String.<jk>class</jk>, Integer.<jk>class</jk>);
075 * </p>
076 *
077 * <h5 class='section'>See Also:</h5><ul>
078 *    <li class='jc'>{@link MethodInfo} - Method introspection
079 *    <li class='jc'>{@link ConstructorInfo} - Constructor introspection
080 *    <li class='jc'>{@link ParameterInfo} - Parameter introspection
081 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
082 * </ul>
083 */
084@SuppressWarnings({
085   "java:S1119", // Labeled 'continue outer' is the clearest way to skip to the next parameter after a match
086   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
087   "java:S3011" // Reflection access needed for executable introspection
088})
089public abstract sealed class ExecutableInfo extends AccessibleInfo permits ConstructorInfo, MethodInfo {
090
091   protected final ClassInfo declaringClass;
092   private final Executable inner;
093   private final boolean isConstructor;
094
095   private final Supplier<List<ParameterInfo>> parameters;  // All parameters of this executable.
096   private final Supplier<List<ClassInfo>> parameterTypes;  // All parameter types of this executable.
097   private final Supplier<List<ClassInfo>> exceptions;  // All exceptions declared by this executable.
098   private final Supplier<List<AnnotationInfo<Annotation>>> declaredAnnotations;  // All annotations declared directly on this executable.
099   private final Supplier<String> nameShort;  // Short name (method/constructor name with parameters).
100   private final Supplier<String> nameFull;  // Fully qualified name (declaring-class.method-name with parameters).
101   private final Supplier<String> toString;  // String representation with modifiers, return type, name, and throws declarations.
102
103   /**
104    * Constructor.
105    *
106    * <p>
107    * Creates a new ExecutableInfo wrapper for the specified executable (method or constructor).
108    * This constructor is protected and should not be called directly. Use the constructors
109    * of {@link MethodInfo} or {@link ConstructorInfo} instead.
110    *
111    * @param declaringClass The ClassInfo for the class that declares this method or constructor. Must not be <jk>null</jk>.
112    * @param inner The constructor or method that this info represents. Must not be <jk>null</jk>.
113    */
114   protected ExecutableInfo(ClassInfo declaringClass, Executable inner) {
115      super(inner, reqnn("inner", inner).getModifiers());
116      this.declaringClass = declaringClass;
117      this.inner = inner;
118      this.isConstructor = inner instanceof Constructor;
119      this.parameters = memoize(this::findParameters);
120      this.parameterTypes = memoize(() -> getParameters().stream().map(ParameterInfo::getParameterType).toList());
121      this.exceptions = memoize(() -> stream(inner.getExceptionTypes()).map(ClassInfo::of).map(ClassInfo.class::cast).toList());
122      this.declaredAnnotations = memoize(() -> stream(inner.getDeclaredAnnotations()).flatMap(AnnotationUtils::streamRepeated).map(a -> ai((Annotatable)this, a)).toList());
123      this.nameShort = memoize(() -> f("%s(%s)", getNameSimple(), getParameters().stream().map(p -> p.getParameterType().getNameSimple()).collect(joining(","))));
124      this.nameFull = memoize(this::findNameFull);
125      this.toString = memoize(this::findToString);
126   }
127
128   /**
129    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
130    *
131    * @return This object.
132    */
133   public ExecutableInfo accessible() {
134      setAccessible();
135      return this;
136   }
137
138   /**
139    * Returns <jk>true</jk> if this executable can accept the specified arguments in the specified order.
140    *
141    * <p>
142    * This method checks if the provided arguments are compatible with the executable's parameter types
143    * in exact order, using {@link Class#isInstance(Object)} for type checking.
144    *
145    * <p>
146    * <strong>Important:</strong> For non-static inner class constructors, the first parameter is the
147    * implicit outer class instance (e.g., {@code Outer.this}). This method checks against the
148    * <em>actual</em> parameters including this implicit parameter.
149    *
150    * <h5 class='section'>Examples:</h5>
151    * <p class='bjava'>
152    *    <jc>// Regular method</jc>
153    *    <jk>public void</jk> foo(String <jv>s</jv>, Integer <jv>i</jv>);
154    *    <jv>methodInfo</jv>.canAccept(<js>"hello"</js>, 42);  <jc>// true</jc>
155    *
156    *    <jc>// Non-static inner class constructor</jc>
157    *    <jk>class</jk> Outer {
158    *       <jk>class</jk> Inner {
159    *          Inner(String <jv>s</jv>) {}
160    *       }
161    *    }
162    *    <jc>// Constructor actually has signature: Inner(Outer this$0, String s)</jc>
163    *    Outer <jv>outer</jv> = <jk>new</jk> Outer();
164    *    <jv>constructorInfo</jv>.canAccept(<js>"hello"</js>);  <jc>// false - missing outer instance</jc>
165    *    <jv>constructorInfo</jv>.canAccept(<jv>outer</jv>, <js>"hello"</js>);  <jc>// true</jc>
166    * </p>
167    *
168    * @param args The arguments to check.
169    * @return <jk>true</jk> if this executable can accept the specified arguments in the specified order.
170    */
171   public final boolean canAccept(Object...args) {
172      Class<?>[] pt = inner.getParameterTypes();
173      if (pt.length != args.length)
174         return false;
175      for (var i = 0; i < pt.length; i++)
176         if (! pt[i].isInstance(args[i]))
177            return false;
178      return true;
179   }
180
181   /**
182    * Returns an array of {@link AnnotatedType} objects that represent the use of types to specify the declared exceptions.
183    *
184    * <p>
185    * The order of the objects corresponds to the order of the exception types in the executable declaration.
186    *
187    * <p>
188    * Same as calling {@link Executable#getAnnotatedExceptionTypes()}.
189    *
190    * <h5 class='section'>Example:</h5>
191    * <p class='bjava'>
192    *    <jc>// Get annotated exception types from method: void myMethod() throws @NotNull IOException</jc>
193    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>);
194    *    AnnotatedType[] <jv>exTypes</jv> = <jv>mi</jv>.getAnnotatedExceptionTypes();
195    * </p>
196    *
197    * @return An array of {@link AnnotatedType} objects, or an empty array if the executable declares no exceptions.
198    * @see Executable#getAnnotatedExceptionTypes()
199    */
200   public final AnnotatedType[] getAnnotatedExceptionTypes() { return inner.getAnnotatedExceptionTypes(); }
201
202   /**
203    * Returns an array of {@link AnnotatedType} objects that represent the use of types to specify formal parameter types.
204    *
205    * <p>
206    * The order of the objects corresponds to the order of the formal parameter types in the executable declaration.
207    *
208    * <p>
209    * Same as calling {@link Executable#getAnnotatedParameterTypes()}.
210    *
211    * <h5 class='section'>Example:</h5>
212    * <p class='bjava'>
213    *    <jc>// Get annotated parameter types from method: void myMethod(@NotNull String s, @Range(min=0) int i)</jc>
214    *    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>);
215    *    AnnotatedType[] <jv>paramTypes</jv> = <jv>mi</jv>.getAnnotatedParameterTypes();
216    * </p>
217    *
218    * @return An array of {@link AnnotatedType} objects, or an empty array if the executable has no parameters.
219    * @see Executable#getAnnotatedParameterTypes()
220    */
221   public final AnnotatedType[] getAnnotatedParameterTypes() { return inner.getAnnotatedParameterTypes(); }
222
223   /**
224    * Returns an {@link AnnotatedType} object that represents the use of a type to specify the receiver type of the method/constructor.
225    *
226    * <p>
227    * Returns <jk>null</jk> if this executable object represents a top-level type or static member.
228    *
229    * <p>
230    * Same as calling {@link Executable#getAnnotatedReceiverType()}.
231    *
232    * <h5 class='section'>Example:</h5>
233    * <p class='bjava'>
234    *    <jc>// Get annotated receiver type from method: void myMethod(@MyAnnotation MyClass this)</jc>
235    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>);
236    *    AnnotatedType <jv>receiverType</jv> = <jv>mi</jv>.getAnnotatedReceiverType();
237    * </p>
238    *
239    * @return An {@link AnnotatedType} object representing the receiver type, or <jk>null</jk> if not applicable.
240    * @see Executable#getAnnotatedReceiverType()
241    */
242   public final AnnotatedType getAnnotatedReceiverType() { return inner.getAnnotatedReceiverType(); }
243
244   /**
245    * Returns the declared annotations on this executable.
246    *
247    * <p>
248    * <b>Note on Repeatable Annotations:</b>
249    * Repeatable annotations (those marked with {@link Repeatable @Repeatable}) are automatically
250    * expanded into their individual annotation instances. For example, if a method has multiple {@code @Marshalled} annotations,
251    * this method returns each {@code @Marshalled} annotation separately, rather than the container annotation.
252    *
253    * @return
254    *    The declared annotations on this executable as {@link AnnotationInfo} objects.
255    *    <br>Repeatable annotations are expanded into individual instances.
256    */
257   public final List<AnnotationInfo<Annotation>> getDeclaredAnnotations() { return declaredAnnotations.get(); }
258
259   /**
260    * Returns the declared annotations of the specified type on this executable.
261    *
262    * @param <A> The annotation type.
263    * @param type The annotation type. Must not be <jk>null</jk>.
264    * @return A stream of matching annotations.
265    */
266   @SuppressWarnings({
267      "unchecked" // Type erasure requires cast for annotation stream
268   })
269   public final <A extends Annotation> Stream<AnnotationInfo<A>> getDeclaredAnnotations(Class<A> type) {
270      reqnn("type", type);
271      // @formatter:off
272      return declaredAnnotations.get().stream()
273         .filter(x -> type.isInstance(x.inner()))
274         .map(x -> (AnnotationInfo<A>)x);
275      // @formatter:on
276   }
277
278   /**
279    * Returns metadata about the class that declared this method or constructor.
280    *
281    * @return Metadata about the class that declared this method or constructor.
282    */
283   public final ClassInfo getDeclaringClass() { return declaringClass; }
284
285   /**
286    * Returns <jk>true</jk> if the declaring class of this executable matches the specified class.
287    *
288    * @param value The class to compare against.
289    * @return <jk>true</jk> if the declaring class matches the specified class.
290    */
291   public final boolean isDeclaringClass(ClassInfo value) { return eq(declaringClass, value); }
292
293   /**
294    * Returns the exception types on this executable.
295    *
296    * @return The exception types on this executable.
297    */
298   public final List<ClassInfo> getExceptionTypes() { return exceptions.get(); }
299
300   /**
301    * Returns the full name of this executable.
302    *
303    * <h5 class='section'>Examples:</h5>
304    * <ul>
305    *    <li><js>"com.foo.MyClass.get(java.util.String)"</js> - Method.
306    *    <li><js>"com.foo.MyClass(java.util.String)"</js> - Constructor.
307    * </ul>
308    *
309    * @return The underlying executable name.
310    */
311   public final String getNameFull() { return nameFull.get(); }
312
313   /**
314    * Returns parameter information at the specified index.
315    *
316    * @param index The parameter index.
317    * @return The parameter information, never <jk>null</jk>.
318    */
319   public final ParameterInfo getParameter(int index) {
320      checkIndex(index);
321      return getParameters().get(index);
322   }
323
324   /**
325    * Returns the number of parameters in this executable.
326    *
327    * <p>
328    * Same as calling {@link Executable#getParameterCount()}.
329    *
330    * @return The number of parameters in this executable.
331    */
332   public final int getParameterCount() { return inner.getParameterCount(); }
333
334   /**
335    * Returns the parameters defined on this executable.
336    *
337    * <p>
338    * Same as calling {@link Executable#getParameters()} but wraps the results
339    *
340    * @return An array of parameter information, never <jk>null</jk>.
341    */
342   public final List<ParameterInfo> getParameters() { return parameters.get(); }
343
344   /**
345    * Returns the parameter types for this executable.
346    *
347    * <p>
348    * This is a convenience method that extracts the parameter types from {@link #getParameters()}.
349    *
350    * <h5 class='section'>Example:</h5>
351    * <p class='bjava'>
352    *    <jc>// Get parameter types: void myMethod(String s, int i)</jc>
353    *    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>);
354    *    List&lt;ClassInfo&gt; <jv>paramTypes</jv> = <jv>mi</jv>.getParameterTypes();
355    *    <jc>// paramTypes contains ClassInfo for String and int</jc>
356    * </p>
357    *
358    * @return A list of parameter types, never <jk>null</jk>.
359    */
360   public final List<ClassInfo> getParameterTypes() {
361      return parameterTypes.get();
362   }
363
364   /**
365    * Returns the short name of this executable.
366    *
367    * <h5 class='section'>Examples:</h5>
368    * <ul>
369    *    <li><js>"MyClass.get(String)"</js> - Method.
370    *    <li><js>"MyClass(String)"</js> - Constructor.
371    * </ul>
372    *
373    * @return The underlying executable name.
374    */
375   public final String getNameShort() { return nameShort.get(); }
376
377   /**
378    * Returns the simple name of the underlying method.
379    *
380    * @return The simple name of the underlying method;
381    */
382   public final String getNameSimple() { return isConstructor ? cns(inner.getDeclaringClass()) : inner.getName(); }
383
384   /**
385    * Returns an array of {@link TypeVariable} objects that represent the type variables declared by the generic declaration.
386    *
387    * <p>
388    * Returns an empty array if the generic declaration declares no type variables.
389    *
390    * <p>
391    * Same as calling {@link Executable#getTypeParameters()}.
392    *
393    * <h5 class='section'>Example:</h5>
394    * <p class='bjava'>
395    *    <jc>// Get type parameters from method: &lt;T extends Number&gt; void myMethod(T value)</jc>
396    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, Number.<jk>class</jk>);
397    *    TypeVariable&lt;?&gt;[] <jv>typeParams</jv> = <jv>mi</jv>.getTypeParameters();
398    *    <jc>// typeParams[0].getName() returns "T"</jc>
399    * </p>
400    *
401    * @return An array of {@link TypeVariable} objects, or an empty array if none.
402    * @see Executable#getTypeParameters()
403    */
404   @SuppressWarnings({
405      "java:S1452"  // Wildcard required - TypeVariable<?> from Executable.getTypeParameters()
406   })
407   public final TypeVariable<?>[] getTypeParameters() { return inner.getTypeParameters(); }
408
409   /**
410    * Returns <jk>true</jk> if this executable has the specified annotation.
411    *
412    * @param <A> The annotation type.
413    * @param type The annotation type.
414    * @return <jk>true</jk> if this executable has the specified annotation.
415    */
416   public <A extends Annotation> boolean hasAnnotation(Class<A> type) {
417      return getDeclaredAnnotations(type).findFirst().isPresent();
418   }
419
420   /**
421    * Returns <jk>true</jk> if this method has a name in the specified set.
422    *
423    * @param names The names to test for.
424    * @return <jk>true</jk> if this method has one of the names.
425    */
426   public final boolean hasAnyName(Collection<String> names) {
427      return names.contains(getNameSimple());
428   }
429
430   /**
431    * Returns <jk>true</jk> if this method has a name in the specified list.
432    *
433    * @param names The names to test for.
434    * @return <jk>true</jk> if this method has one of the names.
435    */
436   public final boolean hasAnyName(String...names) {
437      return stream(names).anyMatch(n -> eq(n, getNameSimple()));
438   }
439
440   /**
441    * Returns <jk>true</jk> if this executable has matching parameter types with the provided parameter list.
442    *
443    * @param params The parameters to match against.
444    * @return <jk>true</jk> if this executable has matching parameter types.
445    */
446   public final boolean hasMatchingParameters(List<ParameterInfo> params) {
447      var myParams = getParameters();
448      return myParams.size() == params.size() && IntStream.range(0, params.size()).allMatch(i -> myParams.get(i).getParameterType().is(params.get(i).getParameterType()));
449   }
450
451   /**
452    * Returns <jk>true</jk> if this method has this name.
453    *
454    * @param name The name to test for.
455    * @return <jk>true</jk> if this method has this name.
456    */
457   @SuppressWarnings({
458      "javabugs:S2259" // getNameSimple() returns the declared name or the declaring class simple name and is never null.
459   })
460   public final boolean hasName(String name) {
461      return getNameSimple().equals(name);
462   }
463
464   /**
465    * Returns <jk>true</jk> if this executable has this number of arguments.
466    *
467    * <p>
468    * Same as calling {@link Executable#getParameterCount()} and comparing the count.
469    *
470    * @param number The number of expected arguments.
471    * @return <jk>true</jk> if this executable has this number of arguments.
472    */
473   public final boolean hasNumParameters(int number) {
474      return getParameterCount() == number;
475   }
476
477   /**
478    * Returns <jk>true</jk> if this executable has at least one parameter.
479    *
480    * <p>
481    * Same as calling {@link Executable#getParameterCount()} and comparing with zero.
482    *
483    * @return <jk>true</jk> if this executable has at least one parameter.
484    */
485   public final boolean hasParameters() {
486      return getParameterCount() != 0;
487   }
488
489   /**
490    * Returns <jk>true</jk> if this method has the specified argument parent classes.
491    *
492    * @param args The arguments to test for.
493    * @return <jk>true</jk> if this method has this arguments in the exact order.
494    */
495   public final boolean hasParameterTypeParents(Class<?>...args) {
496      var params = getParameters();
497      return params.size() == args.length && params.stream().allMatch(p -> stream(args).anyMatch(a -> p.getParameterType().isParentOfLenient(a)));
498   }
499
500   /**
501    * Returns <jk>true</jk> if this method has the specified argument parent classes.
502    *
503    * @param args The arguments to test for.
504    * @return <jk>true</jk> if this method has this arguments in the exact order.
505    */
506   public final boolean hasParameterTypeParents(ClassInfo...args) {
507      var params = getParameters();
508      return params.size() == args.length && params.stream().allMatch(p -> stream(args).anyMatch(a -> p.getParameterType().isParentOfLenient(a)));
509   }
510
511   /**
512    * Returns <jk>true</jk> if this method has the specified arguments.
513    *
514    * @param args The arguments to test for.
515    * @return <jk>true</jk> if this method has this arguments in the exact order.
516    */
517   public final boolean hasParameterTypes(Class<?>...args) {
518      var params = getParameters();
519      return params.size() == args.length && IntStream.range(0, args.length).allMatch(i -> params.get(i).getParameterType().is(args[i]));
520   }
521
522   /**
523    * Returns <jk>true</jk> if this method has the specified arguments.
524    *
525    * @param args The arguments to test for.
526    * @return <jk>true</jk> if this method has this arguments in the exact order.
527    */
528   public final boolean hasParameterTypes(ClassInfo...args) {
529      var params = getParameters();
530      return params.size() == args.length && IntStream.range(0, args.length).allMatch(i -> params.get(i).getParameterType().is(args[i]));
531   }
532
533   /**
534    * Returns <jk>true</jk> if this executable has a parameter of the specified type in any position.
535    *
536    * @param type The type to test for.
537    * @return <jk>true</jk> if this executable has a parameter of the specified type.
538    */
539   public final boolean hasParameterType(ClassInfo type) {
540      return getParameters().stream().anyMatch(p -> p.getParameterType().is(type));
541   }
542
543   /**
544    * Returns <jk>true</jk> if this executable has a parameter that is a parent of the specified type in any position.
545    *
546    * <p>
547    * This means the parameter type can accept an instance of the specified type.
548    *
549    * @param type The type to test for.
550    * @return <jk>true</jk> if this executable has a parameter that can accept the specified type.
551    */
552   public final boolean hasParameterTypeParent(ClassInfo type) {
553      return getParameters().stream().anyMatch(p -> p.getParameterType().isParentOfLenient(type));
554   }
555
556   /**
557    * Returns <jk>true</jk> if this method has at most only these arguments using lenient matching.
558    *
559    * <p>
560    * Lenient matching allows arguments to be matched to parameters based on type compatibility,
561    * where arguments can be in any order.
562    *
563    * @param args The arguments to test for.
564    * @return <jk>true</jk> if this method has at most only these arguments in any order.
565    */
566   public final boolean hasParameterTypesLenient(Class<?>...args) {
567      return parameterMatchesLenientCount(args) != -1;
568   }
569
570   /**
571    * Returns <jk>true</jk> if this method has at most only these arguments using lenient matching.
572    *
573    * <p>
574    * Lenient matching allows arguments to be matched to parameters based on type compatibility,
575    * where arguments can be in any order.
576    *
577    * @param args The arguments to test for.
578    * @return <jk>true</jk> if this method has at most only these arguments in any order.
579    */
580   public final boolean hasParameterTypesLenient(ClassInfo...args) {
581      return parameterMatchesLenientCount(args) != -1;
582   }
583
584   /**
585    * Returns <jk>true</jk> if all specified flags are applicable to this method.
586    *
587    * @param flag The flag to test for.
588    * @return <jk>true</jk> if all specified flags are applicable to this method.
589    */
590   @Override
591   public boolean is(ElementFlag flag) {
592      return switch (flag) {
593         case CONSTRUCTOR -> isConstructor();
594         case NOT_CONSTRUCTOR -> ! isConstructor();
595         case DEPRECATED -> isDeprecated();
596         case NOT_DEPRECATED -> isNotDeprecated();
597         case HAS_PARAMS -> hasParameters();
598         case HAS_NO_PARAMS -> getParameterCount() == 0;
599         case SYNTHETIC -> isSynthetic();
600         case NOT_SYNTHETIC -> ! isSynthetic();  // HTT
601         case VARARGS -> isVarArgs();
602         case NOT_VARARGS -> ! isVarArgs();
603         default -> super.is(flag);
604      };
605   }
606
607   /**
608    * Returns <jk>true</jk> if this executable represents a {@link Constructor}.
609    *
610    * @return
611    *    <jk>true</jk> if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}.
612    *    <jk>false</jk> if this executable represents a {@link Method} and can be cast to {@link MethodInfo}.
613    */
614   public final boolean isConstructor() { return isConstructor; }
615
616   /**
617    * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
618    *
619    * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it.
620    */
621   public final boolean isDeprecated() {
622      return inner.isAnnotationPresent(Deprecated.class);
623
624   }
625
626   /**
627    * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
628    *
629    * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it.
630    */
631   public final boolean isNotDeprecated() {
632      return ! inner.isAnnotationPresent(Deprecated.class);
633
634   }
635
636   /**
637    * Returns <jk>true</jk> if this executable is a synthetic construct as defined by the Java Language Specification.
638    *
639    * <p>
640    * Same as calling {@link Executable#isSynthetic()}.
641    *
642    * <h5 class='section'>Example:</h5>
643    * <p class='bjava'>
644    *    <jc>// Check if method is compiler-generated</jc>
645    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"access$000"</js>);
646    *    <jk>boolean</jk> <jv>isSynthetic</jv> = <jv>mi</jv>.isSynthetic();
647    * </p>
648    *
649    * @return <jk>true</jk> if this executable is a synthetic construct.
650    * @see Executable#isSynthetic()
651    */
652   public final boolean isSynthetic() { return inner.isSynthetic(); }
653
654   /**
655    * Returns <jk>true</jk> if this executable was declared to take a variable number of arguments.
656    *
657    * <p>
658    * Same as calling {@link Executable#isVarArgs()}.
659    *
660    * <h5 class='section'>Example:</h5>
661    * <p class='bjava'>
662    *    <jc>// Check if method accepts varargs</jc>
663    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, String[].<jk>class</jk>);
664    *    <jk>boolean</jk> <jv>isVarArgs</jv> = <jv>mi</jv>.isVarArgs();
665    * </p>
666    *
667    * @return <jk>true</jk> if this executable was declared to take a variable number of arguments.
668    * @see Executable#isVarArgs()
669    */
670   public final boolean isVarArgs() { return inner.isVarArgs(); }
671
672   /**
673    * Identifies if the specified visibility matches this method.
674    *
675    * @param v The visibility to validate against.
676    * @return <jk>true</jk> if this visibility matches the modifier attribute of this method.
677    */
678   public final boolean isVisible(Visibility v) {
679      return v.isVisible(inner);
680   }
681
682   /**
683    * Returns how well this method matches the specified arg types using lenient matching.
684    *
685    * <p>
686    * Lenient matching allows arguments to be matched to parameters based on type compatibility,
687    * where arguments can be in any order.
688    *
689    * <p>
690    * The number returned is the number of method arguments that match the passed in arg types.
691    * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
692    *
693    * @param argTypes The arg types to check against.
694    * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
695    */
696   public final int parameterMatchesLenientCount(Class<?>...argTypes) {
697      int matches = 0;
698      outer: for (var param : getParameters()) {
699         for (var a : argTypes) {
700            if (param.getParameterType().isParentOfLenient(a)) {
701               matches++;
702               continue outer;
703            }
704         }
705         return -1;
706      }
707      return matches;
708   }
709
710   /**
711    * Returns how well this method matches the specified arg types using lenient matching.
712    *
713    * <p>
714    * Lenient matching allows arguments to be matched to parameters based on type compatibility,
715    * where arguments can be in any order.
716    *
717    * <p>
718    * The number returned is the number of method arguments that match the passed in arg types.
719    * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
720    *
721    * @param argTypes The arg types to check against.
722    * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
723    */
724   public final int parameterMatchesLenientCount(ClassInfo...argTypes) {
725      int matches = 0;
726      outer: for (var param : getParameters()) {
727         for (var a : argTypes) {
728            if (param.getParameterType().isParentOfLenient(a)) {
729               matches++;
730               continue outer;
731            }
732         }
733         return -1;
734      }
735      return matches;
736   }
737
738   /**
739    * Returns how well this method matches the specified arg types using lenient matching.
740    *
741    * <p>
742    * Lenient matching allows arguments to be matched to parameters based on type compatibility,
743    * where arguments can be in any order.
744    *
745    * <p>
746    * The number returned is the number of method arguments that match the passed in arg types.
747    * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
748    *
749    * @param argTypes The arg types to check against.
750    * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
751    */
752   public final int parameterMatchesLenientCount(Object...argTypes) {
753      int matches = 0;
754      outer: for (var param : getParameters()) {
755         for (var a : argTypes) {
756            if (param.getParameterType().canAcceptArg(a)) {
757               matches++;
758               continue outer;
759            }
760         }
761         return -1;
762      }
763      return matches;
764   }
765
766   /**
767    * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions.
768    *
769    * @return <jk>true</jk> if call was successful.
770    */
771   @Override
772   public final boolean setAccessible() {
773      // inner can never be null - constructor asserts non-null via reqnn
774      return safeOpt(() -> {
775         inner.setAccessible(true);
776         return true;
777      }).orElse(false);
778   }
779
780   /**
781    * Returns a string describing this executable, including type parameters.
782    *
783    * <p>
784    * The string includes the method/constructor name, parameter types (with generic information), and return type (for methods).
785    *
786    * <p>
787    * Same as calling {@link Executable#toGenericString()}.
788    *
789    * <h5 class='section'>Example:</h5>
790    * <p class='bjava'>
791    *    <jc>// Get generic string for: public &lt;T&gt; List&lt;T&gt; myMethod(T value) throws IOException</jc>
792    *    MethodInfo <jv>mi</jv> = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>).getMethod(<js>"myMethod"</js>, Object.<jk>class</jk>);
793    *    String <jv>str</jv> = <jv>mi</jv>.toGenericString();
794    *    <jc>// Returns: "public &lt;T&gt; java.util.List&lt;T&gt; com.example.MyClass.myMethod(T) throws java.io.IOException"</jc>
795    * </p>
796    *
797    * @return A string describing this executable.
798    * @see Executable#toGenericString()
799    */
800   public final String toGenericString() {
801      return inner.toGenericString();
802   }
803
804   /**
805    * Returns a detailed string representation of this executable (method or constructor).
806    *
807    * <p>
808    * The returned string includes:
809    * <ul>
810    *    <li>Modifiers (public, private, protected, static, final, abstract, synchronized, etc.)
811    *    <li>Synthetic and bridge flags (if applicable)
812    *    <li>Generic type parameters with bounds (if any, e.g., "&lt;T extends Comparable&lt;T&gt;&gt;")
813    *    <li>Return type (for methods only, not constructors)
814    *    <li>Fully qualified name with parameters
815    *    <li>Throws declarations (if any)
816    * </ul>
817    *
818    * <h5 class='section'>Examples:</h5>
819    * <p class='bjava'>
820    *    <jc>// Constructor</jc>
821    *    ConstructorInfo <jv>ci</jv> = ConstructorInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>.getConstructor(String.<jk>class</jk>));
822    *    <jv>ci</jv>.toString();
823    *    <jc>// Returns: "public org.example.MyClass(java.lang.String)"</jc>
824    *
825    *    <jc>// Method</jc>
826    *    MethodInfo <jv>mi</jv> = MethodInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>, <js>"myMethod"</js>, String.<jk>class</jk>);
827    *    <jv>mi</jv>.toString();
828    *    <jc>// Returns: "public java.lang.String org.example.MyClass.myMethod(java.lang.String)"</jc>
829    *
830    *    <jc>// Method with throws</jc>
831    *    MethodInfo <jv>mi2</jv> = MethodInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>, <js>"riskyMethod"</js>);
832    *    <jv>mi2</jv>.toString();
833    *    <jc>// Returns: "public void org.example.MyClass.riskyMethod() throws java.io.IOException, java.lang.Exception"</jc>
834    *
835    *    <jc>// Generic method</jc>
836    *    MethodInfo <jv>mi3</jv> = MethodInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>, <js>"genericMethod"</js>);
837    *    <jv>mi3</jv>.toString();
838    *    <jc>// Returns: "public &lt;T&gt; void org.example.MyClass.genericMethod(T)"</jc>
839    *
840    *    <jc>// Generic method with bounds</jc>
841    *    MethodInfo <jv>mi4</jv> = MethodInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>, <js>"boundedMethod"</js>);
842    *    <jv>mi4</jv>.toString();
843    *    <jc>// Returns: "public &lt;T extends java.lang.Comparable&lt;T&gt;&gt; void org.example.MyClass.boundedMethod(T)"</jc>
844    * </p>
845    *
846    * <h5 class='section'>Comparison with Other Methods:</h5>
847    * <ul class='spaced-list'>
848    *    <li>{@link #getNameShort()} - Returns short name with parameters (e.g., "myMethod(int, String)")
849    *    <li>{@link #getNameFull()} - Returns fully qualified name with parameters (e.g., "org.example.MyClass.myMethod(int, java.lang.String)")
850    *    <li>{@link #toGenericString()} - Returns generic string from underlying Executable
851    * </ul>
852    *
853    * @return A detailed string representation including modifiers, return type, name, and throws declarations.
854    */
855   @Override
856   public String toString() {
857      return toString.get();
858   }
859
860   @SuppressWarnings({
861      "java:S3776", // Cognitive complexity acceptable for toString formatting logic
862      "java:S6541" // Synchronization not needed for local string building
863   })
864   private String findToString() {
865      var sb = new StringBuilder(256);
866
867      // Modifiers
868      var mods = Modifier.toString(getModifiers());
869      if (nn(mods) && ! mods.isEmpty()) {
870         sb.append(mods).append(" ");
871      }
872
873      // Add synthetic, bridge, varargs, and default flags (not actual modifiers but useful to show)
874      if (isSynthetic())
875         sb.append("synthetic ");
876      if (this instanceof MethodInfo this2) {
877         if (this2.isBridge())
878            sb.append("bridge ");
879         if (this2.isDefault())
880            sb.append("default ");
881      }
882      if (isVarArgs())
883         sb.append("varargs ");
884
885      // Type parameters (if any)
886      var typeParams = getTypeParameters();
887      var hasTypeParams = typeParams.length > 0;
888      if (hasTypeParams) {
889         sb.append('<');
890         var first = true;
891         for (var tv : typeParams) {
892            if (! first)
893               sb.append(", ");
894            first = false;
895            sb.append(tv.getName());
896            var bounds = tv.getBounds();
897            if (bounds.length > 0 && (bounds.length > 1 || ! bounds[0].equals(Object.class))) {
898               sb.append(" extends ");
899               var firstBound = true;
900               for (var bound : bounds) {
901                  if (! firstBound)
902                     sb.append(" & ");
903                  firstBound = false;
904                  if (bound instanceof Class<?> bound2) {
905                     ClassInfo.of(bound2).appendNameFormatted(sb, FULL, true, '$', BRACKETS);
906                  } else {
907                     sb.append(bound.getTypeName());
908                  }
909               }
910            }
911         }
912         sb.append("> ");
913      }
914
915      // Return type (skip for constructors)
916      if (this instanceof MethodInfo this2)
917         this2.getReturnType().appendNameFormatted(sb, FULL, true, '$', BRACKETS).append(" ");
918
919      // Full name - use generic parameter types if we have type parameters
920      if (hasTypeParams) {
921         // Build signature with generic parameter types to show type variables instead of erased types
922         var dc = getDeclaringClass();
923         var pi = dc.getPackage();
924         if (nn(pi))
925            sb.append(pi.getName()).append('.');
926         dc.appendNameFormatted(sb, SHORT, true, '$', BRACKETS);
927         if (! isConstructor)
928            sb.append('.').append(getNameSimple());
929         sb.append('(');
930         var genericParamTypes = inner.getGenericParameterTypes();
931         var first = true;
932         for (var gpt : genericParamTypes) {
933            if (! first)
934               sb.append(',');
935            first = false;
936            ClassInfo.of(gpt).appendNameFormatted(sb, FULL, true, '$', BRACKETS);
937         }
938         sb.append(')');
939      } else {
940         // Use regular getNameFull() for non-generic methods
941         sb.append(getNameFull());
942      }
943
944      // Throws declarations
945      var exTypes = getExceptionTypes();
946      if (! exTypes.isEmpty()) {
947         sb.append(" throws ");
948         sb.append(exTypes.stream()
949            .map(e -> e.getNameFormatted(FULL, true, '$', BRACKETS))
950            .collect(joining(", ")));
951      }
952
953      return sb.toString();
954   }
955
956
957
958   private void checkIndex(int index) {
959      int pc = getParameterCount();
960      if (pc == 0)
961         throw new IndexOutOfBoundsException(f("Invalid index '%d'.  No parameters.", index));
962      if (index < 0 || index >= pc)
963         throw new IndexOutOfBoundsException(f("Invalid index '%d'.  Parameter count: %d", index, pc));
964   }
965
966   private String findNameFull() {
967      var sb = new StringBuilder(128);
968      var dc = declaringClass;
969      var pi = dc.getPackage();
970      if (nn(pi))
971         sb.append(pi.getName()).append('.');
972      dc.appendNameFormatted(sb, SHORT, true, '$', BRACKETS);
973      if (! isConstructor)
974         sb.append('.').append(getNameSimple());
975      sb.append('(');
976      sb.append(getParameters().stream().map(p -> p.getParameterType().getNameFormatted(FULL, true, '$', BRACKETS)).collect(joining(",")));
977      sb.append(')');
978      return sb.toString();
979   }
980
981   private List<ParameterInfo> findParameters() {
982      var rp = inner.getParameters();
983      var ptc = inner.getParameterTypes();  // Class<?>[] - includes all parameters (including synthetic enclosing instance)
984      var ptt = inner.getGenericParameterTypes();  // Type[] - generic type information
985      Type[] genericTypes;
986
987      if (ptt.length == ptc.length) {
988         genericTypes = ptt;
989      } else {
990         var ptt2 = new Type[ptc.length];
991         ptt2[0] = ptc[0];  // Enclosing instance type (Class<?>, not a generic Type)
992         for (var i = 0; i < ptt.length; i++)
993            ptt2[i + 1] = ptt[i];  // Copy remaining generic types
994         genericTypes = ptt2;
995      }
996      return IntStream.range(0, rp.length).mapToObj(i -> new ParameterInfo(this, rp[i], i, ClassInfo.of(ptc[i], genericTypes[i]))).toList();
997   }
998}