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}