svnno****@sourc*****
svnno****@sourc*****
2009年 10月 8日 (木) 22:05:37 JST
Revision: 3734 http://sourceforge.jp/projects/jiemamy/svn/view?view=rev&revision=3734 Author: ashigeru Date: 2009-10-08 22:05:37 +0900 (Thu, 08 Oct 2009) Log Message: ----------- factory-enhancer 0.2.0 RC Modified Paths: -------------- leto/factory-enhancer/trunk/pom.xml leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Invocation.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/FactoryEnhancerTest.java Added Paths: ----------- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/AbstractEnhancer.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Enhancer.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Factory.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ConcreteProduct.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProductEx.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ParameterConflictInterfaceFactory.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactoryEx.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java Removed Paths: ------------- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java Property Changed: ---------------- leto/factory-enhancer/trunk/ Property changes on: leto/factory-enhancer/trunk ___________________________________________________________________ Added: svn:mergeinfo + /leto/factory-enhancer/branches/interface-enhancer:3705-3733 /leto/factory-enhancer/branches/interface-enhancer-20091004:3694-3704 Modified: leto/factory-enhancer/trunk/pom.xml =================================================================== --- leto/factory-enhancer/trunk/pom.xml 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/pom.xml 2009-10-08 13:05:37 UTC (rev 3734) @@ -65,6 +65,12 @@ <artifactId>javassist</artifactId> <version>3.8.0.GA</version> </dependency> + <dependency> + <groupId>aopalliance</groupId> + <artifactId>aopalliance</artifactId> + <version>1.0</version> + <optional>true</optional> + </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/AbstractEnhancer.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/AbstractEnhancer.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/AbstractEnhancer.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/AbstractEnhancer.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,63 @@ +/* + * 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.util.concurrent.atomic.AtomicReference; + +/** + * {@link Enhancer}の骨格実装。 + * @since 0.2.0 + * @version 0.2.0 + * @author Suguru ARAKAWA + * @param <T> エンハンスする対象の種類 + */ +public abstract class AbstractEnhancer<T> implements Enhancer<T> { + + private AtomicReference<Factory<? extends T>> factoryCache = new AtomicReference<Factory<? extends T>>(); + + + /** + * エンハンスされたクラスのインスタンスを生成するファクトリを返す。 + * <p> + * この実装では、{@link #createFactory()}でファクトリを生成し、そのキャッシュを保持する。 + * </p> + * @return エンハンスされたクラスのインスタンスを生成するファクトリ + * @throws EnhanceException 拡張に失敗した場合 + */ + public Factory<? extends T> getFactory() throws EnhanceException { + Factory<? extends T> cached = factoryCache.get(); + if (cached != null) { + return cached; + } + Factory<? extends T> factory = createFactory(); + if (factoryCache.compareAndSet(null, factory)) { + return factory; + } + cached = factoryCache.get(); + assert cached != null; + return cached; + } + + /** + * 指定のクラスのインスタンスを生成するファクトリを返す。 + * @return 生成したファクトリ + * @throws EnhanceException ファクトリの生成に失敗した場合 + */ + protected abstract Factory<? extends T> createFactory() throws EnhanceException; +} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Enhancer.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/Enhancer.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Enhancer.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Enhancer.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * エンハンスされたクラスを参照するためのインターフェース。 + * @param <T> エンハンスする対象の種類 + * @version 0.2.0 + * @since 0.2.0 + * @author Suguru ARAKAWA + */ +public interface Enhancer<T> { + + /** + * エンハンスされたクラスのインスタンスを生成するファクトリを返す。 + * @return エンハンスされたクラスのインスタンスを生成するファクトリ + * @throws EnhanceException 拡張に失敗した場合 + * @sine 0.2.0 + */ + Factory<? extends T> getFactory() throws EnhanceException; +} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Factory.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/Factory.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Factory.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Factory.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,46 @@ +/* + * 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.InvocationTargetException; + +/** + * 特定の型のオブジェクトを生成するファクトリ。 + * @version 0.2.0 + * @since 0.2.0 + * @author Suguru ARAKAWA (Gluegent, Inc.) + * @param <T> 生成するインスタンスの型 + */ +public interface Factory<T> { + + /** + * このファクトリが生成するオブジェクトのクラスを返す。 + * @return このファクトリが生成するオブジェクトのクラス + */ + Class<T> getTargetClass(); + + /** + * 指定の引数を利用して、このファクトリが生成可能なオブジェクトを生成して返す。 + * @param arguments オブジェクトの生成に利用する引数の一覧 + * @return 生成したオブジェクト + * @throws IllegalArgumentException 指定された引数がオブジェクトの生成に適さない場合 + * @throws InvocationTargetException オブジェクトの生成中に例外が発生した場合 + */ + T newInstance(Object... arguments) throws InvocationTargetException; +} Modified: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/FactoryEnhancer.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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,22 +27,20 @@ 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; +import org.jiemamy.utils.enhancer.reflection.ReflectionFactory; /** * ファクトリを拡張するエンハンサ。 @@ -103,11 +96,11 @@ * さらに、コンパイラによって合成された特殊なメソッド(ブリッジメソッド等)は拡張対象とならない。 * </li> * </ul> - * @version $Date$ + * @version 0.2.0 * @author Suguru ARAKAWA (Gluegent, Inc.) * @param <T> 拡張するファクトリのインターフェース型 */ -public class FactoryEnhancer<T> { +public class FactoryEnhancer<T> extends AbstractEnhancer<T> { static final Logger LOG = LoggerFactory.getLogger(FactoryEnhancer.class); @@ -188,6 +181,21 @@ * </li> * </ul> * <p> + * 拡張対象の判定は、{@code enhanceList}に含まれるそれぞれの{@link Enhance}が返す、 + * {@link InvocationPointcut}によって行われる。 + * </p> + * <p> + * 単一のジョインポイント(メソッド起動やインスタンス生成)を複数のエンハンスが同時に拡張する場合、 + * 引数{@code enhanceList}に指定された順にハンドラが実行される。 + * たとえば、{@code enhanceList}に対してあるメソッド呼び出しに対して + * 同時に適用可能な拡張{@code [a, b, c]}が含まれる場合、 + * {@code a, b, c}の順に含まれる{@link InvocationHandler}が実行された後、 + * 最後の{@code c}から実際のメソッドが呼び出される。 + * これらの途中で後続する処理を実行しなかった場合、それらの拡張は実際には利用されない。 + * つまり、{@code b}の拡張がハンドラの内部で{@link Invocation#proceed()}を実行しなかった場合、 + * それに続く{@code c}および実際のメソッドは実行されないことになる。 + * </p> + * <p> * なお、プロダクトクラスが拡張される場合、このオブジェクトが作成するファクトリは * 拡張されたプロダクトクラスをインスタンス化して返す。 * ただし、拡張対象となるプロダクトクラスのうち、{@code public}でない @@ -213,7 +221,9 @@ * {@code factoryImplementation}が{@code java.lang.Object}の * 直接のサブクラスでない場合 */ - public FactoryEnhancer(Class<T> factoryInterface, Class<? extends T> factoryImplementation, + public FactoryEnhancer( + Class<T> factoryInterface, + Class<? extends T> factoryImplementation, List<? extends Enhance> enhanceList) { super(); if (factoryInterface == null) { @@ -299,6 +309,7 @@ * </p> * @return 拡張されたファクトリのクラスオブジェクト * @throws EnhanceException 拡張に失敗した場合 + * @see Enhancer#getFactory() */ public synchronized Class<? extends T> getEnhanced() throws EnhanceException { if (enhancedFactory == null) { @@ -310,6 +321,26 @@ } /** + * {@link #getEnhanced()}を利用して拡張されたファクトリクラスに対する、オブジェクトファクトリを返す。 + * @throws EnhanceException {@link #getEnhanced()}の実行に失敗した場合 + * @since 0.2.0 + */ + @Override + protected Factory<? extends T> createFactory() throws EnhanceException { + /* + * わざわざ別メソッドにしているのは、getEnhance()が返すクラスが + * ? extends T であり、これをキャプチャして名前のある型変数にする必要があるため。 + * Javaの言語仕様では、クラスインスタンス生成時にパラメータ化型を利用する場合、 + * その実型引数は型式であってはならない(ワイルドカードが使えない)。 + */ + return createFactory(getEnhanced()); + } + + private static <F>ReflectionFactory<F> createFactory(Class<F> aClass) { + return new ReflectionFactory<F>(aClass); + } + + /** * ファクトリクラスを拡張し、拡張したクラスを表現する{@link java.lang.Class}オブジェクトを返す。 * @return 拡張したクラスを表現する{@link java.lang.Class}オブジェクト * @throws EnhanceException 拡張に失敗した場合 @@ -322,21 +353,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 { @@ -351,73 +377,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}と @@ -453,88 +412,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}) Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,359 @@ +/* + * 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.jiemamy.utils.enhancer.helper.EnhanceManipulator.createFactoryImplementation; +import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.createProductImplementation; +import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.install; +import static org.jiemamy.utils.enhancer.helper.EnhanceManipulator.weavePointcutIntoAllProducts; + +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.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}は引数をとらない{@code public}コンストラクタを提供する </li> + * </ul> + * <p> + * このエンハンサによって拡張されたファクトリは、次のような性質を持つことになる。 + * </p> + * <ul> + * <li> + * ファクトリインターフェースの各メソッド(以下、ファクトリメソッド)は、 + * その戻り値型に指定されたインターフェース(以下、プロダクトインターフェース)を + * 実装するクラスのインスタンス(以下、プロダクトインスタンス)を返す。 + * </li> + * <li> + * プロダクトインターフェースの各メソッドに対して、{@code enhanceList}に指定された拡張が存在する場合、 + * 対応するプロダクトインスタンスの各メソッドはその拡張が適用された状態になっている。 + * </li> + * <li> + * 各ファクトリメソッドは、プロダクトインスタンスの生成に関するジョインポイントを提供する。 + * これに対して拡張を行う場合、次のようなポイントカットを用意する。 + * <ul> + * <li> + * {@link InvocationPointcut#isTarget(CtClass, javassist.CtBehavior)}の + * 第1引数に、対象のファクトリメソッドの戻り値の型(つまり、ファクトリインターフェース)が渡されることを許可する。 + * </li> + * <li> + * {@link InvocationPointcut#isTarget(CtClass, javassist.CtBehavior)}の + * 第2引数に、対象のファクトリメソッドと同じパラメータリストを持つコンストラクタが渡されることを許可する。 + * </li> + * </ul> + * このジョインポイントに対する{@link InvocationHandler}において、独自のクラスインスタンスを返した場合、 + * それらはプロダクトインスタンスとみなされない。 + * つまり、{@code enhanceList}に指定されたメソッドジョインポイントに関する拡張は、 + * それらのインスタンスに対して適用されない。 + * </li> + * </ul> + * <p> + * 拡張対象の判定は、{@code enhanceList}に含まれるそれぞれの{@link Enhance}が返す、 + * {@link InvocationPointcut}によって行われる。 + * </p> + * <p> + * 単一のジョインポイント(メソッド起動やインスタンス生成)を複数のエンハンスが同時に拡張する場合、 + * 引数{@code enhanceList}に指定された順にハンドラが実行される。 + * たとえば、{@code enhanceList}に対してあるメソッド呼び出しに対して + * 同時に適用可能な拡張{@code [a, b, c]}が含まれる場合、 + * {@code a, b, c}の順に含まれる{@link InvocationHandler}が実行された後、 + * 最後の{@code c}から実際のメソッドが呼び出される。 + * これらの途中で後続する処理を実行しなかった場合、それらの拡張は実際には利用されない。 + * つまり、{@code b}の拡張がハンドラの内部で{@link Invocation#proceed()}を実行しなかった場合、 + * それに続く{@code c}および実際のメソッドは実行されないことになる。 + * </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.isAbstract(modifiers)) { + throw new IllegalArgumentException(MessageFormat.format( + "The product super class ({0}) must not be abstract class", + 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)); + } + if (Modifier.isPublic(product.getModifiers()) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "All product interface must be public ({0})", + product)); + } + 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 = createFactory(targetProducts); + + Map<CtClass, AspectList<CtMethod>> allProductAspects = + weavePointcutIntoAllProducts(enhanceManager, targetProducts); + AspectList<CtConstructor> factoryAspects = + weavePointcutIntoFactory(implementation, targetProducts, allProductAspects); + Class<?> installedFactory = + 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 = createProductImplementation(baseInterface, baseClass); + results.put(baseInterface, productClass); + } + LOG.debug("Product map: {}", results); + return results; + } + + private CtClass createFactory(Map<CtClass, CtClass> targetProducts) throws EnhanceException { + assert targetProducts != null; + CtClass factory = converter.loadCtClass(factoryInterface); + CtClass factoryImpl = 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); + } +} Modified: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Invocation.java =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Invocation.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/Invocation.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -26,17 +26,22 @@ public interface Invocation { /** - * 拡張を行う前の本来の呼び出し、またはインスタンス生成を続行する。 + * 拡張を行う前の本来のメソッド起動、またはインスタンス生成を続行する。 * <p> - * このメソッドが返す値は、本来のメソッド呼び出しを実行した際の戻り値である。 + * このメソッドが返す値は、本来のメソッドを起動した際の戻り値である。 * ただし、戻り値の型がプリミティブ型である場合はそのラッパー型での表現を返し、 * {@code void}型である場合には{@code null}を常に返す。 * また、このオブジェクトがインスタンス生成を表す場合には、この呼び出しは実際に生成されたインスタンスを返す。 * </p> * <p> - * 本来の呼び出しを実行した際にその呼び出し先で何らかの例外が発生した場合、 + * 本来の起動を実行した際にその呼び出し先で何らかの例外が発生した場合、 * このメソッドはその例外を{@link InvocationTargetException}でラップした例外をスローする。 * </p> + * <p> + * なお、起動する対象が存在しない場合、{@link InvocationTargetException}がスローされるが + * その原因となる例外は{@link IncompatibleClassChangeError}を親とするクラスのインスタンスである。 + * たとえば、実装されてないインターフェースメソッドを起動した場合、{@link AbstractMethodError}が該当する。 + * </p> * @return * メソッド呼び出し、またはインスタンス生成の結果。 * ただし、{@code void}型のメソッドを呼び出した場合には{@code null}、または Modified: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -17,6 +17,7 @@ import javassist.CtBehavior; import javassist.CtClass; +import javassist.CtMethod; /** * このエンハンサにおいて、拡張する対象のメソッド呼び出しおよび @@ -31,7 +32,7 @@ * <p> * このインターフェースは、クライアントが実装してエンハンサに登録することができる。 * </p> - * @version $Date$ + * @version 0.2.0 * @author Suguru ARAKAWA (Gluegent, Inc.) * @see Pointcuts */ @@ -43,7 +44,11 @@ * {@code self}に渡される値は、対象のメソッドまたはコンストラクタを公開するクラスである。 * メソッドは継承によって子クラスで親クラスのメソッドを公開可能であるため、 * {@code behavior}に指定されるメンバを実際に宣言するクラスとは異なる場合がある。 - * なお、コンストラクタは継承できないため、{@code behavior}を宣言するクラスと必ず一致する。 + * コンストラクタの場合も同様に、対象のインスタンス生成対象がインターフェースであった場合、 + * インターフェースにコンストラクタは定義できないため、元のインターフェースが{@code self}に渡される。 + * 上記のため、 + * {@code CtBehavior#getDeclaringClass() behavior.getDeclaringClass()} + * の値を参考にせずに{@code self}を利用して対象を特定することが望ましい。 * </p> * <p> * メソッド呼び出しに対して検査を行う場合、引数の値は必ず @@ -51,6 +56,33 @@ * そうでなく、インスタンス生成に対して検査を行う場合、引数の値は必ず * {@link javassist.CtConstructor CtConstructor}型となる。 * </p> + * <p> + * なお、このポイントカットに対する{@link InvocationHandler ハンドラ}のメソッド + * {@link InvocationHandler#handle(Invocation)}は、次の要件を満たす必要がある。 + * </p> + * <ul> + * <li> メソッド起動に対するハンドラ + * <ul> + * <li> + * {@link CtMethod#getReturnType() ((CtMethod) behavior).getReturnType()} + * が{@code void}を表現する場合、どのような制約も課されない。 + * </li> + * <li> + * {@link CtMethod#getReturnType() ((CtMethod) behavior).getReturnType()} + * がプリミティブ型を表現する場合、ハンドラのメソッドはそのラッパ型の値を返す必要がある。 + * </li> + * <li> + * {@link CtMethod#getReturnType() ((CtMethod) behavior).getReturnType()} + * がオブジェクト型を表現する場合、ハンドラのメソッドはその型、またはそのサブタイプの値を返す必要がある。 + * </li> + * </ul> + * </li> + * <li> インスタンス生成に対するハンドラ + * <ul> + * <li> {@code self}が表現する型、またはそのサブタイプの値を返す必要がある。 </li> + * </ul> + * </li> + * </ul> * @param self このメソッドまたはコンストラクタを公開するクラス * @param behavior 検査対象のメソッドまたはコンストラクタ * @return ジョインポイントとして利用する場合は{@code true}、そうでない場合は{@code false} Deleted: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,302 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import org.aopalliance.intercept.ConstructorInterceptor; -import org.aopalliance.intercept.ConstructorInvocation; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import org.jiemamy.utils.enhancer.FactoryEnhancer; -import org.jiemamy.utils.enhancer.InterfaceEnhancer; -import org.jiemamy.utils.enhancer.Invocation; -import org.jiemamy.utils.enhancer.InvocationHandler; - -/** - * AOP Allianceが提供するインターセプタの定義を、Jiemamyが提供する - * {@link FactoryEnhancer}, {@link InterfaceEnhancer}でそれぞれ利用可能にするドライバ。 - * - * <p> - * それぞれのインターセプタに渡される{@link org.aopalliance.intercept.Invocation}は - * 本来の仕様と多少異なる場合がある。 - * たとえば、 {@link org.aopalliance.intercept.Invocation#getStaticPart()}は - * インターセプタのチェインがインストールされた実体を正確に指さない場合がある。 - * また、その値を利用する - * {@link MethodInvocation#getMethod()}, {@link ConstructorInvocation#getConstructor()}は - * なども同様に正しい値を返さない場合があるので、注意が必要である。 - * </p> - * - * @version 0.2.0 - * @since 0.2.0 - * @version $Id$ - * @author Suguru ARAKAWA - * @see <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a> - */ -public class AopAllianceDriver { - - /** - * 指定の{@link MethodInterceptor}に処理を委譲する{@link InvocationHandler}を生成して返す。 - * <p> - * 返されるハンドラがメソッド起動をハンドルした場合、 - * 引数に渡された {@link MethodInterceptor#invoke(MethodInvocation)}に対して処理を委譲する。 - * この引数に渡される{@link MethodInvocation}オブジェクトは、 - * {@link InvocationHandler}が受け取った{@link Invocation}オブジェクトをラップしたものである。 - * </p> - * <p> - * 返されるハンドラがインスタンス生成処理をハンドルした場合、 - * そのハンドラは次のハンドラまたは実際の処理を直接実行する ({@link Invocation#proceed()})。 - * </p> - * - * @param interceptor 委譲先のインターセプタ - * @return 引数に渡されたインターセプタ - * @throws NullPointerException 引数に{@code null}が指定された場合 - */ - public static InvocationHandler toHandler(MethodInterceptor interceptor) { - if (interceptor == null) { - throw new NullPointerException("interceptor is null"); //$NON-NLS-1$ - } - return new MethodInterceptorDriver(interceptor); - } - - /** - * 指定の{@link ConstructorInterceptor}に処理を委譲する{@link InvocationHandler}を生成して返す。 - * <p> - * 返されるハンドラがインスタンス生成処理をハンドルした場合、 - * 引数に渡された {@link ConstructorInterceptor#construct(ConstructorInvocation)}に対して処理を委譲する。 - * この引数に渡される{@link ConstructorInvocation}オブジェクトは、 - * {@link InvocationHandler}が受け取った{@link Invocation}オブジェクトをラップしたものである。 - * </p> - * <p> - * 返されるハンドラがメソッド起動をハンドルした場合、 - * そのハンドラは次のハンドラまたは実際の処理を直接実行する ({@link Invocation#proceed()})。 - * </p> - * - * @param interceptor 委譲先のインターセプタ - * @return 引数に渡されたインターセプタ - * @throws NullPointerException 引数に{@code null}が指定された場合 - */ - public static InvocationHandler toHandler(ConstructorInterceptor interceptor) { - if (interceptor == null) { - throw new IllegalArgumentException("interceptor is null"); //$NON-NLS-1$ - } - return new ConstructorInterceptorDriver(interceptor); - } - - /** - * {@link Invocation#proceed() invocation.proceed()}を実行する。 - * @param invocation {@link Invocation#proceed()}を実行する対象 - * @return {@code invocation.proceed()}の実行結果 - * @throws NullPointerException 引数に{@code null}が指定された場合 - * @throws IllegalArgumentException {@code invocation.proceed()}が同例外をスローする場合 - * @throws Throwable {@code invocation.proceed()}が例外を返す場合 - */ - static Object doProceed(Invocation invocation) throws Throwable { - if (invocation == null) { - throw new NullPointerException("invocation is null"); //$NON-NLS-1$ - } - try { - return invocation.proceed(); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } - - /** - * インスタンス生成の禁止。 - */ - private AopAllianceDriver() { - throw new AssertionError(); - } - - - /** - * {@link MethodInterceptor}をラップして{@link InvocationHandler}としての振る舞いを提供する。 - * - * @version $Id$ - * @author Suguru ARAKAWA - */ - private static class MethodInterceptorDriver implements InvocationHandler { - - private MethodInterceptor interceptor; - - - /** - * インスタンスを生成する。 - * - * @param interceptor ラップする {@link MethodInterceptor} - */ - public MethodInterceptorDriver(MethodInterceptor interceptor) { - assert interceptor != null; - this.interceptor = interceptor; - } - - public Object handle(Invocation invocation) throws Throwable { - if ((invocation.getTarget() instanceof Method) == false) { - return doProceed(invocation); - } - return interceptor.invoke(new MethodInvocationDriver(invocation)); - } - } - - /** - * {@link ConstructorInterceptor}をラップして{@link InvocationHandler}としての振る舞いを提供する。 - * - * @version $Id$ - * @author Suguru ARAKAWA - */ - private static class ConstructorInterceptorDriver implements InvocationHandler { - - private ConstructorInterceptor interceptor; - - - /** - * インスタンスを生成する。 - * - * @param interceptor ラップする {@link ConstructorInterceptor} - */ - public ConstructorInterceptorDriver(ConstructorInterceptor interceptor) { - assert interceptor != null; - this.interceptor = interceptor; - } - - public Object handle(Invocation invocation) throws Throwable { - if ((invocation.getTarget() instanceof Constructor<?>) == false) { - return doProceed(invocation); - } - return interceptor.construct(new ConstructorInvocationDriver(invocation)); - } - } - - /** - * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 - * {@link org.aopalliance.intercept.Invocation}としての振る舞いを提供する。 - * <p> - * ただし、AOP AllienceのAPIでは、 - * {@code org.aopalliance.intercept.Invocation}インターフェースを直接実装する - * クラスは存在せず、それぞれ{@link MethodInvocation}や{@link ConstructorInvocation} - * として提供しているようである。 - * このため、このクラスは抽象クラスとして提供し、そのサブクラスでそれぞれのインターフェースを実装している。 - * </p> - * @version $Id$ - * @author Suguru ARAKAWA - */ - private abstract static class AbstractInvocationDriver implements org.aopalliance.intercept.Invocation { - - /** - * ラップする{@link Invocation}。 - */ - protected final Invocation invocation; - - - /** - * インスタンスを生成する。 - * @param invocation 呼び出しを表現するオブジェクト - */ - public AbstractInvocationDriver(Invocation invocation) { - assert invocation != null; - this.invocation = invocation; - } - - public Object[] getArguments() { - return invocation.getArguments(); - } - - public AccessibleObject getStaticPart() { - return (AccessibleObject) invocation.getTarget(); - } - - public Object proceed() throws Throwable { - return doProceed(invocation); - } - } - - /** - * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 - * {@link MethodInvocation}としての振る舞いを提供する。 - * - * @version $Id$ - * @author Suguru ARAKAWA - */ - private static class MethodInvocationDriver extends AbstractInvocationDriver implements MethodInvocation { - - private Method executable; - - - /** - * インスタンスを生成する。 - * @param invocation 呼び出しを表現するオブジェクト - */ - public MethodInvocationDriver(Invocation invocation) { - super(invocation); - Member target = invocation.getTarget(); - assert target instanceof Method; - executable = (Method) target; - } - - public Object getThis() { - if (Modifier.isStatic(executable.getModifiers()) == false) { - return invocation.getInvoker(); - } - return null; - } - - public Method getMethod() { - return executable; - } - } - - /** - * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 - * {@link ConstructorInvocation}としての振る舞いを提供する。 - * - * @version $Id$ - * @author Suguru ARAKAWA - */ - private static class ConstructorInvocationDriver extends AbstractInvocationDriver implements ConstructorInvocation { - - private Constructor<?> executable; - - - /** - * インスタンスを生成する。 - * @param invocation 呼び出しを表現するオブジェクト - */ - public ConstructorInvocationDriver(Invocation invocation) { - super(invocation); - Member target = invocation.getTarget(); - assert target instanceof Constructor<?>; - executable = (Constructor<?>) target; - } - - public Object getThis() { - return null; - } - - public Constructor<?> getConstructor() { - return executable; - } - } -} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriver.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,302 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.aopalliance.intercept.ConstructorInterceptor; +import org.aopalliance.intercept.ConstructorInvocation; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import org.jiemamy.utils.enhancer.FactoryEnhancer; +import org.jiemamy.utils.enhancer.InterfaceEnhancer; +import org.jiemamy.utils.enhancer.Invocation; +import org.jiemamy.utils.enhancer.InvocationHandler; + +/** + * AOP Allianceが提供するインターセプタの定義を、Jiemamyが提供する + * {@link FactoryEnhancer}, {@link InterfaceEnhancer}でそれぞれ利用可能にするドライバ。 + * + * <p> + * それぞれのインターセプタに渡される{@link org.aopalliance.intercept.Invocation}は + * 本来の仕様と多少異なる場合がある。 + * たとえば、 {@link org.aopalliance.intercept.Invocation#getStaticPart()}は + * インターセプタのチェインがインストールされた実体を正確に指さない場合がある。 + * また、その値を利用する + * {@link MethodInvocation#getMethod()}, {@link ConstructorInvocation#getConstructor()}は + * なども同様に正しい値を返さない場合があるので、注意が必要である。 + * </p> + * + * @version 0.2.0 + * @since 0.2.0 + * @version $Id$ + * @author Suguru ARAKAWA + * @see <a href="http://aopalliance.sourceforge.net/">AOP Alliance</a> + */ +public class AopAllianceDriver { + + /** + * 指定の{@link MethodInterceptor}に処理を委譲する{@link InvocationHandler}を生成して返す。 + * <p> + * 返されるハンドラがメソッド起動をハンドルした場合、 + * 引数に渡された {@link MethodInterceptor#invoke(MethodInvocation)}に対して処理を委譲する。 + * この引数に渡される{@link MethodInvocation}オブジェクトは、 + * {@link InvocationHandler}が受け取った{@link Invocation}オブジェクトをラップしたものである。 + * </p> + * <p> + * 返されるハンドラがインスタンス生成処理をハンドルした場合、 + * そのハンドラは次のハンドラまたは実際の処理を直接実行する ({@link Invocation#proceed()})。 + * </p> + * + * @param interceptor 委譲先のインターセプタ + * @return 引数に渡されたインターセプタ + * @throws NullPointerException 引数に{@code null}が指定された場合 + */ + public static InvocationHandler toHandler(MethodInterceptor interceptor) { + if (interceptor == null) { + throw new NullPointerException("interceptor is null"); //$NON-NLS-1$ + } + return new MethodInterceptorDriver(interceptor); + } + + /** + * 指定の{@link ConstructorInterceptor}に処理を委譲する{@link InvocationHandler}を生成して返す。 + * <p> + * 返されるハンドラがインスタンス生成処理をハンドルした場合、 + * 引数に渡された {@link ConstructorInterceptor#construct(ConstructorInvocation)}に対して処理を委譲する。 + * この引数に渡される{@link ConstructorInvocation}オブジェクトは、 + * {@link InvocationHandler}が受け取った{@link Invocation}オブジェクトをラップしたものである。 + * </p> + * <p> + * 返されるハンドラがメソッド起動をハンドルした場合、 + * そのハンドラは次のハンドラまたは実際の処理を直接実行する ({@link Invocation#proceed()})。 + * </p> + * + * @param interceptor 委譲先のインターセプタ + * @return 引数に渡されたインターセプタ + * @throws NullPointerException 引数に{@code null}が指定された場合 + */ + public static InvocationHandler toHandler(ConstructorInterceptor interceptor) { + if (interceptor == null) { + throw new IllegalArgumentException("interceptor is null"); //$NON-NLS-1$ + } + return new ConstructorInterceptorDriver(interceptor); + } + + /** + * {@link Invocation#proceed() invocation.proceed()}を実行する。 + * @param invocation {@link Invocation#proceed()}を実行する対象 + * @return {@code invocation.proceed()}の実行結果 + * @throws NullPointerException 引数に{@code null}が指定された場合 + * @throws IllegalArgumentException {@code invocation.proceed()}が同例外をスローする場合 + * @throws Throwable {@code invocation.proceed()}が例外を返す場合 + */ + static Object doProceed(Invocation invocation) throws Throwable { + if (invocation == null) { + throw new NullPointerException("invocation is null"); //$NON-NLS-1$ + } + try { + return invocation.proceed(); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } + + /** + * インスタンス生成の禁止。 + */ + private AopAllianceDriver() { + throw new AssertionError(); + } + + + /** + * {@link MethodInterceptor}をラップして{@link InvocationHandler}としての振る舞いを提供する。 + * + * @version $Id$ + * @author Suguru ARAKAWA + */ + private static class MethodInterceptorDriver implements InvocationHandler { + + private MethodInterceptor interceptor; + + + /** + * インスタンスを生成する。 + * + * @param interceptor ラップする {@link MethodInterceptor} + */ + public MethodInterceptorDriver(MethodInterceptor interceptor) { + assert interceptor != null; + this.interceptor = interceptor; + } + + public Object handle(Invocation invocation) throws Throwable { + if ((invocation.getTarget() instanceof Method) == false) { + return doProceed(invocation); + } + return interceptor.invoke(new MethodInvocationDriver(invocation)); + } + } + + /** + * {@link ConstructorInterceptor}をラップして{@link InvocationHandler}としての振る舞いを提供する。 + * + * @version $Id$ + * @author Suguru ARAKAWA + */ + private static class ConstructorInterceptorDriver implements InvocationHandler { + + private ConstructorInterceptor interceptor; + + + /** + * インスタンスを生成する。 + * + * @param interceptor ラップする {@link ConstructorInterceptor} + */ + public ConstructorInterceptorDriver(ConstructorInterceptor interceptor) { + assert interceptor != null; + this.interceptor = interceptor; + } + + public Object handle(Invocation invocation) throws Throwable { + if ((invocation.getTarget() instanceof Constructor<?>) == false) { + return doProceed(invocation); + } + return interceptor.construct(new ConstructorInvocationDriver(invocation)); + } + } + + /** + * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 + * {@link org.aopalliance.intercept.Invocation}としての振る舞いを提供する。 + * <p> + * ただし、AOP AllienceのAPIでは、 + * {@code org.aopalliance.intercept.Invocation}インターフェースを直接実装する + * クラスは存在せず、それぞれ{@link MethodInvocation}や{@link ConstructorInvocation} + * として提供しているようである。 + * このため、このクラスは抽象クラスとして提供し、そのサブクラスでそれぞれのインターフェースを実装している。 + * </p> + * @version $Id$ + * @author Suguru ARAKAWA + */ + private abstract static class AbstractInvocationDriver implements org.aopalliance.intercept.Invocation { + + /** + * ラップする{@link Invocation}。 + */ + protected final Invocation invocation; + + + /** + * インスタンスを生成する。 + * @param invocation 呼び出しを表現するオブジェクト + */ + public AbstractInvocationDriver(Invocation invocation) { + assert invocation != null; + this.invocation = invocation; + } + + public Object[] getArguments() { + return invocation.getArguments(); + } + + public AccessibleObject getStaticPart() { + return (AccessibleObject) invocation.getTarget(); + } + + public Object proceed() throws Throwable { + return doProceed(invocation); + } + } + + /** + * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 + * {@link MethodInvocation}としての振る舞いを提供する。 + * + * @version $Id$ + * @author Suguru ARAKAWA + */ + private static class MethodInvocationDriver extends AbstractInvocationDriver implements MethodInvocation { + + private Method executable; + + + /** + * インスタンスを生成する。 + * @param invocation 呼び出しを表現するオブジェクト + */ + public MethodInvocationDriver(Invocation invocation) { + super(invocation); + Member target = invocation.getTarget(); + assert target instanceof Method; + executable = (Method) target; + } + + public Object getThis() { + if (Modifier.isStatic(executable.getModifiers()) == false) { + return invocation.getInvoker(); + } + return null; + } + + public Method getMethod() { + return executable; + } + } + + /** + * {@link org.jiemamy.utils.enhancer.Invocation}をラップして、 + * {@link ConstructorInvocation}としての振る舞いを提供する。 + * + * @version $Id$ + * @author Suguru ARAKAWA + */ + private static class ConstructorInvocationDriver extends AbstractInvocationDriver implements ConstructorInvocation { + + private Constructor<?> executable; + + + /** + * インスタンスを生成する。 + * @param invocation 呼び出しを表現するオブジェクト + */ + public ConstructorInvocationDriver(Invocation invocation) { + super(invocation); + Member target = invocation.getTarget(); + assert target instanceof Constructor<?>; + executable = (Constructor<?>) target; + } + + public Object getThis() { + return null; + } + + public Constructor<?> getConstructor() { + return executable; + } + } +} Deleted: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,172 +0,0 @@ -/* - * Copyright 2009 the Seasar Foundation and the Others. - * - * 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.driver; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Proxy; - -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtConstructor; - -import org.jiemamy.utils.enhancer.Enhance; -import org.jiemamy.utils.enhancer.Invocation; -import org.jiemamy.utils.enhancer.InvocationHandler; -import org.jiemamy.utils.enhancer.InvocationPointcut; - -/** - * {@link java.lang.reflect.Proxy}のインスタンスを生成するための{@link Enhance}を作成する。 - * <p> - * このクラスを利用して作成したエンハンスは、インターフェースのインスタンス生成に関するもののみである。 - * そのため、 - * {@link org.jiemamy.utils.enhancer.FactoryEnhancer}でこれを利用することはできず - * {@link org.jiemamy.utils.enhancer.InterfaceEnhancer}のみで利用できる。 - * </p> - * <p> - * また、このクラスを利用して作成したエンハンスは、インスタンス生成時に既定のインスタンス生成プロセスを利用せず、 - * {@code java.lang.reflect.Proxy}を利用してインスタンスを直接生成する。 - * このため、コンストラクタ起動に対するエンハンスのチェインがあった場合においても、 - * このクラスを利用して作成したエンハンスは、後続するエンハンスのチェインを利用しない。 - * そのため、インスタンス生成に対してさらに拡張を行う場合、このエンハンスは - * エンハンスチェインの末尾におかれる必要がある。 - * </p> - * @version $Date$ - * @author Suguru ARAKAWA - */ -public class ProxyDriver { - - /** - * {@link java.lang.reflect.Proxy}を利用したエンハンスを作成する。 - * <p> - * 返されるエンハンスは、指定のインターフェースが表すインターフェースプロダクトを作成する際に、 - * 本来のプロダクトインスタンスではなく{@link java.lang.reflect.Proxy}を利用した - * プロクシインスタンスを生成して返す。 - * その際に利用する{@link java.lang.reflect.InvocationHandler}は - * 引数に指定したものを利用する。 - * このハンドラオブジェクトは、同一のエンハンスが生成するすべてのプロクシインスタンスで共有する。 - * <p> - * @param anInterface Proxyを作成する対象のインターフェース - * @param handler 指定のインターフェースに対するメソッド呼び出しをハンドルするハンドラ - * これはfactory-enhancerの{@link InvocationHandler}ではなく、 - * 標準の{@link java.lang.reflect.InvocationHandler}である - * @return 指定のインターフェースのメソッドを指定のハンドラでフックするProxyを生成するエンハンス - * @throws IllegalArgumentException 引数{@code anInterface}がProxyを作成できない型である場合 - * @throws NullPointerException 引数に{@code null}が指定された場合 - */ - public static Enhance newEnhance(Class<?> anInterface, java.lang.reflect.InvocationHandler handler) { - if (anInterface == null) { - throw new NullPointerException("anInterface is null"); //$NON-NLS-1$ - } - if (handler == null) { - throw new NullPointerException("handler is null"); //$NON-NLS-1$ - } - - // Proxyクラスを作って、それをインスタンス化するコンストラクタを取り出す - Class<?> proxyClass = Proxy.getProxyClass(anInterface.getClassLoader(), anInterface); - Constructor<?> constructor; - try { - constructor = proxyClass.getConstructor(java.lang.reflect.InvocationHandler.class); - } catch (NoSuchMethodException e) { - throw new AssertionError(e); - } - return new Enhance( - new ProxyPointcut(anInterface.getName()), - new ProxyHandler(constructor, handler)); - } - - /** - * インスタンス生成の禁止。 - */ - private ProxyDriver() { - throw new AssertionError(); - } - - - /** - * プロクシ作成の対象となるインターフェースプロダクト生成へのポイントカット。 - * @version $Date$ - * @author Suguru ARAKAWA - */ - private static class ProxyPointcut implements InvocationPointcut { - - /** - * Proxyインスタンス生成の対象とするインターフェースの名前 - */ - private String targetName; - - - /** - * インスタンスを生成する。 - * @param targetName 対象インターフェースの名称 - */ - public ProxyPointcut(String targetName) { - assert targetName != null; - this.targetName = targetName; - } - - public boolean isTarget(CtClass self, CtBehavior behavior) { - // インスタンス生成のみ - if ((behavior instanceof CtConstructor) == false) { - return false; - } - - // 対象インターフェースのみ - if (self.getName().equals(targetName) == false) { - return false; - } - return true; - } - } - - private static class ProxyHandler implements InvocationHandler { - - /** - * Proxyインスタンスを生成するためのコンストラクタ。 - */ - private Constructor<?> proxyCreator; - - /** - * Proxyインスタンスに渡す{@code java.lang.reflect.InvocationHandler}。 - */ - private java.lang.reflect.InvocationHandler handler; - - - /** - * インスタンスを生成する。 - * @param proxyCreator 該当Proxyのインスタンスを生成するコンストラクタ - * @param handler 該当Proxyの各メソッド呼び出しに対するハンドラ - */ - public ProxyHandler( - Constructor<?> proxyCreator, - java.lang.reflect.InvocationHandler handler) { - assert proxyCreator != null; - assert handler != null; - this.proxyCreator = proxyCreator; - this.handler = handler; - } - - /** - * この実装では{@code invocation.proceed()}を実行せずに、 - * 代わりにProxyインスタンスを生成して返す。 - */ - public Object handle(Invocation invocation) throws Throwable { - assert invocation != null; - return proxyCreator.newInstance(new Object[] { - handler - }); - } - } -} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/driver/ProxyDriver.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,172 @@ +/* + * Copyright 2009 the Seasar Foundation and the Others. + * + * 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.driver; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Proxy; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; + +import org.jiemamy.utils.enhancer.Enhance; +import org.jiemamy.utils.enhancer.Invocation; +import org.jiemamy.utils.enhancer.InvocationHandler; +import org.jiemamy.utils.enhancer.InvocationPointcut; + +/** + * {@link java.lang.reflect.Proxy}のインスタンスを生成するための{@link Enhance}を作成する。 + * <p> + * このクラスを利用して作成したエンハンスは、インターフェースのインスタンス生成に関するもののみである。 + * そのため、 + * {@link org.jiemamy.utils.enhancer.FactoryEnhancer}でこれを利用することはできず + * {@link org.jiemamy.utils.enhancer.InterfaceEnhancer}のみで利用できる。 + * </p> + * <p> + * また、このクラスを利用して作成したエンハンスは、インスタンス生成時に既定のインスタンス生成プロセスを利用せず、 + * {@code java.lang.reflect.Proxy}を利用してインスタンスを直接生成する。 + * このため、コンストラクタ起動に対するエンハンスのチェインがあった場合においても、 + * このクラスを利用して作成したエンハンスは、後続するエンハンスのチェインを利用しない。 + * そのため、インスタンス生成に対してさらに拡張を行う場合、このエンハンスは + * エンハンスチェインの末尾におかれる必要がある。 + * </p> + * @version $Date$ + * @author Suguru ARAKAWA + */ +public class ProxyDriver { + + /** + * {@link java.lang.reflect.Proxy}を利用したエンハンスを作成する。 + * <p> + * 返されるエンハンスは、指定のインターフェースが表すインターフェースプロダクトを作成する際に、 + * 本来のプロダクトインスタンスではなく{@link java.lang.reflect.Proxy}を利用した + * プロクシインスタンスを生成して返す。 + * その際に利用する{@link java.lang.reflect.InvocationHandler}は + * 引数に指定したものを利用する。 + * このハンドラオブジェクトは、同一のエンハンスが生成するすべてのプロクシインスタンスで共有する。 + * <p> + * @param anInterface Proxyを作成する対象のインターフェース + * @param handler 指定のインターフェースに対するメソッド呼び出しをハンドルするハンドラ + * これはfactory-enhancerの{@link InvocationHandler}ではなく、 + * 標準の{@link java.lang.reflect.InvocationHandler}である + * @return 指定のインターフェースのメソッドを指定のハンドラでフックするProxyを生成するエンハンス + * @throws IllegalArgumentException 引数{@code anInterface}がProxyを作成できない型である場合 + * @throws NullPointerException 引数に{@code null}が指定された場合 + */ + public static Enhance newEnhance(Class<?> anInterface, java.lang.reflect.InvocationHandler handler) { + if (anInterface == null) { + throw new NullPointerException("anInterface is null"); //$NON-NLS-1$ + } + if (handler == null) { + throw new NullPointerException("handler is null"); //$NON-NLS-1$ + } + + // Proxyクラスを作って、それをインスタンス化するコンストラクタを取り出す + Class<?> proxyClass = Proxy.getProxyClass(anInterface.getClassLoader(), anInterface); + Constructor<?> constructor; + try { + constructor = proxyClass.getConstructor(java.lang.reflect.InvocationHandler.class); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + return new Enhance( + new ProxyPointcut(anInterface.getName()), + new ProxyHandler(constructor, handler)); + } + + /** + * インスタンス生成の禁止。 + */ + private ProxyDriver() { + throw new AssertionError(); + } + + + /** + * プロクシ作成の対象となるインターフェースプロダクト生成へのポイントカット。 + * @version $Date$ + * @author Suguru ARAKAWA + */ + private static class ProxyPointcut implements InvocationPointcut { + + /** + * Proxyインスタンス生成の対象とするインターフェースの名前 + */ + private String targetName; + + + /** + * インスタンスを生成する。 + * @param targetName 対象インターフェースの名称 + */ + public ProxyPointcut(String targetName) { + assert targetName != null; + this.targetName = targetName; + } + + public boolean isTarget(CtClass self, CtBehavior behavior) { + // インスタンス生成のみ + if ((behavior instanceof CtConstructor) == false) { + return false; + } + + // 対象インターフェースのみ + if (self.getName().equals(targetName) == false) { + return false; + } + return true; + } + } + + private static class ProxyHandler implements InvocationHandler { + + /** + * Proxyインスタンスを生成するためのコンストラクタ。 + */ + private Constructor<?> proxyCreator; + + /** + * Proxyインスタンスに渡す{@code java.lang.reflect.InvocationHandler}。 + */ + private java.lang.reflect.InvocationHandler handler; + + + /** + * インスタンスを生成する。 + * @param proxyCreator 該当Proxyのインスタンスを生成するコンストラクタ + * @param handler 該当Proxyの各メソッド呼び出しに対するハンドラ + */ + public ProxyHandler( + Constructor<?> proxyCreator, + java.lang.reflect.InvocationHandler handler) { + assert proxyCreator != null; + assert handler != null; + this.proxyCreator = proxyCreator; + this.handler = handler; + } + + /** + * この実装では{@code invocation.proceed()}を実行せずに、 + * 代わりにProxyインスタンスを生成して返す。 + */ + public Object handle(Invocation invocation) throws Throwable { + assert invocation != null; + return proxyCreator.newInstance(new Object[] { + handler + }); + } + } +} Modified: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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,264 @@ } /** + * 指定のインターフェースを実装した新しいクラスを作成して返す。 + * <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() + }); + + // http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#13.5.3 + // Adding a method to an interface does not break compatibility with pre-existing binaries. + // とのことで、ここでは baseInterface を初期状態でメソッドを持たないインターフェースとみなし、 + // そこに現在の baseInterface に対して現在のメソッドが追加されたものとする。 + // そう考えた場合、作成したクラス implementation はいくつかのメソッドを実装しない具象クラスとなってソースコード的には不正であるものの + // バイナリ的には問題なくリンクできる。 + + return implementation; + } + + 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> * 拡張クラス上に宣言される移譲コンストラクタは、移譲先のコンストラクタと同一の引数リストを持ち、 @@ -133,7 +414,7 @@ LOG.debug("Skip delegate constructor: {}{}", base.getName(), constructor.getSignature()); continue; } - if (constructor.getName().charAt(0) == '<') { + if (isClassInitializer(constructor)) { continue; } try { @@ -155,6 +436,16 @@ } /** + * 指定のオブジェクトが、コンストラクタではなくクラス初期化子を表現する場合にのみ{@code true}を返す。 + * @param behavior 対象のコンストラクタまたはクラス初期化子 + * @return 引数がクラス初期化子である場合に{@code true} + */ + private static boolean isClassInitializer(CtConstructor behavior) { + assert behavior != null; + return behavior.getName().charAt(0) == '<'; + } + + /** * {@code method}に指定されたメソッドを呼び出すバイパスメソッドを拡張クラス上に新しく作成して返す。 * <p> * バイパスメソッドは、拡張元のクラスが公開するメソッドを安全に呼び出すための方法を提供する。 @@ -188,6 +479,11 @@ * } * </code></pre> * <p> + * このとき、バイパス対象がインターフェースメソッドである場合、親のメソッドを呼び出そうとすると + * リンクエラーになってしまう。 + * そのため、かわりに{@link AbstractMethodError}をスローする。 + * </p> + * <p> * なお、このメソッドでは拡張するクラスにバイパスメソッドを追加するだけで、 * アスペクトを処理するメソッドについては * {@link #createPointcutMethod(CtClass, CtMethod, CtField, int)} @@ -200,12 +496,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()); @@ -213,7 +508,7 @@ try { CtMethod bypass = new CtMethod(method.getReturnType(), getBypassMethodName(method, index), - method.getParameterTypes(), enhance); + method.getParameterTypes(), enhance); LOG.debug("Bypass method: {}#{}{} -> {}#{}{}", new Object[] { bypass.getDeclaringClass().getName(), @@ -224,10 +519,26 @@ method.getSignature() }); bypass.setModifiers(Modifier.PUBLIC); - if (method.getReturnType() == CtClass.voidType) { - bypass.setBody(String.format("super.%s($$);", method.getName())); + if (isDeclaredInClass(method, enhance.getSuperclass()) == false) { + LOG.debug("Bypass target {}{} is not declared in {}; this throws {}", new Object[] { + method.getName(), + method.getSignature(), + enhance.getSuperclass().getName(), + AbstractMethodError.class.getName() + }); + bypass.setBody(String.format("throw new %s(\"%s\");", AbstractMethodError.class.getName(), method + .getName())); } else { - bypass.setBody(String.format("return super.%s($$);", method.getName())); + LOG.debug("Bypass target {}{} is in {}; this just invokes it", new Object[] { + method.getName(), + method.getSignature(), + enhance.getSuperclass().getName() + }); + if (isVoid(method)) { + bypass.setBody(String.format("super.%s($$);", method.getName())); + } else { + bypass.setBody(String.format("return super.%s($$);", method.getName())); + } } enhance.addMethod(bypass); return bypass; @@ -241,6 +552,35 @@ } /** + * 指定のメソッドが指定のクラスで実際に宣言されている場合のみ{@code true}を返す。 + * @param method 対象のメソッド + * @param aClass 対象のクラス + * @return 実際に宣言されている場合に{@code true} + */ + private static boolean isDeclaredInClass(CtMethod method, CtClass aClass) { + assert method != null; + if (aClass == null) { + return false; + } + try { + aClass.getMethod(method.getName(), method.getSignature()); + return true; + } catch (NotFoundException e) { + return false; + } + } + + private static boolean isVoid(CtMethod method) { + assert method != null; + try { + return method.getReturnType() == CtClass.voidType; + } catch (NotFoundException e) { + // 解決できない時点でプリミティブでない + return false; + } + } + + /** * {@code method}の動作をフックするポイントカットメソッドを拡張クラス上に作成して返す。 * <p> * ポイントカットメソッドは、拡張元のクラスが公開するメソッドを上書きして @@ -262,7 +602,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; @@ -282,7 +622,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)); @@ -393,28 +733,146 @@ * {@link #createAdviceTableField(CtClass)}によって作成されるフィールドに含まれている。 * </p> * @param target フックする対象のインスタンス生成式 + * @param resultType 生成するインスタンス型の上限境界 + * (本来実行するインスタンス生成式に対して、拡張前のプログラムが要求する型がインスタンス型のスーパータイプである可能性がある) * @param holder アドバイスを保持するフィールド * @param index 拡張番号 * @throws CannotCompileException コンパイルに失敗した場合 * @throws NullPointerException 引数に{@code null}が指定された場合 + * @since 0.2.0 */ - public static void replaceToPointcut(NewExpr target, CtField holder, int index) throws CannotCompileException { - + public static void replaceToPointcut( + NewExpr target, + CtClass resultType, + CtField holder, + int index) throws CannotCompileException { if (target == null) { - throw new NullPointerException("expr"); + throw new NullPointerException("target is null"); //$NON-NLS-1$ } - + if (resultType == null) { + throw new NullPointerException("resultType 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$ - holder.getName(), index, contextBehaviour.getDeclaringClass().getName() + ".class")); //$NON-NLS-1$ + target.replace(String.format("$_ = (%s) %s[%d].invoke(%s, $args);", //$NON-NLS-1$ + resultType.getName(), + holder.getName(), + index, + contextBehaviour.getDeclaringClass().getName() + ".class")); //$NON-NLS-1$ } else { - target.replace(String.format("$_ = ($r) %s[%d].invoke(this, $args);", //$NON-NLS-1$ - holder.getName(), index)); + target.replace(String.format("$_ = (%s) %s[%d].invoke(this, $args);", //$NON-NLS-1$ + resultType.getName(), + holder.getName(), + index)); } } /** + * 指定のプロダクトクラス一覧に、それぞれアスペクト用のフックを織り込む。 + * <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; + // TODO getMethodsの挙動 where base.isInterface() + 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 @@ -426,6 +884,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(); } Modified: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -17,6 +17,7 @@ import java.text.MessageFormat; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,7 +38,7 @@ /** * ファクトリに含まれるインスタンス生成式を拡張する。 - * @version $Date$ + * @version 0.2.0 * @author Suguru ARAKAWA (Gluegent, Inc.) */ public class NewInstanceEnhancer extends ExprEditor { @@ -67,7 +68,9 @@ * ファクトリに実際に埋め込まれるべきアスペクトの一覧、ひとつも存在しない場合は{@code null} * @throws EnhanceException 拡張に失敗した場合 */ - public static AspectList<CtConstructor> enhance(CtClass target, EnhanceManager enhanceManager, + public static AspectList<CtConstructor> enhance( + CtClass target, + EnhanceManager enhanceManager, Map<? extends CtClass, ? extends CtClass> productsToBeEnhanced, Map<? extends CtClass, AspectList<CtMethod>> allProductAspects) throws EnhanceException { @@ -107,6 +110,8 @@ private final Map<? extends CtClass, ? extends CtClass> productEnhanceMap; + private final Map<CtClass, CtClass> reverseProductEnhanceMap; + private final Map<? extends CtClass, AspectList<CtMethod>> allProductAspects; private final List<Aspect<CtConstructor>> aspects; @@ -127,6 +132,7 @@ this.target = target; this.enhanceManager = enhanceManager; productEnhanceMap = productsToBeEnhanced; + reverseProductEnhanceMap = reverse(productsToBeEnhanced); this.allProductAspects = allProductAspects; aspects = new ArrayList<Aspect<CtConstructor>>(); enhanceIndex = 0; @@ -134,6 +140,22 @@ } /** + * マップのキーと値を転置した新しいマップを返す。 + * @param <K> キーの型 + * @param <V> 値の型 + * @param map 転置するマップ + * @return 転置後のマップ、値が重複していた場合の処理は保証しない + */ + private <K, V>Map<V, K> reverse(Map<? extends K, ? extends V> map) { + assert map != null; + Map<V, K> results = new HashMap<V, K>(); + for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) { + results.put(entry.getValue(), entry.getKey()); + } + return results; + } + + /** * それぞれのインスタンス生成式を必要ならば書き換える。 */ @Override @@ -144,12 +166,12 @@ if (enhanceManager.isLegalJoinpoint(constructor) == false) { return; } - CtClass declaring = constructor.getDeclaringClass(); - List<InvocationHandler> handlers = enhanceManager.findApplicableHandlers(declaring, constructor); + CtClass provider = computeProvider(constructor); + List<InvocationHandler> handlers = enhanceManager.findApplicableHandlers(provider, constructor); // 書き換えても挙動がまったく変化しないため、何も行わないで終了 - if (handlers.isEmpty() && productEnhanceMap.containsKey(declaring) == false - && allProductAspects.containsKey(declaring) == false) { + if (handlers.isEmpty() && productEnhanceMap.containsKey(provider) == false + && allProductAspects.containsKey(provider) == false) { return; } @@ -160,25 +182,25 @@ LOG.debug("Rewrite new[{}]: {} (at {}{}:{})", new Object[] { enhanceIndex, - declaring.getName(), + provider.getName(), expr.where().getName(), expr.where().getSignature(), expr.getLineNumber() }); // new Hoge(...)の部分を、アドバイスの実行に置き換える - EnhanceManipulator.replaceToPointcut(expr, holder, enhanceIndex); + EnhanceManipulator.replaceToPointcut(expr, provider, holder, enhanceIndex); - if (productEnhanceMap.containsKey(declaring)) { + if (productEnhanceMap.containsKey(provider)) { // newする対象のクラス自体がエンハンスされて別クラスになっている場合、 // 呼び出し先のコンストラクタはエンハンス後のクラス上で定義されたものに変更 - CtClass actual = productEnhanceMap.get(declaring); + CtClass actual = productEnhanceMap.get(provider); String descriptor = constructor.getSignature(); CtConstructor actualCtor = actual.getConstructor(descriptor); aspects.add(new Aspect<CtConstructor>(constructor, actualCtor, handlers)); } else { // newする対象自体が同一である場合、呼び出し先のコンストラクタは変更なし - assert allProductAspects.containsKey(declaring) == false; + assert allProductAspects.containsKey(provider) == false; aspects.add(new Aspect<CtConstructor>(constructor, constructor, handlers)); } @@ -189,4 +211,25 @@ throw new CannotCompileException(e); } } + + /** + * このコンストラクタの本来の提供者を返す。 + * <p> + * 不明の場合、コンストラクタを宣言したクラスをそのまま返す。 + * </p> + * @param constructor 対象のコンストラクタ + * @return このコンストラクタの本来の提供者 + */ + private CtClass computeProvider(CtConstructor constructor) { + assert constructor != null; + CtClass declaring = constructor.getDeclaringClass(); + if (productEnhanceMap.containsKey(declaring)) { + return declaring; + } + CtClass original = reverseProductEnhanceMap.get(declaring); + if (original != null) { + return original; + } + return declaring; + } } Deleted: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,126 +0,0 @@ -/* - * 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.reflection; - -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findConstructor; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.toParameterTypes; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.jiemamy.utils.enhancer.Factory; - -/** - * Reflection APIを利用してオブジェクトを生成するファクトリの実装。 - * @version 0.2.0 - * @since 0.2.0 - * @author Suguru ARAKAWA (Gluegent, Inc.) - * @param <T> 生成するオブジェクトの型 - */ -public class ReflectionFactory<T> implements Factory<T> { - - private Class<T> targetClass; - - - /** - * インスタンスを生成する。 - * <p> - * 対象のクラスは、次のようなクラスでなければならない。 - * </p> - * <ul> - * <li> 通常の具象クラスである(インターフェース、列挙、抽象クラスではない) </li> - * <li> パッケージメンバである(トップレベルクラス) </li> - * <li> {@code public}で宣言されている </li> - * </ul> - * @param targetClass ファクトリが生成するインスタンスのクラス - * @throws IllegalArgumentException 引数のクラスが不正である場合 - */ - public ReflectionFactory(Class<T> targetClass) { - if (targetClass == null) { - throw new IllegalArgumentException("targetClass is null"); //$NON-NLS-1$ - } - if (ReflectionUtil.isNormalClass(targetClass) == false) { - throw new IllegalArgumentException(MessageFormat.format( - "{0} is not a normal class", - targetClass)); - } - if (Modifier.isAbstract(targetClass.getModifiers())) { - throw new IllegalArgumentException(MessageFormat.format( - "{0} is not a concrete class", - targetClass)); - } - if (ReflectionUtil.isPackageMember(targetClass) == false) { - throw new IllegalArgumentException(MessageFormat.format( - "{0} is not a package member", - targetClass)); - } - if (Modifier.isPublic(targetClass.getModifiers()) == false) { - throw new IllegalArgumentException(MessageFormat.format( - "{0} is not public", - targetClass)); - } - this.targetClass = targetClass; - } - - public Class<T> getTargetClass() { - return targetClass; - } - - public T newInstance(Object... arguments) throws InvocationTargetException { - if (arguments == null) { - throw new IllegalArgumentException("arguments is null"); //$NON-NLS-1$ - } - List<Constructor<T>> constructors = new ArrayList<Constructor<T>>(); - for (Constructor<?> ctor : targetClass.getConstructors()) { - @SuppressWarnings("unchecked") - Constructor<T> tCtor = (Constructor<T>) ctor; - constructors.add(tCtor); - } - List<Class<?>> parameterTypes = toParameterTypes(arguments); - Collection<Constructor<T>> targets = - findConstructor(constructors, parameterTypes); - if (targets.isEmpty()) { - throw new IllegalArgumentException(MessageFormat.format( - "No applicable constructors in {0} for {1}", - targetClass.getName(), - parameterTypes)); - } - if (targets.size() >= 2) { - throw new IllegalArgumentException(MessageFormat.format( - "Ambiguous target constructors ({0}) for {1}", - targets, - parameterTypes)); - } - Constructor<T> target = targets.iterator().next(); - try { - return target.newInstance(arguments); - } catch (IllegalAccessException e) { - // may not occur - throw new AssertionError(e); - } catch (InstantiationException e) { - // may not occur - throw new AssertionError(e); - } - } -} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactory.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,126 @@ +/* + * 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.reflection; + +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findConstructor; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.toParameterTypes; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.jiemamy.utils.enhancer.Factory; + +/** + * Reflection APIを利用してオブジェクトを生成するファクトリの実装。 + * @version 0.2.0 + * @since 0.2.0 + * @author Suguru ARAKAWA (Gluegent, Inc.) + * @param <T> 生成するオブジェクトの型 + */ +public class ReflectionFactory<T> implements Factory<T> { + + private Class<T> targetClass; + + + /** + * インスタンスを生成する。 + * <p> + * 対象のクラスは、次のようなクラスでなければならない。 + * </p> + * <ul> + * <li> 通常の具象クラスである(インターフェース、列挙、抽象クラスではない) </li> + * <li> パッケージメンバである(トップレベルクラス) </li> + * <li> {@code public}で宣言されている </li> + * </ul> + * @param targetClass ファクトリが生成するインスタンスのクラス + * @throws IllegalArgumentException 引数のクラスが不正である場合 + */ + public ReflectionFactory(Class<T> targetClass) { + if (targetClass == null) { + throw new IllegalArgumentException("targetClass is null"); //$NON-NLS-1$ + } + if (ReflectionUtil.isNormalClass(targetClass) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "{0} is not a normal class", + targetClass)); + } + if (Modifier.isAbstract(targetClass.getModifiers())) { + throw new IllegalArgumentException(MessageFormat.format( + "{0} is not a concrete class", + targetClass)); + } + if (ReflectionUtil.isPackageMember(targetClass) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "{0} is not a package member", + targetClass)); + } + if (Modifier.isPublic(targetClass.getModifiers()) == false) { + throw new IllegalArgumentException(MessageFormat.format( + "{0} is not public", + targetClass)); + } + this.targetClass = targetClass; + } + + public Class<T> getTargetClass() { + return targetClass; + } + + public T newInstance(Object... arguments) throws InvocationTargetException { + if (arguments == null) { + throw new IllegalArgumentException("arguments is null"); //$NON-NLS-1$ + } + List<Constructor<T>> constructors = new ArrayList<Constructor<T>>(); + for (Constructor<?> ctor : targetClass.getConstructors()) { + @SuppressWarnings("unchecked") + Constructor<T> tCtor = (Constructor<T>) ctor; + constructors.add(tCtor); + } + List<Class<?>> parameterTypes = toParameterTypes(arguments); + Collection<Constructor<T>> targets = + findConstructor(constructors, parameterTypes); + if (targets.isEmpty()) { + throw new IllegalArgumentException(MessageFormat.format( + "No applicable constructors in {0} for {1}", + targetClass.getName(), + parameterTypes)); + } + if (targets.size() >= 2) { + throw new IllegalArgumentException(MessageFormat.format( + "Ambiguous target constructors ({0}) for {1}", + targets, + parameterTypes)); + } + Constructor<T> target = targets.iterator().next(); + try { + return target.newInstance(arguments); + } catch (IllegalAccessException e) { + // may not occur + throw new AssertionError(e); + } catch (InstantiationException e) { + // may not occur + throw new AssertionError(e); + } + } +} Deleted: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,518 +0,0 @@ -/* - * 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.reflection; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * リフレクションに関するユーティリティ群。 - * @version $Date$ - * @author Suguru ARAKAWA (Gluegent, Inc.) - * @version 0.2.0 - * @since 0.2.0 - */ -public class ReflectionUtil { - - private static final Map<Class<?>, Class<?>> BOXING; - static { - Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>(); - map.put(Integer.TYPE, Integer.class); - map.put(Long.TYPE, Long.class); - map.put(Float.TYPE, Float.class); - map.put(Double.TYPE, Double.class); - map.put(Short.TYPE, Short.class); - map.put(Byte.TYPE, Byte.class); - map.put(Character.TYPE, Character.class); - map.put(Boolean.TYPE, Boolean.class); - BOXING = Collections.unmodifiableMap(map); - } - - - /** - * インスタンス生成の禁止。 - */ - private ReflectionUtil() { - throw new AssertionError(); - } - - /** - * 指定のオブジェクトが、列挙やインターフェース等でない通常のクラスを表現する場合のみ{@code true}を返す。 - * @param aClass 対象のクラスオブジェクト - * @return 通常のクラスを表現する場合に{@code true} - * @throws IllegalArgumentException 引数に{@code null}が指定された場合 - */ - public static boolean isNormalClass(Class<?> aClass) { - if (aClass == null) { - throw new IllegalArgumentException("aClass is null"); //$NON-NLS-1$ - } - return aClass.isPrimitive() == false - && aClass.isArray() == false - && aClass.isInterface() == false - && aClass.isEnum() == false; - } - - /** - * 指定のクラスがパッケージメンバ(トップレベルクラス)である場合のみ{@code true}を返す。 - * @param aClass 対象のクラスオブジェクト - * @return 指定のクラスがパッケージメンバである場合に{@code true} - * @throws IllegalArgumentException 引数に{@code null}が指定された場合 - */ - public static boolean isPackageMember(Class<?> aClass) { - if (aClass == null) { - throw new IllegalArgumentException("aClass is null"); //$NON-NLS-1$ - } - return aClass.isPrimitive() == false - && aClass.isArray() == false - && aClass.isMemberClass() == false - && aClass.isLocalClass() == false - && aClass.isAnonymousClass() == false; - } - - /** - * 引数の一覧をそのクラスの一覧に変換して返す。 - * <p> - * 引数の要素に{@code null}が指定された場合、対応する結果のクラスも{@code null}となる。 - * このクラスが提供するほかのメソッドでは、クラスに{@code null}が指定された場合に - * {@code null}型を表現するため、このメソッドの実行結果をそのまま利用できる。 - * </p> - * @param arguments 実引数の一覧 - * @return 対応する型引数の一覧 - * @throws IllegalArgumentException 引数に{@code null}が指定された場合 - */ - public static List<Class<?>> toParameterTypes(Object... arguments) { - if (arguments == null) { - throw new IllegalArgumentException("arguments is null"); //$NON-NLS-1$ - } - List<Class<?>> results = new ArrayList<Class<?>>(arguments.length); - for (Object argument : arguments) { - if (argument != null) { - results.add(argument.getClass()); - } else { - results.add(null); - } - } - return results; - } - - /** - * 指定の名前と実引数の型で適用可能なコンストラクタを、指定の一覧の中から検出して返す。 - * <p> - * 複数のメソッドが同時に適用可能である場合、その中から最大限に限定的であるものを計算して返す。 - * ただし、その結果が単一のコンストラクタとなるとは限らない。 - * </p> - * @param <T> それぞれのコンストラクタが生成するインスタンスの型 - * @param constructors 候補のコンストラクタ一覧 - * @param argumentTypes 起動時に利用する実引数の型一覧、この要素に{@code null}が - * 含まれる場合、それは{@code null}型を表現する - * @return 適用可能なコンストラクタのうち限定的であるもの、発見できない場合は空のリスト - * @throws IllegalArgumentException 引数に{@code null}が含まれる場合 - */ - public static <T>Collection<Constructor<T>> findConstructor( - Collection<Constructor<T>> constructors, - List<Class<?>> argumentTypes) { - if (constructors == null) { - throw new IllegalArgumentException("constructors is null"); //$NON-NLS-1$ - } - if (argumentTypes == null) { - throw new IllegalArgumentException("argumentTypes is null"); //$NON-NLS-1$ - } - - Class<?>[] argTypeArray = argumentTypes.toArray( - new Class<?>[argumentTypes.size()]); - - List<Executable<Constructor<T>>> results = - findPotentiallyApplicables(constructors, argTypeArray); - - filterApplicables(results, argTypeArray); - filterMaximallySpecifics(results); - return unpack(results); - } - - /** - * 指定の名前と実引数の型で適用可能なメソッドを、指定の一覧の中から検出して返す。 - * <p> - * 複数のメソッドが同時に適用可能である場合、その中から最大限に限定的であるものを計算して返す。 - * ただし、その結果が単一のメソッドとなるとは限らない。 - * </p> - * @param methods 候補のメソッド一覧 - * @param name 起動するメソッドの名前 - * @param argumentTypes 起動時に利用する実引数の型一覧、この要素に{@code null}が - * 含まれる場合、それは{@code null}型を表現する - * @return 適用可能なメソッドのうち限定的であるもの、発見できない場合は空のリスト - * @throws IllegalArgumentException 引数に{@code null}が含まれる場合 - */ - public static Collection<Method> findMethod( - Collection<Method> methods, - String name, - List<Class<?>> argumentTypes) { - if (methods == null) { - throw new IllegalArgumentException("methods is null"); //$NON-NLS-1$ - } - if (name == null) { - throw new IllegalArgumentException("name is null"); //$NON-NLS-1$ - } - if (argumentTypes == null) { - throw new IllegalArgumentException("argumentTypes is null"); //$NON-NLS-1$ - } - - Class<?>[] argTypeArray = argumentTypes.toArray( - new Class<?>[argumentTypes.size()]); - - LinkedList<Executable<Method>> results = - findPotentiallyApplicables(methods, name, argTypeArray); - - filterApplicables(results, argTypeArray); - filterMaximallySpecifics(results); - return unpack(results); - } - - private static <T extends Member>List<T> unpack(List<Executable<T>> packed) { - assert packed != null; - List<T> results = new ArrayList<T>(); - for (Executable<T> exec : packed) { - results.add(exec.target); - } - return results; - } - - /** - * メソッドの一覧から、潜在的に適用可能な起動候補の一覧を返す。 - * <p> - * 潜在的に適用可能とは、名前、および実引数の数と仮引数の数が一致することを指す。 - * </p> - * @param candidates 起動候補の一覧 - * @param name 起動するメソッドの名前 - * @param argumentTypes 実引数型の一覧 - * @return 潜在的に適用可能な起動候補の一覧 - */ - private static LinkedList<Executable<Method>> findPotentiallyApplicables( - Collection<Method> candidates, - String name, - Class<?>[] argumentTypes) { - assert candidates != null; - assert name != null; - assert argumentTypes != null; - LinkedList<Executable<Method>> results = - new LinkedList<Executable<Method>>(); - for (Method m : candidates) { - // 名前が一致すること - if (m.getName().equals(name) == false) { - continue; - } - // 実引数と仮引数の個数が一致すること - Class<?>[] paramTypes = m.getParameterTypes(); - if (paramTypes.length != argumentTypes.length) { - continue; - } - results.add(new Executable<Method>(m, paramTypes)); - } - return results; - } - - /** - * コンストラクタの一覧から、潜在的に適用可能な起動候補の一覧を返す。 - * <p> - * 潜在的に適用可能とは、実引数の数と仮引数の数が一致することを指す。 - * </p> - * @param <T> コンストラクタが対象とするインスタンスの種類 - * @param candidates 起動候補の一覧 - * @param argumentTypes 実引数型の一覧 - * @return 潜在的に適用可能な起動候補の一覧 - */ - private static <T>LinkedList<Executable<Constructor<T>>> findPotentiallyApplicables( - Collection<Constructor<T>> candidates, - Class<?>[] argumentTypes) { - assert candidates != null; - assert argumentTypes != null; - LinkedList<Executable<Constructor<T>>> results = - new LinkedList<Executable<Constructor<T>>>(); - for (Constructor<T> c : candidates) { - // 実引数と仮引数の個数が一致すること - Class<?>[] parameterTypes = c.getParameterTypes(); - if (argumentTypes.length != parameterTypes.length) { - continue; - } - // 引数を適用できるコンストラクタがひとつとは限らないので全部列挙 - results.add(new Executable<Constructor<T>>(c, parameterTypes)); - } - return results; - } - - /** - * 起動対象の候補の中から、指定の実引数を適用できないものを除外する。 - * @param <T> 起動候補の種類 - * @param candidates 対象の起動候補 - * @param argumentTypes 実引数型の一覧 - */ - private static <T extends Member>void filterApplicables( - Collection<Executable<T>> candidates, - Class<?>[] argumentTypes) { - assert candidates != null; - assert argumentTypes != null; - for (Iterator<Executable<T>> iter = candidates.iterator(); iter - .hasNext();) { - Executable<T> exec = iter.next(); - // 実引数を仮引数に適用できること - if (isApplicable(argumentTypes, exec.parameterTypes) == false) { - iter.remove(); - } - } - } - - /** - * 実引数の型を仮引数の型に適用できる場合のみ{@code true}を返す。 - * <p> - * これは、実引数と仮引数の個数が一致し、それぞれの実引数の型が - * 対応する仮引数の型のサブタイプであることを検査する。 - * </p> - * @param argumentTypes 実引数の型一覧 - * @param parameterTypes 仮引数の型一覧 - * @return 実引数の型を仮引数の型に適用できる場合に{@code true} - */ - private static boolean isApplicable( - Class<?>[] argumentTypes, - Class<?>[] parameterTypes) { - assert argumentTypes != null; - assert parameterTypes != null; - // 引数の個数が一致していないと失敗 - if (argumentTypes.length != parameterTypes.length) { - return false; - } - // ひとつでも適用できない実引数があれば失敗 - for (int i = 0; i < argumentTypes.length; i++) { - if (isApplicable(argumentTypes[i], parameterTypes[i]) == false) { - return false; - } - } - return true; - } - - /** - * 指定の型を持つ実引数を、指定の型を持つ仮引数に適用できる場合のみ{@code true}を返す。 - * <p> - * 実引数、仮引数のいずれか一方<em>のみ</em>がプリミティブ型を表現する場合、 - * その型を対応するラッパー形に変換した後、 - * </p> - * @param argumentType 実引数の型 - * @param parameterType 仮引数の型 - * @return 実引数を駆り引数に適用できる場合のみ{@code true} - */ - private static boolean isApplicable(Class<?> argumentType, Class<?> parameterType) { - // 引数型がnullやvoidであることはありえない - assert parameterType != null; - assert parameterType != void.class; - - // SomeType v = null; は常にOK - if (isNullType(argumentType)) { - return true; - } - // Class.isAssignbleFrom(Class) で簡易検査 - if (parameterType.isAssignableFrom(argumentType)) { - return true; - } - - boolean primitiveArgument = argumentType.isPrimitive(); - boolean primitiveParameter = parameterType.isPrimitive(); - if (primitiveArgument && primitiveParameter == false) { - Class<?> boxed = BOXING.get(argumentType); - assert boxed != null; - return isApplicable(boxed, parameterType); - } - if (primitiveParameter && primitiveArgument == false) { - Class<?> boxed = BOXING.get(parameterType); - assert boxed != null; - return isApplicable(argumentType, boxed); - } - - return false; - } - - /** - * 指定の値が{@code null}型を表現する場合にのみ{@code true}を返す。 - * @param aClass 対象の値 - * @return 指定の値が{@code null}型を表現する場合にのみ{@code true} - */ - private static boolean isNullType(Class<?> aClass) { - return aClass == null; - } - - /** - * 起動候補から最大限に限定的でないものを除外する。 - * @param <T> 起動候補の種類 - * @param candidates 対象の起動候補 - */ - private static <T extends Member>void filterMaximallySpecifics( - List<Executable<T>> candidates) { - assert candidates != null; - - // 引数リストのテーブルを作る - Class<?>[][] table = new Class<?>[candidates.size()][]; - for (int i = 0; i < table.length; i++) { - table[i] = candidates.get(i).parameterTypes; - } - - for (int i = 0; i < table.length; i++) { - if (table[i] == null) { - continue; // less applicableですでに候補から外れてる - } - // ms[i]のほうがms[j]よりもmore applicableなら、ms[j]を候補から消す - // 本来は「厳密に限定的」である必要があるが、ここでは省略 - // FIXME for f(.., int, ..), f(.., Integer, ..) - for (int j = 0; j < table.length; j++) { - if (i == j) { - continue; // 自分自身はスルー - } - if (table[j] == null) { - continue; // less applicableですでに候補から外れてる - } - if (isMoreSpecific(table[i], table[j])) { - table[j] = null; // less applicable は除去 - } - } - } - - // 限定的でないものを除外 - int index = 0; - for (Iterator<Executable<T>> iter = candidates.iterator(); iter.hasNext();) { - iter.next(); - if (table[index] == null) { - iter.remove(); - } - index++; - } - } - - /** - * 第1引数に含まれるクラスの一覧が、第2引数に含まれるクラスの一覧よりも限定的である場合に - * {@code true}を返す。 - * <p> - * クラスの列{@code a}がクラスの列{@code b}よりも限定的であるとは、 - * 次の条件をすべて満たす場合に限られる。 - * </p> - * <ul> - * <li> すべての{@code i}において、{@code a[i] <: b[i]}である </li> - * <li> {@code a[i] < b[i]}であるような{@code i}が1つ以上存在する </li> - * </ul> - * <p> - * このとき、{@code a}, {@code b}の要素数は同一でなければならない。 - * また、いずれかの配列に含まれる{@code null}は、{@code null}型を表現するものとする。 - * </p> - * @param a 限定的であることが検査される型の一覧 - * @param b 限定的であることを検査する型の一覧 - * @return {@code a}が{@code b}よりも限定的である場合に{@code true} - */ - private static boolean isMoreSpecific(Class<?>[] a, Class<?>[] b) { - assert a != null; - assert b != null; - assert a.length == b.length; - boolean result = false; - for (int i = 0; i < a.length; i++) { - if (a[i] == b[i]) { - // 同じ型なら次の引数で判定 - } else if (isMoreSpecific(a[i], b[i])) { - // a[i]とb[i]が違う型で、a[i]をb[i]に適用できると、aのほうが限定的になる可能性がある - result = true; - } else { - // それ以外では、aとbは関係ない型なので、aはbよりも限定的とはいえない - return false; - } - } - return result; - } - - /** - * {@code a}が{@code b}よりも限定的である場合にのみ{@code true}を返す。 - * <p> - * このメソッドはプリミティブ型の取り扱いが{@link #isApplicable(Class, Class)}と多少異なる。 - * ボクシング変換が利用されるのは{@code a}のみで、{@code b}には適用されない。 - * </p> - * <ul> - * <li> {@code a <: b} </li> - * <li> {@code boxing(a) <: b} </li> - * </ul> - * @param a 比較される型 - * @param b 比較する型 - * @return {@code a}が{@code b}よりも限定的である場合に{@code true} - */ - private static boolean isMoreSpecific(Class<?> a, Class<?> b) { - assert a != null; - assert b != null; - // Class.isAssignbleFrom(Class) で簡易検査 - if (b.isAssignableFrom(a)) { - return true; - } - if (a.isPrimitive()) { - Class<?> aBoxed = BOXING.get(a); - assert aBoxed != null; - return b.isAssignableFrom(aBoxed); - } - return false; - } - - - /** - * 任意の起動候補を表す。 - * <p> - * {@link Method}と{@link Constructor}を同時に取り扱うための型。 - * </p> - * @version $Date$ - * @author Suguru ARAKAWA - * @param <T> 内包するメンバ - */ - private static final class Executable<T extends Member> { - - /** - * 実行対象のオブジェクト。 - */ - final T target; - - /** - * 引数の型一覧。 - */ - final Class<?>[] parameterTypes; - - - /** - * インスタンスを生成する。 - * @param target 実行対象 - * @param parameterTypes 引数リスト - */ - Executable(T target, Class<?>[] parameterTypes) { - assert target != null; - assert parameterTypes != null; - this.target = target; - this.parameterTypes = parameterTypes; - } - - @Override - public String toString() { - return target.toString(); - } - } -} Copied: leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java) =================================================================== --- leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java (rev 0) +++ leto/factory-enhancer/trunk/src/main/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtil.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,518 @@ +/* + * 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.reflection; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * リフレクションに関するユーティリティ群。 + * @version $Date$ + * @author Suguru ARAKAWA (Gluegent, Inc.) + * @version 0.2.0 + * @since 0.2.0 + */ +public class ReflectionUtil { + + private static final Map<Class<?>, Class<?>> BOXING; + static { + Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>(); + map.put(Integer.TYPE, Integer.class); + map.put(Long.TYPE, Long.class); + map.put(Float.TYPE, Float.class); + map.put(Double.TYPE, Double.class); + map.put(Short.TYPE, Short.class); + map.put(Byte.TYPE, Byte.class); + map.put(Character.TYPE, Character.class); + map.put(Boolean.TYPE, Boolean.class); + BOXING = Collections.unmodifiableMap(map); + } + + + /** + * インスタンス生成の禁止。 + */ + private ReflectionUtil() { + throw new AssertionError(); + } + + /** + * 指定のオブジェクトが、列挙やインターフェース等でない通常のクラスを表現する場合のみ{@code true}を返す。 + * @param aClass 対象のクラスオブジェクト + * @return 通常のクラスを表現する場合に{@code true} + * @throws IllegalArgumentException 引数に{@code null}が指定された場合 + */ + public static boolean isNormalClass(Class<?> aClass) { + if (aClass == null) { + throw new IllegalArgumentException("aClass is null"); //$NON-NLS-1$ + } + return aClass.isPrimitive() == false + && aClass.isArray() == false + && aClass.isInterface() == false + && aClass.isEnum() == false; + } + + /** + * 指定のクラスがパッケージメンバ(トップレベルクラス)である場合のみ{@code true}を返す。 + * @param aClass 対象のクラスオブジェクト + * @return 指定のクラスがパッケージメンバである場合に{@code true} + * @throws IllegalArgumentException 引数に{@code null}が指定された場合 + */ + public static boolean isPackageMember(Class<?> aClass) { + if (aClass == null) { + throw new IllegalArgumentException("aClass is null"); //$NON-NLS-1$ + } + return aClass.isPrimitive() == false + && aClass.isArray() == false + && aClass.isMemberClass() == false + && aClass.isLocalClass() == false + && aClass.isAnonymousClass() == false; + } + + /** + * 引数の一覧をそのクラスの一覧に変換して返す。 + * <p> + * 引数の要素に{@code null}が指定された場合、対応する結果のクラスも{@code null}となる。 + * このクラスが提供するほかのメソッドでは、クラスに{@code null}が指定された場合に + * {@code null}型を表現するため、このメソッドの実行結果をそのまま利用できる。 + * </p> + * @param arguments 実引数の一覧 + * @return 対応する型引数の一覧 + * @throws IllegalArgumentException 引数に{@code null}が指定された場合 + */ + public static List<Class<?>> toParameterTypes(Object... arguments) { + if (arguments == null) { + throw new IllegalArgumentException("arguments is null"); //$NON-NLS-1$ + } + List<Class<?>> results = new ArrayList<Class<?>>(arguments.length); + for (Object argument : arguments) { + if (argument != null) { + results.add(argument.getClass()); + } else { + results.add(null); + } + } + return results; + } + + /** + * 指定の名前と実引数の型で適用可能なコンストラクタを、指定の一覧の中から検出して返す。 + * <p> + * 複数のメソッドが同時に適用可能である場合、その中から最大限に限定的であるものを計算して返す。 + * ただし、その結果が単一のコンストラクタとなるとは限らない。 + * </p> + * @param <T> それぞれのコンストラクタが生成するインスタンスの型 + * @param constructors 候補のコンストラクタ一覧 + * @param argumentTypes 起動時に利用する実引数の型一覧、この要素に{@code null}が + * 含まれる場合、それは{@code null}型を表現する + * @return 適用可能なコンストラクタのうち限定的であるもの、発見できない場合は空のリスト + * @throws IllegalArgumentException 引数に{@code null}が含まれる場合 + */ + public static <T>Collection<Constructor<T>> findConstructor( + Collection<Constructor<T>> constructors, + List<Class<?>> argumentTypes) { + if (constructors == null) { + throw new IllegalArgumentException("constructors is null"); //$NON-NLS-1$ + } + if (argumentTypes == null) { + throw new IllegalArgumentException("argumentTypes is null"); //$NON-NLS-1$ + } + + Class<?>[] argTypeArray = argumentTypes.toArray( + new Class<?>[argumentTypes.size()]); + + List<Executable<Constructor<T>>> results = + findPotentiallyApplicables(constructors, argTypeArray); + + filterApplicables(results, argTypeArray); + filterMaximallySpecifics(results); + return unpack(results); + } + + /** + * 指定の名前と実引数の型で適用可能なメソッドを、指定の一覧の中から検出して返す。 + * <p> + * 複数のメソッドが同時に適用可能である場合、その中から最大限に限定的であるものを計算して返す。 + * ただし、その結果が単一のメソッドとなるとは限らない。 + * </p> + * @param methods 候補のメソッド一覧 + * @param name 起動するメソッドの名前 + * @param argumentTypes 起動時に利用する実引数の型一覧、この要素に{@code null}が + * 含まれる場合、それは{@code null}型を表現する + * @return 適用可能なメソッドのうち限定的であるもの、発見できない場合は空のリスト + * @throws IllegalArgumentException 引数に{@code null}が含まれる場合 + */ + public static Collection<Method> findMethod( + Collection<Method> methods, + String name, + List<Class<?>> argumentTypes) { + if (methods == null) { + throw new IllegalArgumentException("methods is null"); //$NON-NLS-1$ + } + if (name == null) { + throw new IllegalArgumentException("name is null"); //$NON-NLS-1$ + } + if (argumentTypes == null) { + throw new IllegalArgumentException("argumentTypes is null"); //$NON-NLS-1$ + } + + Class<?>[] argTypeArray = argumentTypes.toArray( + new Class<?>[argumentTypes.size()]); + + LinkedList<Executable<Method>> results = + findPotentiallyApplicables(methods, name, argTypeArray); + + filterApplicables(results, argTypeArray); + filterMaximallySpecifics(results); + return unpack(results); + } + + private static <T extends Member>List<T> unpack(List<Executable<T>> packed) { + assert packed != null; + List<T> results = new ArrayList<T>(); + for (Executable<T> exec : packed) { + results.add(exec.target); + } + return results; + } + + /** + * メソッドの一覧から、潜在的に適用可能な起動候補の一覧を返す。 + * <p> + * 潜在的に適用可能とは、名前、および実引数の数と仮引数の数が一致することを指す。 + * </p> + * @param candidates 起動候補の一覧 + * @param name 起動するメソッドの名前 + * @param argumentTypes 実引数型の一覧 + * @return 潜在的に適用可能な起動候補の一覧 + */ + private static LinkedList<Executable<Method>> findPotentiallyApplicables( + Collection<Method> candidates, + String name, + Class<?>[] argumentTypes) { + assert candidates != null; + assert name != null; + assert argumentTypes != null; + LinkedList<Executable<Method>> results = + new LinkedList<Executable<Method>>(); + for (Method m : candidates) { + // 名前が一致すること + if (m.getName().equals(name) == false) { + continue; + } + // 実引数と仮引数の個数が一致すること + Class<?>[] paramTypes = m.getParameterTypes(); + if (paramTypes.length != argumentTypes.length) { + continue; + } + results.add(new Executable<Method>(m, paramTypes)); + } + return results; + } + + /** + * コンストラクタの一覧から、潜在的に適用可能な起動候補の一覧を返す。 + * <p> + * 潜在的に適用可能とは、実引数の数と仮引数の数が一致することを指す。 + * </p> + * @param <T> コンストラクタが対象とするインスタンスの種類 + * @param candidates 起動候補の一覧 + * @param argumentTypes 実引数型の一覧 + * @return 潜在的に適用可能な起動候補の一覧 + */ + private static <T>LinkedList<Executable<Constructor<T>>> findPotentiallyApplicables( + Collection<Constructor<T>> candidates, + Class<?>[] argumentTypes) { + assert candidates != null; + assert argumentTypes != null; + LinkedList<Executable<Constructor<T>>> results = + new LinkedList<Executable<Constructor<T>>>(); + for (Constructor<T> c : candidates) { + // 実引数と仮引数の個数が一致すること + Class<?>[] parameterTypes = c.getParameterTypes(); + if (argumentTypes.length != parameterTypes.length) { + continue; + } + // 引数を適用できるコンストラクタがひとつとは限らないので全部列挙 + results.add(new Executable<Constructor<T>>(c, parameterTypes)); + } + return results; + } + + /** + * 起動対象の候補の中から、指定の実引数を適用できないものを除外する。 + * @param <T> 起動候補の種類 + * @param candidates 対象の起動候補 + * @param argumentTypes 実引数型の一覧 + */ + private static <T extends Member>void filterApplicables( + Collection<Executable<T>> candidates, + Class<?>[] argumentTypes) { + assert candidates != null; + assert argumentTypes != null; + for (Iterator<Executable<T>> iter = candidates.iterator(); iter + .hasNext();) { + Executable<T> exec = iter.next(); + // 実引数を仮引数に適用できること + if (isApplicable(argumentTypes, exec.parameterTypes) == false) { + iter.remove(); + } + } + } + + /** + * 実引数の型を仮引数の型に適用できる場合のみ{@code true}を返す。 + * <p> + * これは、実引数と仮引数の個数が一致し、それぞれの実引数の型が + * 対応する仮引数の型のサブタイプであることを検査する。 + * </p> + * @param argumentTypes 実引数の型一覧 + * @param parameterTypes 仮引数の型一覧 + * @return 実引数の型を仮引数の型に適用できる場合に{@code true} + */ + private static boolean isApplicable( + Class<?>[] argumentTypes, + Class<?>[] parameterTypes) { + assert argumentTypes != null; + assert parameterTypes != null; + // 引数の個数が一致していないと失敗 + if (argumentTypes.length != parameterTypes.length) { + return false; + } + // ひとつでも適用できない実引数があれば失敗 + for (int i = 0; i < argumentTypes.length; i++) { + if (isApplicable(argumentTypes[i], parameterTypes[i]) == false) { + return false; + } + } + return true; + } + + /** + * 指定の型を持つ実引数を、指定の型を持つ仮引数に適用できる場合のみ{@code true}を返す。 + * <p> + * 実引数、仮引数のいずれか一方<em>のみ</em>がプリミティブ型を表現する場合、 + * その型を対応するラッパー形に変換した後、 + * </p> + * @param argumentType 実引数の型 + * @param parameterType 仮引数の型 + * @return 実引数を駆り引数に適用できる場合のみ{@code true} + */ + private static boolean isApplicable(Class<?> argumentType, Class<?> parameterType) { + // 引数型がnullやvoidであることはありえない + assert parameterType != null; + assert parameterType != void.class; + + // SomeType v = null; は常にOK + if (isNullType(argumentType)) { + return true; + } + // Class.isAssignbleFrom(Class) で簡易検査 + if (parameterType.isAssignableFrom(argumentType)) { + return true; + } + + boolean primitiveArgument = argumentType.isPrimitive(); + boolean primitiveParameter = parameterType.isPrimitive(); + if (primitiveArgument && primitiveParameter == false) { + Class<?> boxed = BOXING.get(argumentType); + assert boxed != null; + return isApplicable(boxed, parameterType); + } + if (primitiveParameter && primitiveArgument == false) { + Class<?> boxed = BOXING.get(parameterType); + assert boxed != null; + return isApplicable(argumentType, boxed); + } + + return false; + } + + /** + * 指定の値が{@code null}型を表現する場合にのみ{@code true}を返す。 + * @param aClass 対象の値 + * @return 指定の値が{@code null}型を表現する場合にのみ{@code true} + */ + private static boolean isNullType(Class<?> aClass) { + return aClass == null; + } + + /** + * 起動候補から最大限に限定的でないものを除外する。 + * @param <T> 起動候補の種類 + * @param candidates 対象の起動候補 + */ + private static <T extends Member>void filterMaximallySpecifics( + List<Executable<T>> candidates) { + assert candidates != null; + + // 引数リストのテーブルを作る + Class<?>[][] table = new Class<?>[candidates.size()][]; + for (int i = 0; i < table.length; i++) { + table[i] = candidates.get(i).parameterTypes; + } + + for (int i = 0; i < table.length; i++) { + if (table[i] == null) { + continue; // less applicableですでに候補から外れてる + } + // ms[i]のほうがms[j]よりもmore applicableなら、ms[j]を候補から消す + // 本来は「厳密に限定的」である必要があるが、ここでは省略 + // FIXME for f(.., int, ..), f(.., Integer, ..) + for (int j = 0; j < table.length; j++) { + if (i == j) { + continue; // 自分自身はスルー + } + if (table[j] == null) { + continue; // less applicableですでに候補から外れてる + } + if (isMoreSpecific(table[i], table[j])) { + table[j] = null; // less applicable は除去 + } + } + } + + // 限定的でないものを除外 + int index = 0; + for (Iterator<Executable<T>> iter = candidates.iterator(); iter.hasNext();) { + iter.next(); + if (table[index] == null) { + iter.remove(); + } + index++; + } + } + + /** + * 第1引数に含まれるクラスの一覧が、第2引数に含まれるクラスの一覧よりも限定的である場合に + * {@code true}を返す。 + * <p> + * クラスの列{@code a}がクラスの列{@code b}よりも限定的であるとは、 + * 次の条件をすべて満たす場合に限られる。 + * </p> + * <ul> + * <li> すべての{@code i}において、{@code a[i] <: b[i]}である </li> + * <li> {@code a[i] < b[i]}であるような{@code i}が1つ以上存在する </li> + * </ul> + * <p> + * このとき、{@code a}, {@code b}の要素数は同一でなければならない。 + * また、いずれかの配列に含まれる{@code null}は、{@code null}型を表現するものとする。 + * </p> + * @param a 限定的であることが検査される型の一覧 + * @param b 限定的であることを検査する型の一覧 + * @return {@code a}が{@code b}よりも限定的である場合に{@code true} + */ + private static boolean isMoreSpecific(Class<?>[] a, Class<?>[] b) { + assert a != null; + assert b != null; + assert a.length == b.length; + boolean result = false; + for (int i = 0; i < a.length; i++) { + if (a[i] == b[i]) { + // 同じ型なら次の引数で判定 + } else if (isMoreSpecific(a[i], b[i])) { + // a[i]とb[i]が違う型で、a[i]をb[i]に適用できると、aのほうが限定的になる可能性がある + result = true; + } else { + // それ以外では、aとbは関係ない型なので、aはbよりも限定的とはいえない + return false; + } + } + return result; + } + + /** + * {@code a}が{@code b}よりも限定的である場合にのみ{@code true}を返す。 + * <p> + * このメソッドはプリミティブ型の取り扱いが{@link #isApplicable(Class, Class)}と多少異なる。 + * ボクシング変換が利用されるのは{@code a}のみで、{@code b}には適用されない。 + * </p> + * <ul> + * <li> {@code a <: b} </li> + * <li> {@code boxing(a) <: b} </li> + * </ul> + * @param a 比較される型 + * @param b 比較する型 + * @return {@code a}が{@code b}よりも限定的である場合に{@code true} + */ + private static boolean isMoreSpecific(Class<?> a, Class<?> b) { + assert a != null; + assert b != null; + // Class.isAssignbleFrom(Class) で簡易検査 + if (b.isAssignableFrom(a)) { + return true; + } + if (a.isPrimitive()) { + Class<?> aBoxed = BOXING.get(a); + assert aBoxed != null; + return b.isAssignableFrom(aBoxed); + } + return false; + } + + + /** + * 任意の起動候補を表す。 + * <p> + * {@link Method}と{@link Constructor}を同時に取り扱うための型。 + * </p> + * @version $Date$ + * @author Suguru ARAKAWA + * @param <T> 内包するメンバ + */ + private static final class Executable<T extends Member> { + + /** + * 実行対象のオブジェクト。 + */ + final T target; + + /** + * 引数の型一覧。 + */ + final Class<?>[] parameterTypes; + + + /** + * インスタンスを生成する。 + * @param target 実行対象 + * @param parameterTypes 引数リスト + */ + Executable(T target, Class<?>[] parameterTypes) { + assert target != null; + assert parameterTypes != null; + this.target = target; + this.parameterTypes = parameterTypes; + } + + @Override + public String toString() { + return target.toString(); + } + } +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,35 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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 class ClassNoDefaultConstructor { + + /** + * インスタンスを生成する。 + * @param i ダミーの引数 + */ + public ClassNoDefaultConstructor(int i) { + return; + } +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,34 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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 class ClassNoPublicConstructor { + + /** + * インスタンスを生成する。 + */ + ClassNoPublicConstructor() { + return; + } +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,28 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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; + +/** + * {@code public}でないクラス。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +class ClassNotPublic { + +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ConcreteProduct.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ConcreteProduct.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ConcreteProduct.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ConcreteProduct.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,34 @@ +/* + * 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: InterfaceProduct.java 3699 2009-10-04 11:33:48Z ashigeru $ + * @author Suguru ARAKAWA + */ +public class ConcreteProduct { + + /** + * @return {@code "Concrete"} + */ + public String getMessage() { + return "Concrete"; + } +} Modified: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/FactoryEnhancerTest.java =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/FactoryEnhancerTest.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/FactoryEnhancerTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -116,12 +116,13 @@ */ @Test(expected = IllegalArgumentException.class) public void testFactoryEnhancer_InterfaceIsAnnotation() throws Exception { - new FactoryEnhancer<Override>(Override.class, new Override() { + Override override = new Override() { public Class<? extends Annotation> annotationType() { return Override.class; } - }.getClass(), enhances()); + }; + new FactoryEnhancer<Override>(Override.class, override.getClass(), enhances()); } /** @@ -154,8 +155,8 @@ Enhance enhance = new Enhance(new StringParameterPointcut(), new BeforeStringInsertHandler("!")); FactoryEnhancer<SingularFactory> enhancer = new FactoryEnhancer<SingularFactory>(SingularFactory.class, SingularFactoryNoPublicProduct.class, - enhance); - SingularFactory factory = enhancer.getEnhanced().newInstance(); + enhance); + SingularFactory factory = enhancer.getFactory().newInstance(); factory.newInstance(); } @@ -237,7 +238,7 @@ assertThat(factory.newProduct1("a").getValue(), is("!a1")); assertThat(factory.newProduct2("b").getValue(), is("!b2")); assertThat(factory.newProduct3("c").getValue(), is("!c3")); - assertThat(factory.newString("s").toString(), is("!s")); + assertThat(factory.newString("s"), is("!s")); } /** @@ -312,7 +313,7 @@ assertThat(factory.newProduct1("a").getValue(), is("a1")); assertThat(factory.newProduct2("b").getValue(), is("b2")); assertThat(factory.newProduct3("c").getValue(), is("c3")); - assertThat(factory.newString("s").toString(), is("s")); + assertThat(factory.newString("s"), is("s")); } /** @@ -336,7 +337,7 @@ public void testGetEnhanced_FinalProductClass() throws Exception { Enhance enhance = new Enhance(and(or(new ClassSuffixPointcut("Product1"), new ClassSuffixPointcut("ProductFinal")), - new StringParameterPointcut()), new BeforeStringInsertHandler("!")); + new StringParameterPointcut()), new BeforeStringInsertHandler("!")); FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, enhance); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); @@ -474,7 +475,7 @@ public void testGetEnhanced_ProductMethodArguments() throws Exception { FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, new Enhance( - new StringParameterPointcut(), new BeforeStringInsertHandler("!"))); + new StringParameterPointcut(), new BeforeStringInsertHandler("!"))); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); TargetFactory factory = enhanced.newInstance(); TargetProduct1 p1 = factory.newProduct1(); @@ -496,7 +497,7 @@ public void testGetEnhanced_ProductMethodResults() throws Exception { FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, new Enhance( - new StringResultPointcut(), new AfterStringAppendHandler("!"))); + new StringResultPointcut(), new AfterStringAppendHandler("!"))); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); TargetFactory factory = enhanced.newInstance(); assertThat(factory.newProduct1("a"), is(p1("a"))); @@ -508,7 +509,7 @@ assertThat(factory.newProduct3("c").getValue(), is("c3!")); // java.lang.String cannot become an enhance target - assertThat(factory.newString("s").toString(), is("s")); + assertThat(factory.newString("s"), is("s")); } /** @@ -532,10 +533,10 @@ public void testGetEnhanced_SelectedMethod() throws Exception { Enhance before = new Enhance(and(new ClassSuffixPointcut("Product2"), new StringParameterPointcut()), - new BeforeStringInsertHandler("!")); + new BeforeStringInsertHandler("!")); Enhance after = new Enhance(and(new ClassSuffixPointcut("Product3"), new StringResultPointcut()), - new AfterStringAppendHandler("!")); + new AfterStringAppendHandler("!")); FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, before, after); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); @@ -599,7 +600,7 @@ public void testGetEnhanced_SingleConstructor() throws Exception { Enhance enhance = new Enhance(and(new ClassSuffixPointcut("Product1"), new StringParameterPointcut()), - new BeforeStringInsertHandler("!")); + new BeforeStringInsertHandler("!")); FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, enhance); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); @@ -612,7 +613,7 @@ assertThat(factory.newProduct1("a").getValue(), is("!a1")); assertThat(factory.newProduct2("b").getValue(), is("b2")); assertThat(factory.newProduct3("c").getValue(), is("c3")); - assertThat(factory.newString("s").toString(), is("s")); + assertThat(factory.newString("s"), is("s")); } /** @@ -632,7 +633,7 @@ private TargetFactory enhancedProduct1() throws Exception { Enhance enhance = new Enhance(and(new ClassSuffixPointcut("Product1"), new StringResultPointcut()), - new AfterStringAppendHandler("!")); + new AfterStringAppendHandler("!")); FactoryEnhancer<TargetFactory> enhancer = new FactoryEnhancer<TargetFactory>(TargetFactory.class, TargetFactoryImpl.class, enhance); Class<? extends TargetFactory> enhanced = enhancer.getEnhanced(); Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,380 @@ +/* + * 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.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; + +import org.junit.Test; + +import org.jiemamy.utils.enhancer.aspect.StringResultPointcut; +import org.jiemamy.utils.enhancer.aspect.ThroughHandler; + +/** + * Test for {@link InterfaceEnhancer}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class InterfaceEnhancerTest { + + /** + * Test method for {@link 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(); + + // getMessage()が実装されないので、AbstractMethodErrorになる + 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_EnhanceMethod() throws Exception { + // Stringを返すメソッドをフックして、かわりに "Hello" を返す。 + 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(); + + // インターフェースメソッドだったgetMessage()が実装される + assertThat(product.getMessage(), is("Hello")); + + // ついでにStringを返すtoString()が上書きされる + assertThat(product.toString(), is("Hello")); + } + + /** + * Test method for {@link AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_Inherit() throws Exception { + // 何もエンハンスしないが、かわりに getMessage() を実装する親クラス ConcreteProduct を指定してやる + InterfaceEnhancer<SimpleInterfaceFactory> enhancer = new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + ConcreteProduct.class, + enhances()); + Factory<? extends SimpleInterfaceFactory> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactory factory = metaFactory.newInstance(); + + // InterfaceProduct.getMessage() が ConcreteProduct のメソッドに束縛される + InterfaceProduct product = factory.newProduct(); + assertThat(product.getMessage(), is("Concrete")); + } + + /** + * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_EnhanceNew() throws Exception { + // InterfaceProduct を生成するファクトリインターフェースに対して行う + Class<SimpleInterfaceFactory> factoryInterface = SimpleInterfaceFactory.class; + + // 全てのプロダクトにPropertiesを継承させる + Class<?> productSuperClass = Properties.class; + + Enhance enhance = new Enhance(new InvocationPointcut() { + + public boolean isTarget(CtClass self, CtBehavior behavior) { + // InterfaceProduct に対するコンストラクタ呼び出しだけを対象にする + if ((behavior instanceof CtConstructor) == false) { + return false; + } + // InterfaceProductと同じ名前? + return self.getName().equals(InterfaceProduct.class.getName()); + } + }, new InvocationHandler() { + + public Object handle(Invocation invocation) throws Throwable { + // InterfaceProductの実装を生成するときに呼び出される + try { + // とりあえずプロダクトのインスタンスを作る + Object result = invocation.proceed(); + + // プロダクトの親クラスはProperties + assertThat(result, instanceOf(Properties.class)); + Properties p = (Properties) result; + + // Hello -> world! を追加して初期化 + p.put("Hello", "world!"); + + return p; + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } + }); + InterfaceEnhancer<SimpleInterfaceFactory> enhancer = new InterfaceEnhancer<SimpleInterfaceFactory>( + factoryInterface, + productSuperClass, + enhances(enhance)); + Factory<? extends SimpleInterfaceFactory> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactory factory = metaFactory.newInstance(); + InterfaceProduct product = factory.newProduct(); + + // プロダクトの親クラスはProperties + assertThat(product, instanceOf(Properties.class)); + + // プロダクトのequalsはオーバーライドしてないうえ、Mapの規約で中身が同じなら一致する + Map<String, String> map = new HashMap<String, String>(); + map.put("Hello", "world!"); + assertThat(product, is((Object) map)); + } + + /** + * Test method for {@link AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_ParameterConflict() throws Exception { + Enhance enhance = new Enhance(new StringResultPointcut(), new InvocationHandler() { + + public Object handle(Invocation invocation) { + return "Hello"; + } + }); + InterfaceEnhancer<ParameterConflictInterfaceFactory> enhancer = + new InterfaceEnhancer<ParameterConflictInterfaceFactory>( + ParameterConflictInterfaceFactory.class, + Object.class, + enhances(enhance)); + Factory<? extends ParameterConflictInterfaceFactory> metaFactory = enhancer.getFactory(); + ParameterConflictInterfaceFactory factory = metaFactory.newInstance(); + + InterfaceProduct p1 = factory.new1(1); + InterfaceProduct p2 = factory.new1(2); + assertThat(p1.getMessage(), is("Hello")); + assertThat(p2.getMessage(), is("Hello")); + } + + /** + * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_Through() throws Exception { + Enhance enhance = new Enhance(new StringResultPointcut(), new ThroughHandler()); + InterfaceEnhancer<SimpleInterfaceFactory> enhancer = new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + ConcreteProduct.class, + enhances(enhance)); + Factory<? extends SimpleInterfaceFactory> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactory factory = metaFactory.newInstance(); + InterfaceProduct product = factory.newProduct(); + assertThat(product.getMessage(), is("Concrete")); + } + + /** + * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * @throws Exception if occur + */ + @Test + public void testGetFactory_Inherited() throws Exception { + Enhance enhance = new Enhance(new StringResultPointcut(), new InvocationHandler() { + + public Object handle(Invocation invocation) { + if (invocation.getInvoker() instanceof InterfaceProductEx) { + return "inherited"; + } + if (invocation.getInvoker() instanceof InterfaceProduct) { + return "base"; + } + throw new AssertionError(); + } + }); + InterfaceEnhancer<SimpleInterfaceFactoryEx> enhancer = new InterfaceEnhancer<SimpleInterfaceFactoryEx>( + SimpleInterfaceFactoryEx.class, + Object.class, + enhances(enhance)); + Factory<? extends SimpleInterfaceFactoryEx> metaFactory = enhancer.getFactory(); + SimpleInterfaceFactoryEx factory = metaFactory.newInstance(); + + InterfaceProduct product = factory.newProduct(); + assertThat(product.getMessage(), is("base")); + + InterfaceProductEx productEx = factory.newProductEx(); + assertThat(productEx.getMessage(), is("inherited")); + assertThat(productEx.getMessageEx(), is("inherited")); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_FactoryNotPublic() { + new InterfaceEnhancer<InterfaceNotPublic>( + InterfaceNotPublic.class, + Object.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_FactoryNotInterface() { + new InterfaceEnhancer<Object>( + Object.class, + Object.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_FactoryNotReturnsInterface() { + new InterfaceEnhancer<InterfaceReturnsNotInterface>( + InterfaceReturnsNotInterface.class, + Object.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_FactoryNotReturnsPublic() { + new InterfaceEnhancer<InterfaceReturnsNotPublic>( + InterfaceReturnsNotPublic.class, + Object.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductNotPublic() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + ClassNotPublic.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductNotConcrete() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + EmptyFactoryAbstract.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductNotInheritable() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + EmptyFactoryFinal.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductNotClass() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + SimpleInterfaceFactory.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductNotNormalClass() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + EmptyFactoryEnum.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductDefaultConstructor() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + ClassNoDefaultConstructor.class, + enhances()); + } + + /** + * Test method for {@link InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testInterfaceEnhancer_ProductConstructorNotPublic() { + new InterfaceEnhancer<SimpleInterfaceFactory>( + SimpleInterfaceFactory.class, + ClassNoPublicConstructor.class, + enhances()); + } + + private static List<Enhance> enhances(Enhance... enhances) { + return Arrays.asList(enhances); + } +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,28 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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; + +/** + * {@code public}でないインターフェース。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +interface InterfaceNotPublic { + +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProduct.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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 InterfaceProduct { + + /** + * @return nothing + */ + String getMessage(); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProductEx.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/InterfaceProductEx.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProductEx.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceProductEx.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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 InterfaceProductEx extends InterfaceProduct { + + /** + * @return nothing + */ + String getMessageEx(); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,37 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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; + +/** + * {@code public}でないインターフェース。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public interface InterfaceReturnsNotInterface { + + /** + * @return インターフェース型 + */ + InterfaceProduct product(); + + /** + * @return クラス型 + */ + String string(); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,37 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/05 + * + * 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; + +/** + * {@code public}でないインターフェースを返す。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public interface InterfaceReturnsNotPublic { + + /** + * @return {@code public} + */ + InterfaceProduct product(); + + /** + * @return {@code public}でない + */ + InterfaceNotPublic notPublic(); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ParameterConflictInterfaceFactory.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/ParameterConflictInterfaceFactory.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ParameterConflictInterfaceFactory.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/ParameterConflictInterfaceFactory.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,39 @@ +/* + * 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 ParameterConflictInterfaceFactory { + + /** + * @param a A parameter + * @return {@link InterfaceProduct} + */ + InterfaceProduct new1(int a); + + /** + * @param a A parameter + * @return {@link InterfaceProduct} + */ + InterfaceProduct new2(int a); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactory.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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(); +} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactoryEx.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactoryEx.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactoryEx.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/SimpleInterfaceFactoryEx.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -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 SimpleInterfaceFactoryEx extends SimpleInterfaceFactory { + + /** + * @return {@link InterfaceProductEx} + */ + InterfaceProductEx newProductEx(); +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,261 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; - -import java.lang.reflect.Constructor; - -import org.aopalliance.intercept.ConstructorInterceptor; -import org.aopalliance.intercept.ConstructorInvocation; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.junit.Test; - -/** - * Test for {@link AopAllianceDriver}. - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class AopAllianceDriverTest extends DriverTestRoot { - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Method_Through() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) throws Throwable { - assertThat(invocation.getThis(), is((Object) "Hello")); - assertThat(invocation.getMethod().getName(), is("charAt")); - return invocation.proceed(); - } - }; - Object result = invoke( - AopAllianceDriver.toHandler(itor), - String.class.getMethod("charAt", int.class), - "Hello", - 0); - assertThat(result, is((Object) 'H')); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Method_Static() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) throws Throwable { - assertThat(invocation.getThis(), is(nullValue())); - return invocation.proceed(); - } - }; - Object result = invoke( - AopAllianceDriver.toHandler(itor), - Math.class.getMethod("abs", int.class), - Math.class, - -1); - assertThat(result, is((Object) 1)); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Method_RewriteResult() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) { - return "World"; - } - }; - Object result = invoke( - AopAllianceDriver.toHandler(itor), - String.class.getMethod("toString"), - "Hello"); - assertThat(result, is((Object) "World")); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Method_RewriteArgument() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) throws Throwable { - Object[] args = invocation.getArguments(); - assertThat(args[0], is((Object) 1)); - args[0] = 2; - return invocation.proceed(); - } - }; - Object result = invoke( - AopAllianceDriver.toHandler(itor), - String.class.getMethod("charAt", int.class), - "Hello", - 1); - assertThat(result, is((Object) 'l')); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test(expected = UnsupportedOperationException.class) - public void testToHandler_Method_Exception() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) { - throw new UnsupportedOperationException(); - } - }; - invoke(AopAllianceDriver.toHandler(itor), - String.class.getMethod("toString"), - "Hello"); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Method_Construct() throws Throwable { - MethodInterceptor itor = new MethodInterceptor() { - - public Object invoke(MethodInvocation invocation) { - throw new UnsupportedOperationException(); - } - }; - Object result = construct(AopAllianceDriver.toHandler(itor), - String.class.getConstructor(String.class), - "Hello"); - assertThat(result, is((Object) "Hello")); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Constructor_Through() throws Throwable { - ConstructorInterceptor itor = new ConstructorInterceptor() { - - public Object construct(ConstructorInvocation invocation) throws Throwable { - assertThat(invocation.getThis(), is(nullValue())); - Constructor<?> ctor = invocation.getConstructor(); - assertThat(ctor.getParameterTypes().length, is(1)); - assertThat(ctor.getParameterTypes()[0], is((Object) String.class)); - return invocation.proceed(); - } - }; - Object result = construct( - AopAllianceDriver.toHandler(itor), - String.class.getConstructor(String.class), - "Hello"); - assertThat(result, is((Object) "Hello")); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Constructor_RewriteResult() throws Throwable { - ConstructorInterceptor itor = new ConstructorInterceptor() { - - public Object construct(ConstructorInvocation invocation) { - return new String("World".toCharArray()); - } - }; - Object result = construct( - AopAllianceDriver.toHandler(itor), - String.class.getConstructor(String.class), - "Hello"); - assertThat(result, is((Object) "World")); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Constructor_RewriteArgument() throws Throwable { - ConstructorInterceptor itor = new ConstructorInterceptor() { - - public Object construct(ConstructorInvocation invocation) throws Throwable { - Object[] arguments = invocation.getArguments(); - assertThat(arguments.length, is(1)); - arguments[0] = "World"; - return invocation.proceed(); - } - }; - Object result = construct( - AopAllianceDriver.toHandler(itor), - String.class.getConstructor(String.class), - "Hello"); - assertThat(result, is((Object) "World")); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. - * @throws Throwable if occur - */ - @Test(expected = IllegalStateException.class) - public void testToHandler_Constructor_Exception() throws Throwable { - ConstructorInterceptor itor = new ConstructorInterceptor() { - - public Object construct(ConstructorInvocation invocation) throws Throwable { - throw new IllegalStateException(); - } - }; - construct( - AopAllianceDriver.toHandler(itor), - String.class.getConstructor(String.class), - "Hello"); - } - - /** - * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. - * @throws Throwable if occur - */ - @Test - public void testToHandler_Constructor_Invoke() throws Throwable { - ConstructorInterceptor itor = new ConstructorInterceptor() { - - public Object construct(ConstructorInvocation invocation) throws Throwable { - throw new AssertionError(); - } - }; - Object result = invoke( - AopAllianceDriver.toHandler(itor), - String.class.getMethod("charAt", int.class), - "Hello", - 1); - assertThat(result, is((Object) 'e')); - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/AopAllianceDriverTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,261 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.lang.reflect.Constructor; + +import org.aopalliance.intercept.ConstructorInterceptor; +import org.aopalliance.intercept.ConstructorInvocation; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.junit.Test; + +/** + * Test for {@link AopAllianceDriver}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class AopAllianceDriverTest extends DriverTestRoot { + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Method_Through() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) throws Throwable { + assertThat(invocation.getThis(), is((Object) "Hello")); + assertThat(invocation.getMethod().getName(), is("charAt")); + return invocation.proceed(); + } + }; + Object result = invoke( + AopAllianceDriver.toHandler(itor), + String.class.getMethod("charAt", int.class), + "Hello", + 0); + assertThat(result, is((Object) 'H')); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Method_Static() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) throws Throwable { + assertThat(invocation.getThis(), is(nullValue())); + return invocation.proceed(); + } + }; + Object result = invoke( + AopAllianceDriver.toHandler(itor), + Math.class.getMethod("abs", int.class), + Math.class, + -1); + assertThat(result, is((Object) 1)); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Method_RewriteResult() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) { + return "World"; + } + }; + Object result = invoke( + AopAllianceDriver.toHandler(itor), + String.class.getMethod("toString"), + "Hello"); + assertThat(result, is((Object) "World")); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Method_RewriteArgument() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) throws Throwable { + Object[] args = invocation.getArguments(); + assertThat(args[0], is((Object) 1)); + args[0] = 2; + return invocation.proceed(); + } + }; + Object result = invoke( + AopAllianceDriver.toHandler(itor), + String.class.getMethod("charAt", int.class), + "Hello", + 1); + assertThat(result, is((Object) 'l')); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test(expected = UnsupportedOperationException.class) + public void testToHandler_Method_Exception() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) { + throw new UnsupportedOperationException(); + } + }; + invoke(AopAllianceDriver.toHandler(itor), + String.class.getMethod("toString"), + "Hello"); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.MethodInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Method_Construct() throws Throwable { + MethodInterceptor itor = new MethodInterceptor() { + + public Object invoke(MethodInvocation invocation) { + throw new UnsupportedOperationException(); + } + }; + Object result = construct(AopAllianceDriver.toHandler(itor), + String.class.getConstructor(String.class), + "Hello"); + assertThat(result, is((Object) "Hello")); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Constructor_Through() throws Throwable { + ConstructorInterceptor itor = new ConstructorInterceptor() { + + public Object construct(ConstructorInvocation invocation) throws Throwable { + assertThat(invocation.getThis(), is(nullValue())); + Constructor<?> ctor = invocation.getConstructor(); + assertThat(ctor.getParameterTypes().length, is(1)); + assertThat(ctor.getParameterTypes()[0], is((Object) String.class)); + return invocation.proceed(); + } + }; + Object result = construct( + AopAllianceDriver.toHandler(itor), + String.class.getConstructor(String.class), + "Hello"); + assertThat(result, is((Object) "Hello")); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Constructor_RewriteResult() throws Throwable { + ConstructorInterceptor itor = new ConstructorInterceptor() { + + public Object construct(ConstructorInvocation invocation) { + return new String("World".toCharArray()); + } + }; + Object result = construct( + AopAllianceDriver.toHandler(itor), + String.class.getConstructor(String.class), + "Hello"); + assertThat(result, is((Object) "World")); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Constructor_RewriteArgument() throws Throwable { + ConstructorInterceptor itor = new ConstructorInterceptor() { + + public Object construct(ConstructorInvocation invocation) throws Throwable { + Object[] arguments = invocation.getArguments(); + assertThat(arguments.length, is(1)); + arguments[0] = "World"; + return invocation.proceed(); + } + }; + Object result = construct( + AopAllianceDriver.toHandler(itor), + String.class.getConstructor(String.class), + "Hello"); + assertThat(result, is((Object) "World")); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. + * @throws Throwable if occur + */ + @Test(expected = IllegalStateException.class) + public void testToHandler_Constructor_Exception() throws Throwable { + ConstructorInterceptor itor = new ConstructorInterceptor() { + + public Object construct(ConstructorInvocation invocation) throws Throwable { + throw new IllegalStateException(); + } + }; + construct( + AopAllianceDriver.toHandler(itor), + String.class.getConstructor(String.class), + "Hello"); + } + + /** + * Test method for {@link AopAllianceDriver#toHandler(org.aopalliance.intercept.ConstructorInterceptor)}. + * @throws Throwable if occur + */ + @Test + public void testToHandler_Constructor_Invoke() throws Throwable { + ConstructorInterceptor itor = new ConstructorInterceptor() { + + public Object construct(ConstructorInvocation invocation) throws Throwable { + throw new AssertionError(); + } + }; + Object result = invoke( + AopAllianceDriver.toHandler(itor), + String.class.getMethod("charAt", int.class), + "Hello", + 1); + assertThat(result, is((Object) 'e')); + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,229 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; - -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.LoaderClassPath; -import javassist.NotFoundException; - -import org.junit.Before; - -import org.jiemamy.utils.enhancer.Invocation; -import org.jiemamy.utils.enhancer.InvocationHandler; -import org.jiemamy.utils.enhancer.InvocationPointcut; - -/** - * このパッケージのテストで利用するテスト基底クラス。 - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class DriverTestRoot { - - /** - * 現在のクラスローダを元に構築したクラスプール。 - */ - protected ClassPool pool; - - - /** - * テストを初期化する。 - * @throws Exception if occur - */ - @Before - public void setUp() throws Exception { - pool = new ClassPool(); - pool.appendClassPath(new LoaderClassPath(getClass().getClassLoader())); - } - - /** - * コンストラクタ起動を指定のハンドラにハンドルさせる。 - * @param handler 対象のハンドラ - * @param constructor 対象のコンストラクタ - * @param arguments 引数リスト - * @return {@code handler.handle()}の実行結果 - * @throws Throwable {@code handler.handle()}の実行時に例外が発生した場合 - */ - protected Object construct( - InvocationHandler handler, - final Constructor<?> constructor, - final Object... arguments) throws Throwable { - Invocation invocation = new Invocation() { - - public Object proceed() throws InvocationTargetException { - try { - return constructor.newInstance(arguments); - } catch (InstantiationException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - - public Member getTarget() { - return constructor; - } - - public Object getInvoker() { - return constructor.getDeclaringClass(); - } - - public Object[] getArguments() { - return arguments; - } - }; - return handler.handle(invocation); - } - - /** - * コンストラクタ起動を指定のハンドラにハンドルさせる。 - * @param handler 対象のハンドラ - * @param method 対象のメソッド - * @param self 対象のコンテキスト(may be {@code this} keyword) - * @param arguments 引数リスト - * @return {@code handler.handle()}の実行結果 - * @throws Throwable {@code handler.handle()}の実行時に例外が発生した場合 - */ - protected Object invoke( - InvocationHandler handler, - final Method method, - final Object self, - final Object... arguments) throws Throwable { - Invocation invocation = new Invocation() { - - public Object proceed() throws InvocationTargetException { - try { - return method.invoke(self, arguments); - } catch (IllegalAccessException e) { - throw new AssertionError(); - } - } - - public Member getTarget() { - return method; - } - - public Object getInvoker() { - return self; - } - - public Object[] getArguments() { - return arguments; - } - }; - return handler.handle(invocation); - } - - /** - * 対象のポイントカットに対し、指定のメソッドが対象となるかどうかを表明する。 - * @param targetOrNot {@code true}ならば対象であることを表明する - * @param pointcut テスト対象のポイントカット - * @param selfClass メソッドを公開するクラス - * @param declaringClass メソッドを実際に宣言するクラス - * @param name メソッド名 - * @param descriptor メソッドデスクリプタ - */ - protected void assertTargetMethod( - boolean targetOrNot, - InvocationPointcut pointcut, - Class<?> selfClass, - Class<?> declaringClass, - String name, - String descriptor) { - try { - CtClass ctSelfClass = pool.get(selfClass.getName()); - CtClass ctDeclaringClass = pool.get(declaringClass.getName()); - CtMethod method = findMethod(ctDeclaringClass, name, descriptor); - assertThat( - ctSelfClass.getName() + "." + name + descriptor, - pointcut.isTarget(ctSelfClass, method), - is(targetOrNot)); - } catch (NotFoundException e) { - throw new AssertionError(e); - } - } - - /** - * 対象のポイントカットに対し、指定のメソッドが対象となるかどうかを表明する。 - * @param targetOrNot {@code true}ならば対象であることを表明する - * @param pointcut テスト対象のポイントカット - * @param selfClass コンストラクタを公開するクラス - * @param declaringClass コンストラクタを実際に宣言するクラス - * @param descriptor メソッドデスクリプタ - */ - protected void assertTargetConstructor( - boolean targetOrNot, - InvocationPointcut pointcut, - Class<?> selfClass, - Class<?> declaringClass, - String descriptor) { - try { - CtClass ctSelfClass = pool.get(selfClass.getName()); - CtClass ctDeclaringClass = pool.get(declaringClass.getName()); - CtConstructor ctor = findConstructor(ctDeclaringClass, descriptor); - assertThat( - ctSelfClass.getName() + descriptor, - pointcut.isTarget(ctSelfClass, ctor), - is(targetOrNot)); - } catch (NotFoundException e) { - throw new AssertionError(e); - } - } - - private CtMethod findMethod( - CtClass declaring, - String name, - String descriptor) { - for (CtMethod method : declaring.getDeclaredMethods()) { - if (method.getName().equals(name) == false) { - continue; - } - if (method.getSignature().equals(descriptor) == false) { - continue; - } - return method; - } - throw new AssertionError(declaring.getName() + "." + name + descriptor); - } - - private CtConstructor findConstructor( - CtClass declaring, - String descriptor) { - for (CtConstructor ctor : declaring.getDeclaredConstructors()) { - if (ctor.isClassInitializer()) { - continue; - } - if (ctor.getSignature().equals(descriptor) == false) { - continue; - } - return ctor; - } - throw new AssertionError(declaring.getName() + "." + descriptor); - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/DriverTestRoot.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,229 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.LoaderClassPath; +import javassist.NotFoundException; + +import org.junit.Before; + +import org.jiemamy.utils.enhancer.Invocation; +import org.jiemamy.utils.enhancer.InvocationHandler; +import org.jiemamy.utils.enhancer.InvocationPointcut; + +/** + * このパッケージのテストで利用するテスト基底クラス。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class DriverTestRoot { + + /** + * 現在のクラスローダを元に構築したクラスプール。 + */ + protected ClassPool pool; + + + /** + * テストを初期化する。 + * @throws Exception if occur + */ + @Before + public void setUp() throws Exception { + pool = new ClassPool(); + pool.appendClassPath(new LoaderClassPath(getClass().getClassLoader())); + } + + /** + * コンストラクタ起動を指定のハンドラにハンドルさせる。 + * @param handler 対象のハンドラ + * @param constructor 対象のコンストラクタ + * @param arguments 引数リスト + * @return {@code handler.handle()}の実行結果 + * @throws Throwable {@code handler.handle()}の実行時に例外が発生した場合 + */ + protected Object construct( + InvocationHandler handler, + final Constructor<?> constructor, + final Object... arguments) throws Throwable { + Invocation invocation = new Invocation() { + + public Object proceed() throws InvocationTargetException { + try { + return constructor.newInstance(arguments); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + public Member getTarget() { + return constructor; + } + + public Object getInvoker() { + return constructor.getDeclaringClass(); + } + + public Object[] getArguments() { + return arguments; + } + }; + return handler.handle(invocation); + } + + /** + * コンストラクタ起動を指定のハンドラにハンドルさせる。 + * @param handler 対象のハンドラ + * @param method 対象のメソッド + * @param self 対象のコンテキスト(may be {@code this} keyword) + * @param arguments 引数リスト + * @return {@code handler.handle()}の実行結果 + * @throws Throwable {@code handler.handle()}の実行時に例外が発生した場合 + */ + protected Object invoke( + InvocationHandler handler, + final Method method, + final Object self, + final Object... arguments) throws Throwable { + Invocation invocation = new Invocation() { + + public Object proceed() throws InvocationTargetException { + try { + return method.invoke(self, arguments); + } catch (IllegalAccessException e) { + throw new AssertionError(); + } + } + + public Member getTarget() { + return method; + } + + public Object getInvoker() { + return self; + } + + public Object[] getArguments() { + return arguments; + } + }; + return handler.handle(invocation); + } + + /** + * 対象のポイントカットに対し、指定のメソッドが対象となるかどうかを表明する。 + * @param targetOrNot {@code true}ならば対象であることを表明する + * @param pointcut テスト対象のポイントカット + * @param selfClass メソッドを公開するクラス + * @param declaringClass メソッドを実際に宣言するクラス + * @param name メソッド名 + * @param descriptor メソッドデスクリプタ + */ + protected void assertTargetMethod( + boolean targetOrNot, + InvocationPointcut pointcut, + Class<?> selfClass, + Class<?> declaringClass, + String name, + String descriptor) { + try { + CtClass ctSelfClass = pool.get(selfClass.getName()); + CtClass ctDeclaringClass = pool.get(declaringClass.getName()); + CtMethod method = findMethod(ctDeclaringClass, name, descriptor); + assertThat( + ctSelfClass.getName() + "." + name + descriptor, + pointcut.isTarget(ctSelfClass, method), + is(targetOrNot)); + } catch (NotFoundException e) { + throw new AssertionError(e); + } + } + + /** + * 対象のポイントカットに対し、指定のメソッドが対象となるかどうかを表明する。 + * @param targetOrNot {@code true}ならば対象であることを表明する + * @param pointcut テスト対象のポイントカット + * @param selfClass コンストラクタを公開するクラス + * @param declaringClass コンストラクタを実際に宣言するクラス + * @param descriptor メソッドデスクリプタ + */ + protected void assertTargetConstructor( + boolean targetOrNot, + InvocationPointcut pointcut, + Class<?> selfClass, + Class<?> declaringClass, + String descriptor) { + try { + CtClass ctSelfClass = pool.get(selfClass.getName()); + CtClass ctDeclaringClass = pool.get(declaringClass.getName()); + CtConstructor ctor = findConstructor(ctDeclaringClass, descriptor); + assertThat( + ctSelfClass.getName() + descriptor, + pointcut.isTarget(ctSelfClass, ctor), + is(targetOrNot)); + } catch (NotFoundException e) { + throw new AssertionError(e); + } + } + + private CtMethod findMethod( + CtClass declaring, + String name, + String descriptor) { + for (CtMethod method : declaring.getDeclaredMethods()) { + if (method.getName().equals(name) == false) { + continue; + } + if (method.getSignature().equals(descriptor) == false) { + continue; + } + return method; + } + throw new AssertionError(declaring.getName() + "." + name + descriptor); + } + + private CtConstructor findConstructor( + CtClass declaring, + String descriptor) { + for (CtConstructor ctor : declaring.getDeclaredConstructors()) { + if (ctor.isClassInitializer()) { + continue; + } + if (ctor.getSignature().equals(descriptor) == false) { + continue; + } + return ctor; + } + throw new AssertionError(declaring.getName() + "." + descriptor); + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,119 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -import org.junit.Test; - -import org.jiemamy.utils.enhancer.Enhance; - -/** - * Test for {@link ProxyDriver}. - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class ProxyDriverTest extends DriverTestRoot { - - private static InvocationHandler DUMMY = new InvocationHandler() { - - public Object invoke(Object proxy, Method method, Object[] args) { - throw new AssertionError(); - } - }; - - - /** - * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testNewEnhance_NotInterface() { - ProxyDriver.newEnhance(Object.class, DUMMY); - } - - /** - * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. - */ - @Test - public void testNewEnhance_TargetInterface() { - Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); - assertTargetConstructor(true, enhance.getPointcut(), - Runnable.class, - Thread.class, - "()V"); - assertTargetConstructor(true, enhance.getPointcut(), - Runnable.class, - Thread.class, - "(Ljava/lang/String;)V"); - } - - /** - * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. - */ - @Test - public void testNewEnhance_TargetSubInterface() { - Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); - assertTargetConstructor(false, enhance.getPointcut(), - RunnableEx.class, - RunnableExImpl.class, - "()V"); - } - - /** - * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. - */ - @Test - public void testNewEnhance_TargetMethod() { - Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); - assertTargetMethod(false, enhance.getPointcut(), - Runnable.class, - Runnable.class, - "run", - "()V"); - } - - /** - * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. - * @throws Throwable if occur - */ - @Test - public void testNewEnhance_Handle() throws Throwable { - Enhance enhance = ProxyDriver.newEnhance(CharSequence.class, new InvocationHandler() { - - public Object invoke(Object proxy, Method method, Object[] args) { - assertThat(method.getName(), is("toString")); - return "OK"; - } - }); - - Object result = construct(enhance.getHandler(), - String.class.getConstructor()); - - assertThat(result, instanceOf(CharSequence.class)); - assertThat(result, not(instanceOf(String.class))); - CharSequence proxy = (CharSequence) result; - assertThat(proxy.toString(), is("OK")); - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/ProxyDriverTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,119 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +import org.junit.Test; + +import org.jiemamy.utils.enhancer.Enhance; + +/** + * Test for {@link ProxyDriver}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class ProxyDriverTest extends DriverTestRoot { + + private static InvocationHandler DUMMY = new InvocationHandler() { + + public Object invoke(Object proxy, Method method, Object[] args) { + throw new AssertionError(); + } + }; + + + /** + * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testNewEnhance_NotInterface() { + ProxyDriver.newEnhance(Object.class, DUMMY); + } + + /** + * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. + */ + @Test + public void testNewEnhance_TargetInterface() { + Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); + assertTargetConstructor(true, enhance.getPointcut(), + Runnable.class, + Thread.class, + "()V"); + assertTargetConstructor(true, enhance.getPointcut(), + Runnable.class, + Thread.class, + "(Ljava/lang/String;)V"); + } + + /** + * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. + */ + @Test + public void testNewEnhance_TargetSubInterface() { + Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); + assertTargetConstructor(false, enhance.getPointcut(), + RunnableEx.class, + RunnableExImpl.class, + "()V"); + } + + /** + * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. + */ + @Test + public void testNewEnhance_TargetMethod() { + Enhance enhance = ProxyDriver.newEnhance(Runnable.class, DUMMY); + assertTargetMethod(false, enhance.getPointcut(), + Runnable.class, + Runnable.class, + "run", + "()V"); + } + + /** + * Test method for {@link ProxyDriver#newEnhance(java.lang.Class, java.lang.reflect.InvocationHandler)}. + * @throws Throwable if occur + */ + @Test + public void testNewEnhance_Handle() throws Throwable { + Enhance enhance = ProxyDriver.newEnhance(CharSequence.class, new InvocationHandler() { + + public Object invoke(Object proxy, Method method, Object[] args) { + assertThat(method.getName(), is("toString")); + return "OK"; + } + }); + + Object result = construct(enhance.getHandler(), + String.class.getConstructor()); + + assertThat(result, instanceOf(CharSequence.class)); + assertThat(result, not(instanceOf(String.class))); + CharSequence proxy = (CharSequence) result; + assertThat(proxy.toString(), is("OK")); + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,28 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -/** - * {@link Runnable}を拡張したインターフェース。 - * @version $Id$ - * @author Suguru ARAKAWA - */ -public interface RunnableEx extends Runnable { - -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableEx.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,28 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +/** + * {@link Runnable}を拡張したインターフェース。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public interface RunnableEx extends Runnable { + +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java 2009-10-08 12:45:24 UTC (rev 3733) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,31 +0,0 @@ -/* - * Copyright 2007-2009 Jiemamy Project and the Others. - * Created on 2009/10/08 - * - * 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.driver; - -/** - * {@link RunnableEx}の実装。 - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class RunnableExImpl implements RunnableEx { - - public void run() { - return; - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java (from rev 3733, leto/factory-enhancer/branches/interface-enhancer/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/driver/RunnableExImpl.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,31 @@ +/* + * Copyright 2007-2009 Jiemamy Project and the Others. + * Created on 2009/10/08 + * + * 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.driver; + +/** + * {@link RunnableEx}の実装。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class RunnableExImpl implements RunnableEx { + + public void run() { + return; + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,28 +0,0 @@ -/* - * 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.reflection; - -/** - * パッケージプライベートクラス。 - * @version $Id$ - * @author Suguru ARAKAWA - */ -class PackagePrivateClass { - // no members -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateClass.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,28 @@ +/* + * 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.reflection; + +/** + * パッケージプライベートクラス。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +class PackagePrivateClass { + // no members +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,34 +0,0 @@ -/* - * 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.reflection; - -/** - * パッケージプライベートコンストラクタをもつクラス。 - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class PackagePrivateConstructor { - - /** - * インスタンスを生成する。 - */ - PackagePrivateConstructor() { - return; - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/PackagePrivateConstructor.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,34 @@ +/* + * 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.reflection; + +/** + * パッケージプライベートコンストラクタをもつクラス。 + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class PackagePrivateConstructor { + + /** + * インスタンスを生成する。 + */ + PackagePrivateConstructor() { + return; + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,163 +0,0 @@ -/* - * 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.reflection; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -import java.text.DateFormat; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - -/** - * Test for {@link ReflectionFactory}. - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class ReflectionFactoryTest { - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test - public void testReflectionFactory() { - ReflectionFactory<String> factory = new ReflectionFactory<String>(String.class); - assertThat(factory.getTargetClass(), is((Object) String.class)); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_Interface() { - factory(Runnable.class); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_Enum() { - factory(TimeUnit.class); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_AbstractClass() { - factory(DateFormat.class); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_MemberClass() { - factory(java.awt.geom.Point2D.Float.class); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_LocalClass() { - class Local { - // no members - } - factory(Local.class); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_AnonymousClass() { - Object o = new Object() { - // no members - }; - factory(o.getClass()); - } - - /** - * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testReflectionFactory_PackagePrivate() { - factory(PackagePrivateClass.class); - } - - /** - * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. - * @throws Exception if occur - */ - @Test - public void testNewInstance() throws Exception { - ReflectionFactory<String> factory = factory(String.class); - assertThat(factory.newInstance(), is("")); - } - - /** - * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. - * @throws Exception if occur - */ - @Test - public void testNewInstance_Arguments() throws Exception { - ReflectionFactory<String> factory = factory(String.class); - assertThat(factory.newInstance("Hello, world!"), is("Hello, world!")); - } - - /** - * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. - * @throws Exception if occur - */ - @Test(expected = IllegalArgumentException.class) - public void testNewInstance_NotFound() throws Exception { - ReflectionFactory<Integer> factory = factory(Integer.class); - factory.newInstance(100L); - } - - /** - * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. - * @throws Exception if occur - */ - @Test(expected = IllegalArgumentException.class) - public void testNewInstance_Ambiguous() throws Exception { - ReflectionFactory<String> factory = factory(String.class); - factory.newInstance(new Object[] { - null - }); - } - - /** - * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. - * @throws Exception if occur - */ - @Test(expected = IllegalArgumentException.class) - public void testNewInstance_NotPublic() throws Exception { - ReflectionFactory<PackagePrivateConstructor> factory = - factory(PackagePrivateConstructor.class); - factory.newInstance(); - } - - private <T>ReflectionFactory<T> factory(Class<T> aClass) { - return new ReflectionFactory<T>(aClass); - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionFactoryTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,163 @@ +/* + * 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.reflection; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.text.DateFormat; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +/** + * Test for {@link ReflectionFactory}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class ReflectionFactoryTest { + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test + public void testReflectionFactory() { + ReflectionFactory<String> factory = new ReflectionFactory<String>(String.class); + assertThat(factory.getTargetClass(), is((Object) String.class)); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_Interface() { + factory(Runnable.class); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_Enum() { + factory(TimeUnit.class); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_AbstractClass() { + factory(DateFormat.class); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_MemberClass() { + factory(java.awt.geom.Point2D.Float.class); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_LocalClass() { + class Local { + // no members + } + factory(Local.class); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_AnonymousClass() { + Object o = new Object() { + // no members + }; + factory(o.getClass()); + } + + /** + * Test method for {@link ReflectionFactory#ReflectionFactory(java.lang.Class)}. + */ + @Test(expected = IllegalArgumentException.class) + public void testReflectionFactory_PackagePrivate() { + factory(PackagePrivateClass.class); + } + + /** + * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. + * @throws Exception if occur + */ + @Test + public void testNewInstance() throws Exception { + ReflectionFactory<String> factory = factory(String.class); + assertThat(factory.newInstance(), is("")); + } + + /** + * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. + * @throws Exception if occur + */ + @Test + public void testNewInstance_Arguments() throws Exception { + ReflectionFactory<String> factory = factory(String.class); + assertThat(factory.newInstance("Hello, world!"), is("Hello, world!")); + } + + /** + * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. + * @throws Exception if occur + */ + @Test(expected = IllegalArgumentException.class) + public void testNewInstance_NotFound() throws Exception { + ReflectionFactory<Integer> factory = factory(Integer.class); + factory.newInstance(100L); + } + + /** + * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. + * @throws Exception if occur + */ + @Test(expected = IllegalArgumentException.class) + public void testNewInstance_Ambiguous() throws Exception { + ReflectionFactory<String> factory = factory(String.class); + factory.newInstance(new Object[] { + null + }); + } + + /** + * Test method for {@link ReflectionFactory#newInstance(java.lang.Object[])}. + * @throws Exception if occur + */ + @Test(expected = IllegalArgumentException.class) + public void testNewInstance_NotPublic() throws Exception { + ReflectionFactory<PackagePrivateConstructor> factory = + factory(PackagePrivateConstructor.class); + factory.newInstance(); + } + + private <T>ReflectionFactory<T> factory(Class<T> aClass) { + return new ReflectionFactory<T>(aClass); + } +} Deleted: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java 2009-10-05 11:36:43 UTC (rev 3704) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -1,260 +0,0 @@ -/* - * 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.reflection; - -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findConstructor; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findMethod; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.isNormalClass; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.isPackageMember; -import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.toParameterTypes; -import static org.junit.Assert.assertThat; - -import java.io.PrintWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - -/** - * Test for {@link ReflectionUtil}. - * @version $Id$ - * @author Suguru ARAKAWA - */ -public class ReflectionUtilTest { - - /** - * Test method for {@link ReflectionUtil#isNormalClass(java.lang.Class)}. - */ - @Test - public void testIsNormalClass() { - assertThat(isNormalClass(Object.class), is(true)); - assertThat(isNormalClass(List.class), is(false)); - assertThat(isNormalClass(TimeUnit.class), is(false)); - assertThat(isNormalClass(Override.class), is(false)); - assertThat(isNormalClass(Object[].class), is(false)); - assertThat(isNormalClass(int.class), is(false)); - } - - /** - * Test method for {@link ReflectionUtil#isPackageMember(java.lang.Class)}. - */ - @Test - public void testIsPackageMember() { - class Local { - // no members - } - Object anonymousObject = new Object() { - // no members - }; - - Class<?> local = Local.class; - Class<?> anonymous = anonymousObject.getClass(); - - assertThat(isPackageMember(Object.class), is(true)); - assertThat(isPackageMember(List.class), is(true)); - assertThat(isPackageMember(TimeUnit.class), is(true)); - assertThat(isPackageMember(Override.class), is(true)); - - assertThat(isPackageMember(Map.Entry.class), is(false)); - assertThat(isPackageMember(local), is(false)); - assertThat(isPackageMember(anonymous), is(false)); - assertThat(isPackageMember(int.class), is(false)); - assertThat(isPackageMember(String[].class), is(false)); - } - - /** - * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. - */ - @Test - public void testToParameterTypes_Empty() { - assertThat(toParameterTypes(), - is(classes())); - assertThat(toParameterTypes(1), - is(classes(Integer.class))); - assertThat(toParameterTypes("Hello"), - is(classes(String.class))); - } - - /** - * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. - */ - @Test - public void testToParameterTypes_Type() { - assertThat(toParameterTypes(1), - is(classes(Integer.class))); - assertThat(toParameterTypes("Hello"), - is(classes(String.class))); - assertThat(toParameterTypes(new Object[] { - new String[0] - }), is(classes(String[].class))); - assertThat(toParameterTypes(new Object[] { - null - }), is(classes(new Class<?>[] { - null - }))); - } - - /** - * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. - */ - @Test - public void testToParameterTypes_Many() { - assertThat(toParameterTypes("Hello", 1, new ArrayList<Object>(), null), - is(classes(String.class, Integer.class, ArrayList.class, null))); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_nameIdentified() { - Collection<Method> methods = findMethod( - methods(Object.class), "hashCode", classes()); - assertJust(methods, method(Object.class, "hashCode")); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_overloadWithSinglePotentially() { - Collection<Method> methods = findMethod( - methods(Object.class), "wait", classes()); - assertJust(methods, method(Object.class, "wait")); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_overloadWithMultiPotentially() { - Collection<Method> methods = findMethod( - methods(String.class), "valueOf", classes(char[].class)); - assertJust(methods, method(String.class, "valueOf", char[].class)); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_overloadWithPrimitive() { - Collection<Method> methods = findMethod( - methods(String.class), "valueOf", classes(int.class)); - assertJust(methods, method(String.class, "valueOf", int.class)); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_overloadWithWrapper() { - Collection<Method> methods = findMethod( - methods(String.class), "valueOf", classes(Integer.class)); - - // Java言語仕様との差異 - // 通常はサブタイピング変換が優先されるが、現在の仕様ではプリミティブが常に勝つ - assertJust(methods, method(String.class, "valueOf", int.class)); - } - - /** - * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. - */ - @Test - public void testFindMethod_overloadWithAmbiguous() { - Collection<Method> methods = findMethod( - methods(PrintWriter.class), "print", - classes(new Class<?>[] { - null - })); - - // PrintWriter.valueOf(null) -> char[] / String / ... - // 現在の仕様ではほぼすべてのメソッドが合致するはず - assertThat(methods.size(), greaterThan(1)); - } - - /** - * Test method for {@link ReflectionUtil#findConstructor(java.util.Collection, java.util.List)}. - */ - @Test - public void testFindConstructor_WithSinglePotentially() { - Collection<Constructor<String>> ctors = findConstructor( - ctors(String.class), classes()); - - assertJust(ctors, ctor(String.class)); - } - - /** - * Test method for {@link ReflectionUtil#findConstructor(java.util.Collection, java.util.List)}. - */ - @Test - public void testFindConstructor_MultiPotentially() { - Collection<Constructor<Integer>> ctors = findConstructor( - ctors(Integer.class), classes(String.class)); - - assertJust(ctors, ctor(Integer.class, String.class)); - } - - private static List<Method> methods(Class<?> aClass) { - return Arrays.asList(aClass.getMethods()); - } - - private static <T>List<Constructor<T>> ctors(Class<T> aClass) { - List<Constructor<T>> results = new ArrayList<Constructor<T>>(); - for (Constructor<?> c : aClass.getConstructors()) { - @SuppressWarnings("unchecked") - Constructor<T> assume = (Constructor<T>) c; - results.add(assume); - } - return results; - } - - private List<Class<?>> classes(Class<?>... classes) { - return Arrays.asList(classes); - } - - private void assertJust(Collection<?> candidates, Object just) { - assertThat(candidates.toString(), candidates.size(), is(1)); - assertThat(new ArrayList<Object>(candidates), - is((Object) Arrays.asList(just))); - } - - private <T>Constructor<T> ctor(Class<T> declaring, Class<?>... parameters) { - try { - return declaring.getDeclaredConstructor(parameters); - } catch (Exception e) { - throw new AssertionError(e); - } - } - - private Method method(Class<?> declaring, String name, Class<?>... parameters) { - try { - return declaring.getDeclaredMethod(name, parameters); - } catch (Exception e) { - throw new AssertionError(e); - } - } -} Copied: leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java (from rev 3704, leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java) =================================================================== --- leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java (rev 0) +++ leto/factory-enhancer/trunk/src/test/java/org/jiemamy/utils/enhancer/reflection/ReflectionUtilTest.java 2009-10-08 13:05:37 UTC (rev 3734) @@ -0,0 +1,260 @@ +/* + * 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.reflection; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findConstructor; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.findMethod; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.isNormalClass; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.isPackageMember; +import static org.jiemamy.utils.enhancer.reflection.ReflectionUtil.toParameterTypes; +import static org.junit.Assert.assertThat; + +import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +/** + * Test for {@link ReflectionUtil}. + * @version $Id$ + * @author Suguru ARAKAWA + */ +public class ReflectionUtilTest { + + /** + * Test method for {@link ReflectionUtil#isNormalClass(java.lang.Class)}. + */ + @Test + public void testIsNormalClass() { + assertThat(isNormalClass(Object.class), is(true)); + assertThat(isNormalClass(List.class), is(false)); + assertThat(isNormalClass(TimeUnit.class), is(false)); + assertThat(isNormalClass(Override.class), is(false)); + assertThat(isNormalClass(Object[].class), is(false)); + assertThat(isNormalClass(int.class), is(false)); + } + + /** + * Test method for {@link ReflectionUtil#isPackageMember(java.lang.Class)}. + */ + @Test + public void testIsPackageMember() { + class Local { + // no members + } + Object anonymousObject = new Object() { + // no members + }; + + Class<?> local = Local.class; + Class<?> anonymous = anonymousObject.getClass(); + + assertThat(isPackageMember(Object.class), is(true)); + assertThat(isPackageMember(List.class), is(true)); + assertThat(isPackageMember(TimeUnit.class), is(true)); + assertThat(isPackageMember(Override.class), is(true)); + + assertThat(isPackageMember(Map.Entry.class), is(false)); + assertThat(isPackageMember(local), is(false)); + assertThat(isPackageMember(anonymous), is(false)); + assertThat(isPackageMember(int.class), is(false)); + assertThat(isPackageMember(String[].class), is(false)); + } + + /** + * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. + */ + @Test + public void testToParameterTypes_Empty() { + assertThat(toParameterTypes(), + is(classes())); + assertThat(toParameterTypes(1), + is(classes(Integer.class))); + assertThat(toParameterTypes("Hello"), + is(classes(String.class))); + } + + /** + * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. + */ + @Test + public void testToParameterTypes_Type() { + assertThat(toParameterTypes(1), + is(classes(Integer.class))); + assertThat(toParameterTypes("Hello"), + is(classes(String.class))); + assertThat(toParameterTypes(new Object[] { + new String[0] + }), is(classes(String[].class))); + assertThat(toParameterTypes(new Object[] { + null + }), is(classes(new Class<?>[] { + null + }))); + } + + /** + * Test method for {@link ReflectionUtil#toParameterTypes(java.lang.Object[])}. + */ + @Test + public void testToParameterTypes_Many() { + assertThat(toParameterTypes("Hello", 1, new ArrayList<Object>(), null), + is(classes(String.class, Integer.class, ArrayList.class, null))); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_nameIdentified() { + Collection<Method> methods = findMethod( + methods(Object.class), "hashCode", classes()); + assertJust(methods, method(Object.class, "hashCode")); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_overloadWithSinglePotentially() { + Collection<Method> methods = findMethod( + methods(Object.class), "wait", classes()); + assertJust(methods, method(Object.class, "wait")); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_overloadWithMultiPotentially() { + Collection<Method> methods = findMethod( + methods(String.class), "valueOf", classes(char[].class)); + assertJust(methods, method(String.class, "valueOf", char[].class)); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_overloadWithPrimitive() { + Collection<Method> methods = findMethod( + methods(String.class), "valueOf", classes(int.class)); + assertJust(methods, method(String.class, "valueOf", int.class)); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_overloadWithWrapper() { + Collection<Method> methods = findMethod( + methods(String.class), "valueOf", classes(Integer.class)); + + // Java言語仕様との差異 + // 通常はサブタイピング変換が優先されるが、現在の仕様ではプリミティブが常に勝つ + assertJust(methods, method(String.class, "valueOf", int.class)); + } + + /** + * Test method for {@link ReflectionUtil#findMethod(java.util.Collection, java.lang.String, java.util.List)}. + */ + @Test + public void testFindMethod_overloadWithAmbiguous() { + Collection<Method> methods = findMethod( + methods(PrintWriter.class), "print", + classes(new Class<?>[] { + null + })); + + // PrintWriter.valueOf(null) -> char[] / String / ... + // 現在の仕様ではほぼすべてのメソッドが合致するはず + assertThat(methods.size(), greaterThan(1)); + } + + /** + * Test method for {@link ReflectionUtil#findConstructor(java.util.Collection, java.util.List)}. + */ + @Test + public void testFindConstructor_WithSinglePotentially() { + Collection<Constructor<String>> ctors = findConstructor( + ctors(String.class), classes()); + + assertJust(ctors, ctor(String.class)); + } + + /** + * Test method for {@link ReflectionUtil#findConstructor(java.util.Collection, java.util.List)}. + */ + @Test + public void testFindConstructor_MultiPotentially() { + Collection<Constructor<Integer>> ctors = findConstructor( + ctors(Integer.class), classes(String.class)); + + assertJust(ctors, ctor(Integer.class, String.class)); + } + + private static List<Method> methods(Class<?> aClass) { + return Arrays.asList(aClass.getMethods()); + } + + private static <T>List<Constructor<T>> ctors(Class<T> aClass) { + List<Constructor<T>> results = new ArrayList<Constructor<T>>(); + for (Constructor<?> c : aClass.getConstructors()) { + @SuppressWarnings("unchecked") + Constructor<T> assume = (Constructor<T>) c; + results.add(assume); + } + return results; + } + + private List<Class<?>> classes(Class<?>... classes) { + return Arrays.asList(classes); + } + + private void assertJust(Collection<?> candidates, Object just) { + assertThat(candidates.toString(), candidates.size(), is(1)); + assertThat(new ArrayList<Object>(candidates), + is((Object) Arrays.asList(just))); + } + + private <T>Constructor<T> ctor(Class<T> declaring, Class<?>... parameters) { + try { + return declaring.getDeclaredConstructor(parameters); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + private Method method(Class<?> declaring, String name, Class<?>... parameters) { + try { + return declaring.getDeclaredMethod(name, parameters); + } catch (Exception e) { + throw new AssertionError(e); + } + } +}