001 /* 002 * $Id: GroovyCategorySupport.java,v 1.6 2004/07/10 03:31:42 bran Exp $version Apr 26, 2004 4:22:50 PM $user Exp $ 003 * 004 * Copyright 2003 (C) Sam Pullara. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 package org.codehaus.groovy.runtime; 035 036 import groovy.lang.Closure; 037 import groovy.lang.MetaMethod; 038 039 import java.lang.reflect.Method; 040 import java.lang.reflect.Modifier; 041 import java.util.ArrayList; 042 import java.util.Collections; 043 import java.util.HashMap; 044 import java.util.Iterator; 045 import java.util.List; 046 import java.util.Map; 047 import java.util.WeakHashMap; 048 049 050 /** 051 * @author sam 052 */ 053 public class GroovyCategorySupport { 054 055 /** 056 * This method is used to pull all the new methods out of the local thread context with a particular name. 057 * 058 * @param categorizedClass 059 * @param name 060 * @return 061 */ 062 public static List getCategoryMethods(Class categorizedClass, String name) { 063 Map properties = getProperties(); 064 List methodList = new ArrayList(); 065 for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) { 066 Class current = (Class) i.next(); 067 if (current.isAssignableFrom(categorizedClass)) { 068 Map metaMethodsMap = (Map) properties.get(current); 069 List newMethodList = (List) metaMethodsMap.get(name); 070 if (newMethodList != null) { 071 methodList.addAll(newMethodList); 072 } 073 } 074 } 075 if (methodList.size() == 0) return null; 076 return methodList; 077 } 078 079 /** 080 * This method is delegated to from the global use(CategoryClass) method. It scans the Category class for static methods 081 * that take 1 or more parameters. The first parameter is the class you are adding the category method to, additional parameters 082 * are those paramteres needed by that method. A use statement cannot be undone and is valid only for the current thread. 083 * 084 * @param categoryClass 085 */ 086 private static void use(Class categoryClass) { 087 Map properties = getProperties(); 088 Method[] methods = categoryClass.getMethods(); 089 for (int i = 0; i < methods.length; i++) { 090 Method method = methods[i]; 091 if (Modifier.isStatic(method.getModifiers())) { 092 Class[] paramTypes = method.getParameterTypes(); 093 if (paramTypes.length > 0) { 094 Class metaClass = paramTypes[0]; 095 Map metaMethodsMap = getMetaMethods(properties, metaClass); 096 List methodList = getMethodList(metaMethodsMap, method.getName()); 097 MetaMethod mmethod = new NewInstanceMetaMethod(new MetaMethod(method)) { 098 public boolean isCacheable() { return false; } 099 }; 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 }