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<ParameterInfo> <jv>params</jv> = <jv>ei</jv>.getParameters(); 068 * 069 * <jc>// Get exceptions</jc> 070 * List<ClassInfo> <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<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}