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.StringUtils.*; 016 017import java.lang.annotation.*; 018import java.lang.reflect.*; 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.internal.*; 023 024/** 025 * Contains common methods between {@link ConstructorInfo} and {@link MethodInfo}. 026 */ 027public abstract class ExecutableInfo { 028 029 final ClassInfo declaringClass; 030 final Executable e, re; 031 final boolean isConstructor; 032 033 private List<ParamInfo> params; 034 private List<ClassInfo> paramTypes, exceptionInfos; 035 private Class<?>[] rawParamTypes, rawExceptionTypes; 036 private Type[] rawGenericParamTypes; 037 private Parameter[] rawParameters; 038 039 /** 040 * Constructor. 041 * 042 * @param declaringClass The class that declares this method or constructor. 043 * @param e The constructor or method that this info represents. 044 * @param re The "real" constructor if the constructor above is defined against a CGLIB proxy. 045 */ 046 protected ExecutableInfo(ClassInfo declaringClass, Executable e, Executable re) { 047 this.declaringClass = declaringClass; 048 this.e = e; 049 this.re = re == null ? e : re; 050 this.isConstructor = e instanceof Constructor; 051 } 052 053 /** 054 * Returns metadata about the class that declared this method or constructor. 055 * 056 * @return Metadata about the class that declared this method or constructor. 057 */ 058 public final ClassInfo getDeclaringClass() { 059 return declaringClass; 060 } 061 062 /** 063 * Returns <jk>true</jk> if this executable represents a {@link Constructor}. 064 * 065 * @return 066 * <jk>true</jk> if this executable represents a {@link Constructor} and can be cast to {@link ConstructorInfo}. 067 * <jk>false</jk> if this executable represents a {@link Method} and can be cast to {@link MethodInfo}. 068 */ 069 public final boolean isConstructor() { 070 return isConstructor; 071 } 072 073 //----------------------------------------------------------------------------------------------------------------- 074 // Parameters 075 //----------------------------------------------------------------------------------------------------------------- 076 077 /** 078 * Returns the number of parameters in this executable. 079 * 080 * <p> 081 * Same as calling {@link Executable#getParameterCount()}. 082 * 083 * @return The number of parameters in this executable. 084 */ 085 public final int getParamCount() { 086 return e.getParameterCount(); 087 } 088 089 /** 090 * Returns <jk>true</jk> if this executable has at least one parameter. 091 * 092 * <p> 093 * Same as calling {@link Executable#getParameterCount()} and comparing with zero. 094 * 095 * @return <jk>true</jk> if this executable has at least one parameter. 096 */ 097 public final boolean hasParams() { 098 return getParamCount() != 0; 099 } 100 101 /** 102 * Returns <jk>true</jk> if this executable has no parameters. 103 * 104 * <p> 105 * Same as calling {@link Executable#getParameterCount()} and comparing with zero. 106 * 107 * @return <jk>true</jk> if this executable has no parameters. 108 */ 109 public final boolean hasNoParams() { 110 return getParamCount() == 0; 111 } 112 113 /** 114 * Returns <jk>true</jk> if this executable has this number of arguments. 115 * 116 * <p> 117 * Same as calling {@link Executable#getParameterCount()} and comparing the count. 118 * 119 * @param number The number of expected arguments. 120 * @return <jk>true</jk> if this executable has this number of arguments. 121 */ 122 public final boolean hasNumParams(int number) { 123 return getParamCount() == number; 124 } 125 126 /** 127 * Returns the parameters defined on this executable. 128 * 129 * <p> 130 * Same as calling {@link Executable#getParameters()} but wraps the results 131 * 132 * @return An array of parameter information, never <jk>null</jk>. 133 */ 134 public final List<ParamInfo> getParams() { 135 if (params == null) { 136 Parameter[] rp = rawParameters(); 137 List<ParamInfo> l = new ArrayList<>(rp.length); 138 for (int i = 0; i < rp.length; i++) 139 l.add(new ParamInfo(this, rp[i], i)); 140 params = Collections.unmodifiableList(l); 141 } 142 return params; 143 } 144 145 /** 146 * Returns parameter information at the specified index. 147 * 148 * @param index The parameter index. 149 * @return The parameter information, never <jk>null</jk>. 150 */ 151 public final ParamInfo getParam(int index) { 152 checkIndex(index); 153 if (params != null) 154 return params.get(index); 155 return new ParamInfo(this, rawParameters()[index], index); 156 } 157 158 /** 159 * Returns the parameter types on this executable. 160 * 161 * @return The parameter types on this executable. 162 */ 163 public final List<ClassInfo> getParamTypes() { 164 if (paramTypes == null) { 165 Class<?>[] ptc = rawParamTypes(); 166 // Note that due to a bug involving Enum constructors, getGenericParameterTypes() may 167 // always return an empty array. This appears to be fixed in Java 8 b75. 168 Type[] ptt = rawGenericParamTypes(); 169 if (ptt.length != ptc.length) 170 ptt = ptc; 171 List<ClassInfo> l = new ArrayList<>(ptc.length); 172 for (int i = 0; i < ptc.length; i++) 173 l.add(ClassInfo.of(ptc[i], ptt[i])); 174 paramTypes = Collections.unmodifiableList(l); 175 } 176 return paramTypes; 177 } 178 179 /** 180 * Returns the parameter type of the parameter at the specified index. 181 * 182 * @param index The parameter index. 183 * @return The parameter type of the parameter at the specified index. 184 */ 185 public final ClassInfo getParamType(int index) { 186 checkIndex(index); 187 if (paramTypes != null) 188 return getParamTypes().get(index); 189 return ClassInfo.of(getRawParamType(index), getRawGenericParamType(index)); 190 } 191 192 /** 193 * Returns the raw parameter types on this executable. 194 * 195 * @return The raw parameter types on this executable. 196 */ 197 public final Class<?>[] getRawParamTypes() { 198 return rawParamTypes().clone(); 199 } 200 201 /** 202 * Returns the raw parameter type of the parameter at the specified index. 203 * 204 * @param index The parameter index. 205 * @return The raw parameter type of the parameter at the specified index. 206 */ 207 public final Class<?> getRawParamType(int index) { 208 checkIndex(index); 209 return rawParamTypes()[index]; 210 } 211 212 /** 213 * Returns the raw generic parameter types on this executable. 214 * 215 * @return The raw generic parameter types on this executable. 216 */ 217 public final Type[] getRawGenericParamTypes() { 218 return rawGenericParamTypes().clone(); 219 } 220 221 /** 222 * Returns the raw generic parameter type of the parameter at the specified index. 223 * 224 * @param index The parameter index. 225 * @return The raw generic parameter type of the parameter at the specified index. 226 */ 227 public final Type getRawGenericParamType(int index) { 228 checkIndex(index); 229 return rawGenericParamTypes()[index]; 230 } 231 232 /** 233 * Returns an array of raw {@link Parameter} objects that represent all the parameters to the underlying executable represented by this object. 234 * 235 * @return An array of raw {@link Parameter} objects, or an empty array if executable has no parameters. 236 * @see Executable#getParameters() 237 */ 238 public final Parameter[] getRawParameters() { 239 return rawParameters().clone(); 240 } 241 242 /** 243 * Returns the raw {@link Parameter} object that represents the parameter at the specified index. 244 * 245 * @param index The parameter index. 246 * @return The raw {@link Parameter} object that represents the parameter at the specified index. 247 * @see Executable#getParameters() 248 */ 249 public final Parameter getRawParameter(int index) { 250 checkIndex(index); 251 return rawParameters()[index]; 252 } 253 254 Class<?>[] rawParamTypes() { 255 if (rawParamTypes == null) 256 rawParamTypes = e.getParameterTypes(); 257 return rawParamTypes; 258 } 259 260 Type[] rawGenericParamTypes() { 261 if (rawGenericParamTypes == null) 262 rawGenericParamTypes = re.getGenericParameterTypes(); 263 return rawGenericParamTypes; 264 } 265 266 Parameter[] rawParameters() { 267 if (rawParameters == null) 268 rawParameters = re.getParameters(); 269 return rawParameters; 270 } 271 272 private void checkIndex(int index) { 273 int pc = getParamCount(); 274 if (pc == 0) 275 throw new IndexOutOfBoundsException(format("Invalid index ''{0}''. No parameters.", index)); 276 if (index < 0 || index >= pc) 277 throw new IndexOutOfBoundsException(format("Invalid index ''{0}''. Parameter count: {1}", index, pc)); 278 } 279 280 //----------------------------------------------------------------------------------------------------------------- 281 // Annotations 282 //----------------------------------------------------------------------------------------------------------------- 283 284 /** 285 * Returns the parameter annotations on this executable. 286 * 287 * @return The parameter annotations on this executable. 288 */ 289 public final Annotation[][] getParameterAnnotations() { 290 return e.getParameterAnnotations(); 291 } 292 293 /** 294 * Returns the parameter annotations on the parameter at the specified index. 295 * 296 * @param index The parameter index. 297 * @return The parameter annotations on the parameter at the specified index. 298 */ 299 public final Annotation[] getParameterAnnotations(int index) { 300 checkIndex(index); 301 return e.getParameterAnnotations()[index]; 302 } 303 304 //----------------------------------------------------------------------------------------------------------------- 305 // Exceptions 306 //----------------------------------------------------------------------------------------------------------------- 307 308 /** 309 * Returns the exception types on this executable. 310 * 311 * @return The exception types on this executable. 312 */ 313 public final List<ClassInfo> getExceptionTypes() { 314 if (exceptionInfos == null) { 315 Class<?>[] exceptionTypes = rawExceptionTypes(); 316 List<ClassInfo> l = new ArrayList<>(exceptionTypes.length); 317 for (Class<?> et : exceptionTypes) 318 l.add(ClassInfo.of(et)); 319 exceptionInfos = Collections.unmodifiableList(l); 320 } 321 return exceptionInfos; 322 } 323 324 /** 325 * Returns the raw exception types on this executable. 326 * 327 * @return The raw exception types on this executable. 328 */ 329 public final Class<?>[] getRawExceptionTypes() { 330 return rawExceptionTypes().clone(); 331 } 332 333 private Class<?>[] rawExceptionTypes() { 334 if (rawExceptionTypes == null) 335 rawExceptionTypes = e.getExceptionTypes(); 336 return rawExceptionTypes; 337 } 338 339 //----------------------------------------------------------------------------------------------------------------- 340 // Characteristics 341 //----------------------------------------------------------------------------------------------------------------- 342 343 /** 344 * Returns <jk>true</jk> if all specified flags are applicable to this method. 345 * 346 * @param flags The flags to test for. 347 * @return <jk>true</jk> if all specified flags are applicable to this method. 348 */ 349 public final boolean isAll(ReflectFlags...flags) { 350 for (ReflectFlags f : flags) { 351 switch (f) { 352 case DEPRECATED: 353 if (isNotDeprecated()) 354 return false; 355 break; 356 case NOT_DEPRECATED: 357 if (isDeprecated()) 358 return false; 359 break; 360 case HAS_PARAMS: 361 if (hasNoParams()) 362 return false; 363 break; 364 case HAS_NO_PARAMS: 365 if (hasParams()) 366 return false; 367 break; 368 case PUBLIC: 369 if (isNotPublic()) 370 return false; 371 break; 372 case NOT_PUBLIC: 373 if (isPublic()) 374 return false; 375 break; 376 case STATIC: 377 if (isNotStatic()) 378 return false; 379 break; 380 case NOT_STATIC: 381 if (isStatic()) 382 return false; 383 break; 384 case ABSTRACT: 385 if (isNotAbstract()) 386 return false; 387 break; 388 case NOT_ABSTRACT: 389 if (isAbstract()) 390 return false; 391 break; 392 default: 393 throw new RuntimeException("Invalid flag for executable: " + f); 394 } 395 } 396 return true; 397 } 398 399 /** 400 * Returns <jk>true</jk> if all specified flags are applicable to this method. 401 * 402 * @param flags The flags to test for. 403 * @return <jk>true</jk> if all specified flags are applicable to this method. 404 */ 405 public final boolean isAny(ReflectFlags...flags) { 406 for (ReflectFlags f : flags) { 407 switch (f) { 408 case DEPRECATED: 409 if (isDeprecated()) 410 return true; 411 break; 412 case NOT_DEPRECATED: 413 if (isNotDeprecated()) 414 return true; 415 break; 416 case HAS_PARAMS: 417 if (hasParams()) 418 return true; 419 break; 420 case HAS_NO_PARAMS: 421 if (hasNoParams()) 422 return true; 423 break; 424 case PUBLIC: 425 if (isPublic()) 426 return true; 427 break; 428 case NOT_PUBLIC: 429 if (isNotPublic()) 430 return true; 431 break; 432 case STATIC: 433 if (isStatic()) 434 return true; 435 break; 436 case NOT_STATIC: 437 if (isNotStatic()) 438 return true; 439 break; 440 case ABSTRACT: 441 if (isAbstract()) 442 return true; 443 break; 444 case NOT_ABSTRACT: 445 if (isNotAbstract()) 446 return true; 447 break; 448 default: 449 throw new RuntimeException("Invalid flag for executable: " + f); 450 } 451 } 452 return false; 453 } 454 455 /** 456 * Returns <jk>true</jk> if this method has the specified arguments. 457 * 458 * @param args The arguments to test for. 459 * @return <jk>true</jk> if this method has this arguments in the exact order. 460 */ 461 public final boolean hasParamTypes(Class<?>...args) { 462 Class<?>[] pt = rawParamTypes(); 463 if (pt.length == args.length) { 464 for (int i = 0; i < pt.length; i++) 465 if (! pt[i].equals(args[i])) 466 return false; 467 return true; 468 } 469 return false; 470 } 471 472 /** 473 * Returns <jk>true</jk> if this method has the specified arguments. 474 * 475 * @param args The arguments to test for. 476 * @return <jk>true</jk> if this method has this arguments in the exact order. 477 */ 478 public final boolean hasParamTypes(ClassInfo...args) { 479 Class<?>[] pt = rawParamTypes(); 480 if (pt.length == args.length) { 481 for (int i = 0; i < pt.length; i++) 482 if (! pt[i].equals(args[i].inner())) 483 return false; 484 return true; 485 } 486 return false; 487 } 488 489 /** 490 * Returns <jk>true</jk> if this method has the specified argument parent classes. 491 * 492 * @param args The arguments to test for. 493 * @return <jk>true</jk> if this method has this arguments in the exact order. 494 */ 495 public final boolean hasParamTypeParents(Class<?>...args) { 496 Class<?>[] pt = rawParamTypes(); 497 if (pt.length == args.length) { 498 for (int i = 0; i < pt.length; i++) 499 if (! args[i].isAssignableFrom(pt[i])) 500 return false; 501 return true; 502 } 503 return false; 504 } 505 506 /** 507 * Returns <jk>true</jk> if this method has the specified argument parent classes. 508 * 509 * @param args The arguments to test for. 510 * @return <jk>true</jk> if this method has this arguments in the exact order. 511 */ 512 public final boolean hasParamTypeParents(ClassInfo...args) { 513 Class<?>[] pt = rawParamTypes(); 514 if (pt.length == args.length) { 515 for (int i = 0; i < pt.length; i++) 516 if (! args[i].inner().isAssignableFrom(pt[i])) 517 return false; 518 return true; 519 } 520 return false; 521 } 522 523 /** 524 * Returns <jk>true</jk> if this method has at most only this arguments in any order. 525 * 526 * @param args The arguments to test for. 527 * @return <jk>true</jk> if this method has at most only this arguments in any order. 528 */ 529 public final boolean hasFuzzyParamTypes(Class<?>...args) { 530 return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1; 531 } 532 533 /** 534 * Returns <jk>true</jk> if this method has at most only this arguments in any order. 535 * 536 * @param args The arguments to test for. 537 * @return <jk>true</jk> if this method has at most only this arguments in any order. 538 */ 539 public boolean hasFuzzyParamTypes(ClassInfo...args) { 540 return ClassUtils.fuzzyArgsMatch(rawParamTypes(), args) != -1; 541 } 542 543 /** 544 * Returns <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it. 545 * 546 * @return <jk>true</jk> if this method has the {@link Deprecated @Deprecated} annotation on it. 547 */ 548 public final boolean isDeprecated() { 549 return e.isAnnotationPresent(Deprecated.class); 550 551 } 552 553 /** 554 * Returns <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. 555 * 556 * @return <jk>true</jk> if this method doesn't have the {@link Deprecated @Deprecated} annotation on it. 557 */ 558 public final boolean isNotDeprecated() { 559 return ! e.isAnnotationPresent(Deprecated.class); 560 561 } 562 563 /** 564 * Returns <jk>true</jk> if this method is abstract. 565 * 566 * @return <jk>true</jk> if this method is abstract. 567 */ 568 public final boolean isAbstract() { 569 return Modifier.isAbstract(e.getModifiers()); 570 } 571 572 /** 573 * Returns <jk>true</jk> if this method is not abstract. 574 * 575 * @return <jk>true</jk> if this method is not abstract. 576 */ 577 public final boolean isNotAbstract() { 578 return ! Modifier.isAbstract(e.getModifiers()); 579 } 580 581 /** 582 * Returns <jk>true</jk> if this method is public. 583 * 584 * @return <jk>true</jk> if this method is public. 585 */ 586 public final boolean isPublic() { 587 return Modifier.isPublic(e.getModifiers()); 588 } 589 590 /** 591 * Returns <jk>true</jk> if this method is not public. 592 * 593 * @return <jk>true</jk> if this method is not public. 594 */ 595 public final boolean isNotPublic() { 596 return ! Modifier.isPublic(e.getModifiers()); 597 } 598 599 /** 600 * Returns <jk>true</jk> if this method is static. 601 * 602 * @return <jk>true</jk> if this method is static. 603 */ 604 public final boolean isStatic() { 605 return Modifier.isStatic(e.getModifiers()); 606 } 607 608 /** 609 * Returns <jk>true</jk> if this method is not static. 610 * 611 * @return <jk>true</jk> if this method is not static. 612 */ 613 public final boolean isNotStatic() { 614 return ! Modifier.isStatic(e.getModifiers()); 615 } 616 617 //----------------------------------------------------------------------------------------------------------------- 618 // Visibility 619 //----------------------------------------------------------------------------------------------------------------- 620 621 /** 622 * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. 623 * 624 * @return <jk>true</jk> if call was successful. 625 */ 626 public final boolean setAccessible() { 627 try { 628 if (! (e.isAccessible())) 629 e.setAccessible(true); 630 return true; 631 } catch (SecurityException e) { 632 return false; 633 } 634 } 635 636 /** 637 * Identifies if the specified visibility matches this method. 638 * 639 * @param v The visibility to validate against. 640 * @return <jk>true</jk> if this visibility matches the modifier attribute of this method. 641 */ 642 public final boolean isVisible(Visibility v) { 643 return v.isVisible(e); 644 } 645 646 //----------------------------------------------------------------------------------------------------------------- 647 // Labels 648 //----------------------------------------------------------------------------------------------------------------- 649 650 /** 651 * Returns <jk>true</jk> if this method has this name. 652 * 653 * @param name The name to test for. 654 * @return <jk>true</jk> if this method has this name. 655 */ 656 public final boolean hasName(String name) { 657 return getSimpleName().equals(name); 658 } 659 660 /** 661 * Returns <jk>true</jk> if this method has a name in the specified list. 662 * 663 * @param names The names to test for. 664 * @return <jk>true</jk> if this method has one of the names. 665 */ 666 public final boolean hasName(String...names) { 667 for (String n : names) 668 if (getSimpleName().equals(n)) 669 return true; 670 return false; 671 } 672 673 /** 674 * Returns <jk>true</jk> if this method has a name in the specified set. 675 * 676 * @param names The names to test for. 677 * @return <jk>true</jk> if this method has one of the names. 678 */ 679 public final boolean hasName(Set<String> names) { 680 return names.contains(getSimpleName()); 681 } 682 683 //----------------------------------------------------------------------------------------------------------------- 684 // Labels 685 //----------------------------------------------------------------------------------------------------------------- 686 687 /** 688 * Returns the full name of this executable. 689 * 690 * <h5 class='section'>Examples:</h5> 691 * <ul> 692 * <li><js>"com.foo.MyClass.get(java.util.String)"</js> - Method. 693 * <li><js>"com.foo.MyClass(java.util.String)"</js> - Constructor. 694 * </ul> 695 * 696 * @return The underlying executable name. 697 */ 698 public final String getFullName() { 699 StringBuilder sb = new StringBuilder(128); 700 ClassInfo dc = declaringClass; 701 Package p = dc.getPackage(); 702 if (p != null) 703 sb.append(p.getName()).append('.'); 704 dc.appendShortName(sb); 705 if (! isConstructor) 706 sb.append('.').append(getSimpleName()); 707 sb.append('('); 708 List<ClassInfo> pt = getParamTypes(); 709 for (int i = 0; i < pt.size(); i++) { 710 if (i > 0) 711 sb.append(','); 712 pt.get(i).appendFullName(sb); 713 } 714 sb.append(')'); 715 return sb.toString(); 716 } 717 718 /** 719 * Returns the short name of this executable. 720 * 721 * <h5 class='section'>Examples:</h5> 722 * <ul> 723 * <li><js>"MyClass.get(String)"</js> - Method. 724 * <li><js>"MyClass(String)"</js> - Constructor. 725 * </ul> 726 * 727 * @return The underlying executable name. 728 */ 729 public final String getShortName() { 730 StringBuilder sb = new StringBuilder(64); 731 sb.append(getSimpleName()).append('('); 732 Class<?>[] pt = rawParamTypes(); 733 for (int i = 0; i < pt.length; i++) { 734 if (i > 0) 735 sb.append(','); 736 sb.append(pt[i].getSimpleName()); 737 } 738 sb.append(')'); 739 return sb.toString(); 740 } 741 742 /** 743 * Returns the simple name of the underlying method. 744 * 745 * @return The simple name of the underlying method; 746 */ 747 public final String getSimpleName() { 748 return isConstructor ? e.getDeclaringClass().getSimpleName() : e.getName(); 749 } 750 751 @Override 752 public String toString() { 753 return getShortName(); 754 } 755}