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 /***
80 * This method is delegated to from the global use(CategoryClass) method. It scans the Category class for static methods
81 * that take 1 or more parameters. The first parameter is the class you are adding the category method to, additional parameters
82 * are those paramteres needed by that method. A use statement cannot be undone and is valid only for the current thread.
83 *
84 * @param categoryClass
85 */
86 private static void use(Class categoryClass) {
87 Map properties = getProperties();
88 Method[] methods = categoryClass.getMethods();
89 for (int i = 0; i < methods.length; i++) {
90 Method method = methods[i];
91 if (Modifier.isStatic(method.getModifiers())) {
92 Class[] paramTypes = method.getParameterTypes();
93 if (paramTypes.length > 0) {
94 Class metaClass = paramTypes[0];
95 Map metaMethodsMap = getMetaMethods(properties, metaClass);
96 List methodList = getMethodList(metaMethodsMap, method.getName());
97 MetaMethod mmethod = new NewInstanceMetaMethod(new MetaMethod(method)) {
98 public boolean isCacheable() { return false; }
99 };
100 methodList.add(mmethod);
101 }
102 }
103 }
104 }
105
106 /***
107 * @param clazz
108 * @param closure
109 */
110 public static void use(Class clazz, Closure closure) {
111 newScope();
112 try {
113 use(clazz);
114 closure.call();
115 } finally {
116 endScope();
117 }
118 }
119
120 /***
121 * @param classes
122 * @param closure
123 */
124 public static void use(List classes, Closure closure) {
125 newScope();
126 try {
127 for (Iterator i = classes.iterator(); i.hasNext(); ) {
128 Class clazz = (Class) i.next();
129 use(clazz);
130 }
131 closure.call();
132 } finally {
133 endScope();
134 }
135 }
136
137 private static ThreadLocal local = new ThreadLocal() {
138 protected Object initialValue() {
139 List stack = new ArrayList();
140 stack.add(Collections.EMPTY_MAP);
141 return stack;
142 }
143 };
144
145 private static void newScope() {
146 List stack = (List) local.get();
147 Map properties = new WeakHashMap(getProperties());
148 stack.add(properties);
149 }
150
151 private static void endScope() {
152 List stack = (List) local.get();
153 stack.remove(stack.size() - 1);
154 }
155
156 private static Map getProperties() {
157 List stack = (List) local.get();
158 Map properties = (Map) stack.get(stack.size() - 1);
159 return properties;
160 }
161
162 /***
163 * @param method
164 * @param metaMethodsMap
165 * @return
166 */
167 private static List getMethodList(Map metaMethodsMap, String name) {
168 List methodList = (List) metaMethodsMap.get(name);
169 if (methodList == null) {
170 methodList = new ArrayList(1);
171 metaMethodsMap.put(name, methodList);
172 }
173 return methodList;
174 }
175
176 /***
177 * @param properties
178 * @param metaClass
179 * @return
180 */
181 private static Map getMetaMethods(Map properties, Class metaClass) {
182 Map metaMethodsMap = (Map) properties.get(metaClass);
183 if (metaMethodsMap == null) {
184 metaMethodsMap = new HashMap();
185 properties.put(metaClass, metaMethodsMap);
186 }
187 return metaMethodsMap;
188 }
189
190 }