001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.reflect; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016import static org.apache.juneau.internal.ConsumerUtils.*; 017 018import java.beans.*; 019import java.lang.annotation.*; 020import java.lang.reflect.*; 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.*; 025import org.apache.juneau.internal.*; 026 027/** 028 * Lightweight utility class for introspecting information about a method. 029 * 030 * <h5 class='section'>See Also:</h5><ul> 031 * </ul> 032 */ 033@FluentSetters 034public final class MethodInfo extends ExecutableInfo implements Comparable<MethodInfo> { 035 036 //----------------------------------------------------------------------------------------------------------------- 037 // Static 038 //----------------------------------------------------------------------------------------------------------------- 039 040 /** 041 * Convenience method for instantiating a {@link MethodInfo}; 042 * 043 * @param declaringClass The class that declares this method. 044 * @param m The method being wrapped. 045 * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null; 046 */ 047 public static MethodInfo of(ClassInfo declaringClass, Method m) { 048 if (m == null) 049 return null; 050 return declaringClass.getMethodInfo(m); 051 } 052 053 /** 054 * Convenience method for instantiating a {@link MethodInfo}; 055 * 056 * @param declaringClass The class that declares this method. 057 * @param m The method being wrapped. 058 * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null; 059 */ 060 public static MethodInfo of(Class<?> declaringClass, Method m) { 061 if (m == null) 062 return null; 063 return ClassInfo.of(declaringClass).getMethodInfo(m); 064 } 065 066 /** 067 * Convenience method for instantiating a {@link MethodInfo}; 068 * 069 * @param m The method being wrapped. 070 * @return A new {@link MethodInfo} object, or <jk>null</jk> if the method was null; 071 */ 072 public static MethodInfo of(Method m) { 073 if (m == null) 074 return null; 075 return ClassInfo.of(m.getDeclaringClass()).getMethodInfo(m); 076 } 077 078 //----------------------------------------------------------------------------------------------------------------- 079 // Instance 080 //----------------------------------------------------------------------------------------------------------------- 081 082 private final Method m; 083 private volatile ClassInfo returnType; 084 private volatile MethodInfo[] matching; 085 086 /** 087 * Constructor. 088 * 089 * @param declaringClass The class that declares this method. 090 * @param m The method being wrapped. 091 */ 092 protected MethodInfo(ClassInfo declaringClass, Method m) { 093 super(declaringClass, m); 094 this.m = m; 095 } 096 097 /** 098 * Returns the wrapped method. 099 * 100 * @return The wrapped method. 101 */ 102 public Method inner() { 103 return m; 104 } 105 106 //----------------------------------------------------------------------------------------------------------------- 107 // Matching methods. 108 //----------------------------------------------------------------------------------------------------------------- 109 110 /** 111 * Returns <jk>true</jk> if this constructor can accept the specified arguments in the specified order. 112 * 113 * @param args The arguments to check. 114 * @return <jk>true</jk> if this constructor can accept the specified arguments in the specified order. 115 */ 116 public boolean canAccept(Object...args) { 117 Class<?>[] pt = m.getParameterTypes(); 118 if (pt.length != args.length) 119 return false; 120 for (int i = 0; i < pt.length; i++) 121 if (! pt[i].isInstance(args[i])) 122 return false; 123 return true; 124 } 125 126 /** 127 * Returns the number of matching arguments for this method. 128 * 129 * @param args The arguments to check. 130 * @return the number of matching arguments for this method. 131 */ 132 public int canAcceptFuzzy(Object...args) { 133 int matches = 0; 134 outer: for (ClassInfo pi : _getParameterTypes()) { 135 for (Object a : args) { 136 if (pi.canAcceptArg(a)) { 137 matches++; 138 continue outer; 139 } 140 } 141 return -1; 142 } 143 return matches; 144 } 145 146 /** 147 * Performs an action on all matching declared methods with the same name and arguments on all superclasses and interfaces. 148 * 149 * <p> 150 * Methods are accessed from child-to-parent order. 151 * 152 * @param filter A predicate to apply to the entries to determine if action should be performed. Can be <jk>null</jk>. 153 * @param action An action to perform on the entry. 154 * @return This object. 155 */ 156 public MethodInfo forEachMatching(Predicate<MethodInfo> filter, Consumer<MethodInfo> action) { 157 for (MethodInfo m : _getMatching()) 158 consume(filter, action, m); 159 return this; 160 } 161 162 /** 163 * Performs an action on all matching declared methods with the same name and arguments on all superclasses and interfaces. 164 * 165 * <p> 166 * Methods are accessed from parent-to-child order. 167 * 168 * @param filter A predicate to apply to the entries to determine if action should be performed. Can be <jk>null</jk>. 169 * @param action An action to perform on the entry. 170 * @return This object. 171 */ 172 public MethodInfo forEachMatchingParentFirst(Predicate<MethodInfo> filter, Consumer<MethodInfo> action) { 173 MethodInfo[] m = _getMatching(); 174 for (int i = m.length-1; i >= 0; i--) 175 consume(filter, action, m[i]); 176 return this; 177 } 178 179 private static List<MethodInfo> findMatching(List<MethodInfo> l, MethodInfo m, ClassInfo c) { 180 for (MethodInfo m2 : c._getDeclaredMethods()) 181 if (m.hasName(m2.getName()) && Arrays.equals(m._getParameterTypes(), m2._getParameterTypes())) 182 l.add(m2); 183 ClassInfo pc = c.getSuperclass(); 184 if (pc != null) 185 findMatching(l, m, pc); 186 for (ClassInfo ic : c._getDeclaredInterfaces()) 187 findMatching(l, m, ic); 188 return l; 189 } 190 191 private MethodInfo findMatchingOnClass(ClassInfo c) { 192 for (MethodInfo m2 : c._getDeclaredMethods()) 193 if (hasName(m2.getName()) && Arrays.equals(_getParameterTypes(), m2._getParameterTypes())) 194 return m2; 195 return null; 196 } 197 198 MethodInfo[] _getMatching() { 199 if (matching == null) { 200 synchronized(this) { 201 List<MethodInfo> l = findMatching(list(), this, getDeclaringClass()); 202 matching = l.toArray(new MethodInfo[l.size()]); 203 } 204 } 205 return matching; 206 } 207 208 //----------------------------------------------------------------------------------------------------------------- 209 // Annotations 210 //----------------------------------------------------------------------------------------------------------------- 211 212 /** 213 * Finds the annotation of the specified type defined on this method. 214 * 215 * <p> 216 * If this is a method and the annotation cannot be found on the immediate method, searches methods with the same 217 * signature on the parent classes or interfaces. 218 * <br>The search is performed in child-to-parent order. 219 * 220 * @param <A> The annotation type to look for. 221 * @param type The annotation to look for. 222 * @return The annotation if found, or <jk>null</jk> if not. 223 */ 224 public final <A extends Annotation> A getAnnotation(Class<A> type) { 225 return getAnnotation(AnnotationProvider.DEFAULT, type); 226 } 227 228 /** 229 * Finds the annotation of the specified type defined on this method. 230 * 231 * <p> 232 * Searches all methods with the same signature on the parent classes or interfaces 233 * and the return type on the method. 234 * 235 * @param <A> The annotation type to look for. 236 * @param annotationProvider The annotation provider. 237 * @param type The annotation to look for. 238 * @return The first annotation found, or <jk>null</jk> if it doesn't exist. 239 */ 240 public final <A extends Annotation> A getAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 241 if (type == null) 242 return null; 243 Value<A> t = Value.empty(); 244 for (MethodInfo m2 : _getMatching()) { 245 annotationProvider.forEachAnnotation(type, m2.inner(), x -> true, x -> t.set(x)); 246 if (t.isPresent()) 247 return t.get(); 248 } 249 return null; 250 } 251 252 /** 253 * Returns <jk>true</jk> if the specified annotation is present on this method. 254 * 255 * @param <A> The annotation type to look for. 256 * @param type The annotation to look for. 257 * @return <jk>true</jk> if the specified annotation is present on this method. 258 */ 259 public final <A extends Annotation> boolean hasAnnotation(Class<A> type) { 260 return hasAnnotation(AnnotationProvider.DEFAULT, type); 261 } 262 263 /** 264 * Returns <jk>true</jk> if the specified annotation is present on this method. 265 * 266 * @param <A> The annotation type to look for. 267 * @param annotationProvider The annotation provider. 268 * @param type The annotation to look for. 269 * @return <jk>true</jk> if the specified annotation is present on this method. 270 */ 271 public final <A extends Annotation> boolean hasAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 272 for (MethodInfo m2 : _getMatching()) 273 if (annotationProvider.firstAnnotation(type, m2.inner(), x -> true) != null) 274 return true; 275 return false; 276 } 277 278 /** 279 * Returns <jk>true</jk> if the specified annotation is not present on this method. 280 * 281 * @param <A> The annotation type to look for. 282 * @param annotationProvider The annotation provider. 283 * @param type The annotation to look for. 284 * @return <jk>true</jk> if the specified annotation is not present on this method. 285 */ 286 public final <A extends Annotation> boolean hasNoAnnotation(AnnotationProvider annotationProvider, Class<A> type) { 287 return ! hasAnnotation(annotationProvider, type); 288 } 289 290 /** 291 * Returns <jk>true</jk> if the specified annotation is not present on this method. 292 * 293 * @param <A> The annotation type to look for. 294 * @param type The annotation to look for. 295 * @return <jk>true</jk> if the specified annotation is not present on this method. 296 */ 297 public final <A extends Annotation> boolean hasNoAnnotation(Class<A> type) { 298 return getAnnotation(type) == null; 299 } 300 301 /** 302 * Returns <jk>true</jk> if at least one of the specified annotation is present on this method. 303 * 304 * @param types The annotation to look for. 305 * @return <jk>true</jk> if at least one of the specified annotation is present on this method. 306 */ 307 @SafeVarargs 308 public final boolean hasAnyAnnotations(Class<? extends Annotation>...types) { 309 for (Class<? extends Annotation> a : types) 310 if (hasAnnotation(a)) 311 return true; 312 return false; 313 } 314 315 /** 316 * Performs an action on all matching annotations defined on this method. 317 * 318 * <p> 319 * Searches all methods with the same signature on the parent classes or interfaces 320 * and the return type on the method. 321 * <br>Results are parent-to-child ordered. 322 * 323 * @param <A> The annotation type to look for. 324 * @param type The annotation to look for. 325 * @param filter A predicate to apply to the entries to determine if action should be performed. Can be <jk>null</jk>. 326 * @param action An action to perform on the entry. 327 * @return This object. 328 */ 329 public <A extends Annotation> MethodInfo forEachAnnotation(Class<A> type, Predicate<A> filter, Consumer<A> action) { 330 return forEachAnnotation(AnnotationProvider.DEFAULT, type, filter, action); 331 } 332 333 /** 334 * Performs an action on all matching annotations defined on this method. 335 * 336 * <p> 337 * Searches all methods with the same signature on the parent classes or interfaces 338 * and the return type on the method. 339 * <br>Results are parent-to-child ordered. 340 * 341 * @param <A> The annotation type to look for. 342 * @param annotationProvider The annotation provider. 343 * @param type The annotation type. 344 * @param filter A predicate to apply to the entries to determine if action should be performed. Can be <jk>null</jk>. 345 * @param action An action to perform on the entry. 346 * @return This object. 347 */ 348 public <A extends Annotation> MethodInfo forEachAnnotation(AnnotationProvider annotationProvider, Class<A> type, Predicate<A> filter, Consumer<A> action) { 349 declaringClass.forEachAnnotation(annotationProvider, type, filter, action); 350 MethodInfo[] m = _getMatching(); 351 for (int i = m.length-1; i >= 0; i--) 352 for (Annotation a2 : m[i]._getDeclaredAnnotations()) 353 consume(type, filter, action, a2); 354 getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(annotationProvider, type, filter, action); 355 return this; 356 } 357 358 /** 359 * Returns the first annotation in the specified list on this method. 360 * 361 * @param types The annotations to look for. 362 * @return The first matching annotation. 363 */ 364 @SafeVarargs 365 public final Annotation getAnyAnnotation(Class<? extends Annotation>...types) { 366 for (Class<? extends Annotation> cc : types) { 367 Annotation a = getAnnotation(cc); 368 if (a != null) 369 return a; 370 } 371 return null; 372 } 373 374 /** 375 * Constructs an {@link AnnotationList} of all annotations found on this method. 376 * 377 * <p> 378 * Annotations are appended in the following orders: 379 * <ol> 380 * <li>On the package of this class. 381 * <li>On interfaces ordered parent-to-child. 382 * <li>On parent classes ordered parent-to-child. 383 * <li>On this class. 384 * <li>On this method and matching methods ordered parent-to-child. 385 * </ol> 386 * 387 * @return A new {@link AnnotationList} object on every call. 388 */ 389 public AnnotationList getAnnotationList() { 390 return getAnnotationList(x -> true); 391 } 392 393 /** 394 * Constructs an {@link AnnotationList} of all matching annotations found on this method. 395 * 396 * <p> 397 * Annotations are appended in the following orders: 398 * <ol> 399 * <li>On the package of this class. 400 * <li>On interfaces ordered parent-to-child. 401 * <li>On parent classes ordered parent-to-child. 402 * <li>On this class. 403 * <li>On this method and matching methods ordered parent-to-child. 404 * </ol> 405 * 406 * @param filter A predicate to apply to the entries to determine if value should be added. Can be <jk>null</jk>. 407 * @return A new {@link AnnotationList} object on every call. 408 */ 409 public AnnotationList getAnnotationList(Predicate<AnnotationInfo<?>> filter) { 410 AnnotationList al = new AnnotationList(); 411 forEachAnnotationInfo(filter, x -> al.add(x)); 412 return al; 413 } 414 415 /** 416 * Same as {@link #getAnnotationList(Predicate)} except only returns annotations defined on methods. 417 * 418 * @param filter A predicate to apply to the entries to determine if value should be added. Can be <jk>null</jk>. 419 * @return A new {@link AnnotationList} object on every call. 420 */ 421 public AnnotationList getAnnotationListMethodOnly(Predicate<AnnotationInfo<?>> filter) { 422 AnnotationList al = new AnnotationList(); 423 forEachAnnotationInfoMethodOnly(filter, x -> al.add(x)); 424 return al; 425 } 426 427 /** 428 * Perform an action on all matching annotations on this method. 429 * 430 * @param filter A predicate to apply to the entries to determine if action should be performed. Can be <jk>null</jk>. 431 * @param action An action to perform on the entry. 432 * @return This object. 433 */ 434 public MethodInfo forEachAnnotationInfo(Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) { 435 ClassInfo c = this.declaringClass; 436 forEachDeclaredAnnotationInfo(c.getPackage(), filter, action); 437 ClassInfo[] interfaces = c._getInterfaces(); 438 for (int i = interfaces.length-1; i >= 0; i--) { 439 forEachDeclaredAnnotationInfo(interfaces[i], filter, action); 440 forEachDeclaredMethodAnnotationInfo(interfaces[i], filter, action); 441 } 442 ClassInfo[] parents = c._getParents(); 443 for (int i = parents.length-1; i >= 0; i--) { 444 forEachDeclaredAnnotationInfo(parents[i], filter, action); 445 forEachDeclaredMethodAnnotationInfo(parents[i], filter, action); 446 } 447 return this; 448 } 449 450 private void forEachAnnotationInfoMethodOnly(Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) { 451 ClassInfo c = this.declaringClass; 452 ClassInfo[] interfaces = c._getInterfaces(); 453 for (int i = interfaces.length-1; i >= 0; i--) 454 forEachDeclaredMethodAnnotationInfo(interfaces[i], filter, action); 455 ClassInfo[] parents = c._getParents(); 456 for (int i = parents.length-1; i >= 0; i--) 457 forEachDeclaredMethodAnnotationInfo(parents[i], filter, action); 458 } 459 460 private void forEachDeclaredAnnotationInfo(Package p, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) { 461 if (p != null) 462 for (Annotation a : p.getDeclaredAnnotations()) 463 AnnotationInfo.of(p, a).accept(filter, action); 464 } 465 466 private void forEachDeclaredAnnotationInfo(ClassInfo ci, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) { 467 if (ci != null) 468 for (Annotation a : ci._getDeclaredAnnotations()) 469 AnnotationInfo.of(ci, a).accept(filter, action); 470 } 471 472 private void forEachDeclaredMethodAnnotationInfo(ClassInfo ci, Predicate<AnnotationInfo<?>> filter, Consumer<AnnotationInfo<?>> action) { 473 MethodInfo m = findMatchingOnClass(ci); 474 if (m != null) 475 for (Annotation a : m._getDeclaredAnnotations()) 476 AnnotationInfo.of(m, a).accept(filter, action); 477 } 478 479 //----------------------------------------------------------------------------------------------------------------- 480 // Return type. 481 //----------------------------------------------------------------------------------------------------------------- 482 483 /** 484 * Returns the generic return type of this method as a {@link ClassInfo} object. 485 * 486 * @return The generic return type of this method. 487 */ 488 public ClassInfo getReturnType() { 489 if (returnType == null) { 490 synchronized(this) { 491 returnType = ClassInfo.of(m.getReturnType(), m.getGenericReturnType()); 492 } 493 } 494 return returnType; 495 } 496 497 /** 498 * Returns <jk>true</jk> if this method has this return type. 499 * 500 * @param c The return type to test for. 501 * @return <jk>true</jk> if this method has this return type. 502 */ 503 public boolean hasReturnType(Class<?> c) { 504 return m.getReturnType() == c; 505 } 506 507 /** 508 * Returns <jk>true</jk> if this method has this return type. 509 * 510 * @param ci The return type to test for. 511 * @return <jk>true</jk> if this method has this return type. 512 */ 513 public boolean hasReturnType(ClassInfo ci) { 514 return hasReturnType(ci.inner()); 515 } 516 517 /** 518 * Returns <jk>true</jk> if this method has this parent return type. 519 * 520 * @param c The return type to test for. 521 * @return <jk>true</jk> if this method has this parent return type. 522 */ 523 public boolean hasReturnTypeParent(Class<?> c) { 524 return ClassInfo.of(c).isParentOf(m.getReturnType()); 525 } 526 527 /** 528 * Returns <jk>true</jk> if this method has this parent return type. 529 * 530 * @param ci The return type to test for. 531 * @return <jk>true</jk> if this method has this parent return type. 532 */ 533 public boolean hasReturnTypeParent(ClassInfo ci) { 534 return hasReturnTypeParent(ci.inner()); 535 } 536 537 //----------------------------------------------------------------------------------------------------------------- 538 // Other methods 539 //----------------------------------------------------------------------------------------------------------------- 540 541 /** 542 * Returns <jk>true</jk> if this object passes the specified predicate test. 543 * 544 * @param test The test to perform. 545 * @return <jk>true</jk> if this object passes the specified predicate test. 546 */ 547 public boolean matches(Predicate<MethodInfo> test) { 548 return test(test, this); 549 } 550 551 /** 552 * Performs an action on this object if the specified predicate test passes. 553 * 554 * @param test A test to apply to determine if action should be executed. Can be <jk>null</jk>. 555 * @param action An action to perform on this object. 556 * @return This object. 557 */ 558 public MethodInfo accept(Predicate<MethodInfo> test, Consumer<MethodInfo> action) { 559 if (matches(test)) 560 action.accept(this); 561 return this; 562 } 563 564 /** 565 * Shortcut for calling the invoke method on the underlying method. 566 * 567 * @param <T> The method return type. 568 * @param obj the object the underlying method is invoked from. 569 * @param args the arguments used for the method call 570 * @return The object returned from the method. 571 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 572 */ 573 @SuppressWarnings("unchecked") 574 public <T> T invoke(Object obj, Object...args) throws ExecutableException { 575 try { 576 return (T)m.invoke(obj, args); 577 } catch (IllegalAccessException e) { 578 throw new ExecutableException(e); 579 } catch (InvocationTargetException e) { 580 throw new ExecutableException(e.getTargetException()); 581 } 582 } 583 584 /** 585 * Invokes the specified method using fuzzy-arg matching. 586 * 587 * <p> 588 * Arguments will be matched to the parameters based on the parameter types. 589 * <br>Arguments can be in any order. 590 * <br>Extra arguments will be ignored. 591 * <br>Missing arguments will be left <jk>null</jk>. 592 * 593 * <p> 594 * Note that this only works for methods that have distinguishable argument types. 595 * <br>It's not going to work on methods with generic argument types like <c>Object</c> 596 * 597 * @param pojo 598 * The POJO the method is being called on. 599 * <br>Can be <jk>null</jk> for static methods. 600 * @param args 601 * The arguments to pass to the method. 602 * @return 603 * The results of the method invocation. 604 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 605 */ 606 public Object invokeFuzzy(Object pojo, Object...args) throws ExecutableException { 607 try { 608 return m.invoke(pojo, ClassUtils.getMatchingArgs(m.getParameterTypes(), args)); 609 } catch (IllegalAccessException | InvocationTargetException e) { 610 throw new ExecutableException(e); 611 } 612 } 613 614 /** 615 * Returns the signature of this method. 616 * 617 * <p> 618 * For no-arg methods, the signature will be a simple string such as <js>"toString"</js>. 619 * For methods with one or more args, the arguments will be fully-qualified class names (e.g. 620 * <js>"append(java.util.StringBuilder,boolean)"</js>) 621 * 622 * @return The methods signature. 623 */ 624 public String getSignature() { 625 StringBuilder sb = new StringBuilder(128); 626 sb.append(m.getName()); 627 Class<?>[] pt = _getRawParamTypes(); 628 if (pt.length > 0) { 629 sb.append('('); 630 List<ParamInfo> mpi = getParams(); 631 for (int i = 0; i < pt.length; i++) { 632 if (i > 0) 633 sb.append(','); 634 mpi.get(i).getParameterType().appendFullName(sb); 635 } 636 sb.append(')'); 637 } 638 return sb.toString(); 639 } 640 641 /** 642 * Returns the bean property name if this is a getter or setter. 643 * 644 * @return The bean property name, or <jk>null</jk> if this isn't a getter or setter. 645 */ 646 public String getPropertyName() { 647 String n = m.getName(); 648 if ((n.startsWith("get") || n.startsWith("set")) && n.length() > 3) 649 return Introspector.decapitalize(n.substring(3)); 650 if (n.startsWith("is") && n.length() > 2) 651 return Introspector.decapitalize(n.substring(2)); 652 return n; 653 } 654 655 /** 656 * Returns <jk>true</jk> if the parameters on the method only consist of the types specified in the list. 657 * 658 * <h5 class='figure'>Example:</h5> 659 * <p class='bjava'> 660 * 661 * <jc>// Example method:</jc> 662 * <jk>public void</jk> foo(String <jv>bar</jv>, Integer <jv>baz</jv>); 663 * 664 * argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>, Integer.<jk>class</jk>); <jc>// True.</jc> 665 * argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>, Integer.<jk>class</jk>, Map.<jk>class</jk>); <jc>// True.</jc> 666 * argsOnlyOfType(<jv>fooMethod</jv>, String.<jk>class</jk>); <jc>// False.</jc> 667 * </p> 668 * 669 * @param args The valid class types (exact) for the arguments. 670 * @return <jk>true</jk> if the method parameters only consist of the types specified in the list. 671 */ 672 public boolean argsOnlyOfType(Class<?>...args) { 673 for (Class<?> c1 : _getRawParamTypes()) { 674 boolean foundMatch = false; 675 for (Class<?> c2 : args) 676 if (c1 == c2) 677 foundMatch = true; 678 if (! foundMatch) 679 return false; 680 } 681 return true; 682 } 683 684 /** 685 * Returns <jk>true</jk> if this method has at least the specified parameters. 686 * 687 * <p> 688 * Method may or may not have additional parameters besides those specified. 689 * 690 * @param requiredParams The parameter types to check for. 691 * @return <jk>true</jk> if this method has at least the specified parameters. 692 */ 693 public boolean hasAllArgs(Class<?>...requiredParams) { 694 List<Class<?>> rawParamTypes = getRawParamTypes(); 695 696 for (Class<?> c : requiredParams) 697 if (! rawParamTypes.contains(c)) 698 return false; 699 700 return true; 701 } 702 703 /** 704 * Returns <jk>true</jk> if this method has the specified parameter. 705 * 706 * <p> 707 * Method may or may not have additional parameters besides the one specified. 708 * 709 * @param requiredParam The parameter type to check for. 710 * @return <jk>true</jk> if this method has at least the specified parameter. 711 */ 712 public boolean hasArg(Class<?> requiredParam) { 713 return hasAllArgs(requiredParam); 714 } 715 716 /** 717 * Returns <jk>true</jk> if this method is a bridge method. 718 * 719 * @return <jk>true</jk> if this method is a bridge method. 720 */ 721 public boolean isBridge() { 722 return m.isBridge(); 723 } 724 725 /** 726 * Returns the name of this method. 727 * 728 * @return The name of this method 729 */ 730 public String getName() { 731 return m.getName(); 732 } 733 734 @Override 735 public int compareTo(MethodInfo o) { 736 int i = getSimpleName().compareTo(o.getSimpleName()); 737 if (i == 0) { 738 i = _getRawParamTypes().length - o._getRawParamTypes().length; 739 if (i == 0) { 740 for (int j = 0; j < _getRawParamTypes().length && i == 0; j++) { 741 i = _getRawParamTypes()[j].getName().compareTo(o._getRawParamTypes()[j].getName()); 742 } 743 } 744 } 745 return i; 746 } 747 748 // <FluentSetters> 749 750 @Override /* GENERATED - org.apache.juneau.reflect.ExecutableInfo */ 751 public MethodInfo accessible() { 752 super.accessible(); 753 return this; 754 } 755 756 // </FluentSetters> 757}