KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > core > ReflectiveVisitorHelper


1 /*
2  * Copyright 2002-2005 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.core;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.lang.reflect.Modifier JavaDoc;
21 import java.util.LinkedList JavaDoc;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25
26 import org.springframework.util.Assert;
27 import org.springframework.util.CachingMapDecorator;
28 import org.springframework.util.ReflectionUtils;
29
30 /**
31  * Helper implementation for a reflective visitor.
32  * Mainly for internal use within the framework.
33  *
34  * <p>To use, call <code>invokeVisit</code>, passing a Visitor object
35  * and the data argument to accept (double-dispatch). For example:
36  *
37  * <pre>
38  * public String styleValue(Object value) {
39  * reflectiveVistorSupport.invokeVisit(this, value)
40  * }
41  *
42  * // visit call back will be invoked via reflection
43  * String visit(&lt;valueType&gt; arg) {
44  * // process argument of type &lt;valueType&gt;
45  * }
46  * </pre>
47  *
48  * See the DefaultValueStyler class for a concrete usage.
49  *
50  * @author Keith Donald
51  * @since 1.2.2
52  * @see org.springframework.core.style.DefaultValueStyler
53  */

54 public final class ReflectiveVisitorHelper {
55
56     private static final String JavaDoc VISIT_METHOD = "visit";
57
58     private static final String JavaDoc VISIT_NULL = "visitNull";
59
60     private static final Log logger = LogFactory.getLog(ReflectiveVisitorHelper.class);
61
62
63     private CachingMapDecorator visitorClassVisitMethods = new CachingMapDecorator() {
64         public Object JavaDoc create(Object JavaDoc key) {
65             return new ClassVisitMethods((Class JavaDoc) key);
66         }
67     };
68
69
70     /**
71      * Use reflection to call the appropriate <code>visit</code> method
72      * on the provided visitor, passing in the specified argument.
73      * @param visitor the visitor encapsulating the logic to process the argument
74      * @param argument the argument to dispatch
75      * @throws IllegalArgumentException if the visitor parameter is null
76      */

77     public Object JavaDoc invokeVisit(Object JavaDoc visitor, Object JavaDoc argument) {
78         Assert.notNull(visitor, "The visitor to visit is required");
79         // Perform call back on the visitor through reflection.
80
Method JavaDoc method = getMethod(visitor.getClass(), argument);
81         if (method == null) {
82             if (logger.isWarnEnabled()) {
83                 logger.warn("No method found by reflection for visitor class [" + visitor.getClass().getName()
84                         + "] and argument of type [" + (argument != null ? argument.getClass().getName() : "") + "]");
85             }
86             return null;
87         }
88         try {
89             Object JavaDoc[] args = null;
90             if (argument != null) {
91                 args = new Object JavaDoc[] { argument };
92             }
93             if (!Modifier.isPublic(method.getModifiers()) && !method.isAccessible()) {
94                 method.setAccessible(true);
95             }
96             return method.invoke(visitor, args);
97
98         }
99         catch (Exception JavaDoc ex) {
100             ReflectionUtils.handleReflectionException(ex);
101             throw new IllegalStateException JavaDoc("Should never get here");
102         }
103     }
104
105     /**
106      * Determines the most appropriate visit method for the
107      * given visitor class and argument.
108      */

109     private Method JavaDoc getMethod(Class JavaDoc visitorClass, Object JavaDoc argument) {
110         ClassVisitMethods visitMethods = (ClassVisitMethods) this.visitorClassVisitMethods.get(visitorClass);
111         return visitMethods.getVisitMethod(argument != null ? argument.getClass() : null);
112     }
113
114
115     /**
116      * Internal class caching visitor methods by argument class.
117      */

118     private static class ClassVisitMethods {
119
120         private final Class JavaDoc visitorClass;
121
122         private CachingMapDecorator visitMethodCache = new CachingMapDecorator() {
123             public Object JavaDoc create(Object JavaDoc argumentClazz) {
124                 if (argumentClazz == null) {
125                     return findNullVisitorMethod();
126                 }
127                 Method JavaDoc method = findVisitMethod((Class JavaDoc)argumentClazz);
128                 if (method == null) {
129                     method = findDefaultVisitMethod();
130                 }
131                 return method;
132             }
133         };
134
135         private ClassVisitMethods(Class JavaDoc visitorClass) {
136             this.visitorClass = visitorClass;
137         }
138
139         private Method JavaDoc findNullVisitorMethod() {
140             for (Class JavaDoc clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
141                 try {
142                     return clazz.getDeclaredMethod(VISIT_NULL, (Class JavaDoc[])null);
143                 }
144                 catch (NoSuchMethodException JavaDoc e) {
145                 }
146             }
147             return findDefaultVisitMethod();
148         }
149
150         private Method JavaDoc findDefaultVisitMethod() {
151             final Class JavaDoc[] args = {Object JavaDoc.class};
152             for (Class JavaDoc clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
153                 try {
154                     return clazz.getDeclaredMethod(VISIT_METHOD, args);
155                 }
156                 catch (NoSuchMethodException JavaDoc e) {
157                 }
158             }
159             if (logger.isWarnEnabled()) {
160                 logger.warn("No default '" + VISIT_METHOD + "' method found. Returning <null>");
161             }
162             return null;
163         }
164
165         /**
166          * Gets a cached visitor method for the specified argument type.
167          */

168         private Method JavaDoc getVisitMethod(Class JavaDoc argumentClass) {
169             return (Method JavaDoc) this.visitMethodCache.get(argumentClass);
170         }
171
172         /**
173          * Traverses class hierarchy looking for applicable visit() method.
174          */

175         private Method JavaDoc findVisitMethod(Class JavaDoc rootArgumentType) {
176             if (rootArgumentType == Object JavaDoc.class) {
177                 return null;
178             }
179             LinkedList JavaDoc classQueue = new LinkedList JavaDoc();
180             classQueue.addFirst(rootArgumentType);
181
182             while (!classQueue.isEmpty()) {
183                 Class JavaDoc argumentType = (Class JavaDoc)classQueue.removeLast();
184                 // Check for a visit method on the visitor class matching this
185
// argument type.
186
try {
187                     if (logger.isDebugEnabled()) {
188                         logger.debug("Looking for method " + VISIT_METHOD + "(" + argumentType + ")");
189                     }
190                     return findVisitMethod(this.visitorClass, argumentType);
191                 }
192                 catch (NoSuchMethodException JavaDoc e) {
193                     // Queue up the argument super class if it's not of type Object.
194
if (!argumentType.isInterface() && (argumentType.getSuperclass() != Object JavaDoc.class)) {
195                         classQueue.addFirst(argumentType.getSuperclass());
196                     }
197
198                     // Queue up argument's implemented interfaces.
199
Class JavaDoc[] interfaces = argumentType.getInterfaces();
200                     for (int i = 0; i < interfaces.length; i++) {
201                         classQueue.addFirst(interfaces[i]);
202                     }
203                 }
204             }
205             // No specific method found -> return the default.
206
return findDefaultVisitMethod();
207         }
208
209         private Method JavaDoc findVisitMethod(Class JavaDoc visitorClass, Class JavaDoc argumentType) throws NoSuchMethodException JavaDoc {
210             try {
211                 return visitorClass.getDeclaredMethod(VISIT_METHOD, new Class JavaDoc[] {argumentType});
212             }
213             catch (NoSuchMethodException JavaDoc ex) {
214                 // Try visitorClass superclasses.
215
if (visitorClass.getSuperclass() != Object JavaDoc.class) {
216                     return findVisitMethod(visitorClass.getSuperclass(), argumentType);
217                 }
218                 else {
219                     throw ex;
220                 }
221             }
222         }
223     }
224
225 }
226
Popular Tags