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<ParameterInfo> <jv>params</jv> = <jv>ei</jv>.getParameters();
68 *
69 * <jc>// Get exceptions</jc>
70 * List<ClassInfo> <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<ClassInfo> <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: <T extends Number> 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<?>[] <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 <T> List<T> 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 <T> java.util.List<T> 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 }