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