001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| 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.conqat.lib.commons.reflect; 018 019import java.awt.Color; 020import java.io.DataInputStream; 021import java.io.File; 022import java.io.IOException; 023import java.io.InputStream; 024import java.lang.annotation.Annotation; 025import java.lang.reflect.Array; 026import java.lang.reflect.Constructor; 027import java.lang.reflect.Field; 028import java.lang.reflect.InvocationTargetException; 029import java.lang.reflect.Method; 030import java.lang.reflect.Modifier; 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.Enumeration; 036import java.util.HashMap; 037import java.util.HashSet; 038import java.util.LinkedList; 039import java.util.List; 040import java.util.Map; 041import java.util.Queue; 042import java.util.Set; 043import java.util.jar.JarEntry; 044import java.util.jar.JarFile; 045 046import org.conqat.lib.commons.assertion.CCSMAssert; 047import org.conqat.lib.commons.color.ColorUtils; 048import org.conqat.lib.commons.enums.EnumUtils; 049import org.conqat.lib.commons.string.StringUtils; 050import org.conqat.lib.commons.version.Version; 051 052/** 053 * This class provides utility methods for reflection purposes. In particular it 054 * provides access to {@link FormalParameter}. 055 */ 056public class ReflectionUtils { 057 058 /** To compare formal parameters. */ 059 private static FormalParameterComparator comparator = new FormalParameterComparator(); 060 061 /** 062 * Convert a String to an Object of the provided type. It supports conversion to 063 * primitive types and simple tests (char: use first character of string, 064 * boolean: test for values "true", "on", "1", "yes"). Enums are handled by the 065 * {@link EnumUtils#valueOfIgnoreCase(Class, String)} method. Otherwise it is 066 * checked if the target type has a constructor that takes a single string and 067 * it is invoked. For all other cases an exception is thrown, as no conversion 068 * is possible. 069 * 070 * <i>Maintainer note</i>: Make sure this method is changed in accordance with 071 * method {@link #isConvertibleFromString(Class)} 072 * 073 * @see #convertString(String, Class) 074 * 075 * @param value 076 * the string to be converted. 077 * @param targetType 078 * the type of the resulting object. 079 * @return the converted object. 080 * @throws TypeConversionException 081 * in the case that no conversion could be performed. 082 * @see #isConvertibleFromString(Class) 083 * 084 */ 085 @SuppressWarnings({ "unchecked", "rawtypes" }) 086 public static <T> T convertString(String value, Class<T> targetType) throws TypeConversionException { 087 088 // value must be provided 089 if (value == null) { 090 if (String.class.equals(targetType)) { 091 return (T) StringUtils.EMPTY_STRING; 092 } 093 throw new TypeConversionException("Null value can't be converted to type '" + targetType.getName() + "'."); 094 } 095 096 if (targetType.equals(Object.class) || targetType.equals(String.class)) { 097 return (T) value; 098 } 099 if (targetType.isPrimitive() || EJavaPrimitive.isWrapperType(targetType)) { 100 return convertPrimitive(value, targetType); 101 } 102 if (targetType.isEnum()) { 103 // we checked manually before 104 Object result = EnumUtils.valueOfIgnoreCase((Class) targetType, value); 105 if (result == null) { 106 throw new TypeConversionException("'" + value + "' is no valid value for enum " + targetType.getName()); 107 } 108 return (T) result; 109 110 } 111 if (targetType.equals(Color.class)) { 112 Color result = ColorUtils.fromString(value); 113 if (result == null) { 114 throw new TypeConversionException("'" + value + "' is not a valid color!"); 115 } 116 return (T) result; 117 } 118 119 // Check if the target type has a constructor taking a single string. 120 try { 121 Constructor<T> c = targetType.getConstructor(String.class); 122 return c.newInstance(value); 123 } catch (Exception e) { 124 throw new TypeConversionException("No constructor taking one String argument found for type '" + targetType 125 + "' (" + e.getMessage() + ")", e); 126 } 127 128 } 129 130 /** Convert String to object of specified type */ 131 public static Object convertTo(String valueString, String typeName) 132 throws TypeConversionException, ClassNotFoundException { 133 Class<?> clazz = resolveType(typeName); 134 return convertString(valueString, clazz); 135 } 136 137 /** 138 * This method checks if the provided type can be converted from a string. With 139 * respect to {@link #convertString(String, Class)} the semantics are the 140 * following: If this method returns <code>true</code> a particular string 141 * <i>may</i> be convertible to the target type. If this method returns 142 * <code>false</code>, a call to {@link #convertString(String, Class)} is 143 * guaranteed to result in a {@link TypeConversionException}. If a call to 144 * {@link #convertString(String, Class)} does not result in an exception, a call 145 * to this method is guaranteed to return <code>true</code>. 146 * <p> 147 * <i>Maintainer note</i>: Make sure this method is change in accordance with 148 * method {@link #convertString(String, Class)} 149 * 150 * @see #convertString(String, Class) 151 */ 152 public static boolean isConvertibleFromString(Class<?> targetType) { 153 154 if (targetType.equals(Object.class) || targetType.equals(String.class)) { 155 return true; 156 } 157 if (targetType.isPrimitive() || EJavaPrimitive.isWrapperType(targetType)) { 158 return true; 159 } 160 if (targetType.isEnum()) { 161 return true; 162 163 } 164 if (targetType.equals(Color.class)) { 165 return true; 166 } 167 168 try { 169 targetType.getConstructor(String.class); 170 return true; 171 } catch (SecurityException e) { 172 // case is handled at method end 173 } catch (NoSuchMethodException e) { 174 // case is handled at method end 175 } 176 177 return false; 178 } 179 180 /** 181 * Obtain array of formal parameters for a method. 182 * 183 * @see FormalParameter 184 */ 185 public static FormalParameter[] getFormalParameters(Method method) { 186 187 int parameterCount = method.getParameterTypes().length; 188 189 FormalParameter[] parameters = new FormalParameter[parameterCount]; 190 191 for (int i = 0; i < parameterCount; i++) { 192 parameters[i] = new FormalParameter(method, i); 193 } 194 195 return parameters; 196 } 197 198 /** 199 * Get super class list of a class. 200 * 201 * @param clazz 202 * the class to start traversal from 203 * @return a list of super class where the direct super class of the provided 204 * class is the first member of the list. <br> 205 * For {@link Object}, primitives and interfaces this returns an empty 206 * list. <br> 207 * For arrays this returns a list containing only {@link Object}. <br> 208 * For enums this returns a list containing {@link Enum} and 209 * {@link Object} 210 */ 211 public static List<Class<?>> getSuperClasses(Class<?> clazz) { 212 ArrayList<Class<?>> superClasses = new ArrayList<>(); 213 findSuperClasses(clazz, superClasses); 214 return superClasses; 215 } 216 217 /** 218 * Invoke a method with parameters. 219 * 220 * @param method 221 * the method to invoke 222 * @param object 223 * the object the underlying method is invoked from 224 * @param parameterMap 225 * this maps from the formal parameter of the method to the parameter 226 * value 227 * @return the result of dispatching the method 228 * @throws IllegalArgumentException 229 * if the method is an instance method and the specified object 230 * argument is not an instance of the class or interface declaring 231 * the underlying method (or of a subclass or implementor thereof); 232 * if the number of actual and formal parameters differ; if an 233 * unwrapping conversion for primitive arguments fails; or if, after 234 * possible unwrapping, a parameter value cannot be converted to the 235 * corresponding formal parameter type by a method invocation 236 * conversion; if formal parameters belong to different methods. 237 * 238 * @throws IllegalAccessException 239 * if this Method object enforces Java language access control and 240 * the underlying method is inaccessible. 241 * @throws InvocationTargetException 242 * if the underlying method throws an exception. 243 * @throws NullPointerException 244 * if the specified object is null and the method is an instance 245 * method. 246 * @throws ExceptionInInitializerError 247 * if the initialization provoked by this method fails. 248 */ 249 public static Object invoke(Method method, Object object, Map<FormalParameter, Object> parameterMap) 250 throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { 251 252 for (FormalParameter formalParameter : parameterMap.keySet()) { 253 if (!formalParameter.getMethod().equals(method)) { 254 throw new IllegalArgumentException("Parameters must belong to method."); 255 } 256 } 257 258 Object[] parameters = obtainParameters(parameterMap); 259 260 return method.invoke(object, parameters); 261 262 } 263 264 /** 265 * Check whether an Object of the source type can be used instead of an Object 266 * of the target type. This method is required, as the 267 * {@link Class#isAssignableFrom(java.lang.Class)} does not handle primitive 268 * types. 269 * 270 * @param source 271 * type of the source object 272 * @param target 273 * type of the target object 274 * @return whether an assignment would be possible. 275 */ 276 public static boolean isAssignable(Class<?> source, Class<?> target) { 277 return resolvePrimitiveClass(target).isAssignableFrom(resolvePrimitiveClass(source)); 278 } 279 280 /** 281 * Returns the wrapper class type for a primitive type (e.g. 282 * <code>Integer</code> for an <code>int</code>). If the given class is not a 283 * primitive, the class itself is returned. 284 * 285 * @param clazz 286 * the class. 287 * @return the corresponding class type. 288 */ 289 public static Class<?> resolvePrimitiveClass(Class<?> clazz) { 290 if (!clazz.isPrimitive()) { 291 return clazz; 292 } 293 294 EJavaPrimitive primitive = EJavaPrimitive.getForPrimitiveClass(clazz); 295 if (primitive == null) { 296 throw new IllegalStateException("Did Java get a new primitive? " + clazz.getName()); 297 } 298 return primitive.getWrapperClass(); 299 } 300 301 /** 302 * Convert a String to an Object of the provided type. This only works for 303 * primitive types and wrapper types. 304 * 305 * @param value 306 * the string to be converted. 307 * @param targetType 308 * the type of the resulting object. 309 * @return the converted object. 310 * @throws TypeConversionException 311 * in the case that no conversion could be performed. 312 */ 313 @SuppressWarnings("unchecked") 314 /* package */static <T> T convertPrimitive(String value, Class<T> targetType) throws TypeConversionException { 315 316 EJavaPrimitive primitive = EJavaPrimitive.getForPrimitiveOrWrapperClass(targetType); 317 if (primitive == null) { 318 throw new IllegalArgumentException("Type '" + targetType.getName() + "' is not a primitive type!"); 319 } 320 321 try { 322 323 switch (primitive) { 324 case BOOLEAN: 325 boolean b = "1".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) 326 || "on".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value); 327 return (T) Boolean.valueOf(b); 328 329 case CHAR: 330 return (T) Character.valueOf(value.charAt(0)); 331 332 case BYTE: 333 return (T) Byte.valueOf(value); 334 335 case SHORT: 336 return (T) Short.valueOf(value); 337 338 case INT: 339 return (T) Integer.valueOf(value); 340 341 case LONG: 342 return (T) Long.valueOf(value); 343 344 case FLOAT: 345 return (T) Float.valueOf(value); 346 347 case DOUBLE: 348 return (T) Double.valueOf(value); 349 350 default: 351 throw new TypeConversionException("No conversion possible for " + primitive); 352 } 353 354 } catch (NumberFormatException e) { 355 throw new TypeConversionException("Value'" + value + "' can't be converted to type '" + targetType.getName() 356 + "' (" + e.getMessage() + ").", e); 357 } 358 } 359 360 /** 361 * Resolves the class object for a type name. Type name can be a primitive. For 362 * resolution, {@link Class#forName(String)} is used, that uses the caller's 363 * class loader. 364 * <p> 365 * While method <code>Class.forName(...)</code> resolves fully qualified names, 366 * it does not resolve primitives, e.g. "java.lang.Boolean" can be resolved but 367 * "boolean" cannot. 368 * 369 * @param typeName 370 * name of the type. For primitives case is ignored. 371 * 372 * @throws ClassNotFoundException 373 * if the typeName neither resolves to a primitive, nor to a known 374 * class. 375 */ 376 public static Class<?> resolveType(String typeName) throws ClassNotFoundException { 377 return resolveType(typeName, null); 378 } 379 380 /** 381 * Resolves the class object for a type name. Type name can be a primitive. For 382 * resolution, the given class loader is used. 383 * <p> 384 * While method <code>Class.forName(...)</code> resolves fully qualified names, 385 * it does not resolve primitives, e.g. "java.lang.Boolean" can be resolved but 386 * "boolean" cannot. 387 * 388 * @param typeName 389 * name of the type. For primitives case is ignored. 390 * 391 * @param classLoader 392 * the class loader used for loading the class. If this is null, the 393 * caller class loader is used. 394 * 395 * @throws ClassNotFoundException 396 * if the typeName neither resolves to a primitive, nor to a known 397 * class. 398 */ 399 public static Class<?> resolveType(String typeName, ClassLoader classLoader) throws ClassNotFoundException { 400 401 EJavaPrimitive primitive = EJavaPrimitive.getPrimitiveIgnoreCase(typeName); 402 403 if (primitive != null) { 404 return primitive.getClassObject(); 405 } 406 407 if (classLoader == null) { 408 return Class.forName(typeName); 409 } 410 return Class.forName(typeName, true, classLoader); 411 } 412 413 /** 414 * Recursively add super classes to a list. 415 * 416 * @param clazz 417 * class to start from 418 * @param superClasses 419 * list to store super classes. 420 */ 421 private static void findSuperClasses(Class<?> clazz, List<Class<?>> superClasses) { 422 Class<?> superClass = clazz.getSuperclass(); 423 if (superClass == null) { 424 return; 425 } 426 superClasses.add(superClass); 427 findSuperClasses(superClass, superClasses); 428 } 429 430 /** 431 * Obtain parameter array from parameter map. 432 */ 433 private static Object[] obtainParameters(Map<FormalParameter, Object> parameterMap) { 434 435 ArrayList<FormalParameter> formalParameters = new ArrayList<>(parameterMap.keySet()); 436 437 formalParameters.sort(comparator); 438 439 Object[] result = new Object[formalParameters.size()]; 440 441 for (int i = 0; i < formalParameters.size(); i++) { 442 result[i] = parameterMap.get(formalParameters.get(i)); 443 } 444 445 return result; 446 } 447 448 /** 449 * Obtain the return type of method. This method deals with bridge methods 450 * introduced by generics. This works for methods without parameters only. 451 * 452 * @param clazz 453 * the class 454 * @param methodName 455 * the name of the method. 456 * @return the return type 457 * @throws NoSuchMethodException 458 * if the class doesn't contain the desired method 459 */ 460 public static Class<?> obtainMethodReturnType(Class<?> clazz, String methodName) throws NoSuchMethodException { 461 462 // due to the potential presense of bridge methods we can't use 463 // Clazz.getMethod() and have to iterate over all methods. 464 for (Method method : clazz.getMethods()) { 465 if (isValid(method, methodName)) { 466 return method.getReturnType(); 467 } 468 } 469 // method not found 470 throw new NoSuchMethodException( 471 "Class " + clazz.getName() + " doesn't have parameterless method named " + methodName); 472 } 473 474 /** 475 * Obtain the generic return type of method. This method deals with the gory 476 * details of bridge methods and generics. This works for methods without 477 * parameters only. This doesn't work for interfaces, arrays and enums. 478 * 479 * @param clazz 480 * the class 481 * @param methodName 482 * the name of the method. 483 * @return the return type 484 * @throws NoSuchMethodException 485 * if the class doesn't contain the desired method 486 */ 487 public static Class<?> obtainGenericMethodReturnType(Class<?> clazz, String methodName) 488 throws NoSuchMethodException { 489 490 if (clazz.isArray() || clazz.isEnum()) { 491 throw new IllegalArgumentException("Doesn't work for arrays and enums."); 492 } 493 if (clazz.getTypeParameters().length != 0) { 494 throw new IllegalArgumentException("Doesn't work for generic classes."); 495 } 496 497 for (Method method : clazz.getMethods()) { 498 if (isValid(method, methodName)) { 499 return new GenericTypeResolver(clazz).resolveGenericType(method.getGenericReturnType()); 500 } 501 } 502 503 // method not found 504 throw new NoSuchMethodException( 505 "Class " + clazz.getName() + " doesn't have parameterless method named " + methodName); 506 } 507 508 /** 509 * Tests if a method has the correct name, no parameters and is no bridge 510 * method. 511 */ 512 private static boolean isValid(Method method, String methodName) { 513 return method.getName().equals(methodName) && method.getParameterTypes().length == 0 && !method.isBridge(); 514 } 515 516 /** 517 * Returns the value from the map, whose key is the best match for the given 518 * class. The best match is defined by the first match occurring in a breath 519 * first search of the inheritance tree, where the base class is always visited 520 * before the implemented interfaces. Interfaces are traversed in the order they 521 * are defined in the source file. The only exception is {@link Object}, which 522 * is considered only as the very last option. 523 * <p> 524 * As this lookup can be expensive (reflective iteration over the entire 525 * inheritance tree) the results should be cached if multiple lookups for the 526 * same class are expected. 527 * 528 * 529 * @param clazz 530 * the class being looked up. 531 * @param classMap 532 * the map to perform the lookup in. 533 * @return the best match found or <code>null</code> if no matching entry was 534 * found. Note that <code>null</code> will also be returned if the entry 535 * for the best matching class was <code>null</code>. 536 */ 537 public static <T> T performNearestClassLookup(Class<?> clazz, Map<Class<?>, T> classMap) { 538 Queue<Class<?>> q = new LinkedList<>(); 539 q.add(clazz); 540 541 while (!q.isEmpty()) { 542 Class<?> current = q.poll(); 543 if (classMap.containsKey(current)) { 544 return classMap.get(current); 545 } 546 547 Class<?> superClass = current.getSuperclass(); 548 if (superClass != null && superClass != Object.class) { 549 q.add(superClass); 550 } 551 552 q.addAll(Arrays.asList(current.getInterfaces())); 553 } 554 return classMap.get(Object.class); 555 } 556 557 /** 558 * Returns whether the given object is an instance of at least one of the given 559 * classes. 560 */ 561 public static boolean isInstanceOfAny(Object o, Class<?>... classes) { 562 for (Class<?> c : classes) { 563 if (c.isInstance(o)) { 564 return true; 565 } 566 } 567 return false; 568 } 569 570 /** 571 * Returns whether the given object is an instance of all of the given classes. 572 */ 573 public static boolean isInstanceOfAll(Object o, Class<?>... classes) { 574 for (Class<?> c : classes) { 575 if (!c.isInstance(o)) { 576 return false; 577 } 578 } 579 return true; 580 } 581 582 /** 583 * Returns the first object in the given collection which is an instance of the 584 * given class (or null otherwise). 585 */ 586 @SuppressWarnings("unchecked") 587 public static <T> T pickInstanceOf(Class<T> clazz, Collection<?> objects) { 588 for (Object o : objects) { 589 if (clazz.isInstance(o)) { 590 return (T) o; 591 } 592 } 593 return null; 594 } 595 596 /** 597 * Obtains the version of a Java class file. 598 * 599 * Class file major versions (from 600 * http://en.wikipedia.org/wiki/Class_(file_format)): 601 * 602 * <pre> 603 * J2SE 7 = 51 604 * J2SE 6.0 = 50 605 * J2SE 5.0 = 49 606 * JDK 1.4 = 48 607 * JDK 1.3 = 47 608 * JDK 1.2 = 46 609 * JDK 1.1 = 45 610 * </pre> 611 * 612 * @param inputStream 613 * stream to read class file from. 614 * @return the class file version or <code>null</code> if stream does not 615 * contain a class file. 616 * @throws IOException 617 * if an IO problem occurs. 618 */ 619 public static Version obtainClassFileVersion(InputStream inputStream) throws IOException { 620 DataInputStream classfile = new DataInputStream(inputStream); 621 int magic = classfile.readInt(); 622 if (magic != 0xcafebabe) { 623 return null; 624 } 625 int minorVersion = classfile.readUnsignedShort(); 626 int majorVersion = classfile.readUnsignedShort(); 627 628 return new Version(majorVersion, minorVersion); 629 } 630 631 /** 632 * This method extracts the class file version from each class file in the 633 * provided jar. 634 * 635 * @return the result maps from the class file to its version. 636 */ 637 public static HashMap<String, Version> getClassFileVersions(File jarFile) throws IOException { 638 639 HashMap<String, Version> result = new HashMap<>(); 640 641 JarFile jar = new JarFile(jarFile); 642 Enumeration<JarEntry> entries = jar.entries(); 643 644 while (entries.hasMoreElements()) { 645 JarEntry entry = entries.nextElement(); 646 if (!entry.isDirectory() && entry.getName().endsWith(".class")) { 647 InputStream entryStream = jar.getInputStream(entry); 648 Version version = obtainClassFileVersion(entryStream); 649 result.put(entry.getName(), version); 650 entryStream.close(); 651 } 652 } 653 654 jar.close(); 655 656 return result; 657 } 658 659 /** 660 * Creates a list that contains only the types that are instances of a specified 661 * type from the objects of an input list. The input list is not modified. 662 * 663 * @param objects 664 * List of objects that gets filtered 665 * 666 * @param type 667 * target type whose instances are returned 668 */ 669 @SuppressWarnings("unchecked") 670 public static <T> List<T> listInstances(List<?> objects, Class<T> type) { 671 List<T> filtered = new ArrayList<>(); 672 673 for (Object object : objects) { 674 if (type.isInstance(object)) { 675 filtered.add((T) object); 676 } 677 } 678 679 return filtered; 680 } 681 682 /** 683 * Determines the most specific common base class. If one of the types is 684 * primitive or an interface, null is returned. 685 */ 686 public static Class<?> determineCommonBase(Class<?> class1, Class<?> class2) { 687 List<Class<?>> hierarchy1 = getBaseHierarchy(class1); 688 List<Class<?>> hierarchy2 = getBaseHierarchy(class2); 689 690 if (hierarchy1.isEmpty() || hierarchy2.isEmpty() || !hierarchy1.get(0).equals(hierarchy2.get(0))) { 691 return null; 692 } 693 694 int index = 0; 695 while (index < hierarchy1.size() && index < hierarchy2.size() 696 && hierarchy1.get(index).equals(hierarchy2.get(index))) { 697 index += 1; 698 } 699 if (index <= hierarchy1.size()) { 700 index -= 1; 701 } 702 703 return hierarchy1.get(index); 704 } 705 706 /** 707 * Returns the hierarchy of base classes starting with {@link Object} up to the 708 * class itself. 709 */ 710 public static List<Class<?>> getBaseHierarchy(Class<?> baseClass) { 711 List<Class<?>> hierarchy = ReflectionUtils.getSuperClasses(baseClass); 712 Collections.reverse(hierarchy); 713 hierarchy.add(baseClass); 714 return hierarchy; 715 } 716 717 /** 718 * Returns the set of all interfaces implemented by the given class. This 719 * includes also interfaces that are indirectly implemented as they are extended 720 * by an interfaces that is implemented by the given class. If the given class 721 * is an interface, it is included itself. 722 */ 723 public static Set<Class<?>> getAllInterfaces(Class<?> baseClass) { 724 Queue<Class<?>> q = new LinkedList<>(); 725 q.add(baseClass); 726 727 Set<Class<?>> result = new HashSet<>(); 728 if (baseClass.isInterface()) { 729 result.add(baseClass); 730 } 731 732 while (!q.isEmpty()) { 733 for (Class<?> iface : q.poll().getInterfaces()) { 734 if (result.add(iface)) { 735 q.add(iface); 736 } 737 } 738 } 739 740 return result; 741 } 742 743 /** 744 * Returns all fields declared for a class (all visibilities and also inherited 745 * from super classes). Note that multiple fields with the same name may exist 746 * due to redeclaration/shadowing in sub classes. 747 */ 748 public static List<Field> getAllFields(Class<?> type) { 749 List<Field> fields = new ArrayList<>(); 750 while (type != null) { 751 fields.addAll(Arrays.asList(type.getDeclaredFields())); 752 type = type.getSuperclass(); 753 } 754 return fields; 755 } 756 757 /** 758 * Returns all methods declared for a class (all visibilities and also inherited 759 * from super classes). Note that multiple methods with the same name may exist 760 * due to redeclaration/shadowing in sub classes. 761 */ 762 public static List<Method> getAllMethods(Class<?> type) { 763 List<Method> methods = new ArrayList<>(); 764 while (type != null) { 765 methods.addAll(Arrays.asList(type.getDeclaredMethods())); 766 type = type.getSuperclass(); 767 } 768 return methods; 769 } 770 771 /** 772 * Returns the field with the given name from the given type or null if none 773 * exists. If the field cannot be found in the given type, all super-classes are 774 * searched as well. Different from {@link Class#getField(String)}, this will 775 * also work for non-public fields. 776 */ 777 public static Field getField(Class<?> type, String name) throws SecurityException { 778 while (type != null) { 779 try { 780 return type.getDeclaredField(name); 781 } catch (NoSuchFieldException e) { 782 type = type.getSuperclass(); 783 } 784 } 785 return null; 786 } 787 788 /** 789 * Sets the value of a field on the given object. The field may also be private 790 * and/or final. 791 */ 792 public static void setFieldValue(Object object, String fieldName, Object newValue) throws IOException { 793 Field field = ReflectionUtils.getField(object.getClass(), fieldName); 794 Field modifiersField; 795 try { 796 modifiersField = Field.class.getDeclaredField("modifiers"); 797 modifiersField.setAccessible(true); 798 modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 799 field.set(object, newValue); 800 } catch (NoSuchFieldException e) { 801 throw new IOException("Failed to get modifiers", e); 802 } catch (IllegalAccessException e) { 803 throw new IOException("Failed to access field", e); 804 } 805 } 806 807 /** 808 * Returns true when the given object is an array or implements 809 * {@link Iterable}, false otherwise. 810 */ 811 public static <T> boolean isIterable(T parsedObject) { 812 return parsedObject.getClass().isArray() || parsedObject instanceof Iterable; 813 } 814 815 /** 816 * Unified interface to iterable types (i.e. array or implements 817 * {@link Iterable}). 818 * 819 * @throws IllegalArgumentException 820 * if given object does not conform to one of the expected types 821 */ 822 public static Iterable<?> asIterable(Object arrayOrIterable) { 823 if (arrayOrIterable instanceof Iterable) { 824 return (Iterable<?>) arrayOrIterable; 825 } 826 if (arrayOrIterable.getClass().isArray()) { 827 Class<?> componentType = arrayOrIterable.getClass().getComponentType(); 828 if (componentType.isArray() || componentType.isPrimitive()) { 829 ArrayList<Object> arrayContent = new ArrayList<>(); 830 for (int i = 0; i < Array.getLength(arrayOrIterable) - 1; i++) { 831 arrayContent.add(Array.get(arrayOrIterable, i)); 832 } 833 return arrayContent; 834 } 835 return Arrays.asList((Object[]) arrayOrIterable); 836 } 837 throw new IllegalArgumentException( 838 "Array or Iterable expected, but was given " + arrayOrIterable.getClass().getCanonicalName()); 839 } 840 841 /** 842 * Returns the annotation the given element is annotated with. If the annotation 843 * is not present an {@link AssertionError} is thrown. 844 */ 845 public static <T extends Annotation> T getAnnotation(Class<?> element, Class<T> annotationClass) 846 throws AssertionError { 847 T annotation = element.getAnnotation(annotationClass); 848 CCSMAssert.isNotNull(annotation, element + " is not annotated with @" + annotationClass.getSimpleName()); 849 return annotation; 850 } 851}