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.commons.lang3; 018 019import java.lang.reflect.Method; 020import java.lang.reflect.Modifier; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.commons.lang3.mutable.MutableObject; 032 033/** 034 * <p>Operates on classes without using reflection.</p> 035 * 036 * <p>This class handles invalid {@code null} inputs as best it can. 037 * Each method documents its behaviour in more detail.</p> 038 * 039 * <p>The notion of a {@code canonical name} includes the human 040 * readable name for the type, for example {@code int[]}. The 041 * non-canonical method variants work with the JVM names, such as 042 * {@code [I}. </p> 043 * 044 * @since 2.0 045 * @version $Id: ClassUtils.java 1669311 2015-03-26 10:24:19Z britter $ 046 */ 047public class ClassUtils { 048 /** 049 * Inclusivity literals for {@link #hierarchy(Class, Interfaces)}. 050 * @since 3.2 051 */ 052 public enum Interfaces { 053 INCLUDE, EXCLUDE 054 } 055 056 /** 057 * The package separator character: <code>'.' == {@value}</code>. 058 */ 059 public static final char PACKAGE_SEPARATOR_CHAR = '.'; 060 061 /** 062 * The package separator String: <code>"."</code>. 063 */ 064 public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); 065 066 /** 067 * The inner class separator character: <code>'$' == {@value}</code>. 068 */ 069 public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; 070 071 /** 072 * The inner class separator String: {@code "$"}. 073 */ 074 public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); 075 076 /** 077 * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. 078 */ 079 private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>(); 080 static { 081 primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); 082 primitiveWrapperMap.put(Byte.TYPE, Byte.class); 083 primitiveWrapperMap.put(Character.TYPE, Character.class); 084 primitiveWrapperMap.put(Short.TYPE, Short.class); 085 primitiveWrapperMap.put(Integer.TYPE, Integer.class); 086 primitiveWrapperMap.put(Long.TYPE, Long.class); 087 primitiveWrapperMap.put(Double.TYPE, Double.class); 088 primitiveWrapperMap.put(Float.TYPE, Float.class); 089 primitiveWrapperMap.put(Void.TYPE, Void.TYPE); 090 } 091 092 /** 093 * Maps wrapper {@code Class}es to their corresponding primitive types. 094 */ 095 private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>(); 096 static { 097 for (final Class<?> primitiveClass : primitiveWrapperMap.keySet()) { 098 final Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass); 099 if (!primitiveClass.equals(wrapperClass)) { 100 wrapperPrimitiveMap.put(wrapperClass, primitiveClass); 101 } 102 } 103 } 104 105 /** 106 * Maps a primitive class name to its corresponding abbreviation used in array class names. 107 */ 108 private static final Map<String, String> abbreviationMap; 109 110 /** 111 * Maps an abbreviation used in array class names to corresponding primitive class name. 112 */ 113 private static final Map<String, String> reverseAbbreviationMap; 114 115 /** 116 * Feed abbreviation maps 117 */ 118 static { 119 final Map<String, String> m = new HashMap<String, String>(); 120 m.put("int", "I"); 121 m.put("boolean", "Z"); 122 m.put("float", "F"); 123 m.put("long", "J"); 124 m.put("short", "S"); 125 m.put("byte", "B"); 126 m.put("double", "D"); 127 m.put("char", "C"); 128 m.put("void", "V"); 129 final Map<String, String> r = new HashMap<String, String>(); 130 for (final Map.Entry<String, String> e : m.entrySet()) { 131 r.put(e.getValue(), e.getKey()); 132 } 133 abbreviationMap = Collections.unmodifiableMap(m); 134 reverseAbbreviationMap = Collections.unmodifiableMap(r); 135 } 136 137 /** 138 * <p>ClassUtils instances should NOT be constructed in standard programming. 139 * Instead, the class should be used as 140 * {@code ClassUtils.getShortClassName(cls)}.</p> 141 * 142 * <p>This constructor is public to permit tools that require a JavaBean 143 * instance to operate.</p> 144 */ 145 public ClassUtils() { 146 super(); 147 } 148 149 // Short class name 150 // ---------------------------------------------------------------------- 151 /** 152 * <p>Gets the class name minus the package name for an {@code Object}.</p> 153 * 154 * @param object the class to get the short name for, may be null 155 * @param valueIfNull the value to return if null 156 * @return the class name of the object without the package name, or the null value 157 */ 158 public static String getShortClassName(final Object object, final String valueIfNull) { 159 if (object == null) { 160 return valueIfNull; 161 } 162 return getShortClassName(object.getClass()); 163 } 164 165 /** 166 * <p>Gets the class name minus the package name from a {@code Class}.</p> 167 * 168 * <p>Consider using the Java 5 API {@link Class#getSimpleName()} instead. 169 * The one known difference is that this code will return {@code "Map.Entry"} while 170 * the {@code java.lang.Class} variant will simply return {@code "Entry"}. </p> 171 * 172 * @param cls the class to get the short name for. 173 * @return the class name without the package name or an empty string 174 */ 175 public static String getShortClassName(final Class<?> cls) { 176 if (cls == null) { 177 return StringUtils.EMPTY; 178 } 179 return getShortClassName(cls.getName()); 180 } 181 182 /** 183 * <p>Gets the class name minus the package name from a String.</p> 184 * 185 * <p>The string passed in is assumed to be a class name - it is not checked.</p> 186 187 * <p>Note that this method differs from Class.getSimpleName() in that this will 188 * return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply 189 * return {@code "Entry"}. </p> 190 * 191 * @param className the className to get the short name for 192 * @return the class name of the class without the package name or an empty string 193 */ 194 public static String getShortClassName(String className) { 195 if (StringUtils.isEmpty(className)) { 196 return StringUtils.EMPTY; 197 } 198 199 final StringBuilder arrayPrefix = new StringBuilder(); 200 201 // Handle array encoding 202 if (className.startsWith("[")) { 203 while (className.charAt(0) == '[') { 204 className = className.substring(1); 205 arrayPrefix.append("[]"); 206 } 207 // Strip Object type encoding 208 if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { 209 className = className.substring(1, className.length() - 1); 210 } 211 212 if (reverseAbbreviationMap.containsKey(className)) { 213 className = reverseAbbreviationMap.get(className); 214 } 215 } 216 217 final int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); 218 final int innerIdx = className.indexOf( 219 INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); 220 String out = className.substring(lastDotIdx + 1); 221 if (innerIdx != -1) { 222 out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); 223 } 224 return out + arrayPrefix; 225 } 226 227 /** 228 * <p>Null-safe version of <code>aClass.getSimpleName()</code></p> 229 * 230 * @param cls the class for which to get the simple name. 231 * @return the simple class name. 232 * @since 3.0 233 * @see Class#getSimpleName() 234 */ 235 public static String getSimpleName(final Class<?> cls) { 236 if (cls == null) { 237 return StringUtils.EMPTY; 238 } 239 return cls.getSimpleName(); 240 } 241 242 /** 243 * <p>Null-safe version of <code>aClass.getSimpleName()</code></p> 244 * 245 * @param object the object for which to get the simple class name. 246 * @param valueIfNull the value to return if <code>object</code> is <code>null</code> 247 * @return the simple class name. 248 * @since 3.0 249 * @see Class#getSimpleName() 250 */ 251 public static String getSimpleName(final Object object, final String valueIfNull) { 252 if (object == null) { 253 return valueIfNull; 254 } 255 return getSimpleName(object.getClass()); 256 } 257 258 // Package name 259 // ---------------------------------------------------------------------- 260 /** 261 * <p>Gets the package name of an {@code Object}.</p> 262 * 263 * @param object the class to get the package name for, may be null 264 * @param valueIfNull the value to return if null 265 * @return the package name of the object, or the null value 266 */ 267 public static String getPackageName(final Object object, final String valueIfNull) { 268 if (object == null) { 269 return valueIfNull; 270 } 271 return getPackageName(object.getClass()); 272 } 273 274 /** 275 * <p>Gets the package name of a {@code Class}.</p> 276 * 277 * @param cls the class to get the package name for, may be {@code null}. 278 * @return the package name or an empty string 279 */ 280 public static String getPackageName(final Class<?> cls) { 281 if (cls == null) { 282 return StringUtils.EMPTY; 283 } 284 return getPackageName(cls.getName()); 285 } 286 287 /** 288 * <p>Gets the package name from a {@code String}.</p> 289 * 290 * <p>The string passed in is assumed to be a class name - it is not checked.</p> 291 * <p>If the class is unpackaged, return an empty string.</p> 292 * 293 * @param className the className to get the package name for, may be {@code null} 294 * @return the package name or an empty string 295 */ 296 public static String getPackageName(String className) { 297 if (StringUtils.isEmpty(className)) { 298 return StringUtils.EMPTY; 299 } 300 301 // Strip array encoding 302 while (className.charAt(0) == '[') { 303 className = className.substring(1); 304 } 305 // Strip Object type encoding 306 if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { 307 className = className.substring(1); 308 } 309 310 final int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); 311 if (i == -1) { 312 return StringUtils.EMPTY; 313 } 314 return className.substring(0, i); 315 } 316 317 // Abbreviated name 318 // ---------------------------------------------------------------------- 319 /** 320 * <p>Gets the abbreviated name of a {@code Class}.</p> 321 * 322 * @param cls the class to get the abbreviated name for, may be {@code null} 323 * @param len the desired length of the abbreviated name 324 * @return the abbreviated name or an empty string 325 * @throws IllegalArgumentException if len <= 0 326 * @see #getAbbreviatedName(String, int) 327 * @since 3.4 328 */ 329 public static String getAbbreviatedName(final Class<?> cls, int len) { 330 if (cls == null) { 331 return StringUtils.EMPTY; 332 } 333 return getAbbreviatedName(cls.getName(), len); 334 } 335 336 /** 337 * <p>Gets the abbreviated class name from a {@code String}.</p> 338 * 339 * <p>The string passed in is assumed to be a class name - it is not checked.</p> 340 * 341 * <p>The abbreviation algorithm will shorten the class name, usually without 342 * significant loss of meaning.</p> 343 * <p>The abbreviated class name will always include the complete package hierarchy. 344 * If enough space is available, rightmost sub-packages will be displayed in full 345 * length.</p> 346 * 347 * <p>The following table illustrates the algorithm:</p> 348 * <table summary="abbreviation examples"> 349 * <tr><td>className</td><td>len</td><td>return</td></tr> 350 * <tr><td> null</td><td> 1</td><td>""</td></tr> 351 * <tr><td>"java.lang.String"</td><td> 5</td><td>"j.l.String"</td></tr> 352 * <tr><td>"java.lang.String"</td><td>15</td><td>"j.lang.String"</td></tr> 353 * <tr><td>"java.lang.String"</td><td>30</td><td>"java.lang.String"</td></tr> 354 * </table> 355 * @param className the className to get the abbreviated name for, may be {@code null} 356 * @param len the desired length of the abbreviated name 357 * @return the abbreviated name or an empty string 358 * @throws IllegalArgumentException if len <= 0 359 * @since 3.4 360 */ 361 public static String getAbbreviatedName(String className, int len) { 362 if (len <= 0) { 363 throw new IllegalArgumentException("len must be > 0"); 364 } 365 if (className == null) { 366 return StringUtils.EMPTY; 367 } 368 369 int availableSpace = len; 370 int packageLevels = StringUtils.countMatches(className, '.'); 371 String[] output = new String[packageLevels + 1]; 372 int endIndex = className.length() - 1; 373 for (int level = packageLevels; level >= 0; level--) { 374 int startIndex = className.lastIndexOf('.', endIndex); 375 String part = className.substring(startIndex + 1, endIndex + 1); 376 availableSpace -= part.length(); 377 if (level > 0) { 378 // all elements except top level require an additional char space 379 availableSpace--; 380 } 381 if (level == packageLevels) { 382 // ClassName is always complete 383 output[level] = part; 384 } else { 385 if (availableSpace > 0) { 386 output[level] = part; 387 } else { 388 // if no space is left still the first char is used 389 output[level] = part.substring(0, 1); 390 } 391 } 392 endIndex = startIndex - 1; 393 } 394 395 return StringUtils.join(output, '.'); 396 } 397 398 // Superclasses/Superinterfaces 399 // ---------------------------------------------------------------------- 400 /** 401 * <p>Gets a {@code List} of superclasses for the given class.</p> 402 * 403 * @param cls the class to look up, may be {@code null} 404 * @return the {@code List} of superclasses in order going up from this one 405 * {@code null} if null input 406 */ 407 public static List<Class<?>> getAllSuperclasses(final Class<?> cls) { 408 if (cls == null) { 409 return null; 410 } 411 final List<Class<?>> classes = new ArrayList<Class<?>>(); 412 Class<?> superclass = cls.getSuperclass(); 413 while (superclass != null) { 414 classes.add(superclass); 415 superclass = superclass.getSuperclass(); 416 } 417 return classes; 418 } 419 420 /** 421 * <p>Gets a {@code List} of all interfaces implemented by the given 422 * class and its superclasses.</p> 423 * 424 * <p>The order is determined by looking through each interface in turn as 425 * declared in the source file and following its hierarchy up. Then each 426 * superclass is considered in the same way. Later duplicates are ignored, 427 * so the order is maintained.</p> 428 * 429 * @param cls the class to look up, may be {@code null} 430 * @return the {@code List} of interfaces in order, 431 * {@code null} if null input 432 */ 433 public static List<Class<?>> getAllInterfaces(final Class<?> cls) { 434 if (cls == null) { 435 return null; 436 } 437 438 final LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>(); 439 getAllInterfaces(cls, interfacesFound); 440 441 return new ArrayList<Class<?>>(interfacesFound); 442 } 443 444 /** 445 * Get the interfaces for the specified class. 446 * 447 * @param cls the class to look up, may be {@code null} 448 * @param interfacesFound the {@code Set} of interfaces for the class 449 */ 450 private static void getAllInterfaces(Class<?> cls, final HashSet<Class<?>> interfacesFound) { 451 while (cls != null) { 452 final Class<?>[] interfaces = cls.getInterfaces(); 453 454 for (final Class<?> i : interfaces) { 455 if (interfacesFound.add(i)) { 456 getAllInterfaces(i, interfacesFound); 457 } 458 } 459 460 cls = cls.getSuperclass(); 461 } 462 } 463 464 // Convert list 465 // ---------------------------------------------------------------------- 466 /** 467 * <p>Given a {@code List} of class names, this method converts them into classes.</p> 468 * 469 * <p>A new {@code List} is returned. If the class name cannot be found, {@code null} 470 * is stored in the {@code List}. If the class name in the {@code List} is 471 * {@code null}, {@code null} is stored in the output {@code List}.</p> 472 * 473 * @param classNames the classNames to change 474 * @return a {@code List} of Class objects corresponding to the class names, 475 * {@code null} if null input 476 * @throws ClassCastException if classNames contains a non String entry 477 */ 478 public static List<Class<?>> convertClassNamesToClasses(final List<String> classNames) { 479 if (classNames == null) { 480 return null; 481 } 482 final List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size()); 483 for (final String className : classNames) { 484 try { 485 classes.add(Class.forName(className)); 486 } catch (final Exception ex) { 487 classes.add(null); 488 } 489 } 490 return classes; 491 } 492 493 /** 494 * <p>Given a {@code List} of {@code Class} objects, this method converts 495 * them into class names.</p> 496 * 497 * <p>A new {@code List} is returned. {@code null} objects will be copied into 498 * the returned list as {@code null}.</p> 499 * 500 * @param classes the classes to change 501 * @return a {@code List} of class names corresponding to the Class objects, 502 * {@code null} if null input 503 * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry 504 */ 505 public static List<String> convertClassesToClassNames(final List<Class<?>> classes) { 506 if (classes == null) { 507 return null; 508 } 509 final List<String> classNames = new ArrayList<String>(classes.size()); 510 for (final Class<?> cls : classes) { 511 if (cls == null) { 512 classNames.add(null); 513 } else { 514 classNames.add(cls.getName()); 515 } 516 } 517 return classNames; 518 } 519 520 // Is assignable 521 // ---------------------------------------------------------------------- 522 /** 523 * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> 524 * 525 * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each 526 * Class pair in the input arrays. It can be used to check if a set of arguments 527 * (the first parameter) are suitably compatible with a set of method parameter types 528 * (the second parameter).</p> 529 * 530 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this 531 * method takes into account widenings of primitive classes and 532 * {@code null}s.</p> 533 * 534 * <p>Primitive widenings allow an int to be assigned to a {@code long}, 535 * {@code float} or {@code double}. This method returns the correct 536 * result for these cases.</p> 537 * 538 * <p>{@code Null} may be assigned to any reference type. This method will 539 * return {@code true} if {@code null} is passed in and the toClass is 540 * non-primitive.</p> 541 * 542 * <p>Specifically, this method tests whether the type represented by the 543 * specified {@code Class} parameter can be converted to the type 544 * represented by this {@code Class} object via an identity conversion 545 * widening primitive or widening reference conversion. See 546 * <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>, 547 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 548 * 549 * <p><strong>Since Lang 3.0,</strong> this method will default behavior for 550 * calculating assignability between primitive and wrapper types <em>corresponding 551 * to the running Java version</em>; i.e. autoboxing will be the default 552 * behavior in VMs running Java versions > 1.5.</p> 553 * 554 * @param classArray the array of Classes to check, may be {@code null} 555 * @param toClassArray the array of Classes to try to assign into, may be {@code null} 556 * @return {@code true} if assignment possible 557 */ 558 public static boolean isAssignable(final Class<?>[] classArray, final Class<?>... toClassArray) { 559 return isAssignable(classArray, toClassArray, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); 560 } 561 562 /** 563 * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> 564 * 565 * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each 566 * Class pair in the input arrays. It can be used to check if a set of arguments 567 * (the first parameter) are suitably compatible with a set of method parameter types 568 * (the second parameter).</p> 569 * 570 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this 571 * method takes into account widenings of primitive classes and 572 * {@code null}s.</p> 573 * 574 * <p>Primitive widenings allow an int to be assigned to a {@code long}, 575 * {@code float} or {@code double}. This method returns the correct 576 * result for these cases.</p> 577 * 578 * <p>{@code Null} may be assigned to any reference type. This method will 579 * return {@code true} if {@code null} is passed in and the toClass is 580 * non-primitive.</p> 581 * 582 * <p>Specifically, this method tests whether the type represented by the 583 * specified {@code Class} parameter can be converted to the type 584 * represented by this {@code Class} object via an identity conversion 585 * widening primitive or widening reference conversion. See 586 * <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>, 587 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 588 * 589 * @param classArray the array of Classes to check, may be {@code null} 590 * @param toClassArray the array of Classes to try to assign into, may be {@code null} 591 * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers 592 * @return {@code true} if assignment possible 593 */ 594 public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray, final boolean autoboxing) { 595 if (ArrayUtils.isSameLength(classArray, toClassArray) == false) { 596 return false; 597 } 598 if (classArray == null) { 599 classArray = ArrayUtils.EMPTY_CLASS_ARRAY; 600 } 601 if (toClassArray == null) { 602 toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; 603 } 604 for (int i = 0; i < classArray.length; i++) { 605 if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { 606 return false; 607 } 608 } 609 return true; 610 } 611 612 /** 613 * Returns whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, 614 * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). 615 * 616 * @param type 617 * The class to query or null. 618 * @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, 619 * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). 620 * @since 3.1 621 */ 622 public static boolean isPrimitiveOrWrapper(final Class<?> type) { 623 if (type == null) { 624 return false; 625 } 626 return type.isPrimitive() || isPrimitiveWrapper(type); 627 } 628 629 /** 630 * Returns whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, 631 * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). 632 * 633 * @param type 634 * The class to query or null. 635 * @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, 636 * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). 637 * @since 3.1 638 */ 639 public static boolean isPrimitiveWrapper(final Class<?> type) { 640 return wrapperPrimitiveMap.containsKey(type); 641 } 642 643 /** 644 * <p>Checks if one {@code Class} can be assigned to a variable of 645 * another {@code Class}.</p> 646 * 647 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, 648 * this method takes into account widenings of primitive classes and 649 * {@code null}s.</p> 650 * 651 * <p>Primitive widenings allow an int to be assigned to a long, float or 652 * double. This method returns the correct result for these cases.</p> 653 * 654 * <p>{@code Null} may be assigned to any reference type. This method 655 * will return {@code true} if {@code null} is passed in and the 656 * toClass is non-primitive.</p> 657 * 658 * <p>Specifically, this method tests whether the type represented by the 659 * specified {@code Class} parameter can be converted to the type 660 * represented by this {@code Class} object via an identity conversion 661 * widening primitive or widening reference conversion. See 662 * <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>, 663 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 664 * 665 * <p><strong>Since Lang 3.0,</strong> this method will default behavior for 666 * calculating assignability between primitive and wrapper types <em>corresponding 667 * to the running Java version</em>; i.e. autoboxing will be the default 668 * behavior in VMs running Java versions > 1.5.</p> 669 * 670 * @param cls the Class to check, may be null 671 * @param toClass the Class to try to assign into, returns false if null 672 * @return {@code true} if assignment possible 673 */ 674 public static boolean isAssignable(final Class<?> cls, final Class<?> toClass) { 675 return isAssignable(cls, toClass, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); 676 } 677 678 /** 679 * <p>Checks if one {@code Class} can be assigned to a variable of 680 * another {@code Class}.</p> 681 * 682 * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, 683 * this method takes into account widenings of primitive classes and 684 * {@code null}s.</p> 685 * 686 * <p>Primitive widenings allow an int to be assigned to a long, float or 687 * double. This method returns the correct result for these cases.</p> 688 * 689 * <p>{@code Null} may be assigned to any reference type. This method 690 * will return {@code true} if {@code null} is passed in and the 691 * toClass is non-primitive.</p> 692 * 693 * <p>Specifically, this method tests whether the type represented by the 694 * specified {@code Class} parameter can be converted to the type 695 * represented by this {@code Class} object via an identity conversion 696 * widening primitive or widening reference conversion. See 697 * <em><a href="http://docs.oracle.com/javase/specs/">The Java Language Specification</a></em>, 698 * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> 699 * 700 * @param cls the Class to check, may be null 701 * @param toClass the Class to try to assign into, returns false if null 702 * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers 703 * @return {@code true} if assignment possible 704 */ 705 public static boolean isAssignable(Class<?> cls, final Class<?> toClass, final boolean autoboxing) { 706 if (toClass == null) { 707 return false; 708 } 709 // have to check for null, as isAssignableFrom doesn't 710 if (cls == null) { 711 return !toClass.isPrimitive(); 712 } 713 //autoboxing: 714 if (autoboxing) { 715 if (cls.isPrimitive() && !toClass.isPrimitive()) { 716 cls = primitiveToWrapper(cls); 717 if (cls == null) { 718 return false; 719 } 720 } 721 if (toClass.isPrimitive() && !cls.isPrimitive()) { 722 cls = wrapperToPrimitive(cls); 723 if (cls == null) { 724 return false; 725 } 726 } 727 } 728 if (cls.equals(toClass)) { 729 return true; 730 } 731 if (cls.isPrimitive()) { 732 if (toClass.isPrimitive() == false) { 733 return false; 734 } 735 if (Integer.TYPE.equals(cls)) { 736 return Long.TYPE.equals(toClass) 737 || Float.TYPE.equals(toClass) 738 || Double.TYPE.equals(toClass); 739 } 740 if (Long.TYPE.equals(cls)) { 741 return Float.TYPE.equals(toClass) 742 || Double.TYPE.equals(toClass); 743 } 744 if (Boolean.TYPE.equals(cls)) { 745 return false; 746 } 747 if (Double.TYPE.equals(cls)) { 748 return false; 749 } 750 if (Float.TYPE.equals(cls)) { 751 return Double.TYPE.equals(toClass); 752 } 753 if (Character.TYPE.equals(cls)) { 754 return Integer.TYPE.equals(toClass) 755 || Long.TYPE.equals(toClass) 756 || Float.TYPE.equals(toClass) 757 || Double.TYPE.equals(toClass); 758 } 759 if (Short.TYPE.equals(cls)) { 760 return Integer.TYPE.equals(toClass) 761 || Long.TYPE.equals(toClass) 762 || Float.TYPE.equals(toClass) 763 || Double.TYPE.equals(toClass); 764 } 765 if (Byte.TYPE.equals(cls)) { 766 return Short.TYPE.equals(toClass) 767 || Integer.TYPE.equals(toClass) 768 || Long.TYPE.equals(toClass) 769 || Float.TYPE.equals(toClass) 770 || Double.TYPE.equals(toClass); 771 } 772 // should never get here 773 return false; 774 } 775 return toClass.isAssignableFrom(cls); 776 } 777 778 /** 779 * <p>Converts the specified primitive Class object to its corresponding 780 * wrapper Class object.</p> 781 * 782 * <p>NOTE: From v2.2, this method handles {@code Void.TYPE}, 783 * returning {@code Void.TYPE}.</p> 784 * 785 * @param cls the class to convert, may be null 786 * @return the wrapper class for {@code cls} or {@code cls} if 787 * {@code cls} is not a primitive. {@code null} if null input. 788 * @since 2.1 789 */ 790 public static Class<?> primitiveToWrapper(final Class<?> cls) { 791 Class<?> convertedClass = cls; 792 if (cls != null && cls.isPrimitive()) { 793 convertedClass = primitiveWrapperMap.get(cls); 794 } 795 return convertedClass; 796 } 797 798 /** 799 * <p>Converts the specified array of primitive Class objects to an array of 800 * its corresponding wrapper Class objects.</p> 801 * 802 * @param classes the class array to convert, may be null or empty 803 * @return an array which contains for each given class, the wrapper class or 804 * the original class if class is not a primitive. {@code null} if null input. 805 * Empty array if an empty array passed in. 806 * @since 2.1 807 */ 808 public static Class<?>[] primitivesToWrappers(final Class<?>... classes) { 809 if (classes == null) { 810 return null; 811 } 812 813 if (classes.length == 0) { 814 return classes; 815 } 816 817 final Class<?>[] convertedClasses = new Class[classes.length]; 818 for (int i = 0; i < classes.length; i++) { 819 convertedClasses[i] = primitiveToWrapper(classes[i]); 820 } 821 return convertedClasses; 822 } 823 824 /** 825 * <p>Converts the specified wrapper class to its corresponding primitive 826 * class.</p> 827 * 828 * <p>This method is the counter part of {@code primitiveToWrapper()}. 829 * If the passed in class is a wrapper class for a primitive type, this 830 * primitive type will be returned (e.g. {@code Integer.TYPE} for 831 * {@code Integer.class}). For other classes, or if the parameter is 832 * <b>null</b>, the return value is <b>null</b>.</p> 833 * 834 * @param cls the class to convert, may be <b>null</b> 835 * @return the corresponding primitive type if {@code cls} is a 836 * wrapper class, <b>null</b> otherwise 837 * @see #primitiveToWrapper(Class) 838 * @since 2.4 839 */ 840 public static Class<?> wrapperToPrimitive(final Class<?> cls) { 841 return wrapperPrimitiveMap.get(cls); 842 } 843 844 /** 845 * <p>Converts the specified array of wrapper Class objects to an array of 846 * its corresponding primitive Class objects.</p> 847 * 848 * <p>This method invokes {@code wrapperToPrimitive()} for each element 849 * of the passed in array.</p> 850 * 851 * @param classes the class array to convert, may be null or empty 852 * @return an array which contains for each given class, the primitive class or 853 * <b>null</b> if the original class is not a wrapper class. {@code null} if null input. 854 * Empty array if an empty array passed in. 855 * @see #wrapperToPrimitive(Class) 856 * @since 2.4 857 */ 858 public static Class<?>[] wrappersToPrimitives(final Class<?>... classes) { 859 if (classes == null) { 860 return null; 861 } 862 863 if (classes.length == 0) { 864 return classes; 865 } 866 867 final Class<?>[] convertedClasses = new Class[classes.length]; 868 for (int i = 0; i < classes.length; i++) { 869 convertedClasses[i] = wrapperToPrimitive(classes[i]); 870 } 871 return convertedClasses; 872 } 873 874 // Inner class 875 // ---------------------------------------------------------------------- 876 /** 877 * <p>Is the specified class an inner class or static nested class.</p> 878 * 879 * @param cls the class to check, may be null 880 * @return {@code true} if the class is an inner or static nested class, 881 * false if not or {@code null} 882 */ 883 public static boolean isInnerClass(final Class<?> cls) { 884 return cls != null && cls.getEnclosingClass() != null; 885 } 886 887 // Class loading 888 // ---------------------------------------------------------------------- 889 /** 890 * Returns the class represented by {@code className} using the 891 * {@code classLoader}. This implementation supports the syntaxes 892 * "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", 893 * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". 894 * 895 * @param classLoader the class loader to use to load the class 896 * @param className the class name 897 * @param initialize whether the class must be initialized 898 * @return the class represented by {@code className} using the {@code classLoader} 899 * @throws ClassNotFoundException if the class is not found 900 */ 901 public static Class<?> getClass( 902 final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { 903 try { 904 Class<?> clazz; 905 if (abbreviationMap.containsKey(className)) { 906 final String clsName = "[" + abbreviationMap.get(className); 907 clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); 908 } else { 909 clazz = Class.forName(toCanonicalName(className), initialize, classLoader); 910 } 911 return clazz; 912 } catch (final ClassNotFoundException ex) { 913 // allow path separators (.) as inner class name separators 914 final int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); 915 916 if (lastDotIndex != -1) { 917 try { 918 return getClass(classLoader, className.substring(0, lastDotIndex) + 919 INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), 920 initialize); 921 } catch (final ClassNotFoundException ex2) { // NOPMD 922 // ignore exception 923 } 924 } 925 926 throw ex; 927 } 928 } 929 930 /** 931 * Returns the (initialized) class represented by {@code className} 932 * using the {@code classLoader}. This implementation supports 933 * the syntaxes "{@code java.util.Map.Entry[]}", 934 * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", 935 * and "{@code [Ljava.util.Map$Entry;}". 936 * 937 * @param classLoader the class loader to use to load the class 938 * @param className the class name 939 * @return the class represented by {@code className} using the {@code classLoader} 940 * @throws ClassNotFoundException if the class is not found 941 */ 942 public static Class<?> getClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException { 943 return getClass(classLoader, className, true); 944 } 945 946 /** 947 * Returns the (initialized) class represented by {@code className} 948 * using the current thread's context class loader. This implementation 949 * supports the syntaxes "{@code java.util.Map.Entry[]}", 950 * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", 951 * and "{@code [Ljava.util.Map$Entry;}". 952 * 953 * @param className the class name 954 * @return the class represented by {@code className} using the current thread's context class loader 955 * @throws ClassNotFoundException if the class is not found 956 */ 957 public static Class<?> getClass(final String className) throws ClassNotFoundException { 958 return getClass(className, true); 959 } 960 961 /** 962 * Returns the class represented by {@code className} using the 963 * current thread's context class loader. This implementation supports the 964 * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", 965 * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". 966 * 967 * @param className the class name 968 * @param initialize whether the class must be initialized 969 * @return the class represented by {@code className} using the current thread's context class loader 970 * @throws ClassNotFoundException if the class is not found 971 */ 972 public static Class<?> getClass(final String className, final boolean initialize) throws ClassNotFoundException { 973 final ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); 974 final ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; 975 return getClass(loader, className, initialize); 976 } 977 978 // Public method 979 // ---------------------------------------------------------------------- 980 /** 981 * <p>Returns the desired Method much like {@code Class.getMethod}, however 982 * it ensures that the returned Method is from a public class or interface and not 983 * from an anonymous inner class. This means that the Method is invokable and 984 * doesn't fall foul of Java bug 985 * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).</p> 986 * 987 * <pre> 988 * <code>Set set = Collections.unmodifiableSet(...); 989 * Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]); 990 * Object result = method.invoke(set, new Object[]);</code> 991 * </pre> 992 * 993 * @param cls the class to check, not null 994 * @param methodName the name of the method 995 * @param parameterTypes the list of parameters 996 * @return the method 997 * @throws NullPointerException if the class is null 998 * @throws SecurityException if a security violation occurred 999 * @throws NoSuchMethodException if the method is not found in the given class 1000 * or if the method doesn't conform with the requirements 1001 */ 1002 public static Method getPublicMethod(final Class<?> cls, final String methodName, final Class<?>... parameterTypes) 1003 throws SecurityException, NoSuchMethodException { 1004 1005 final Method declaredMethod = cls.getMethod(methodName, parameterTypes); 1006 if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { 1007 return declaredMethod; 1008 } 1009 1010 final List<Class<?>> candidateClasses = new ArrayList<Class<?>>(); 1011 candidateClasses.addAll(getAllInterfaces(cls)); 1012 candidateClasses.addAll(getAllSuperclasses(cls)); 1013 1014 for (final Class<?> candidateClass : candidateClasses) { 1015 if (!Modifier.isPublic(candidateClass.getModifiers())) { 1016 continue; 1017 } 1018 Method candidateMethod; 1019 try { 1020 candidateMethod = candidateClass.getMethod(methodName, parameterTypes); 1021 } catch (final NoSuchMethodException ex) { 1022 continue; 1023 } 1024 if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { 1025 return candidateMethod; 1026 } 1027 } 1028 1029 throw new NoSuchMethodException("Can't find a public method for " + 1030 methodName + " " + ArrayUtils.toString(parameterTypes)); 1031 } 1032 1033 // ---------------------------------------------------------------------- 1034 /** 1035 * Converts a class name to a JLS style class name. 1036 * 1037 * @param className the class name 1038 * @return the converted name 1039 */ 1040 private static String toCanonicalName(String className) { 1041 className = StringUtils.deleteWhitespace(className); 1042 if (className == null) { 1043 throw new NullPointerException("className must not be null."); 1044 } else if (className.endsWith("[]")) { 1045 final StringBuilder classNameBuffer = new StringBuilder(); 1046 while (className.endsWith("[]")) { 1047 className = className.substring(0, className.length() - 2); 1048 classNameBuffer.append("["); 1049 } 1050 final String abbreviation = abbreviationMap.get(className); 1051 if (abbreviation != null) { 1052 classNameBuffer.append(abbreviation); 1053 } else { 1054 classNameBuffer.append("L").append(className).append(";"); 1055 } 1056 className = classNameBuffer.toString(); 1057 } 1058 return className; 1059 } 1060 1061 /** 1062 * <p>Converts an array of {@code Object} in to an array of {@code Class} objects. 1063 * If any of these objects is null, a null element will be inserted into the array.</p> 1064 * 1065 * <p>This method returns {@code null} for a {@code null} input array.</p> 1066 * 1067 * @param array an {@code Object} array 1068 * @return a {@code Class} array, {@code null} if null array input 1069 * @since 2.4 1070 */ 1071 public static Class<?>[] toClass(final Object... array) { 1072 if (array == null) { 1073 return null; 1074 } else if (array.length == 0) { 1075 return ArrayUtils.EMPTY_CLASS_ARRAY; 1076 } 1077 final Class<?>[] classes = new Class[array.length]; 1078 for (int i = 0; i < array.length; i++) { 1079 classes[i] = array[i] == null ? null : array[i].getClass(); 1080 } 1081 return classes; 1082 } 1083 1084 // Short canonical name 1085 // ---------------------------------------------------------------------- 1086 /** 1087 * <p>Gets the canonical name minus the package name for an {@code Object}.</p> 1088 * 1089 * @param object the class to get the short name for, may be null 1090 * @param valueIfNull the value to return if null 1091 * @return the canonical name of the object without the package name, or the null value 1092 * @since 2.4 1093 */ 1094 public static String getShortCanonicalName(final Object object, final String valueIfNull) { 1095 if (object == null) { 1096 return valueIfNull; 1097 } 1098 return getShortCanonicalName(object.getClass().getName()); 1099 } 1100 1101 /** 1102 * <p>Gets the canonical name minus the package name from a {@code Class}.</p> 1103 * 1104 * @param cls the class to get the short name for. 1105 * @return the canonical name without the package name or an empty string 1106 * @since 2.4 1107 */ 1108 public static String getShortCanonicalName(final Class<?> cls) { 1109 if (cls == null) { 1110 return StringUtils.EMPTY; 1111 } 1112 return getShortCanonicalName(cls.getName()); 1113 } 1114 1115 /** 1116 * <p>Gets the canonical name minus the package name from a String.</p> 1117 * 1118 * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> 1119 * 1120 * @param canonicalName the class name to get the short name for 1121 * @return the canonical name of the class without the package name or an empty string 1122 * @since 2.4 1123 */ 1124 public static String getShortCanonicalName(final String canonicalName) { 1125 return ClassUtils.getShortClassName(getCanonicalName(canonicalName)); 1126 } 1127 1128 // Package name 1129 // ---------------------------------------------------------------------- 1130 /** 1131 * <p>Gets the package name from the canonical name of an {@code Object}.</p> 1132 * 1133 * @param object the class to get the package name for, may be null 1134 * @param valueIfNull the value to return if null 1135 * @return the package name of the object, or the null value 1136 * @since 2.4 1137 */ 1138 public static String getPackageCanonicalName(final Object object, final String valueIfNull) { 1139 if (object == null) { 1140 return valueIfNull; 1141 } 1142 return getPackageCanonicalName(object.getClass().getName()); 1143 } 1144 1145 /** 1146 * <p>Gets the package name from the canonical name of a {@code Class}.</p> 1147 * 1148 * @param cls the class to get the package name for, may be {@code null}. 1149 * @return the package name or an empty string 1150 * @since 2.4 1151 */ 1152 public static String getPackageCanonicalName(final Class<?> cls) { 1153 if (cls == null) { 1154 return StringUtils.EMPTY; 1155 } 1156 return getPackageCanonicalName(cls.getName()); 1157 } 1158 1159 /** 1160 * <p>Gets the package name from the canonical name. </p> 1161 * 1162 * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> 1163 * <p>If the class is unpackaged, return an empty string.</p> 1164 * 1165 * @param canonicalName the canonical name to get the package name for, may be {@code null} 1166 * @return the package name or an empty string 1167 * @since 2.4 1168 */ 1169 public static String getPackageCanonicalName(final String canonicalName) { 1170 return ClassUtils.getPackageName(getCanonicalName(canonicalName)); 1171 } 1172 1173 /** 1174 * <p>Converts a given name of class into canonical format. 1175 * If name of class is not a name of array class it returns 1176 * unchanged name.</p> 1177 * <p>Example: 1178 * <ul> 1179 * <li>{@code getCanonicalName("[I") = "int[]"}</li> 1180 * <li>{@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}</li> 1181 * <li>{@code getCanonicalName("java.lang.String") = "java.lang.String"}</li> 1182 * </ul> 1183 * </p> 1184 * 1185 * @param className the name of class 1186 * @return canonical form of class name 1187 * @since 2.4 1188 */ 1189 private static String getCanonicalName(String className) { 1190 className = StringUtils.deleteWhitespace(className); 1191 if (className == null) { 1192 return null; 1193 } 1194 int dim = 0; 1195 while (className.startsWith("[")) { 1196 dim++; 1197 className = className.substring(1); 1198 } 1199 if (dim < 1) { 1200 return className; 1201 } 1202 if (className.startsWith("L")) { 1203 className = className.substring( 1204 1, 1205 className.endsWith(";") 1206 ? className.length() - 1 1207 : className.length()); 1208 } else { 1209 if (className.length() > 0) { 1210 className = reverseAbbreviationMap.get(className.substring(0, 1)); 1211 } 1212 } 1213 final StringBuilder canonicalClassNameBuffer = new StringBuilder(className); 1214 for (int i = 0; i < dim; i++) { 1215 canonicalClassNameBuffer.append("[]"); 1216 } 1217 return canonicalClassNameBuffer.toString(); 1218 } 1219 1220 /** 1221 * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order, 1222 * excluding interfaces. 1223 * 1224 * @param type the type to get the class hierarchy from 1225 * @return Iterable an Iterable over the class hierarchy of the given class 1226 * @since 3.2 1227 */ 1228 public static Iterable<Class<?>> hierarchy(final Class<?> type) { 1229 return hierarchy(type, Interfaces.EXCLUDE); 1230 } 1231 1232 /** 1233 * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order. 1234 * 1235 * @param type the type to get the class hierarchy from 1236 * @param interfacesBehavior switch indicating whether to include or exclude interfaces 1237 * @return Iterable an Iterable over the class hierarchy of the given class 1238 * @since 3.2 1239 */ 1240 public static Iterable<Class<?>> hierarchy(final Class<?> type, final Interfaces interfacesBehavior) { 1241 final Iterable<Class<?>> classes = new Iterable<Class<?>>() { 1242 1243 @Override 1244 public Iterator<Class<?>> iterator() { 1245 final MutableObject<Class<?>> next = new MutableObject<Class<?>>(type); 1246 return new Iterator<Class<?>>() { 1247 1248 @Override 1249 public boolean hasNext() { 1250 return next.getValue() != null; 1251 } 1252 1253 @Override 1254 public Class<?> next() { 1255 final Class<?> result = next.getValue(); 1256 next.setValue(result.getSuperclass()); 1257 return result; 1258 } 1259 1260 @Override 1261 public void remove() { 1262 throw new UnsupportedOperationException(); 1263 } 1264 1265 }; 1266 } 1267 1268 }; 1269 if (interfacesBehavior != Interfaces.INCLUDE) { 1270 return classes; 1271 } 1272 return new Iterable<Class<?>>() { 1273 1274 @Override 1275 public Iterator<Class<?>> iterator() { 1276 final Set<Class<?>> seenInterfaces = new HashSet<Class<?>>(); 1277 final Iterator<Class<?>> wrapped = classes.iterator(); 1278 1279 return new Iterator<Class<?>>() { 1280 Iterator<Class<?>> interfaces = Collections.<Class<?>> emptySet().iterator(); 1281 1282 @Override 1283 public boolean hasNext() { 1284 return interfaces.hasNext() || wrapped.hasNext(); 1285 } 1286 1287 @Override 1288 public Class<?> next() { 1289 if (interfaces.hasNext()) { 1290 final Class<?> nextInterface = interfaces.next(); 1291 seenInterfaces.add(nextInterface); 1292 return nextInterface; 1293 } 1294 final Class<?> nextSuperclass = wrapped.next(); 1295 final Set<Class<?>> currentInterfaces = new LinkedHashSet<Class<?>>(); 1296 walkInterfaces(currentInterfaces, nextSuperclass); 1297 interfaces = currentInterfaces.iterator(); 1298 return nextSuperclass; 1299 } 1300 1301 private void walkInterfaces(final Set<Class<?>> addTo, final Class<?> c) { 1302 for (final Class<?> iface : c.getInterfaces()) { 1303 if (!seenInterfaces.contains(iface)) { 1304 addTo.add(iface); 1305 } 1306 walkInterfaces(addTo, iface); 1307 } 1308 } 1309 1310 @Override 1311 public void remove() { 1312 throw new UnsupportedOperationException(); 1313 } 1314 1315 }; 1316 } 1317 }; 1318 } 1319 1320}