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