svnno****@sourc*****
svnno****@sourc*****
2009年 10月 4日 (日) 20:33:49 JST
Revision: 3699 http://sourceforge.jp/projects/jiemamy/svn/view?view=rev&revision=3699 Author: ashigeru Date: 2009-10-04 20:33:48 +0900 (Sun, 04 Oct 2009) Log Message: ----------- InterfaceEnhancerを仮実装 Modified Paths: -------------- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java Added Paths: ----------- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java 2009-10-04 06:55:17 UTC (rev 3698) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -15,16 +15,11 @@ */ package org.jiemamy.utils.enhancer; -import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createAdviceTableField; -import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createBypassMethod; import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createCopyClass; import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createInheritedClass; -import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createPointcutMethod; -import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -32,19 +27,16 @@ import javassist.CtClass; import javassist.CtConstructor; -import javassist.CtField; import javassist.CtMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.jiemamy.utils.enhancer.helper.AccessibilityValidator; -import org.jiemamy.utils.enhancer.helper.AdviceApplier; -import org.jiemamy.utils.enhancer.helper.AdviceTable; -import org.jiemamy.utils.enhancer.helper.Aspect; import org.jiemamy.utils.enhancer.helper.AspectList; import org.jiemamy.utils.enhancer.helper.CtClassComparator; import org.jiemamy.utils.enhancer.helper.EnhanceManager; +import org.jiemamy.utils.enhancer.helper.EnhanceManipulator; import org.jiemamy.utils.enhancer.helper.EnhanceTargetProductCollector; import org.jiemamy.utils.enhancer.helper.JavassistConverter; import org.jiemamy.utils.enhancer.helper.NewInstanceEnhancer; @@ -344,21 +336,16 @@ AccessibilityValidator.validate(enhance); - Map<CtClass, CtClass> targetProducts = prepareProducts(enhance); - Map<CtClass, AspectList<CtMethod>> allProductAspects = weavePointcutIntoAllProducts(targetProducts); - AspectList<CtConstructor> factoryAspects = weavePointcutIntoFactory(enhance, targetProducts, allProductAspects); + Map<CtClass, CtClass> targetProducts = + prepareProducts(enhance); + Map<CtClass, AspectList<CtMethod>> allProductAspects = + EnhanceManipulator.weavePointcutIntoAllProducts(enhanceManager, targetProducts); + AspectList<CtConstructor> factoryAspects = + weavePointcutIntoFactory(enhance, targetProducts, allProductAspects); + Class<?> installedFactory = + EnhanceManipulator.install(converter, enhance, targetProducts, factoryAspects, allProductAspects); - Map<CtClass, CtClass> restProducts = loadAndInitializeProducts(targetProducts, allProductAspects); - for (CtClass klass : restProducts.values()) { - converter.toClass(klass); - } - - Class<?> result = converter.toClass(enhance); - if (factoryAspects != null) { - registerAdviceTable(result, converter.toConstructorAspects(factoryAspects)); - } - - return result.asSubclass(factoryInterface); + return installedFactory.asSubclass(factoryInterface); } private Map<CtClass, CtClass> prepareProducts(CtClass enhance) throws EnhanceException { @@ -373,73 +360,6 @@ } /** - * 拡張されるプロダクトの一覧をロードしたのち、それぞれを初期化する。 - * <p> - * {@code productAspects.base IN productsToBeEnhanced.base} - * </p> - * @param productsToBeEnhanced - * 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) - * @param allProductAspects - * それぞれのプロダクトクラスに対するメソッドアスペクトの一覧 - * ({@code base -> aspect for each method}) - * @return - * アスペクトが適用されなかった拡張されるべきプロダクトクラスの一覧 - * ({@code base -> toBeEnhanced}) - * @throws EnhanceException 拡張に失敗した場合 - * @see #testAllWeaveTargetWillBeEnhanced(Map, Map) - */ - private Map<CtClass, CtClass> loadAndInitializeProducts(Map<CtClass, CtClass> productsToBeEnhanced, - Map<CtClass, AspectList<CtMethod>> allProductAspects) throws EnhanceException { - - assert productsToBeEnhanced != null; - assert allProductAspects != null; - assert testAllWeaveTargetWillBeEnhanced(productsToBeEnhanced, allProductAspects); - - Map<CtClass, CtClass> rest = newMap(); - rest.putAll(productsToBeEnhanced); - for (Map.Entry<CtClass, AspectList<CtMethod>> entry : allProductAspects.entrySet()) { - CtClass orig = entry.getKey(); - AspectList<CtMethod> aspects = entry.getValue(); - CtClass enhanced = rest.remove(orig); - AdviceTable table = converter.toMethodAspects(aspects); - registerAdviceTable(converter.toClass(enhanced), table); - } - return rest; - } - - /** - * 指定のクラスにアドバイステーブルの値を設定する。 - * @param klass 設定対象のクラス - * @param adviceTable 設定するアドバイステーブル - * @throws EnhanceException 設定に失敗した場合 - */ - private static void registerAdviceTable(Class<?> klass, AdviceTable adviceTable) throws EnhanceException { - - assert klass != null; - assert adviceTable != null; - - AspectList<?> aspects = adviceTable.getAspects(); - AdviceApplier[] appliers = adviceTable.toAppliers(); - LOG.debug("Initialize advice table: {}", adviceTable); - try { - Field field = klass.getField(aspects.getAdviceTableHolder().getName()); - field.set(null, appliers); - } catch (SecurityException e) { - throw new EnhanceException(MessageFormat.format("Cannot access to enhanced field for ({0})", klass - .getName()), e); - } catch (NoSuchFieldException e) { - // may not occur - throw new AssertionError(e); - } catch (IllegalArgumentException e) { - // may not occur - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // may not occur - throw new AssertionError(e); - } - } - - /** * 指定のファクトリクラスに、アスペクト用のフックを織り込む。 * <p> * 実際に織り込まれるアスペクトの一覧は、{@code enhanceList}と @@ -475,88 +395,6 @@ } /** - * 指定のプロダクトクラス一覧に、それぞれアスペクト用のフックを織り込む。 - * <p> - * 実際に織り込まれるアスペクトの一覧は、{@code enhanceList}を元に計算される。 - * このメソッドでは、{@code enhanceList}に規定される拡張の一覧を - * 実際に適用するためのフックのみを織り込む。 - * </p> - * @param productsToBeEnhanced - * 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) - * @return - * 計算されたそれぞれのプロダクトクラスに対するメソッドアスペクトの一覧 - * ({@code base -> aspect for each method}) - * @throws EnhanceException 拡張に失敗した場合 - * @see #testAllWeaveTargetWillBeEnhanced(Map, Map) - */ - private Map<CtClass, AspectList<CtMethod>> weavePointcutIntoAllProducts(Map<CtClass, CtClass> productsToBeEnhanced) - throws EnhanceException { - - assert productsToBeEnhanced != null; - - Map<CtClass, AspectList<CtMethod>> allProductAspects = newMap(); - for (Map.Entry<CtClass, CtClass> entry : productsToBeEnhanced.entrySet()) { - CtClass base = entry.getKey(); - CtClass enhanced = entry.getValue(); - AspectList<CtMethod> aspects = weavePointcutIntoSingleProduct(base, enhanced); - if (aspects != null) { - allProductAspects.put(base, aspects); - } - } - return allProductAspects; - } - - /** - * 単一のプロダクトクラスにアスペクト用のフックを織り込む。 - * <p> - * {@code enhance <: base} - * </p> - * @param base 拡張される前のプロダクトクラス定義 - * @param enhance 拡張対象となるプロダクトクラス定義 - * @return 対象のプロダクトクラスに適用すべきアスペクトの一覧、ひとつも存在しない場合は{@code null} - * @throws EnhanceException 拡張に失敗した場合 - * @see #weavePointcutIntoAllProducts(Map) - */ - private AspectList<CtMethod> weavePointcutIntoSingleProduct(CtClass base, CtClass enhance) throws EnhanceException { - - assert base != null; - assert enhance != null; - assert enhance.subclassOf(base); - - LOG.trace("Weaving pointcuts: {}", enhance.getName()); - List<Aspect<CtMethod>> results = new ArrayList<Aspect<CtMethod>>(); - CtField holder = null; - int enhanceIndex = 0; - for (CtMethod method : base.getMethods()) { - if (enhanceManager.isLegalJoinpoint(method) == false) { - continue; - } - List<InvocationHandler> handlers = enhanceManager.findApplicableHandlers(base, method); - if (handlers.isEmpty()) { - continue; - } - - if (enhanceIndex == 0) { - // 最初の拡張メソッドを発見したら、参照するアドバイステーブルフィールドも作成する - holder = createAdviceTableField(enhance); - } - assert holder != null; - CtMethod bypass = createBypassMethod(enhance, method, enhanceIndex); - createPointcutMethod(enhance, method, holder, enhanceIndex); - results.add(new Aspect<CtMethod>(method, bypass, handlers)); - enhanceIndex++; - } - - if (results.isEmpty()) { - assert holder == null; - return null; - } else { - assert holder != null; - return new AspectList<CtMethod>(holder, results); - } - } - - /** * {@code productAspects.base IN productsToBeEnhanced.base} * @param productsToBeEnhanced * 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -0,0 +1,354 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/04 + * + * This file is part of Jiemamy. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.jiemamy.utils.enhancer; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.jiemamy.utils.enhancer.helper.AspectList; +import org.jiemamy.utils.enhancer.helper.EnhanceManager; +import org.jiemamy.utils.enhancer.helper.EnhanceManipulator; +import org.jiemamy.utils.enhancer.helper.JavassistConverter; +import org.jiemamy.utils.enhancer.helper.NewInstanceEnhancer; +import org.jiemamy.utils.enhancer.reflection.ReflectionFactory; +import org.jiemamy.utils.enhancer.reflection.ReflectionUtil; + +/** + * インターフェースのみが提供されるファクトリを拡張し、またそのファクトリが提供する + * プロダクトオブジェクトについても拡張を行う。 + * <p> + * このエンハンサは主に次のようなことができる。 + * </p> + * <ul> + * <li> + * ファクトリインターフェースの各メソッドの戻り値型をプロダクトの型とみなし、 + * そのプロダクトオブジェクトを生成するコードを自動的に生成する。 + * ただし、それぞれのプロダクトはインターフェース型で指定される必要がある。 + * (以下、これらを<em>プロダクトインターフェース</em>と呼ぶ) + * </li> + * <li> + * 各プロダクトインターフェースの実装を自動的に生成する。 + * その際に、親クラスを指定することができる。 + * </li> + * <li> + * プロダクトインターフェースで宣言されるメソッドを拡張し、呼び出し時にその実行をインターセプトして + * 前後に別の処理を記述することができる。 + * </li> + * </ul> + * <p> + * このエンハンサには次のような制約がある。 + * </p> + * <ul> + * <li> + * ファクトリインターフェース、プロダクトインターフェース、プロダクトインターフェースの親クラスは、 + * いずれも{@code public}で宣言されなければならない。 + * </li> + * <li> + * ファクトリインターフェースはインターフェースとして宣言されなければならない。 + * </li> + * <li> + * ファクトリインターフェースの各メソッドは、その戻り値として常にインターフェース型をとらなければならない。 + * これらはすべて、プロダクトインターフェースとして扱われる。 + * </li> + * <li> + * プロダクトインターフェースの親に指定するクラスは、列挙でない具象クラスである。 + * また、{@code final}で指定されていてはならず、引数をとらない公開コンストラクタを提供する必要がある。 + * </li> + * </ul> + * @version 0.2.0 + * @since 0.2.0 + * @author Suguru ARAKAWA + * @param <T> 実装するファクトリのインターフェース型 + */ +public class InterfaceEnhancer<T> extends AbstractEnhancer<T> { + + private static final Logger LOG = + LoggerFactory.getLogger(InterfaceEnhancer.class); + + private final Class<T> factoryInterface; + + private final Set<Class<?>> productInterfaces; + + private final Class<?> productSuperClass; + + private EnhanceManager enhanceManager; + + private JavassistConverter converter; + + private volatile Class<? extends T> factoryImplementation; + + + /** + * インスタンスを生成する。 + * <p> + * 指定する引数はそれぞれ下記の制約を同時に満たす必要がある。 + * </p> + * <ul> + * <li> {@code factoryInterface}は{@code public}の可視性を持つ </li> + * <li> {@code factoryInterface}はインターフェース型を表現する </li> + * <li> {@code factoryInterface}のすべてのメソッドは、インターフェース型の値を返す </li> + * <li> {@code factoryInterface}のそれぞれのメソッド返すインターフェースは、いずれも{@code public}で宣言される </li> + * <li> {@code productSuperClass}は{@code public}の可視性を持つ </li> + * <li> {@code productSuperClass}は具象クラスである </li> + * <li> {@code productSuperClass}は継承可能である </li> + * <li> {@code productSuperClass}は(列挙でない)クラス型を表現する </li> + * <li> {@code productSuperClass}は引数をとらないコンストラクタを提供する </li> + * </ul> + * <p> + * また、このクラスによって実装される対象は、つぎのそれぞれの条件をすべて満たすもののみである。 + * </p> + * <ul> + * <li> 拡張されるインスタンス生成式 <ul> + * <li> + * {@code factoryImplementation}に直接記述されたクラスインスタンス生成式である + * </li> + * <li> 対象のクラスが{@code public}で宣言されている </li> + * <li> 実行するコンストラクタが{@code public}で宣言されている </li> + * <li> + * 実行するコンストラクタが{@code enhanceList}に含まれるいずれかの拡張対象となる + * </li> </ul> + * </li> + * <li> 拡張されるプロダクトクラス <ul> + * <li> 下記のすべてを満たすいずれかのインスタンス生成式の対象にとるクラスである : <ul> + * <li> + * {@code factoryImplementation}に直接記述されたインスタンス生成式である + * </li> + * <li> 対象のクラスが{@code public}で宣言されている </li> + * <li> 対象のクラスが{@code final}で宣言されて<b>いない</b> </li> + * <li> 実行対象のコンストラクタが{@code public}で宣言されている </li> </ul> + * </li> + * <li> クラスが公開するいずれかのメソッドが次の条件をすべて満たす + * (つまり、このクラスが拡張されるプロダクトメソッドを含む): <ul> + * <li> {@code public}で宣言されている </li> + * <li> {@code final}で宣言されて<b>いない</b> </li> + * <li> {@code static}で宣言されて<b>いない</b> </li> + * <li> + * {@code enhanceList}に含まれるいずれかの拡張対象となる + * </li> </ul> + * </li> </ul> + * </li> + * <li> 拡張されるプロダクトメソッド <ul> + * <li> + * 上記"拡張されるプロダクトクラス"の対象となったクラスが公開するメソッドである + * (継承したメソッドを含む) + * </li> + * <li> {@code public}で宣言されている </li> + * <li> {@code final}で宣言されて<b>いない</b> </li> + * <li> {@code static}で宣言されて<b>いない</b> </li> + * <li> ブリッジメソッドでない </li> + * <li> コンパイラによって合成された({@code synthetic})メソッドでない </li> + * <li> + * {@code enhanceList}に含まれるいずれかの拡張対象となる + * </li> </ul> + * </li> + * </ul> + * <p> + * なお、プロダクトクラスが拡張される場合、このオブジェクトが作成するファクトリは + * 拡張されたプロダクトクラスをインスタンス化して返す。 + * ただし、拡張対象となるプロダクトクラスのうち、{@code public}でない + * コンストラクタを起動するようなインスタンス生成式は拡張されていない通常のプロダクトクラスの + * インスタンスを生成する。 + * </p> + * @param factoryInterface 実装を生成する対象のファクトリインターフェース + * @param productSuperClass それぞれのプロダクトが実装する親クラス + * @param enhanceList 拡張を定義するオブジェクトの一覧 + * @throws IllegalArgumentException それぞれの引数が上記制約を満たさない場合 + * @throws NullPointerException 引数に{@code null}が含まれる場合 + */ + public InterfaceEnhancer( + Class<T> factoryInterface, + Class<?> productSuperClass, + List<? extends Enhance> enhanceList) { + if (factoryInterface == null) { + throw new NullPointerException("factoryInterface is null"); //$NON-NLS-1$ + } + if (productSuperClass == null) { + throw new NullPointerException("productSuperClass is null"); //$NON-NLS-1$ + } + if (enhanceList == null) { + throw new NullPointerException("enhanceList is null"); //$NON-NLS-1$ + } + checkFactoryInterfaceConstraint(factoryInterface); + checkProductSuperClassConstraint(productSuperClass); + this.factoryInterface = factoryInterface; + this.productInterfaces = computeProductInterfaces(factoryInterface); + this.productSuperClass = productSuperClass; + this.enhanceManager = new EnhanceManager(enhanceList); + this.converter = new JavassistConverter(factoryInterface); + } + + private static void checkFactoryInterfaceConstraint(Class<?> anInterface) { + assert anInterface != null; + int modifiers = anInterface.getModifiers(); + if (Modifier.isInterface(modifiers) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "The factory ({0}) must be an interface", + anInterface.getName())); + } + if (Modifier.isPublic(modifiers) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "The factory ({0}) must be public", + anInterface.getName())); + } + } + + private void checkProductSuperClassConstraint(Class<?> aClass) { + assert aClass != null; + if (ReflectionUtil.isNormalClass(aClass) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "The product super class ({0}) must be a normal class", + aClass.getName())); + } + int modifiers = aClass.getModifiers(); + if (Modifier.isPublic(modifiers) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "The product super class ({0}) must be public", + aClass.getName())); + } + if (Modifier.isFinal(modifiers)) { + throw new IllegalArgumentException(MessageFormat.format( + "The product super class ({0}) must not be final class", + aClass.getName())); + } + try { + aClass.getConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(MessageFormat.format( + "Product super class ({0}) must have a public non-parameters constructor", + aClass.getName()), + e); + } + } + + private static Set<Class<?>> computeProductInterfaces(Class<?> factoryInterface) { + assert factoryInterface != null; + Set<Class<?>> results = new HashSet<Class<?>>(); + for (Method method : factoryInterface.getMethods()) { + // java.lang.Objectで定義されたメソッドは無視 + if (method.getDeclaringClass() == Object.class) { + continue; + } + assert Modifier.isAbstract(method.getModifiers()) : method; + + Class<?> product = method.getReturnType(); + if (product.isInterface() == false) { + throw new IllegalArgumentException(MessageFormat.format( + "All product must be declared as an interface ({0})", + method)); + } + results.add(product); + } + return Collections.unmodifiableSet(results); + } + + private Class<? extends T> createImplementation() throws EnhanceException { + assert enhanceManager != null; + assert converter != null; + LOG.trace("Creating an implementation of factory: {}", factoryImplementation); + + Map<CtClass, CtClass> targetProducts = createProductMap(); + CtClass implementation = createFactoryImplementation(targetProducts); + + Map<CtClass, AspectList<CtMethod>> allProductAspects = + EnhanceManipulator.weavePointcutIntoAllProducts(enhanceManager, targetProducts); + AspectList<CtConstructor> factoryAspects = + weavePointcutIntoFactory(implementation, targetProducts, allProductAspects); + Class<?> installedFactory = + EnhanceManipulator + .install(converter, implementation, targetProducts, factoryAspects, allProductAspects); + + return installedFactory.asSubclass(factoryInterface); + } + + private AspectList<CtConstructor> weavePointcutIntoFactory( + CtClass implementation, + Map<CtClass, CtClass> targetProducts, + Map<CtClass, AspectList<CtMethod>> allProductAspects) throws EnhanceException { + assert implementation != null; + assert targetProducts != null; + assert allProductAspects != null; + + LOG.trace("Weaving pointcuts: {}", implementation.getName()); + return NewInstanceEnhancer.enhance(implementation, enhanceManager, targetProducts, allProductAspects); + } + + private Map<CtClass, CtClass> createProductMap() throws EnhanceException { + LOG.trace("Creating each product map: {}", productInterfaces); + Map<CtClass, CtClass> results = new HashMap<CtClass, CtClass>(); + CtClass baseClass = converter.loadCtClass(productSuperClass); + for (Class<?> productInterface : productInterfaces) { + CtClass baseInterface = converter.loadCtClass(productInterface); + CtClass productClass = EnhanceManipulator.createProductImplementation(baseInterface, baseClass); + results.put(baseInterface, productClass); + } + LOG.debug("Product map: {}", results); + return results; + } + + private CtClass createFactoryImplementation(Map<CtClass, CtClass> targetProducts) throws EnhanceException { + assert targetProducts != null; + CtClass factory = converter.loadCtClass(factoryInterface); + CtClass factoryImpl = EnhanceManipulator.createFactoryImplementation(factory, targetProducts); + return factoryImpl; + } + + private synchronized Class<? extends T> getImplemtation() throws EnhanceException { + if (factoryImplementation == null) { + this.factoryImplementation = createImplementation(); + + // prune javassist information + enhanceManager = null; + converter = null; + } + return this.factoryImplementation; + } + + /** + * 生成したファクトリをインスタンスかするためのメタファクトリを返す。 + * @throws EnhanceException ファクトリの拡張に失敗した場合 + */ + @Override + protected Factory<? extends T> createFactory() throws EnhanceException { + /* + * わざわざ別メソッドにしているのは、getEnhance()が返すクラスが + * ? extends T であり、これをキャプチャして名前のある型変数にする必要があるため。 + * Javaの言語仕様では、クラスインスタンス生成時にパラメータ化型を利用する場合、 + * その実型引数は型式であってはならない(ワイルドカードが使えない)。 + */ + return createFactory(getImplemtation()); + } + + private static <F>ReflectionFactory<F> createFactory(Class<F> aClass) { + return new ReflectionFactory<F>(aClass); + } +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-04 06:55:17 UTC (rev 3698) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -15,11 +15,20 @@ */ package org.jiemamy.utils.enhancer.helper; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import javassist.CannotCompileException; +import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; @@ -33,10 +42,12 @@ import org.slf4j.LoggerFactory; import org.jiemamy.utils.enhancer.EnhanceException; +import org.jiemamy.utils.enhancer.InvocationHandler; /** * エンハンスを行う際の定型的なクラス書き換えを行うためのライブラリ。 - * @version $Date$ + * @version 0.2.0 + * @since 0.2.0 * @author Suguru ARAKAWA (Gluegent, Inc.) */ public class EnhanceManipulator { @@ -60,10 +71,16 @@ * @param klass コピー元のクラス * @return 作成したクラス * @throws EnhanceException クラスのコピーに失敗した場合 + * @throws IllegalArgumentException 引数に通常のクラスでない型が指定された場合 + * @throws NullPointerException 引数に{@code null}が指定された場合 */ public static CtClass createCopyClass(CtClass klass) throws EnhanceException { - assert klass != null; - assert klass.isInterface() == false && klass.isEnum() == false; + if (klass == null) { + throw new NullPointerException("klass is null"); //$NON-NLS-1$ + } + if (klass.isInterface() || klass.isEnum()) { + throw new IllegalArgumentException(); + } LOG.trace("Creating a copy: {}", klass); String name = klass.getName(); try { @@ -86,11 +103,17 @@ * @param klass 元となるクラス * @return 対象のクラスを継承した新しいクラス * @throws EnhanceException クラスの作成に失敗した場合 + * @throws IllegalArgumentException 引数に通常のクラスでない型が指定された場合 + * @throws NullPointerException 引数に{@code null}が指定された場合 * @see #createDelegateConstructors(CtClass) */ public static CtClass createInheritedClass(CtClass klass) throws EnhanceException { - assert klass != null; - assert klass.isInterface() == false && klass.isEnum() == false; + if (klass == null) { + throw new NullPointerException("klass is null"); //$NON-NLS-1$ + } + if (klass.isInterface() || klass.isEnum()) { + throw new IllegalArgumentException(); + } LOG.trace("Creating an inherited class: {}", klass.getName()); CtClass copy = klass.getClassPool().makeClass(getEnhanceClassName(klass.getName())); copy.setModifiers(Modifier.PUBLIC | Modifier.FINAL); @@ -105,6 +128,348 @@ } /** + * 指定のインターフェースを実装した新しいクラスを作成して返す。 + * <p> + * このメソッドでは、主に次の3つが行われる。 + * </p> + * <ul> + * <li> ファクトリインターフェースを実装した新しいファクトリ実装クラスの作成 </li> + * <li> ファクトリインターフェースに含まれるすべてのメソッドについて + * <ul> + * <li> そのメソッドが生成するプロダクトの実装に対し、そのメソッドと同一の引数リストを取るコンストラクタの追加 </li> + * <li> 追加したコンストラクタを起動するメソッドをファクトリ実装クラス内に生成 </li> + * </ul> + * </li> + * </ul> + * @param factoryInterface 実装を生成する対象のファクトリインターフェース + * @param targetProducts プロダクトインターフェースと、その実装のマッピング + * @return 生成したファクトリ実装クラス + * @throws EnhanceException ファクトリ実装クラスの作成に失敗した場合 + * @since 0.2.0 + */ + public static CtClass createFactoryImplementation( + CtClass factoryInterface, + Map<CtClass, CtClass> targetProducts) throws EnhanceException { + if (factoryInterface == null) { + throw new IllegalArgumentException("factoryInterface is null"); //$NON-NLS-1$ + } + if (targetProducts == null) { + throw new IllegalArgumentException("targetProducts is null"); //$NON-NLS-1$ + } + LOG.trace("Creating a factory implementation: {}", factoryInterface.getName()); + ClassPool pool = factoryInterface.getClassPool(); + CtClass implementation = pool.makeClass(getEnhanceClassName(factoryInterface.getName())); + implementation.setModifiers(Modifier.PUBLIC); + implementation.setInterfaces(new CtClass[] { + factoryInterface + }); + + LOG.debug("A factory implementation: {} implements {}", + implementation.getName(), factoryInterface.getName()); + + // 二回計算してるので遅いが、たぶん重要じゃない + for (NameAndDescriptor identity : collectInterfaceMethods(factoryInterface)) { + implementFactoryMethod(implementation, identity.method, targetProducts); + } + return implementation; + } + + private static void implementFactoryMethod( + CtClass implementation, + CtMethod factoryMethod, + Map<CtClass, CtClass> targetProducts) throws EnhanceException { + assert implementation != null; + assert factoryMethod != null; + assert targetProducts != null; + LOG.trace("Implementing a factory method: {}", factoryMethod); + + CtClass productType; + try { + productType = factoryMethod.getReturnType(); + } catch (NotFoundException e) { + throw new EnhanceException(MessageFormat.format( + "Cannot detect product interface for {0}", + factoryMethod), + e); + } + CtClass productImpl = targetProducts.get(productType); + if (productImpl == null) { + throw new EnhanceException(MessageFormat.format( + "{0} is not a valid product interface", + productType.getName()), + null); + } + createProductConstructor(productImpl, factoryMethod); + createFactoryMethod(implementation, factoryMethod, productImpl); + } + + private static void createProductConstructor( + CtClass productImplementation, + CtMethod factoryMethod) throws EnhanceException { + assert productImplementation != null; + assert factoryMethod != null; + LOG.trace("Creating product constructor: {}", factoryMethod); + try { + if (isConstructorExist(productImplementation, factoryMethod.getParameterTypes())) { + LOG.debug("The product constructor already exists: {}<-{}", + productImplementation.getName(), factoryMethod.getSignature()); + return; + } + CtConstructor constructor = new CtConstructor( + factoryMethod.getParameterTypes(), + productImplementation); + constructor.setModifiers(Modifier.PUBLIC); + constructor.setBody(null); + productImplementation.addConstructor(constructor); + LOG.debug("Product constructor: {}{}", + productImplementation.getName(), constructor.getSignature()); + } catch (CannotCompileException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot create constructor {1}{2} to {0}", + productImplementation.getName(), + factoryMethod.getName(), + factoryMethod.getSignature()), + e); + } catch (NotFoundException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot create constructor {1}{2} to {0}", + productImplementation.getName(), + factoryMethod.getName(), + factoryMethod.getSignature()), + e); + } + } + + private static boolean isConstructorExist(CtClass aClass, CtClass[] parameterTypes) { + assert aClass != null; + assert parameterTypes != null; + try { + aClass.getDeclaredConstructor(parameterTypes); + return true; + } catch (NotFoundException e) { + return false; + } + } + + private static void createFactoryMethod( + CtClass factoryImplementation, + CtMethod factoryMethod, + CtClass productImplementation) throws EnhanceException { + assert factoryImplementation != null; + assert factoryMethod != null; + assert productImplementation != null; + LOG.trace("Creating factory method: {} -> new {}", factoryMethod, productImplementation.getName()); + try { + CtMethod factoryMethodImpl = new CtMethod( + factoryMethod.getReturnType(), + factoryMethod.getName(), + factoryMethod.getParameterTypes(), + factoryImplementation); + factoryMethodImpl.setModifiers(Modifier.PUBLIC); + factoryMethodImpl.setBody(String.format("return new %s($$);", //$NON-NLS-1$ + productImplementation.getName())); + factoryImplementation.addMethod(factoryMethodImpl); + LOG.trace("Factory method: {}{}{} -> new {}", new Object[] { + factoryImplementation.getName(), + factoryMethodImpl.getName(), + factoryMethodImpl.getSignature(), + productImplementation.getName() + }); + } catch (CannotCompileException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot create factory method {1}{2} to {0}", + factoryImplementation.getName(), + factoryMethod.getName(), + factoryMethod.getSignature()), + e); + } catch (NotFoundException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot create factory method {1}{2} to {0}", + factoryImplementation.getName(), + factoryMethod.getName(), + factoryMethod.getSignature()), + e); + } + } + + /** + * インターフェースプロダクトの実装クラスを生成して返す。 + * <p> + * 生成されるクラスは、指定された基本クラスとインターフェースをそれぞれ親にもち。 + * コンストラクタは定義されない(最後まで定義されないと、Javassistがデフォルトコンストラクタを生成する)。 + * </p> + * @param baseInterface 生成するクラスの親インターフェース + * @param baseClass 生成するクラスの親クラス + * @return 生成したクラス + * @throws EnhanceException クラスの生成に失敗した場合 + * @since 0.2.0 + */ + public static CtClass createProductImplementation( + CtClass baseInterface, + CtClass baseClass) throws EnhanceException { + if (baseInterface == null) { + throw new NullPointerException("baseInterface is null"); //$NON-NLS-1$ + } + if (baseClass == null) { + throw new NullPointerException("baseClass is null"); //$NON-NLS-1$ + } + LOG.trace("Creating product implementation: implements {} extends {}", + baseInterface.getName(), baseClass.getName()); + + ClassPool pool = baseInterface.getClassPool(); + CtClass implementation = pool.makeClass(getEnhanceClassName(baseInterface.getName())); + implementation.setModifiers(Modifier.PUBLIC | Modifier.FINAL); + try { + implementation.setSuperclass(baseClass); + } catch (CannotCompileException e) { + throw new EnhanceException(MessageFormat.format( + "Cannot inherit {0}", + baseClass.getName()), + e); + } + implementation.addInterface(baseInterface); + + LOG.debug("Implementation: {} extends {} implements {}", new Object[] { + implementation.getName(), + baseClass.getName(), + baseInterface.getName() + }); + + return implementation; + } + + /* + // 未検証だが、binary compatibilityの関係で、インターフェースメソッドが実装されていなくても問題なく動きそう + private static void createImplementationMethodStubs( + CtClass implementation, + CtClass baseInterface) throws EnhanceException { + assert implementation != null; + assert baseInterface != null; + LOG.trace("Creating implementation method stubs: {} -> {}", + implementation.getName(), baseInterface.getName()); + + Set<NameAndDescriptor> methods = collectInterfaceMethods(baseInterface); + for (NameAndDescriptor identity : methods) { + CtMethod method = getMethod(implementation, identity); + + // オーバーライドできないメソッドはスキップ + if (method != null && canOverride(method) == false) { + LOG.debug("A implemented method {}{} is final, so point cut is not created.", + method.getName(), method.getSignature()); + continue; + } + + // オーバーライドしてデリゲートか、未実装例外をスローするかどちらかのメソッドを追加 + addImplementationMethod(implementation, identity.method, method != null); + } + } + + private static boolean canOverride(CtMethod method) { + assert method != null; + int modifiers = method.getModifiers(); + return Modifier.isFinal(modifiers) == false + && Modifier.isStatic(modifiers) == false; + } + + private static void addImplementationMethod( + CtClass implementation, + CtMethod interfaceMethod, + boolean delegateSuper) throws EnhanceException { + assert implementation != null; + assert interfaceMethod != null; + assert isStatic(interfaceMethod) == false; + + LOG.trace("Implementing method: {}", interfaceMethod.getName()); + + CtMethod stub; + try { + stub = new CtMethod( + interfaceMethod.getReturnType(), + interfaceMethod.getName(), + interfaceMethod.getParameterTypes(), + implementation); + } catch (NotFoundException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot create stub method for {0} to {1}", + interfaceMethod, + implementation.getName()), + e); + } + stub.setModifiers(Modifier.PUBLIC); + try { + if (delegateSuper) { + // 親クラスのメソッドを呼び出し + if (isVoid(interfaceMethod)) { + stub.setBody(String.format( + "super.%s($$);", + interfaceMethod.getName())); + } else { + stub.setBody(String.format( + "return super.%s($$);", + interfaceMethod.getName())); + } + } else { + // 親クラスのメソッドがないので、AbstractMethodErrorをスローしておく + stub.setBody(String.format( + "throw new java.lang.AbstractMethodError(\"%s\");", + interfaceMethod.getName())); + } + } catch (CannotCompileException e) { + e.printStackTrace(); + } + } + + private static CtMethod getMethod(CtClass aClass, NameAndDescriptor method) { + try { + return aClass.getMethod(method.name, method.descriptor); + } catch (NotFoundException e) { + return null; + } + } + */ + + private static Set<NameAndDescriptor> collectInterfaceMethods(CtClass anInterface) throws EnhanceException { + assert anInterface != null; + assert anInterface.isInterface(); + Set<NameAndDescriptor> results = new HashSet<NameAndDescriptor>(); + + LinkedList<CtClass> work = new LinkedList<CtClass>(); + work.addFirst(anInterface); + Set<String> saw = new HashSet<String>(); + while (work.isEmpty() == false) { + CtClass target = work.removeFirst(); + if (saw.contains(target.getName())) { + continue; + } + saw.add(target.getName()); + for (CtMethod method : target.getDeclaredMethods()) { + NameAndDescriptor identity = new NameAndDescriptor(method); + if (results.contains(identity) == false) { + results.add(identity); + } + } + try { + for (CtClass superInterface : target.getInterfaces()) { + work.addFirst(superInterface); + } + } catch (NotFoundException e) { + throw new EnhanceException( + MessageFormat.format( + "Cannot resolve super interfaces for {0}", + target.getName()), + e); + } + } + + return results; + } + + /** * 親クラスで宣言されたコンストラクタに処理を委譲するコンストラクタを拡張クラス上に宣言する。 * <p> * 拡張クラス上に宣言される移譲コンストラクタは、移譲先のコンストラクタと同一の引数リストを持ち、 @@ -210,12 +575,11 @@ * @throws EnhanceException バイパスメソッドの作成に失敗した場合 * @see #createPointcutMethod(CtClass, CtMethod, CtField, int) */ - public static CtMethod createBypassMethod(CtClass enhance, CtMethod method, int index) throws EnhanceException { - + private static CtMethod createBypassMethod(CtClass enhance, CtMethod method, int index) throws EnhanceException { assert enhance != null; assert method != null; assert index >= 0; - assert enhance.subclassOf(method.getDeclaringClass()); + assert isSubtype(enhance, method.getDeclaringClass()); assert isStatic(method) == false; LOG.trace("Creating bypass method: {}#{}", method.getDeclaringClass().getName(), method.getName()); @@ -234,7 +598,9 @@ method.getSignature() }); bypass.setModifiers(Modifier.PUBLIC); - if (method.getReturnType() == CtClass.voidType) { + if (method.getDeclaringClass().isInterface()) { + bypass.setBody(String.format("throw new java.lang.AbstractMethodError(\"%s\");", method.getName())); + } else if (isVoid(method)) { bypass.setBody(String.format("super.%s($$);", method.getName())); } else { bypass.setBody(String.format("return super.%s($$);", method.getName())); @@ -250,6 +616,16 @@ } } + private static boolean isVoid(CtMethod method) { + assert method != null; + try { + return method.getReturnType() == CtClass.voidType; + } catch (NotFoundException e) { + // 解決できない時点でプリミティブでない + return false; + } + } + /** * {@code method}の動作をフックするポイントカットメソッドを拡張クラス上に作成して返す。 * <p> @@ -272,7 +648,7 @@ assert enhance != null; assert method != null; - assert enhance.subclassOf(method.getDeclaringClass()); + assert isSubtype(enhance, method.getDeclaringClass()); assert holder != null; assert enhance.equals(holder.getDeclaringClass()); assert index >= 0; @@ -292,7 +668,7 @@ }); pointcut.setModifiers(Modifier.PUBLIC); - if (method.getReturnType() == CtClass.voidType) { + if (isVoid(method)) { pointcut.setBody(String.format("%s[%d].invoke(this, $args);", holder.getName(), index)); } else { pointcut.setBody(String.format("return ($r) %s[%d].invoke(this, $args);", holder.getName(), index)); @@ -409,11 +785,12 @@ * @throws NullPointerException 引数に{@code null}が指定された場合 */ public static void replaceToPointcut(NewExpr target, CtField holder, int index) throws CannotCompileException { - if (target == null) { - throw new NullPointerException("expr"); + throw new NullPointerException("target is null"); //$NON-NLS-1$ } - + if (holder == null) { + throw new NullPointerException("holder is null"); //$NON-NLS-1$ + } CtBehavior contextBehaviour = target.where(); if (isStatic(contextBehaviour)) { target.replace(String.format("$_ = ($r) %s[%d].invoke(%s, $args);", //$NON-NLS-1$ @@ -425,6 +802,107 @@ } /** + * 指定のプロダクトクラス一覧に、それぞれアスペクト用のフックを織り込む。 + * <p> + * 実際に織り込まれるアスペクトの一覧は、{@code enhanceManager}を元に計算される。 + * このメソッドでは、{@code enhanceList}に規定される拡張の一覧を + * 実際に適用するためのフックのみを織り込む。 + * </p> + * @param enhanceManager + * エンハンスに関する情報を一意に管理するオブジェクト + * @param productsToBeEnhanced + * 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) + * @return + * 計算されたそれぞれのプロダクトクラスに対するメソッドアスペクトの一覧 + * ({@code base -> aspect for each method}) + * @throws EnhanceException 拡張に失敗した場合 + * @throws NullPointerException 引数に{@code null}が指定された場合 + */ + public static Map<CtClass, AspectList<CtMethod>> weavePointcutIntoAllProducts( + EnhanceManager enhanceManager, + Map<CtClass, CtClass> productsToBeEnhanced) throws EnhanceException { + if (enhanceManager == null) { + throw new NullPointerException("enhanceManager is null"); //$NON-NLS-1$ + } + if (productsToBeEnhanced == null) { + throw new NullPointerException("productsToBeEnhanced is null"); //$NON-NLS-1$ + } + Map<CtClass, AspectList<CtMethod>> allProductAspects = new HashMap<CtClass, AspectList<CtMethod>>(); + for (Map.Entry<CtClass, CtClass> entry : productsToBeEnhanced.entrySet()) { + CtClass base = entry.getKey(); + CtClass enhanced = entry.getValue(); + AspectList<CtMethod> aspects = weavePointcutIntoSingleProduct(enhanceManager, base, enhanced); + if (aspects != null) { + allProductAspects.put(base, aspects); + } + } + return allProductAspects; + } + + /** + * 単一のプロダクトクラスにアスペクト用のフックを織り込む。 + * <p> + * {@code enhance <: base} + * </p> + * @param enhanceManager エンハンスに関する情報を一意に管理するオブジェクト + * @param base 拡張される前のプロダクトクラス定義 + * @param enhance 拡張対象となるプロダクトクラス定義 + * @return 対象のプロダクトクラスに適用すべきアスペクトの一覧、ひとつも存在しない場合は{@code null} + * @throws EnhanceException 拡張に失敗した場合 + */ + private static AspectList<CtMethod> weavePointcutIntoSingleProduct( + EnhanceManager enhanceManager, + CtClass base, + CtClass enhance) throws EnhanceException { + assert enhanceManager != null; + assert base != null; + assert enhance != null; + assert isSubtype(enhance, base); + + LOG.trace("Weaving pointcuts: {}", enhance.getName()); + List<Aspect<CtMethod>> results = new ArrayList<Aspect<CtMethod>>(); + CtField holder = null; + int enhanceIndex = 0; + for (CtMethod method : base.getMethods()) { + if (enhanceManager.isLegalJoinpoint(method) == false) { + continue; + } + List<InvocationHandler> handlers = enhanceManager.findApplicableHandlers(base, method); + if (handlers.isEmpty()) { + continue; + } + + if (enhanceIndex == 0) { + // 最初の拡張メソッドを発見したら、参照するアドバイステーブルフィールドも作成する + holder = createAdviceTableField(enhance); + } + assert holder != null; + CtMethod bypass = createBypassMethod(enhance, method, enhanceIndex); + createPointcutMethod(enhance, method, holder, enhanceIndex); + results.add(new Aspect<CtMethod>(method, bypass, handlers)); + enhanceIndex++; + } + + if (results.isEmpty()) { + assert holder == null; + return null; + } else { + assert holder != null; + return new AspectList<CtMethod>(holder, results); + } + } + + private static boolean isSubtype(CtClass subtype, CtClass supertype) { + assert subtype != null; + assert supertype != null; + try { + return subtype.subtypeOf(supertype); + } catch (NotFoundException e) { + return false; + } + } + + /** * 指定のメンバが{@code static}として宣言されている場合のみ{@code true}を返す。 * @param member 対象のメンバ * @return @@ -436,6 +914,172 @@ return Modifier.isStatic(member.getModifiers()); } + /** + * 指定のファクトリクラスをVM上にインストールする。 + * @param converter {@link java.lang.Class}と{@link javassist.CtClass}を相互に変換する + * @param targetFactory ファクトリクラスの実装 + * @param targetProducts 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) + * @param factoryAspects ファクトリに実際に埋め込まれるべきアスペクトの一覧、ひとつも存在しない場合は{@code null} + * @param allProductAspects それぞれのプロダクトクラスに対するメソッドアスペクトの一覧 ({@code base -> aspect for each method})、 + * @return ロードしたファクトリクラス + * @throws EnhanceException いずれかのロードに失敗した場合 + * @since 0.2.0 + */ + public static Class<?> install( + JavassistConverter converter, + CtClass targetFactory, + Map<CtClass, CtClass> targetProducts, + AspectList<CtConstructor> factoryAspects, // can be null + Map<CtClass, AspectList<CtMethod>> allProductAspects) throws EnhanceException { + if (converter == null) { + throw new NullPointerException("converter is null"); //$NON-NLS-1$ + } + if (targetFactory == null) { + throw new NullPointerException("targetFactory is null"); //$NON-NLS-1$ + } + if (targetProducts == null) { + throw new NullPointerException("targetProducts is null"); //$NON-NLS-1$ + } + if (allProductAspects == null) { + throw new NullPointerException("allProductAspects is null"); //$NON-NLS-1$ + } + Map<CtClass, CtClass> restProducts = loadAndInitializeProducts(converter, targetProducts, allProductAspects); + for (CtClass klass : restProducts.values()) { + converter.toClass(klass); + } + Class<?> result = converter.toClass(targetFactory); + if (factoryAspects != null) { + registerAdviceTable(result, converter.toConstructorAspects(factoryAspects)); + } + return result; + } + + /** + * 拡張されるプロダクトの一覧をロードしたのち、それぞれを初期化する。 + * <p> + * {@code productAspects.base IN productsToBeEnhanced.base} + * </p> + * @param converter Javassistを利用してCtClassとClassを相互に変換するオブジェクト + * @param productsToBeEnhanced + * 拡張されるべきプロダクトクラスの一覧 ({@code base -> toBeEnhanced}) + * @param allProductAspects + * それぞれのプロダクトクラスに対するメソッドアスペクトの一覧 + * ({@code base -> aspect for each method}) + * @return + * アスペクトが適用されなかった拡張されるべきプロダクトクラスの一覧 + * ({@code base -> toBeEnhanced}) + * @throws EnhanceException 拡張に失敗した場合 + */ + private static Map<CtClass, CtClass> loadAndInitializeProducts( + JavassistConverter converter, + Map<CtClass, CtClass> productsToBeEnhanced, + Map<CtClass, AspectList<CtMethod>> allProductAspects) throws EnhanceException { + + assert productsToBeEnhanced != null; + assert allProductAspects != null; + + Map<CtClass, CtClass> rest = new HashMap<CtClass, CtClass>(); + rest.putAll(productsToBeEnhanced); + for (Map.Entry<CtClass, AspectList<CtMethod>> entry : allProductAspects.entrySet()) { + CtClass orig = entry.getKey(); + AspectList<CtMethod> aspects = entry.getValue(); + CtClass enhanced = rest.remove(orig); + AdviceTable table = converter.toMethodAspects(aspects); + registerAdviceTable(converter.toClass(enhanced), table); + } + return rest; + } + + /** + * 指定のクラスにアドバイステーブルの値を設定する。 + * @param klass 設定対象のクラス + * @param adviceTable 設定するアドバイステーブル + * @throws EnhanceException 設定に失敗した場合 + */ + private static void registerAdviceTable(Class<?> klass, AdviceTable adviceTable) throws EnhanceException { + + assert klass != null; + assert adviceTable != null; + + AspectList<?> aspects = adviceTable.getAspects(); + AdviceApplier[] appliers = adviceTable.toAppliers(); + LOG.debug("Initialize advice table: {}", adviceTable); + try { + Field field = klass.getField(aspects.getAdviceTableHolder().getName()); + field.set(null, appliers); + } catch (SecurityException e) { + throw new EnhanceException(MessageFormat.format("Cannot access to enhanced field for ({0})", klass + .getName()), e); + } catch (NoSuchFieldException e) { + // may not occur + throw new AssertionError(e); + } catch (IllegalArgumentException e) { + // may not occur + throw new AssertionError(e); + } catch (IllegalAccessException e) { + // may not occur + throw new AssertionError(e); + } + } + + + /** + * 名前とデスクリプタのペア。主にオーバーロード判定に利用する。 + * @version $Id$ + * @author Suguru ARAKAWA + */ + private static class NameAndDescriptor { + + final CtMethod method; + + final String name; + + final String descriptor; + + + /** + * インスタンスを生成する。 + * @param method 対象のメソッド + */ + public NameAndDescriptor(CtMethod method) { + assert method != null; + name = method.getName(); + descriptor = method.getSignature(); + this.method = method; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + descriptor.hashCode(); + result = prime * result + name.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NameAndDescriptor other = (NameAndDescriptor) obj; + if (descriptor.equals(other.descriptor) == false) { + return false; + } + if (name.equals(other.name) == false) { + return false; + } + return true; + } + } + + private EnhanceManipulator() { super(); } Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -0,0 +1,96 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/04 + * + * This file is part of Jiemamy. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.jiemamy.utils.enhancer; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Ignore; +import org.junit.Test; + +import org.jiemamy.utils.enhancer.aspect.StringResultPointcut; + +/** + * Test for {@link InterfaceEnhancer}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class InterfaceEnhancerTest { + + /** + * Test method for {@link org.jiemamy.utils.enhancer.InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Ignore + @Test + public void testInterfaceEnhancer() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_NoEnhance() throws Exception { + InterfaceEnhancer<SimpleInterfaceFactory> enhancer = new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + Object.class, + enhances()); + Factory<? extends SimpleInterfaceFactory> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactory factory = metaFactory.newInstance(); + InterfaceProduct product = factory.newProduct(); + try { + product.getMessage(); + fail(); + } catch (AbstractMethodError e) { + // ok. + } + } + + /** + * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_Enhanced() throws Exception { + Enhance enhance = new Enhance(new StringResultPointcut(), new InvocationHandler() { + + public Object handle(Invocation invocation) { + return "Hello"; + } + }); + InterfaceEnhancer<SimpleInterfaceFactory> enhancer = new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + Object.class, + enhances(enhance)); + Factory<? extends SimpleInterfaceFactory> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactory factory = metaFactory.newInstance(); + InterfaceProduct product = factory.newProduct(); + assertThat(product.getMessage(), is("Hello")); + assertThat(product.toString(), is("Hello")); + } + + private static List<Enhance> enhances(Enhance... enhances) { + return Arrays.asList(enhances); + } +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -0,0 +1,33 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/04 + * + * This file is part of Jiemamy. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.jiemamy.utils.enhancer; + +/** + * 単純なプロダクト。 + * @since 0.3 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public interface InterfaceProduct { + + /** + * @return nothing + */ + String getMessage(); +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java 2009-10-04 11:33:48 UTC (rev 3699) @@ -0,0 +1,32 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/04 + * + * This file is part of Jiemamy. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.jiemamy.utils.enhancer; + +/** + * 単純なインターフェースファクトリ。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public interface SimpleInterfaceFactory { + + /** + * @return {@link InterfaceProduct} + */ + InterfaceProduct newProduct(); +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native