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());
+       }
+
+}


Reply via email to