1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package org/codehaus/groovy/runtime/package-summary.html">> org.codehaus.groovy.runtime;
35
36 import groovy.lang.Closure;
37 import groovy.lang.MetaMethod;
38
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Modifier;
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.HashMap;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.WeakHashMap;
48
49
50 /***
51 * @author sam
52 */
53 public class GroovyCategorySupport {
54
55 /***
56 * This method is used to pull all the new methods out of the local thread context with a particular name.
57 *
58 * @param categorizedClass
59 * @param name
60 * @return
61 */
62 public static List getCategoryMethods(Class categorizedClass, String name) {
63 Map properties = getProperties();
64 List methodList = new ArrayList();
65 for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
66 Class current = (Class) i.next();
67 if (current.isAssignableFrom(categorizedClass)) {
68 Map metaMethodsMap = (Map) properties.get(current);
69 List newMethodList = (List) metaMethodsMap.get(name);
70 if (newMethodList != null) {
71 methodList.addAll(newMethodList);
72 }
73 }
74 }
75 if (methodList.size() == 0) return null;
76 return methodList;
77 }
78
79 private static class CategoryMethod extends NewInstanceMetaMethod implements Comparable {
80 private Class metaClass;
81
82 public CategoryMethod(MetaMethod metaMethod, Class metaClass) {
83 super(metaMethod);
84 this.metaClass = metaClass;
85 }
86
87 public boolean isCacheable() { return false; }
88
89 /***
90 * Sort by most specific to least specific.
91 * @param o
92 * @return
93 */
94 public int compareTo(Object o) {
95 CategoryMethod thatMethod = (CategoryMethod) o;
96 Class thisClass = metaClass;
97 Class thatClass = thatMethod.metaClass;
98 if (thisClass == thatClass) return 0;
99 Class loop = thisClass;
100 while(loop != Object.class) {
101 loop = thisClass.getSuperclass();
102 if (loop == thatClass) {
103 return -1;
104 }
105 }
106 loop = thatClass;
107 while (loop != Object.class) {
108 loop = thatClass.getSuperclass();
109 if (loop == thisClass) {
110 return 1;
111 }
112 }
113 return 0;
114 }
115 }
116
117 /***
118 * This method is delegated to from the global use(CategoryClass) method. It scans the Category class for static methods
119 * that take 1 or more parameters. The first parameter is the class you are adding the category method to, additional parameters
120 * are those paramteres needed by that method. A use statement cannot be undone and is valid only for the current thread.
121 *
122 * @param categoryClass
123 */
124 private static void use(Class categoryClass) {
125 Map properties = getProperties();
126 Method[] methods = categoryClass.getMethods();
127 for (int i = 0; i < methods.length; i++) {
128 Method method = methods[i];
129 if (Modifier.isStatic(method.getModifiers())) {
130 Class[] paramTypes = method.getParameterTypes();
131 if (paramTypes.length > 0) {
132 Class metaClass = paramTypes[0];
133 Map metaMethodsMap = getMetaMethods(properties, metaClass);
134 List methodList = getMethodList(metaMethodsMap, method.getName());
135 MetaMethod mmethod = new CategoryMethod(new MetaMethod(method), metaClass);
136 methodList.add(mmethod);
137 Collections.sort(methodList);
138 }
139 }
140 }
141 }
142
143 /***
144 * @param clazz
145 * @param closure
146 */
147 public static void use(Class clazz, Closure closure) {
148 newScope();
149 try {
150 use(clazz);
151 closure.call();
152 } finally {
153 endScope();
154 }
155 }
156
157 /***
158 * @param classes
159 * @param closure
160 */
161 public static void use(List classes, Closure closure) {
162 newScope();
163 try {
164 for (Iterator i = classes.iterator(); i.hasNext(); ) {
165 Class clazz = (Class) i.next();
166 use(clazz);
167 }
168 closure.call();
169 } finally {
170 endScope();
171 }
172 }
173
174 private static ThreadLocal local = new ThreadLocal() {
175 protected Object initialValue() {
176 List stack = new ArrayList();
177 stack.add(Collections.EMPTY_MAP);
178 return stack;
179 }
180 };
181
182 private static void newScope() {
183 List stack = (List) local.get();
184 Map properties = new WeakHashMap(getProperties());
185 stack.add(properties);
186 }
187
188 private static void endScope() {
189 List stack = (List) local.get();
190 stack.remove(stack.size() - 1);
191 }
192
193 private static Map getProperties() {
194 List stack = (List) local.get();
195 Map properties = (Map) stack.get(stack.size() - 1);
196 return properties;
197 }
198
199 /***
200 * @param method
201 * @param metaMethodsMap
202 * @return
203 */
204 private static List getMethodList(Map metaMethodsMap, String name) {
205 List methodList = (List) metaMethodsMap.get(name);
206 if (methodList == null) {
207 methodList = new ArrayList(1);
208 metaMethodsMap.put(name, methodList);
209 }
210 return methodList;
211 }
212
213 /***
214 * @param properties
215 * @param metaClass
216 * @return
217 */
218 private static Map getMetaMethods(Map properties, Class metaClass) {
219 Map metaMethodsMap = (Map) properties.get(metaClass);
220 if (metaMethodsMap == null) {
221 metaMethodsMap = new HashMap();
222 properties.put(metaClass, metaMethodsMap);
223 }
224 return metaMethodsMap;
225 }
226
227 }