View Javadoc

1   /*
2    * $Id: GroovyCategorySupport.java,v 1.7 2005/06/19 19:01:25 spullara Exp $version Apr 26, 2004 4:22:50 PM $user Exp $
3    * 
4    * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
5    * 
6    * Redistribution and use of this software and associated documentation
7    * ("Software"), with or without modification, are permitted provided that the
8    * following conditions are met: 1. Redistributions of source code must retain
9    * copyright statements and notices. Redistributions must also contain a copy
10   * of this document. 2. Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following disclaimer in
12   * the documentation and/or other materials provided with the distribution. 3.
13   * The name "groovy" must not be used to endorse or promote products derived
14   * from this Software without prior written permission of The Codehaus. For
15   * written permission, please contact info@codehaus.org. 4. Products derived
16   * from this Software may not be called "groovy" nor may "groovy" appear in
17   * their names without prior written permission of The Codehaus. "groovy" is a
18   * registered trademark of The Codehaus. 5. Due credit should be given to The
19   * Codehaus - http://groovy.codehaus.org/
20   * 
21   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
22   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31   * DAMAGE.
32   *  
33   */
34   package> 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 }