From eb45c596a86f8cdfb75d03e1a006951b14e27fd5 Mon Sep 17 00:00:00 2001 From: Matthew Jason Benson Date: Sun, 4 Aug 2013 18:36:17 +0000 Subject: [PATCH] [LANG-907] retrieve class hierarchy git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1510298 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/commons/lang3/ClassUtils.java | 110 +++++++++++++++++- .../apache/commons/lang3/ClassUtilsTest.java | 24 ++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 015e6dd6a..2492182dd 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -19,12 +19,16 @@ package org.apache.commons.lang3; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.mutable.MutableObject; /** *

Operates on classes without using reflection.

@@ -41,6 +45,12 @@ import java.util.Map; * @version $Id$ */ public class ClassUtils { + /** + * @see ClassUtils#hierarchy(Class, Interfaces) + */ + public enum Interfaces { + INCLUDE, EXCLUDE; + } /** * The package separator character: '.' == {@value}. @@ -1129,4 +1139,102 @@ public class ClassUtils { } } -} + /** + * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order, + * excluding interfaces. + * + * @param type + * @return Iterable + */ + public static Iterable> hierarchy(final Class type) { + return hierarchy(type, Interfaces.EXCLUDE); + } + + /** + * Get an {@link Iterable} that can iterate over a class hierarchy in ascending (subclass to superclass) order. + * + * @param type + * @param interfacesBehavior + * @return Iterable + */ + public static Iterable> hierarchy(final Class type, Interfaces interfacesBehavior) { + final Iterable> classes = new Iterable>() { + + @Override + public Iterator> iterator() { + final MutableObject> next = new MutableObject>(type); + return new Iterator>() { + + @Override + public boolean hasNext() { + return next.getValue() != null; + } + + @Override + public Class next() { + final Class result = next.getValue(); + next.setValue(result.getSuperclass()); + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + + }; + if (interfacesBehavior != Interfaces.INCLUDE) { + return classes; + } + return new Iterable>() { + + @Override + public Iterator> iterator() { + final Set> seenInterfaces = new HashSet>(); + final Iterator> wrapped = classes.iterator(); + + return new Iterator>() { + Iterator> interfaces = Collections.> emptySet().iterator(); + + @Override + public boolean hasNext() { + return interfaces.hasNext() || wrapped.hasNext(); + } + + @Override + public Class next() { + if (interfaces.hasNext()) { + final Class nextInterface = interfaces.next(); + seenInterfaces.add(nextInterface); + return nextInterface; + } + final Class nextSuperclass = wrapped.next(); + final Set> currentInterfaces = new LinkedHashSet>(); + walkInterfaces(currentInterfaces, nextSuperclass); + interfaces = currentInterfaces.iterator(); + return nextSuperclass; + } + + private void walkInterfaces(Set> addTo, Class c) { + for (Class iface : c.getInterfaces()) { + if (!seenInterfaces.contains(iface)) { + addTo.add(iface); + } + walkInterfaces(addTo, iface); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + }; + } + +} \ No newline at end of file diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java index ce6752cdb..f613c3acd 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java @@ -34,10 +34,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.lang3.ClassUtils.Interfaces; +import org.apache.commons.lang3.reflect.testbed.GenericConsumer; +import org.apache.commons.lang3.reflect.testbed.GenericParent; +import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild; import org.junit.Test; /** @@ -1221,4 +1226,23 @@ public class ClassUtilsTest { assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtilsTest$Inner")); } + @Test + public void testHierarchyIncludingInterfaces() { + final Iterator> iter = + ClassUtils.hierarchy(StringParameterizedChild.class, Interfaces.INCLUDE).iterator(); + assertEquals(StringParameterizedChild.class, iter.next()); + assertEquals(GenericParent.class, iter.next()); + assertEquals(GenericConsumer.class, iter.next()); + assertEquals(Object.class, iter.next()); + assertFalse(iter.hasNext()); + } + + @Test + public void testHierarchyExcludingInterfaces() { + final Iterator> iter = ClassUtils.hierarchy(StringParameterizedChild.class).iterator(); + assertEquals(StringParameterizedChild.class, iter.next()); + assertEquals(GenericParent.class, iter.next()); + assertEquals(Object.class, iter.next()); + assertFalse(iter.hasNext()); + } }