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.internal; 014 015import java.lang.annotation.*; 016import java.lang.reflect.*; 017import java.util.*; 018import java.util.concurrent.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.utils.*; 022 023/** 024 * Class-related utility methods. 025 */ 026public final class ClassUtils { 027 028 private static final Map<Class<?>,ConstructorCacheEntry> CONSTRUCTOR_CACHE = new ConcurrentHashMap<>(); 029 private static final Map<Class<?>,Stringify<?>> STRINGIFY_CACHE = new ConcurrentHashMap<>(); 030 031 // Special cases. 032 static { 033 034 // TimeZone doesn't follow any standard conventions. 035 STRINGIFY_CACHE.put(TimeZone.class, new Stringify<TimeZone>(TimeZone.class){ 036 @Override 037 public TimeZone fromString(String s) { 038 return TimeZone.getTimeZone(s); 039 } 040 @Override 041 public String toString(TimeZone tz) { 042 return tz.getID(); 043 } 044 }); 045 046 // Locale(String) doesn't work on strings like "ja_JP". 047 STRINGIFY_CACHE.put(Locale.class, new Stringify<Locale>(Locale.class){ 048 @Override 049 public Locale fromString(String s) { 050 return Locale.forLanguageTag(s.replace('_', '-')); 051 } 052 }); 053 } 054 055 /** 056 * Given the specified list of objects, return readable names for the class types of the objects. 057 * 058 * @param o The objects. 059 * @return An array of readable class type strings. 060 */ 061 public static ObjectList getReadableClassNames(Object[] o) { 062 ObjectList l = new ObjectList(); 063 for (int i = 0; i < o.length; i++) 064 l.add(o[i] == null ? "null" : getReadableClassName(o[i].getClass())); 065 return l; 066 } 067 068 /** 069 * Shortcut for calling <code><jsm>getReadableClassName</jsm>(c.getName())</code> 070 * 071 * @param c The class. 072 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>. 073 */ 074 public static String getReadableClassName(Class<?> c) { 075 if (c == null) 076 return null; 077 return getReadableClassName(c.getName()); 078 } 079 080 /** 081 * Shortcut for calling <code><jsm>getReadableClassName</jsm>(c.getClass().getName())</code> 082 * 083 * @param o The object whose class we want to render. 084 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>. 085 */ 086 public static String getReadableClassNameForObject(Object o) { 087 if (o == null) 088 return null; 089 return getReadableClassName(o.getClass().getName()); 090 } 091 092 /** 093 * Converts the specified class name to a readable form when class name is a special construct like <js>"[[Z"</js>. 094 * 095 * <h5 class='section'>Example:</h5> 096 * <p class='bcode'> 097 * <jsm>getReadableClassName</jsm>(<js>"java.lang.Object"</js>); <jc>// Returns "java.lang.Object"</jc> 098 * <jsm>getReadableClassName</jsm>(<js>"boolean"</js>); <jc>// Returns "boolean"</jc> 099 * <jsm>getReadableClassName</jsm>(<js>"[Z"</js>); <jc>// Returns "boolean[]"</jc> 100 * <jsm>getReadableClassName</jsm>(<js>"[[Z"</js>); <jc>// Returns "boolean[][]"</jc> 101 * <jsm>getReadableClassName</jsm>(<js>"[Ljava.lang.Object;"</js>); <jc>// Returns "java.lang.Object[]"</jc> 102 * <jsm>getReadableClassName</jsm>(<jk>null</jk>); <jc>// Returns null</jc> 103 * </p> 104 * 105 * @param className The class name. 106 * @return A readable class type name, or <jk>null</jk> if parameter is <jk>null</jk>. 107 */ 108 public static String getReadableClassName(String className) { 109 if (className == null) 110 return null; 111 if (! StringUtils.startsWith(className, '[')) 112 return className; 113 int depth = 0; 114 for (int i = 0; i < className.length(); i++) { 115 if (className.charAt(i) == '[') 116 depth++; 117 else 118 break; 119 } 120 char type = className.charAt(depth); 121 String c; 122 switch (type) { 123 case 'Z': c = "boolean"; break; 124 case 'B': c = "byte"; break; 125 case 'C': c = "char"; break; 126 case 'D': c = "double"; break; 127 case 'F': c = "float"; break; 128 case 'I': c = "int"; break; 129 case 'J': c = "long"; break; 130 case 'S': c = "short"; break; 131 default: c = className.substring(depth+1, className.length()-1); 132 } 133 StringBuilder sb = new StringBuilder(c.length() + 2*depth).append(c); 134 for (int i = 0; i < depth; i++) 135 sb.append("[]"); 136 return sb.toString(); 137 } 138 139 /** 140 * Converts the string generated by {@link #getReadableClassName(Class)} back into a {@link Class}. 141 * 142 * <p> 143 * Generics are stripped from the string since they cannot be converted to a class. 144 * 145 * @param cl The classloader to use to load the class. 146 * @param name The readable class name. 147 * @return The class object. 148 * @throws ClassNotFoundException 149 */ 150 public static Class<?> getClassFromReadableName(ClassLoader cl, String name) throws ClassNotFoundException { 151 return cl.loadClass(name); 152 } 153 154 /** 155 * Returns <jk>true</jk> if <code>parent</code> is a parent class of <code>child</code>. 156 * 157 * @param parent The parent class. 158 * @param child The child class. 159 * @param strict If <jk>true</jk> returns <jk>false</jk> if the classes are the same. 160 * @return <jk>true</jk> if <code>parent</code> is a parent class of <code>child</code>. 161 */ 162 public static boolean isParentClass(Class<?> parent, Class<?> child, boolean strict) { 163 return parent.isAssignableFrom(child) && ((!strict) || ! parent.equals(child)); 164 } 165 166 /** 167 * Returns <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>. 168 * 169 * @param parent The parent class. 170 * @param child The child class. 171 * @return <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>. 172 */ 173 public static boolean isParentClass(Class<?> parent, Class<?> child) { 174 return isParentClass(parent, child, false); 175 } 176 177 /** 178 * Returns <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>. 179 * 180 * @param parent The parent class. 181 * @param child The child class. 182 * @return <jk>true</jk> if <code>parent</code> is a parent class or the same as <code>child</code>. 183 */ 184 public static boolean isParentClass(Class<?> parent, Type child) { 185 if (child instanceof Class) 186 return isParentClass(parent, (Class<?>)child); 187 return false; 188 } 189 190 /** 191 * Returns the signature of the specified method. 192 * 193 * <p> 194 * For no-arg methods, the signature will be a simple string such as <js>"toString"</js>. 195 * For methods with one or more args, the arguments will be fully-qualified class names (e.g. 196 * <js>"append(java.util.StringBuilder,boolean)"</js>) 197 * 198 * @param m The methods to get the signature on. 199 * @return The methods signature. 200 */ 201 public static String getMethodSignature(Method m) { 202 StringBuilder sb = new StringBuilder(m.getName()); 203 Class<?>[] pt = m.getParameterTypes(); 204 if (pt.length > 0) { 205 sb.append('('); 206 for (int i = 0; i < pt.length; i++) { 207 if (i > 0) 208 sb.append(','); 209 sb.append(getReadableClassName(pt[i])); 210 } 211 sb.append(')'); 212 } 213 return sb.toString(); 214 } 215 216 private static final Map<Class<?>, Class<?>> 217 pmap1 = new HashMap<>(), 218 pmap2 = new HashMap<>(); 219 static { 220 pmap1.put(boolean.class, Boolean.class); 221 pmap1.put(byte.class, Byte.class); 222 pmap1.put(short.class, Short.class); 223 pmap1.put(char.class, Character.class); 224 pmap1.put(int.class, Integer.class); 225 pmap1.put(long.class, Long.class); 226 pmap1.put(float.class, Float.class); 227 pmap1.put(double.class, Double.class); 228 pmap2.put(Boolean.class, boolean.class); 229 pmap2.put(Byte.class, byte.class); 230 pmap2.put(Short.class, short.class); 231 pmap2.put(Character.class, char.class); 232 pmap2.put(Integer.class, int.class); 233 pmap2.put(Long.class, long.class); 234 pmap2.put(Float.class, float.class); 235 pmap2.put(Double.class, double.class); 236 } 237 238 /** 239 * If the specified class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class 240 * (e.g. <code>Integer.<jk>class</jk></code>). 241 * 242 * @param c The class. 243 * @return The wrapper class, or <jk>null</jk> if class is not a primitive. 244 */ 245 public static Class<?> getPrimitiveWrapper(Class<?> c) { 246 return pmap1.get(c); 247 } 248 249 /** 250 * If the specified class is a primitive wrapper (e.g. <code><jk>Integer</jk>.<jk>class</jk></code>) returns it's 251 * primitive class (e.g. <code>int.<jk>class</jk></code>). 252 * 253 * @param c The class. 254 * @return The primitive class, or <jk>null</jk> if class is not a primitive wrapper. 255 */ 256 public static Class<?> getPrimitiveForWrapper(Class<?> c) { 257 return pmap2.get(c); 258 } 259 260 /** 261 * If the specified class is a primitive (e.g. <code><jk>int</jk>.<jk>class</jk></code>) returns it's wrapper class 262 * (e.g. <code>Integer.<jk>class</jk></code>). 263 * 264 * @param c The class. 265 * @return The wrapper class if it's primitive, or the same class if class is not a primitive. 266 */ 267 public static Class<?> getWrapperIfPrimitive(Class<?> c) { 268 if (! c.isPrimitive()) 269 return c; 270 return pmap1.get(c); 271 } 272 273 /** 274 * Returns <jk>true</jk> if the specified class has the {@link Deprecated @Deprecated} annotation on it. 275 * 276 * @param c The class. 277 * @return <jk>true</jk> if the specified class has the {@link Deprecated @Deprecated} annotation on it. 278 */ 279 public static boolean isNotDeprecated(Class<?> c) { 280 return ! c.isAnnotationPresent(Deprecated.class); 281 } 282 283 /** 284 * Returns <jk>true</jk> if the specified method has the {@link Deprecated @Deprecated} annotation on it. 285 * 286 * @param m The method. 287 * @return <jk>true</jk> if the specified method has the {@link Deprecated @Deprecated} annotation on it. 288 */ 289 public static boolean isNotDeprecated(Method m) { 290 return ! m.isAnnotationPresent(Deprecated.class); 291 292 } 293 294 /** 295 * Returns <jk>true</jk> if the specified constructor has the {@link Deprecated @Deprecated} annotation on it. 296 * 297 * @param c The constructor. 298 * @return <jk>true</jk> if the specified constructor has the {@link Deprecated @Deprecated} annotation on it. 299 */ 300 public static boolean isNotDeprecated(Constructor<?> c) { 301 return ! c.isAnnotationPresent(Deprecated.class); 302 } 303 304 /** 305 * Returns <jk>true</jk> if the specified class is public. 306 * 307 * @param c The class. 308 * @return <jk>true</jk> if the specified class is public. 309 */ 310 public static boolean isPublic(Class<?> c) { 311 return Modifier.isPublic(c.getModifiers()); 312 } 313 314 /** 315 * Returns <jk>true</jk> if the specified class is public. 316 * 317 * @param c The class. 318 * @return <jk>true</jk> if the specified class is public. 319 */ 320 public static boolean isStatic(Class<?> c) { 321 return Modifier.isStatic(c.getModifiers()); 322 } 323 324 /** 325 * Returns <jk>true</jk> if the specified class is abstract. 326 * 327 * @param c The class. 328 * @return <jk>true</jk> if the specified class is abstract. 329 */ 330 public static boolean isAbstract(Class<?> c) { 331 return Modifier.isAbstract(c.getModifiers()); 332 } 333 334 /** 335 * Returns <jk>true</jk> if the specified method is public. 336 * 337 * @param m The method. 338 * @return <jk>true</jk> if the specified method is public. 339 */ 340 public static boolean isPublic(Method m) { 341 return Modifier.isPublic(m.getModifiers()); 342 } 343 344 /** 345 * Returns <jk>true</jk> if the specified method is static. 346 * 347 * @param m The method. 348 * @return <jk>true</jk> if the specified method is static. 349 */ 350 public static boolean isStatic(Method m) { 351 return Modifier.isStatic(m.getModifiers()); 352 } 353 354 /** 355 * Returns <jk>true</jk> if the specified constructor is public. 356 * 357 * @param c The constructor. 358 * @return <jk>true</jk> if the specified constructor is public. 359 */ 360 public static boolean isPublic(Constructor<?> c) { 361 return Modifier.isPublic(c.getModifiers()); 362 } 363 364 /** 365 * Returns the specified annotation on the specified method. 366 * 367 * <p> 368 * Similar to {@link Method#getAnnotation(Class)}, but searches up the parent hierarchy for the annotation 369 * defined on parent classes and interfaces. 370 * 371 * <p> 372 * Normally, annotations defined on methods of parent classes and interfaces are not inherited by the child methods. 373 * This utility method gets around that limitation by searching the class hierarchy for the "same" method 374 * (i.e. the same name and arguments). 375 * 376 * @param a The annotation to search for. 377 * @param m The method to search. 378 * @return The annotation, or <jk>null</jk> if it wasn't found. 379 */ 380 public static <T extends Annotation> T getMethodAnnotation(Class<T> a, Method m) { 381 return getMethodAnnotation(a, m.getDeclaringClass(), m); 382 } 383 384 /** 385 * Returns the specified annotation on the specified method. 386 * 387 * <p> 388 * Similar to {@link Method#getAnnotation(Class)}, but searches up the parent hierarchy for the annotation defined 389 * on parent classes and interfaces. 390 * 391 * <p> 392 * Normally, annotations defined on methods of parent classes and interfaces are not inherited by the child methods. 393 * This utility method gets around that limitation by searching the class hierarchy for the "same" method 394 * (i.e. the same name and arguments). 395 * 396 * @param a The annotation to search for. 397 * @param c 398 * The child class to start searching from. 399 * Note that it can be a descendant class of the actual declaring class of the method passed in. 400 * This allows you to find annotations on methods overridden by the method passed in. 401 * @param method The method to search. 402 * @return The annotation, or <jk>null</jk> if it wasn't found. 403 */ 404 public static <T extends Annotation> T getMethodAnnotation(Class<T> a, Class<?> c, Method method) { 405 for (Method m : c.getDeclaredMethods()) { 406 if (isSameMethod(method, m)) { 407 T t = m.getAnnotation(a); 408 if (t != null) 409 return t; 410 } 411 } 412 Class<?> pc = c.getSuperclass(); 413 if (pc != null) { 414 T t = getMethodAnnotation(a, pc, method); 415 if (t != null) 416 return t; 417 } 418 for (Class<?> ic : c.getInterfaces()) { 419 T t = getMethodAnnotation(a, ic, method); 420 if (t != null) 421 return t; 422 } 423 return null; 424 } 425 426 private static boolean isSameMethod(Method m1, Method m2) { 427 return m1.getName().equals(m2.getName()) && Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes()); 428 } 429 430 /** 431 * Locates the no-arg constructor for the specified class. 432 * 433 * <p> 434 * Constructor must match the visibility requirements specified by parameter 'v'. 435 * If class is abstract, always returns <jk>null</jk>. 436 * Note that this also returns the 1-arg constructor for non-static member classes. 437 * 438 * @param c The class from which to locate the no-arg constructor. 439 * @param v The minimum visibility. 440 * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility. 441 */ 442 @SuppressWarnings({"rawtypes","unchecked"}) 443 public static final <T> Constructor<T> findNoArgConstructor(Class<T> c, Visibility v) { 444 int mod = c.getModifiers(); 445 if (Modifier.isAbstract(mod)) 446 return null; 447 boolean isMemberClass = c.isMemberClass() && ! isStatic(c); 448 for (Constructor cc : c.getConstructors()) { 449 mod = cc.getModifiers(); 450 if (cc.getParameterTypes().length == (isMemberClass ? 1 : 0) && v.isVisible(mod) && isNotDeprecated(cc)) 451 return v.transform(cc); 452 } 453 return null; 454 } 455 456 /** 457 * Finds the real parameter type of the specified class. 458 * 459 * @param c The class containing the parameters (e.g. PojoSwap<T,S>) 460 * @param index The zero-based index of the parameter to resolve. 461 * @param oc The class we're trying to resolve the parameter type for. 462 * @return The resolved real class. 463 */ 464 public static Class<?> resolveParameterType(Class<?> c, int index, Class<?> oc) { 465 466 // We need to make up a mapping of type names. 467 Map<Type,Type> typeMap = new HashMap<>(); 468 while (c != oc.getSuperclass()) { 469 extractTypes(typeMap, oc); 470 oc = oc.getSuperclass(); 471 } 472 473 Type gsc = oc.getGenericSuperclass(); 474 475 // Not actually a parameterized type. 476 if (! (gsc instanceof ParameterizedType)) 477 return Object.class; 478 479 ParameterizedType opt = (ParameterizedType)gsc; 480 Type actualType = opt.getActualTypeArguments()[index]; 481 482 if (typeMap.containsKey(actualType)) 483 actualType = typeMap.get(actualType); 484 485 if (actualType instanceof Class) { 486 return (Class<?>)actualType; 487 488 } else if (actualType instanceof GenericArrayType) { 489 Class<?> cmpntType = (Class<?>)((GenericArrayType)actualType).getGenericComponentType(); 490 return Array.newInstance(cmpntType, 0).getClass(); 491 492 } else if (actualType instanceof TypeVariable) { 493 TypeVariable<?> typeVariable = (TypeVariable<?>)actualType; 494 List<Class<?>> nestedOuterTypes = new LinkedList<>(); 495 for (Class<?> ec = oc.getEnclosingClass(); ec != null; ec = ec.getEnclosingClass()) { 496 try { 497 Class<?> outerClass = oc.getClass(); 498 nestedOuterTypes.add(outerClass); 499 Map<Type,Type> outerTypeMap = new HashMap<>(); 500 extractTypes(outerTypeMap, outerClass); 501 for (Map.Entry<Type,Type> entry : outerTypeMap.entrySet()) { 502 Type key = entry.getKey(), value = entry.getValue(); 503 if (key instanceof TypeVariable) { 504 TypeVariable<?> keyType = (TypeVariable<?>)key; 505 if (keyType.getName().equals(typeVariable.getName()) && isInnerClass(keyType.getGenericDeclaration(), typeVariable.getGenericDeclaration())) { 506 if (value instanceof Class) 507 return (Class<?>)value; 508 typeVariable = (TypeVariable<?>)entry.getValue(); 509 } 510 } 511 } 512 } catch (Exception e) { 513 throw new RuntimeException(e); 514 } 515 } 516 throw new FormattedRuntimeException("Could not resolve type: {0}", actualType); 517 } else { 518 throw new FormattedRuntimeException("Invalid type found in resolveParameterType: {0}", actualType); 519 } 520 } 521 522 private static boolean isInnerClass(GenericDeclaration od, GenericDeclaration id) { 523 if (od instanceof Class && id instanceof Class) { 524 Class<?> oc = (Class<?>)od; 525 Class<?> ic = (Class<?>)id; 526 while ((ic = ic.getEnclosingClass()) != null) 527 if (ic == oc) 528 return true; 529 } 530 return false; 531 } 532 533 private static void extractTypes(Map<Type,Type> typeMap, Class<?> c) { 534 Type gs = c.getGenericSuperclass(); 535 if (gs instanceof ParameterizedType) { 536 ParameterizedType pt = (ParameterizedType)gs; 537 Type[] typeParameters = ((Class<?>)pt.getRawType()).getTypeParameters(); 538 Type[] actualTypeArguments = pt.getActualTypeArguments(); 539 for (int i = 0; i < typeParameters.length; i++) { 540 if (typeMap.containsKey(actualTypeArguments[i])) 541 actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]); 542 typeMap.put(typeParameters[i], actualTypeArguments[i]); 543 } 544 } 545 } 546 547 /** 548 * Finds a public method with the specified parameters. 549 * 550 * @param c The class to look for the method. 551 * @param name The method name. 552 * @param returnType 553 * The return type of the method. 554 * Can be a super type of the actual return type. 555 * For example, if the actual return type is <code>CharSequence</code>, then <code>Object</code> will match but 556 * <code>String</code> will not. 557 * @param argTypes 558 * The argument types of the method. 559 * Can be subtypes of the actual parameter types. 560 * For example, if the parameter type is <code>CharSequence</code>, then <code>String</code> will match but 561 * <code>Object</code> will not. 562 * @return The matched method, or <jk>null</jk> if no match was found. 563 */ 564 public static Method findPublicMethod(Class<?> c, String name, Class<?> returnType, Class<?>...argTypes) { 565 for (Method m : c.getMethods()) { 566 if (isPublic(m) && m.getName().equals(name)) { 567 Class<?> rt = m.getReturnType(); 568 if (isParentClass(returnType, rt) && argsMatch(m.getParameterTypes(), argTypes)) 569 return m; 570 } 571 } 572 return null; 573 } 574 575 /** 576 * Finds a public constructor with the specified parameters without throwing an exception. 577 * 578 * @param c The class to search for a constructor. 579 * @param fuzzyArgs 580 * Use fuzzy-arg matching. 581 * Find a constructor that best matches the specified args. 582 * @param argTypes 583 * The argument types in the constructor. 584 * Can be subtypes of the actual constructor argument types. 585 * @return The matching constructor, or <jk>null</jk> if constructor could not be found. 586 */ 587 public static <T> Constructor<T> findPublicConstructor(Class<T> c, boolean fuzzyArgs, Class<?>...argTypes) { 588 return findConstructor(c, Visibility.PUBLIC, fuzzyArgs, argTypes); 589 } 590 591 /** 592 * Finds a constructor with the specified parameters without throwing an exception. 593 * 594 * @param c The class to search for a constructor. 595 * @param vis The minimum visibility. 596 * @param fuzzyArgs 597 * Use fuzzy-arg matching. 598 * Find a constructor that best matches the specified args. 599 * @param argTypes 600 * The argument types in the constructor. 601 * Can be subtypes of the actual constructor argument types. 602 * @return The matching constructor, or <jk>null</jk> if constructor could not be found. 603 */ 604 @SuppressWarnings("unchecked") 605 public static <T> Constructor<T> findConstructor(Class<T> c, Visibility vis, boolean fuzzyArgs, Class<?>...argTypes) { 606 ConstructorCacheEntry cce = CONSTRUCTOR_CACHE.get(c); 607 if (cce != null && argsMatch(cce.paramTypes, argTypes) && cce.isVisible(vis)) 608 return (Constructor<T>)cce.constructor; 609 610 if (fuzzyArgs) { 611 int bestCount = -1; 612 Constructor<?> bestMatch = null; 613 for (Constructor<?> n : c.getDeclaredConstructors()) { 614 if (vis.isVisible(n)) { 615 int m = fuzzyArgsMatch(n.getParameterTypes(), argTypes); 616 if (m > bestCount) { 617 bestCount = m; 618 bestMatch = n; 619 } 620 } 621 } 622 if (bestCount >= 0) 623 CONSTRUCTOR_CACHE.put(c, new ConstructorCacheEntry(c, bestMatch)); 624 return (Constructor<T>)bestMatch; 625 } 626 627 for (Constructor<?> n : c.getConstructors()) { 628 if (argsMatch(n.getParameterTypes(), argTypes) && vis.isVisible(n)) { 629 CONSTRUCTOR_CACHE.put(c, new ConstructorCacheEntry(c, n)); 630 return (Constructor<T>)n; 631 } 632 } 633 634 return null; 635 } 636 637 638 639 private static final class ConstructorCacheEntry { 640 final Constructor<?> constructor; 641 final Class<?>[] paramTypes; 642 643 ConstructorCacheEntry(Class<?> forClass, Constructor<?> constructor) { 644 this.constructor = constructor; 645 this.paramTypes = constructor.getParameterTypes(); 646 } 647 648 boolean isVisible(Visibility vis) { 649 return vis.isVisible(constructor); 650 } 651 } 652 653 /** 654 * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types. 655 * 656 * @param paramTypes The parameters types specified on a method. 657 * @param argTypes The class types of the arguments being passed to the method. 658 * @return <jk>true</jk> if the arguments match the parameters. 659 */ 660 public static boolean argsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) { 661 if (paramTypes.length == argTypes.length) { 662 for (int i = 0; i < paramTypes.length; i++) 663 if (! isParentClass(paramTypes[i], argTypes[i])) 664 return false; 665 return true; 666 } 667 return false; 668 } 669 670 /** 671 * Returns a number representing the number of arguments that match the specified parameters. 672 * 673 * @param paramTypes The parameters types specified on a method. 674 * @param argTypes The class types of the arguments being passed to the method. 675 * @return The number of matching arguments, or <code>-1</code> a parameter was found that isn't in the list of args. 676 */ 677 public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) { 678 int matches = 0; 679 outer: for (Class<?> p : paramTypes) { 680 p = getWrapperIfPrimitive(p); 681 for (Class<?> a : argTypes) { 682 if (isParentClass(p, a)) { 683 matches++; 684 continue outer; 685 } 686 } 687 return -1; 688 } 689 return matches; 690 } 691 692 /** 693 * Finds the public constructor that can take in the specified arguments. 694 * 695 * @param c The class we're trying to construct. 696 * @param args The arguments we want to pass into the constructor. 697 * @return 698 * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified 699 * arguments. 700 */ 701 public static <T> Constructor<T> findPublicConstructor(Class<T> c, Object...args) { 702 return findPublicConstructor(c, false, getClasses(args)); 703 } 704 705 /** 706 * Finds the public constructor that can take in the specified arguments. 707 * 708 * @param c The class we're trying to construct. 709 * @param args The argument types we want to pass into the constructor. 710 * @return 711 * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified 712 * arguments. 713 */ 714 public static <T> Constructor<T> findPublicConstructor(Class<T> c, Class<?>...args) { 715 return findPublicConstructor(c, false, args); 716 } 717 718 /** 719 * Finds the public constructor that can take in the specified arguments. 720 * 721 * @param c The class we're trying to construct. 722 * @param fuzzyArgs 723 * Use fuzzy-arg matching. 724 * Find a constructor that best matches the specified args. 725 * @param args The arguments we want to pass into the constructor. 726 * @return 727 * The constructor, or <jk>null</jk> if a public constructor could not be found that takes in the specified 728 * arguments. 729 */ 730 public static <T> Constructor<T> findPublicConstructor(Class<T> c, boolean fuzzyArgs, Object...args) { 731 return findPublicConstructor(c, fuzzyArgs, getClasses(args)); 732 } 733 734 /** 735 * Returns the class types for the specified arguments. 736 * 737 * @param args The objects we're getting the classes of. 738 * @return The classes of the arguments. 739 */ 740 public static Class<?>[] getClasses(Object...args) { 741 Class<?>[] pt = new Class<?>[args.length]; 742 for (int i = 0; i < args.length; i++) 743 pt[i] = args[i] == null ? null : args[i].getClass(); 744 return pt; 745 } 746 747 /** 748 * Returns a {@link MethodInfo} bean that describes the specified method. 749 * 750 * @param m The method to describe. 751 * @return The bean with information about the method. 752 */ 753 public static MethodInfo getMethodInfo(Method m) { 754 return new MethodInfo(m); 755 } 756 757 /** 758 * Returns {@link MethodInfo} beans that describe the specified methods. 759 * 760 * @param m The methods to describe. 761 * @return The beans with information about the methods. 762 */ 763 public static MethodInfo[] getMethodInfo(Collection<Method> m) { 764 MethodInfo[] mi = new MethodInfo[m.size()]; 765 int i = 0; 766 for (Method mm : m) 767 mi[i++] = getMethodInfo(mm); 768 return mi; 769 } 770 771 /** 772 * Simple bean that shows the name, parameter types, and return type of a method. 773 */ 774 @SuppressWarnings("javadoc") 775 public static class MethodInfo { 776 public final String methodName; 777 public final String[] parameterTypes; 778 public final String returnType; 779 780 MethodInfo(Method m) { 781 methodName = m.getName(); 782 Type[] pt = m.getGenericParameterTypes(); 783 parameterTypes = new String[pt.length]; 784 for (int i = 0; i < pt.length; i++) 785 parameterTypes[i] = BeanContext.DEFAULT.getClassMeta(pt[i]).toString(); 786 returnType = BeanContext.DEFAULT.getClassMeta(m.getGenericReturnType()).toString(); 787 } 788 } 789 790 /** 791 * Creates an instance of the specified class. 792 * 793 * @param c 794 * The class to cast to. 795 * @param c2 796 * The class to instantiate. 797 * Can also be an instance of the class. 798 * @return 799 * The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface. 800 * @throws 801 * RuntimeException if constructor could not be found or called. 802 */ 803 public static <T> T newInstance(Class<T> c, Object c2) { 804 return newInstanceFromOuter(null, c, c2, false); 805 } 806 807 /** 808 * Creates an instance of the specified class. 809 * 810 * @param c 811 * The class to cast to. 812 * @param c2 813 * The class to instantiate. 814 * Can also be an instance of the class. 815 * @param fuzzyArgs 816 * Use fuzzy constructor arg matching. 817 * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored. 818 * <br>No-arg constructors are also used if no other constructors are found. 819 * @param args 820 * The arguments to pass to the constructor. 821 * @return 822 * The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface. 823 * @throws 824 * RuntimeException if constructor could not be found or called. 825 */ 826 public static <T> T newInstance(Class<T> c, Object c2, boolean fuzzyArgs, Object...args) { 827 return newInstanceFromOuter(null, c, c2, fuzzyArgs, args); 828 } 829 830 /** 831 * Creates an instance of the specified class from within the context of another object. 832 * 833 * @param outer 834 * The outer object. 835 * Can be <jk>null</jk>. 836 * @param c 837 * The class to cast to. 838 * @param c2 839 * The class to instantiate. 840 * Can also be an instance of the class. 841 * @param fuzzyArgs 842 * Use fuzzy constructor arg matching. 843 * <br>When <jk>true</jk>, constructor args can be in any order and extra args are ignored. 844 * <br>No-arg constructors are also used if no other constructors are found. 845 * @param args 846 * The arguments to pass to the constructor. 847 * @return 848 * The new class instance, or <jk>null</jk> if the class was <jk>null</jk> or is abstract or an interface. 849 * @throws 850 * RuntimeException if constructor could not be found or called. 851 */ 852 @SuppressWarnings("unchecked") 853 public static <T> T newInstanceFromOuter(Object outer, Class<T> c, Object c2, boolean fuzzyArgs, Object...args) { 854 if (c2 == null) 855 return null; 856 if (c2 instanceof Class) { 857 try { 858 Class<?> c3 = (Class<?>)c2; 859 if (c3.isInterface() || isAbstract(c3)) 860 return null; 861 862 // First look for an exact match. 863 Constructor<?> con = findPublicConstructor(c3, false, args); 864 if (con != null) 865 return (T)con.newInstance(args); 866 867 // Next look for an exact match including the outer. 868 if (outer != null) { 869 args = new AList<>().append(outer).appendAll(args).toArray(); 870 con = findPublicConstructor(c3, false, args); 871 if (con != null) 872 return (T)con.newInstance(args); 873 } 874 875 // Finally use fuzzy matching. 876 if (fuzzyArgs) { 877 con = findPublicConstructor(c3, true, args); 878 if (con != null) 879 return (T)con.newInstance(getMatchingArgs(con, args)); 880 } 881 882 throw new FormattedRuntimeException("Could not instantiate class {0}/{1}. Constructor not found.", c.getName(), c2); 883 } catch (Exception e) { 884 throw new FormattedRuntimeException(e, "Could not instantiate class {0}", c.getName()); 885 } 886 } else if (isParentClass(c, c2.getClass())) { 887 return (T)c2; 888 } else { 889 throw new FormattedRuntimeException("Object of type {0} found but was expecting {1}.", c2.getClass(), c.getClass()); 890 } 891 } 892 893 private static Object[] getMatchingArgs(Constructor<?> con, Object[] args) { 894 Class<?>[] paramTypes = con.getParameterTypes(); 895 Object[] params = new Object[paramTypes.length]; 896 for (int i = 0; i < paramTypes.length; i++) { 897 Class<?> pt = getWrapperIfPrimitive(paramTypes[i]); 898 for (int j = 0; j < args.length; j++) { 899 if (isParentClass(pt, args[j].getClass())) { 900 params[i] = args[j]; 901 break; 902 } 903 } 904 } 905 return params; 906 } 907 908 /** 909 * Returns all the fields in the specified class and all parent classes. 910 * 911 * <p> 912 * Fields are ordered in either parent-to-child, or child-to-parent order, then alphabetically. 913 * 914 * @param c The class to get all fields on. 915 * @param parentFirst Order them in parent-class-to-child-class order, otherwise child-class-to-parent-class order. 916 * @return An iterable of all fields in the specified class. 917 */ 918 @SuppressWarnings("rawtypes") 919 public static Iterable<Field> getAllFields(final Class c, final boolean parentFirst) { 920 return new Iterable<Field>() { 921 @Override 922 public Iterator<Field> iterator() { 923 return new Iterator<Field>(){ 924 final Iterator<Class<?>> classIterator = getParentClasses(c, parentFirst, false); 925 Field[] fields = classIterator.hasNext() ? sort(classIterator.next().getDeclaredFields()) : new Field[0]; 926 int fIndex = 0; 927 Field next; 928 929 @Override 930 public boolean hasNext() { 931 prime(); 932 return next != null; 933 } 934 935 private void prime() { 936 if (next == null) { 937 while (fIndex >= fields.length) { 938 if (classIterator.hasNext()) { 939 fields = sort(classIterator.next().getDeclaredFields()); 940 fIndex = 0; 941 } else { 942 fIndex = -1; 943 } 944 } 945 if (fIndex != -1) 946 next = fields[fIndex++]; 947 } 948 } 949 950 @Override 951 public Field next() { 952 prime(); 953 Field f = next; 954 next = null; 955 return f; 956 } 957 958 @Override 959 public void remove() { 960 } 961 }; 962 } 963 }; 964 } 965 966 /** 967 * Returns all the methods in the specified class and all parent classes. 968 * 969 * <p> 970 * Methods are ordered in either parent-to-child, or child-to-parent order, then alphabetically. 971 * 972 * @param c The class to get all methods on. 973 * @param parentFirst Order them in parent-class-to-child-class order, otherwise child-class-to-parent-class order. 974 * @return An iterable of all methods in the specified class. 975 */ 976 @SuppressWarnings("rawtypes") 977 public static Iterable<Method> getAllMethods(final Class c, final boolean parentFirst) { 978 return new Iterable<Method>() { 979 @Override 980 public Iterator<Method> iterator() { 981 return new Iterator<Method>(){ 982 final Iterator<Class<?>> classIterator = getParentClasses(c, parentFirst, true); 983 Method[] methods = classIterator.hasNext() ? sort(classIterator.next().getDeclaredMethods()) : new Method[0]; 984 int mIndex = 0; 985 Method next; 986 987 @Override 988 public boolean hasNext() { 989 prime(); 990 return next != null; 991 } 992 993 private void prime() { 994 if (next == null) { 995 while (mIndex >= methods.length) { 996 if (classIterator.hasNext()) { 997 methods = sort(classIterator.next().getDeclaredMethods()); 998 mIndex = 0; 999 } else { 1000 mIndex = -1; 1001 } 1002 } 1003 if (mIndex != -1) 1004 next = methods[mIndex++]; 1005 } 1006 } 1007 1008 @Override 1009 public Method next() { 1010 prime(); 1011 Method m = next; 1012 next = null; 1013 return m; 1014 } 1015 1016 @Override 1017 public void remove() { 1018 } 1019 }; 1020 } 1021 }; 1022 } 1023 1024 private static Comparator<Method> METHOD_COMPARATOR = new Comparator<Method>() { 1025 1026 @Override 1027 public int compare(Method o1, Method o2) { 1028 int i = o1.getName().compareTo(o2.getName()); 1029 if (i == 0) { 1030 i = o1.getParameterTypes().length - o2.getParameterTypes().length; 1031 if (i == 0) { 1032 for (int j = 0; j < o1.getParameterTypes().length && i == 0; j++) { 1033 i = o1.getParameterTypes()[j].getName().compareTo(o2.getParameterTypes()[j].getName()); 1034 } 1035 } 1036 } 1037 return i; 1038 } 1039 }; 1040 1041 /** 1042 * Sorts methods in alphabetical order. 1043 * 1044 * @param m The methods to sort. 1045 * @return The same array, but with elements sorted. 1046 */ 1047 public static Method[] sort(Method[] m) { 1048 Arrays.sort(m, METHOD_COMPARATOR); 1049 return m; 1050 } 1051 1052 private static Comparator<Field> FIELD_COMPARATOR = new Comparator<Field>() { 1053 1054 @Override 1055 public int compare(Field o1, Field o2) { 1056 return o1.getName().compareTo(o2.getName()); 1057 } 1058 }; 1059 1060 /** 1061 * Sorts methods in alphabetical order. 1062 * 1063 * @param m The methods to sort. 1064 * @return The same array, but with elements sorted. 1065 */ 1066 public static Field[] sort(Field[] m) { 1067 Arrays.sort(m, FIELD_COMPARATOR); 1068 return m; 1069 } 1070 1071 /** 1072 * Returns a list of all the parent classes of the specified class including the class itself. 1073 * 1074 * @param c The class to retrieve the parent classes. 1075 * @param parentFirst In parent-to-child order, otherwise child-to-parent. 1076 * @param includeInterfaces Include interfaces. 1077 * @return An iterator of parent classes in the class hierarchy. 1078 */ 1079 public static Iterator<Class<?>> getParentClasses(final Class<?> c, boolean parentFirst, boolean includeInterfaces) { 1080 List<Class<?>> l = getParentClasses(new ArrayList<Class<?>>(), c, parentFirst, includeInterfaces); 1081 return l.iterator(); 1082 } 1083 1084 private static List<Class<?>> getParentClasses(List<Class<?>> l, Class<?> c, boolean parentFirst, boolean includeInterfaces) { 1085 if (parentFirst) { 1086 if (includeInterfaces) 1087 for (Class<?> i : c.getInterfaces()) 1088 l.add(i); 1089 if (c.getSuperclass() != Object.class && c.getSuperclass() != null) 1090 getParentClasses(l, c.getSuperclass(), parentFirst, includeInterfaces); 1091 l.add(c); 1092 } else { 1093 l.add(c); 1094 if (c.getSuperclass() != Object.class && c.getSuperclass() != null) 1095 getParentClasses(l, c.getSuperclass(), parentFirst, includeInterfaces); 1096 if (includeInterfaces) 1097 for (Class<?> i : c.getInterfaces()) 1098 l.add(i); 1099 } 1100 return l; 1101 } 1102 1103 /** 1104 * Returns the default value for the specified primitive class. 1105 * 1106 * @param primitiveClass The primitive class to get the default value for. 1107 * @return The default value, or <jk>null</jk> if the specified class is not a primitive class. 1108 */ 1109 public static Object getPrimitiveDefault(Class<?> primitiveClass) { 1110 return primitiveDefaultMap.get(primitiveClass); 1111 } 1112 1113 private static final Map<Class<?>,Object> primitiveDefaultMap = Collections.unmodifiableMap( 1114 new AMap<Class<?>,Object>() 1115 .append(Boolean.TYPE, false) 1116 .append(Character.TYPE, (char)0) 1117 .append(Short.TYPE, (short)0) 1118 .append(Integer.TYPE, 0) 1119 .append(Long.TYPE, 0l) 1120 .append(Float.TYPE, 0f) 1121 .append(Double.TYPE, 0d) 1122 .append(Byte.TYPE, (byte)0) 1123 .append(Boolean.class, false) 1124 .append(Character.class, (char)0) 1125 .append(Short.class, (short)0) 1126 .append(Integer.class, 0) 1127 .append(Long.class, 0l) 1128 .append(Float.class, 0f) 1129 .append(Double.class, 0d) 1130 .append(Byte.class, (byte)0) 1131 ); 1132 1133 /** 1134 * Returns a readable representation of the specified method. 1135 * 1136 * <p> 1137 * The format of the string is <js>"full-qualified-class.method-name(parameter-simple-class-names)"</js>. 1138 * 1139 * @param m The method to stringify. 1140 * @return The stringified method. 1141 */ 1142 public static String toString(Method m) { 1143 StringBuilder sb = new StringBuilder(m.getDeclaringClass().getName() + "." + m.getName() + "("); 1144 for (int i = 0; i < m.getParameterTypes().length; i++) { 1145 if (i > 0) 1146 sb.append(","); 1147 sb.append(m.getParameterTypes()[i].getSimpleName()); 1148 } 1149 sb.append(")"); 1150 return sb.toString(); 1151 } 1152 1153 /** 1154 * Returns a readable representation of the specified field. 1155 * 1156 * <p> 1157 * The format of the string is <js>"full-qualified-class.field-name"</js>. 1158 * 1159 * @param f The field to stringify. 1160 * @return The stringified field. 1161 */ 1162 public static String toString(Field f) { 1163 return f.getDeclaringClass().getName() + "." + f.getName(); 1164 } 1165 1166 /** 1167 * Throws an {@link IllegalArgumentException} if the parameters on the method are not in the specified list provided. 1168 * 1169 * @param m The method to test. 1170 * @param args The valid class types (exact) for the arguments. 1171 * @throws FormattedIllegalArgumentException If any of the parameters on the method weren't in the list. 1172 */ 1173 public static void assertArgsOfType(Method m, Class<?>...args) throws FormattedIllegalArgumentException { 1174 for (Class<?> c1 : m.getParameterTypes()) { 1175 boolean foundMatch = false; 1176 for (Class<?> c2 : args) 1177 if (c1 == c2) 1178 foundMatch = true; 1179 if (! foundMatch) 1180 throw new FormattedIllegalArgumentException("Invalid argument of type {0} passed in method {1}. Only arguments of type {2} are allowed.", c1, m, args); 1181 } 1182 } 1183 1184 /** 1185 * Finds the public static "fromString" method on the specified class. 1186 * 1187 * <p> 1188 * Looks for the following method names: 1189 * <ul> 1190 * <li><code>fromString</code> 1191 * <li><code>fromValue</code> 1192 * <li><code>valueOf</code> 1193 * <li><code>parse</code> 1194 * <li><code>parseString</code> 1195 * <li><code>forName</code> 1196 * <li><code>forString</code> 1197 * </ul> 1198 * 1199 * @param c The class to find the method on. 1200 * @return The static method, or <jk>null</jk> if it couldn't be found. 1201 */ 1202 public static Method findPublicFromStringMethod(Class<?> c) { 1203 for (String methodName : new String[]{"create","fromString","fromValue","valueOf","parse","parseString","forName","forString"}) { 1204 for (Method m : c.getMethods()) { 1205 if (isStatic(m) && isPublic(m) && isNotDeprecated(m)) { 1206 String mName = m.getName(); 1207 if (mName.equals(methodName) && m.getReturnType() == c) { 1208 Class<?>[] args = m.getParameterTypes(); 1209 if (args.length == 1 && args[0] == String.class) { 1210 return m; 1211 } 1212 } 1213 } 1214 } 1215 } 1216 return null; 1217 } 1218 1219 /** 1220 * Constructs a new instance of the specified class from the specified string. 1221 * 1222 * <p> 1223 * Class must be one of the following: 1224 * <ul> 1225 * <li>Have a public constructor that takes in a single <code>String</code> argument. 1226 * <li>Have a static <code>fromString(String)</code> (or related) method. 1227 * <br>See {@link #findPublicFromStringMethod(Class)} for the list of possible static method names. 1228 * <li>Be an <code>enum</code>. 1229 * </ul> 1230 * 1231 * @param c The class. 1232 * @param s The string to create the instance from. 1233 * @return A new object instance, or <jk>null</jk> if a method for converting the string to an object could not be found. 1234 */ 1235 @SuppressWarnings({ "unchecked" }) 1236 public static <T> T fromString(Class<T> c, String s) { 1237 return (T)getStringify(c).fromString(s); 1238 } 1239 1240 /** 1241 * Converts an object to a string. 1242 * 1243 * <p> 1244 * Normally, this is just going to call <code>toString()</code> on the object. 1245 * However, the {@link Locale} and {@link TimeZone} objects are treated special so that the returned value 1246 * works with the {@link #fromString(Class, String)} method. 1247 * 1248 * @param o The object to convert to a string. 1249 * @return The stringified object, or <jk>null</jk> if the object was <jk>null</jk>. 1250 */ 1251 @SuppressWarnings({ "unchecked" }) 1252 public static String toString(Object o) { 1253 if (o == null) 1254 return null; 1255 return getStringify(o.getClass()).toString(o); 1256 } 1257 1258 @SuppressWarnings({ "unchecked", "rawtypes" }) 1259 private static Stringify getStringify(Class c) { 1260 Stringify fs = STRINGIFY_CACHE.get(c); 1261 if (fs == null) { 1262 for (Iterator<Class<?>> i = getParentClasses(c, false, true); i.hasNext(); ) { 1263 Class c2 = i.next(); 1264 fs = STRINGIFY_CACHE.get(c2); 1265 if (fs != null) { 1266 STRINGIFY_CACHE.put(c, fs); 1267 break; 1268 } 1269 } 1270 if (fs == null) { 1271 fs = new Stringify(c); 1272 STRINGIFY_CACHE.put(c, fs); 1273 } 1274 } 1275 return fs; 1276 } 1277 1278 @SuppressWarnings({"unchecked","rawtypes"}) 1279 private static class Stringify<T> { 1280 final Constructor<?> constructor; 1281 final Method fromStringMethod; 1282 final Class<? extends Enum> enumClass; 1283 1284 Stringify(Class<?> c) { 1285 enumClass = c.isEnum() ? (Class<? extends Enum>)c : null; 1286 fromStringMethod = enumClass != null ? null : findPublicFromStringMethod(c); 1287 constructor = enumClass != null || fromStringMethod != null ? null : findPublicConstructor(c, String.class); 1288 } 1289 1290 public T fromString(String s) { 1291 try { 1292 if (fromStringMethod != null) 1293 return (T)fromStringMethod.invoke(null, s); 1294 if (constructor != null) 1295 return (T)constructor.newInstance(s); 1296 if (enumClass != null) 1297 return (T)Enum.valueOf(enumClass, s); 1298 return null; 1299 } catch (Exception e) { 1300 throw new RuntimeException(e); 1301 } 1302 } 1303 1304 public String toString(T t) { 1305 return t.toString(); 1306 } 1307 } 1308}