FrontPage

DispatchしたときのGroovyCategorySupportの振る舞いの原因を探る上でGroovyCategorySupport?の振る舞いについて調べたときのメモです。
無論コードに関しては、一切いじってません。
対象はGroovyのJSR05版のGroovyCategorySupport?クラスのソースコード。ワタクシ脳みそのL2キャッシュが16KBぐらいなので、とにかくしょうもないコメントをたくさん追加しました。

package org.codehaus.groovy.runtime;

import groovy.lang.Closure;
import groovy.lang.MetaMethod;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;


/**
 * @author sam
 */
public class GroovyCategorySupport {


  /**
   * This method is used to pull all the new methods out of 
   * the local thread context with a particular name.
   * 
   * @param categorizedClass
   * @param name
   * @return
   */
  public static List getCategoryMethods(Class categorizedClass, String name) {
    //ThreadLocaに保持しているstackの最末端のMapを取得
    Map properties = getProperties();
    //該当するカテゴリメソッド情報すべてを保持するList
    List methodList = new ArrayList();
    //keyであるクラスをイテレート
    for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
      Class current = (Class) i.next();
      //keyのClassがカテゴリ対象のClassのインタフェース、クラス等であるか判定
      //この処理により該当するカテゴリサポート情報すべてに対して処理ができる
      if (current.isAssignableFrom(categorizedClass)) {
        /*
         * MetaMethodsの構造
         * Map型
         * key: Categoryメソッド名
         * value: MethodList keyに対応するMetaMethodを要素に持つArrayList
         * 
         * MethodListの構造
         * List型
         * 要素:MetaMethod
         */
        Map metaMethodsMap = (Map) properties.get(current);
        List newMethodList = (List) metaMethodsMap.get(name);
        
        if (newMethodList != null) {
          //すべて追加
          methodList.addAll(newMethodList);
        }
      }
    }
    if (methodList.size() == 0) return null;
    return methodList;
  }

  /*
   * 本来のGroovyのruntimeで利用するMetaMethodに対する
   * Category用のデコレーション?
   * Sort用にComparableのimplement
   * NewInstanceMetaMethodはデコレータ作成用のベースクラスか?
   */
  private static class CategoryMethod
   extends NewInstanceMetaMethod
   implements Comparable {
    private Class metaClass;

    public CategoryMethod(MetaMethod metaMethod, Class metaClass) {
      super(metaMethod);
      this.metaClass = metaClass;
    }

    public boolean isCacheable() { return false; }

    /**
     * Sort by most specific to least specific.
     * 昇順ソートであれば、metaClassがサブクラスであるもの
     * (より末端の実装)から順にソートされる。
     * @param o
     * @return
     */
    public int compareTo(Object o) {
      //当然、引数をCategoryMethodとしてキャスト
      CategoryMethod thatMethod = (CategoryMethod) o;
      //自分のmetaClassフィールドを参照
      Class thisClass = metaClass;
      //引数のmetaClassフィールドを参照
      Class thatClass = thatMethod.metaClass;
      //等しければ0をリターン
      if (thisClass == thatClass) return 0;
      
      //自分のmetaClassの親クラスのいずれかが、引数のmetaClassと等しい場合
      //自分が子なので負の整数をリターン
      Class loop = thisClass;
      while(loop != Object.class) {
        loop = thisClass.getSuperclass();
        if (loop == thatClass) {
          return -1;
        }
      }
      
      //引数のmetaClassの親クラスのいずれかが、自分のmetaClassと等しい場合
      //自分が親なので正の整数をリターン
      loop = thatClass;
      while (loop != Object.class) {
        loop = thatClass.getSuperclass();
        if (loop == thisClass) {
          return 1;
        }
      }
      
      //いずれにも該当しない場合は等しいという判断
      return 0;
    }
  }

  /**
   * This method is delegated to from the global 
   * use(CategoryClass) method.  It scans the Category 
   * class for static methods
   * that take 1 or more parameters.  The first 
   * parameter is the class you are adding the category
   * method to, additional parameters
   * are those paramteres needed by that method.  
   * A use statement cannot be undone and is valid only 
   * for the current thread.
   * 
   * @param categoryClass
   */
  private static void use(Class categoryClass) {
    //ThreadLocaに保持しているstackの最末端のMapを取得
    Map properties = getProperties();
    
    //Categoryクラスからメソッド一覧を取得
    Method[] methods = categoryClass.getMethods();
    //メソッドの数だけループ
    for (int i = 0; i < methods.length; i++) {
      Method method = methods[i];
      //static methodのみを抽出
      if (Modifier.isStatic(method.getModifiers())) {
        //methodの引数の型配列を取得
        Class[] paramTypes = method.getParameterTypes();
        //引数の型配列が0以上であること
        if (paramTypes.length > 0) {
          //0番目の引数は常にCategorySupportの対象クラスとなる
          Class metaClass = paramTypes[0];
          //対象クラスのMethodインスタンスのListを保持するMapを取得
          Map metaMethodsMap = getMetaMethods(properties, metaClass);
          //Categoryメソッドと同じ名称を持つメソッド一覧
          //(メタメソッド一覧)をmetaMethodsMapから取得
          List methodList = getMethodList(metaMethodsMap, method.getName());
          
          //この部分こそがCategoryメソッドの情報の最小単位を形作っている部分!
          MetaMethod mmethod
           = new CategoryMethod(new MetaMethod(method), metaClass);
          
          //生成したMetaMethodをメタメソッド一覧に追加
          methodList.add(mmethod);
          //metaClassがサブクラスであるもの(より末端の実装)から順にソート
          Collections.sort(methodList);
        }
      }
    }
  }
  
  /**
   * @param clazz
   * @param closure
   */
  public static void use(Class clazz, Closure closure) {
    //CategorySupportを利用する都度に新規スタック
    newScope();
    try {
      //CategoryMethod情報の取得生成処理
      use(clazz);
      //通常はクロージャ内部でGroovyScriptの実行
      closure.call();
    } finally {
      //必ずスタックを1つ削除
      endScope();
    }
  }

  /**
   * @param classes
   * @param closure
   */
  public static void use(List classes, Closure closure) {
    //CategorySupportを利用する都度に新規スタック
    newScope();
    try {
      for (Iterator i = classes.iterator(); i.hasNext(); ) {
        Class clazz = (Class) i.next();
        //CategoryMethod情報の取得生成処理
        use(clazz);
      }
      //通常はクロージャ内部でGroovyScriptの実行
      closure.call();
    } finally {
      //必ずスタックを1つ削除
      endScope();
    }    
  }

  /*
   * ThreadLocal変数として保持しているListの論理構造
   * stackの構造
   * List型
   * 要素:properties Map(key:metaClass,value:Map)
   * 
   * propertiesの構造
   * Map型
   * key:Categoryメソッドの対象クラスのClassインスタンス
   * value:MetaMethods Map型(key:メソッド名,value:MetaMethodを要素に持つList)
   * 
   * MetaMethodsの構造
   * Map型
   * key: Categoryメソッド名
   * value: MethodList keyに対応するMetaMethodを要素に持つArrayList
   * 
   * MethodListの構造
   * List型
   * 要素:MetaMethod
   */
  private static ThreadLocal local = new ThreadLocal() {
    protected Object initialValue() {
        List stack = new ArrayList();
        //すべてのMapのオリジナルとなる最初の要素
        stack.add(Collections.EMPTY_MAP);
        return stack;
      }
  };
  
  private static void newScope() {
    List stack = (List) local.get();
    //stackの最末端のMapを取得した後、それを元に弱参照Mapを構築
    Map properties = new WeakHashMap(getProperties());
    //スタックにput
    stack.add(properties);
  }
  
  private static void endScope() {
    List stack = (List) local.get();
    //stackの最末端の要素を削除
    stack.remove(stack.size() - 1);
  }
  
  private static Map getProperties() {
    //Thread単位の変数であるThreadLocalよりListを取得
    List stack = (List) local.get();
    //Listの最末端の値を取得、MapにCast
    Map properties = (Map) stack.get(stack.size() - 1);
    return properties;
  }
  
  /**
   * @param method
   * @param metaMethodsMap
   * @return
   */
  private static List getMethodList(Map metaMethodsMap, String name) {
    //引数のCategoryメソッド名で登録されているMetaMethodのListを取得
    List methodList = (List) metaMethodsMap.get(name);
    //methodListがnullならnew ArrayList(1)をput
    if (methodList == null) {
      methodList = new ArrayList(1);//通常は一つだからArrayList(1)?
      metaMethodsMap.put(name, methodList);
    }
    return methodList;//nullを返すことはない。
  }

  /**
   * @param properties
   * @param metaClass
   * @return
   */
  private static Map getMetaMethods(Map properties, Class metaClass) {
    //対象クラスClassをkeyにpropertiesからMethodのMapを取得
    Map metaMethodsMap = (Map) properties.get(metaClass);
    //nullなら新規にMapを生成し、対処クラスClassをkeyにput
    if (metaMethodsMap == null) {
      metaMethodsMap = new HashMap();
      properties.put(metaClass, metaMethodsMap);
    }
    return metaMethodsMap;//nullを返すことはない
  }
}

リロード   新規 編集 凍結 差分 添付 複製 改名   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2010-03-14 (日) 07:00:41 (2718d)