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 org.apache.juneau.commons.reflect.ClassArrayFormat.*;
020import static org.apache.juneau.commons.reflect.ClassNameFormat.*;
021import static org.apache.juneau.commons.utils.AssertionUtils.*;
022import static org.apache.juneau.commons.utils.CollectionUtils.*;
023import static org.apache.juneau.commons.utils.Utils.*;
024import static java.util.stream.Collectors.*;
025
026import java.lang.annotation.*;
027import java.lang.reflect.*;
028import java.util.*;
029import java.util.function.*;
030import java.util.stream.*;
031
032import org.apache.juneau.commons.utils.*;
033
034/**
035 * Abstract base class containing common functionality for {@link ConstructorInfo} and {@link MethodInfo}.
036 *
037 * <p>
038 * This class provides shared functionality for both constructors and methods, which are both types of
039 * {@link Executable} in Java. It extends {@link AccessibleInfo} to provide {@link AccessibleObject}
040 * functionality for accessing private methods and constructors.
041 *
042 * <h5 class='section'>Features:</h5>
043 * <ul class='spaced-list'>
044 *    <li>Parameter introspection - access method/constructor parameters
045 *    <li>Exception handling - get declared exceptions
046 *    <li>Annotation support - get annotations declared on the executable
047 *    <li>Name formatting - get short and fully qualified names
048 *    <li>Parameter matching - match parameters by type (strict and lenient)
049 *    <li>Accessibility control - make private executables accessible
050 * </ul>
051 *
052 * <h5 class='section'>Use Cases:</h5>
053 * <ul class='spaced-list'>
054 *    <li>Working with both methods and constructors in a unified way
055 *    <li>Finding methods/constructors that match specific parameter types
056 *    <li>Introspecting parameter and exception information
057 *    <li>Building frameworks that need to analyze executable signatures
058 * </ul>
059 *
060 * <h5 class='section'>Usage:</h5>
061 * <p class='bjava'>
062 *    <jc>// Get ExecutableInfo (could be MethodInfo or ConstructorInfo)</jc>
063 *    MethodInfo <jv>mi</jv> = ...;
064 *    ExecutableInfo <jv>ei</jv> = <jv>mi</jv>;  <jc>// MethodInfo extends ExecutableInfo</jc>
065 *
066 *    <jc>// Get parameters</jc>
067 *    List&lt;ParameterInfo&gt; <jv>params</jv> = <jv>ei</jv>.getParameters();
068 *
069 *    <jc>// Get exceptions</jc>
070 *    List&lt;ClassInfo&gt; <jv>exceptions</jv> = <jv>ei</jv>.getExceptions();
071 *
072 *    <jc>// Check parameter matching</jc>
073 *    <jk>boolean</jk> <jv>matches</jv> = <jv>ei</jv>.parameterMatches(String.<jk>class</jk>, Integer.<jk>class</jk>);
074 * </p>
075 *
076 * <h5 class='section'>See Also:</h5><ul>
077 *    <li class='jc'>{@link MethodInfo} - Method introspection
078 *    <li class='jc'>{@link ConstructorInfo} - Constructor introspection
079 *    <li class='jc'>{@link ParameterInfo} - Parameter introspection
080 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsReflection">Reflection Package</a>
081 * </ul>
082 */
083public abstract class ExecutableInfo extends AccessibleInfo {
084
085   protected final ClassInfo declaringClass;
086   private final Executable inner;
087   private final boolean isConstructor;
088
089   private final Supplier<List<ParameterInfo>> parameters;  // All parameters of this executable.
090   private final Supplier<List<ClassInfo>> parameterTypes;  // All parameter types of this executable.
091   private final Supplier<List<ClassInfo>> exceptions;  // All exceptions declared by this executable.
092   private final Supplier<List<AnnotationInfo<Annotation>>> declaredAnnotations;  // All annotations declared directly on this executable.
093   private final Supplier<String> shortName;  // Short name (method/constructor name with parameters).
094   private final Supplier<String> fullName;  // Fully qualified name (declaring-class.method-name with parameters).
095
096   /**
097    * Constructor.
098    *
099    * <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}