Author: ivaynberg
Date: Sun Oct 29 16:07:00 2006
New Revision: 469027
URL: http://svn.apache.org/viewvc?view=rev&rev=469027
Log:
utilities to make working with reflection easier
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassHieararchyIterator.java
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassOrder.java
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassReflectionCache.java
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/IMethodFilter.java
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ReflectionUtils.java
incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/
incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/ClassHierarchyIteratorTest.java
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassHieararchyIterator.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassHieararchyIterator.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassHieararchyIterator.java
(added)
+++
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassHieararchyIterator.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,89 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * Iterator used to iterate over class hieararchies
+ *
+ * @author ivaynberg
+ */
+public class ClassHieararchyIterator implements Iterator<Class>
+{
+ /** list of classes that define the iteration in specified order */
+ ArrayList<Class> hierarchy = new ArrayList<Class>();
+
+ /** current position of the iterator */
+ private int index = 0;
+
+ /**
+ * Construct.
+ *
+ * @param clazz
+ * class whose hierarchy will be iterated
+ * @param scanOrder
+ * direction of iteration
+ */
+ public ClassHieararchyIterator(Class clazz, ClassOrder scanOrder)
+ {
+ // build the hierarchy iteration
+ Class cursor = clazz;
+ while (cursor != null)
+ {
+ switch (scanOrder)
+ {
+ case SUB_TO_SUPER :
+ hierarchy.add(cursor);
+ break;
+ case SUPER_TO_SUB :
+ hierarchy.add(0, cursor);
+ break;
+ }
+ cursor = cursor.getSuperclass();
+ }
+ }
+
+ /**
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext()
+ {
+ return index < hierarchy.size();
+ }
+
+ /**
+ * @see java.util.Iterator#next()
+ */
+ public Class next()
+ {
+ return hierarchy.get(index++);
+ }
+
+ /**
+ * @see java.util.Iterator#remove()
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException("remove() is not
supported by "
+ + getClass().getName());
+ }
+}
\ No newline at end of file
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassOrder.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassOrder.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassOrder.java
(added)
+++
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassOrder.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,31 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+/**
+ * Determines an ordering of classes
+ *
+ * @author ivaynberg
+ */
+public enum ClassOrder {
+ /** downwards from super class to subclass */
+ SUPER_TO_SUB,
+ /** upwards from subclass to superclass */
+ SUB_TO_SUPER
+}
\ No newline at end of file
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassReflectionCache.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassReflectionCache.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassReflectionCache.java
(added)
+++
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ClassReflectionCache.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,119 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+/**
+ * Provides a threadsafe cache of classes reflection information.
+ *
+ * The slowest part of reflection is the discovery of methods and fields, this
+ * class caches that information and provides utility methods for accessing it.
+ *
+ * The cache has the following indecies that make for efficient lookups:
+ * <ul>
+ * <li>annotation to list of methods that are annotated with it</li>
+ * </ul>
+ *
+ * @author ivaynberg
+ */
+public class ClassReflectionCache
+{
+ private final Map<Class<? extends Annotation>, List<Method>>
annotToMethods;
+
+ /**
+ * Construct.
+ *
+ * @param clazz
+ * class whose reflection information is cached
+ * @param order
+ * order in which fields and methods are sorted
+ * @param methodFilter
+ * filter used to determine whether a method should be
stored in
+ * the cache
+ */
+ public ClassReflectionCache(Class clazz, ClassOrder order,
IMethodFilter methodFilter)
+ {
+ // build the cache
+
+ Map<Class<? extends Annotation>, ArrayList<Method>> map;
+ map = new HashMap<Class<? extends Annotation>,
ArrayList<Method>>();
+
+ ClassHieararchyIterator classes = new
ClassHieararchyIterator(clazz, order);
+ while (classes.hasNext())
+ {
+ Method[] methods = classes.next().getDeclaredMethods();
+ for (Method method : methods)
+ {
+ Annotation[] annots =
method.getDeclaredAnnotations();
+ for (Annotation annot : annots)
+ {
+ ArrayList<Method> annotatedMethods =
map.get(annot.annotationType());
+ if (annotatedMethods == null)
+ {
+ annotatedMethods = new
ArrayList<Method>();
+ map.put(annot.annotationType(),
annotatedMethods);
+ }
+ if (methodFilter.accept(method,
annotatedMethods))
+ {
+ annotatedMethods.add(method);
+ }
+
+ }
+ }
+ }
+
+ annotToMethods = new HashMap<Class<? extends Annotation>,
List<Method>>();
+ for (Entry<Class<? extends Annotation>, ArrayList<Method>>
mapping : map.entrySet())
+ {
+ ArrayList<Method> methods = mapping.getValue();
+ methods.trimToSize();
+ annotToMethods.put(mapping.getKey(),
Collections.unmodifiableList(methods));
+ }
+ }
+
+ /**
+ * Returns all methods annotated with the specified annotaiton
+ *
+ * @param annot
+ * annotation
+ * @return list of methods
+ */
+ public List<Method> methodsForAnnot(Class<? extends Annotation> annot)
+ {
+ List<Method> methods = annotToMethods.get(annot);
+ if (methods == null)
+ {
+ return Collections.emptyList();
+ }
+ else
+ {
+ return methods;
+ }
+
+ }
+}
\ No newline at end of file
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/IMethodFilter.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/IMethodFilter.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/IMethodFilter.java
(added)
+++
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/IMethodFilter.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,85 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+
+/**
+ * Filter that can be used to cecide whether or not a method should be added to
+ * a collection of other methods
+ *
+ * @author ivaynberg
+ */
+public interface IMethodFilter
+{
+ /**
+ * @param check
+ * method that needs to be decided upon
+ * @param accepted
+ * collection of already added methods
+ * @return true if the method should be added, false otherwise
+ */
+ boolean accept(Method check, Collection<Method> accepted);
+
+ /**
+ * Filter that allows any method to be added
+ */
+ public static final IMethodFilter ANY = new IMethodFilter()
+ {
+
+ /**
+ * @see
wicket.util.lang.reflect.IMethodFilter#accept(java.lang.reflect.Method,
+ * java.util.Collection)
+ */
+ public boolean accept(Method check, Collection<Method> accepted)
+ {
+ return true;
+ }
+
+ };
+
+ /**
+ * Filter that allows a method to be added if and only if a
representative
+ * of its override chain is not already in the collection.
+ *
+ * This makes it easy to have only a single representative on an
invocation
+ * chain in the list so when the collection of methods is invoked one
by one
+ * an overridden method is only called once.
+ */
+ public static final IMethodFilter IGNORE_OVERRIDES = new IMethodFilter()
+ {
+ /**
+ * @see
wicket.util.lang.reflect.IMethodFilter#accept(java.lang.reflect.Method,
+ * java.util.Collection)
+ */
+ public boolean accept(Method check, Collection<Method> accepted)
+ {
+ for (Method method : accepted)
+ {
+ if (ReflectionUtils.overrides(method, check))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+}
Added:
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ReflectionUtils.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ReflectionUtils.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ReflectionUtils.java
(added)
+++
incubator/wicket/trunk/wicket/src/main/java/wicket/util/lang/reflect/ReflectionUtils.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,165 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * Reflection utilities
+ *
+ * @author ivaynberg
+ */
[EMAIL PROTECTED]("unchecked")
+public class ReflectionUtils
+{
+ /**
+ * Constructor
+ */
+ private ReflectionUtils()
+ {
+
+ }
+
+ /**
+ * array that contains class reflection caches
+ *
+ * index[0] contains a reflection cache of all declared methods
+ *
+ * index[1] and index[2] contain reflection caches of methods with
overrides
+ * removed
+ *
+ * index[1] is sorted sub to super
+ *
+ * index[2] is sorted super to sub
+ */
+ private static final Map<Class, ClassReflectionCache> classToMethods[]
= new Map[] {
+ new ConcurrentHashMap<Class, ClassReflectionCache>(),
+ new ConcurrentHashMap<Class, ClassReflectionCache>(),
+ new ConcurrentHashMap<Class, ClassReflectionCache>() };
+
+
+ /**
+ * Returns a list of methods that are annotated with the specified
+ * annotation
+ *
+ * @param clazz
+ * @param annot
+ * @return list of methods
+ */
+ public static List<Method> methodsWithAnnotation(Class clazz, Class<?
extends Annotation> annot)
+ {
+ ClassReflectionCache classCache = classToMethods[0].get(clazz);
+ if (classCache == null)
+ {
+ classCache = new ClassReflectionCache(clazz,
ClassOrder.SUPER_TO_SUB, IMethodFilter.ANY);
+ classToMethods[0].put(clazz, classCache);
+ }
+ return classCache.methodsForAnnot(annot);
+ }
+
+ /**
+ * Returns a list of methods that are annotated with the specified
+ * annotation and with multiple representatives of the same override
chain
+ * filtered
+ *
+ * @param clazz
+ * @param annot
+ * @param order
+ * @return list of methods
+ */
+ public static List<Method> invocationChainForAnnotation(Class clazz,
+ Class<? extends Annotation> annot, ClassOrder order)
+ {
+ int index = 0;
+ switch (order)
+ {
+ case SUB_TO_SUPER :
+ index = 1;
+ break;
+ case SUPER_TO_SUB :
+ index = 2;
+ break;
+ }
+
+ ClassReflectionCache classCache =
classToMethods[index].get(clazz);
+ if (classCache == null)
+ {
+ classCache = new ClassReflectionCache(clazz, order,
IMethodFilter.IGNORE_OVERRIDES);
+ classToMethods[index].put(clazz, classCache);
+ }
+ return classCache.methodsForAnnot(annot);
+
+ }
+
+ /**
+ * Checks if either of the two methods is an override of the other
+ *
+ * @param a
+ * method a
+ * @param b
+ * method b
+ * @return true if a overrides b or b overrides a
+ */
+ public static final boolean overrides(Method a, Method b)
+ {
+ if (a.getName().equals(b.getName()))
+ {
+ // have same names
+ if (Arrays.equals(a.getParameterTypes(),
b.getParameterTypes()))
+ {
+ // have same parameter types
+
+ final int amods = a.getModifiers();
+ final int bmods = b.getModifiers();
+
+ if (Modifier.isPublic(amods) ||
Modifier.isProtected(amods))
+ {
+ if (Modifier.isPublic(bmods) ||
Modifier.isProtected(bmods))
+ {
+ // are public or protected - so
must be overrides
+ return true;
+ }
+ }
+
+ final Package apack =
a.getDeclaringClass().getPackage();
+ final Package bpack =
b.getDeclaringClass().getPackage();
+ if (apack == bpack || apack.equals(bpack))
+ {
+ // are in the same package
+ if (!Modifier.isPrivate(amods) &&
!Modifier.isPrivate(bmods))
+ {
+ // both are not private and not
public or protected - so
+ // must be package private and
are overrides
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
Added:
incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/ClassHierarchyIteratorTest.java
URL:
http://svn.apache.org/viewvc/incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/ClassHierarchyIteratorTest.java?view=auto&rev=469027
==============================================================================
---
incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/ClassHierarchyIteratorTest.java
(added)
+++
incubator/wicket/trunk/wicket/src/test/java/wicket/util/lang/reflect/ClassHierarchyIteratorTest.java
Sun Oct 29 16:07:00 2006
@@ -0,0 +1,72 @@
+/*
+ * $Id: org.eclipse.jdt.ui.prefs 5004 2006-03-17 20:47:08 -0800 (Fri, 17 Mar
2006) eelco12 $
+ * $Revision: 5004 $
+ * $Date: 2006-03-17 20:47:08 -0800 (Fri, 17 Mar 2006) $
+ *
+ *
==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package wicket.util.lang.reflect;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for [EMAIL PROTECTED] ClassHierarchyIteratorTest}
+ *
+ * @author ivaynberg
+ */
+public class ClassHierarchyIteratorTest extends TestCase
+{
+ private static class A
+ {
+
+ }
+
+ private static class B extends A
+ {
+
+ }
+
+ /**
+ * Tests subclass to superclass ordered iterator
+ */
+ public void testSubToSuper()
+ {
+ Iterator<Class> it = new ClassHieararchyIterator(B.class,
ClassOrder.SUB_TO_SUPER);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), B.class);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), A.class);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), Object.class);
+ assertFalse(it.hasNext());
+ }
+
+ /**
+ * Tests superclass to subclass ordered iterator
+ */
+ public void testSuperToSub()
+ {
+ Iterator<Class> it = new ClassHieararchyIterator(B.class,
ClassOrder.SUPER_TO_SUB);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), Object.class);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), A.class);
+ assertTrue(it.hasNext());
+ assertEquals(it.next(), B.class);
+ assertFalse(it.hasNext());
+ }
+
+}