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</strong<a href="../../../../> 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 }