svnno****@sourc*****
svnno****@sourc*****
2009年 10月 5日 (月) 19:36:49 JST
Revision: 3702 http://sourceforge.jp/projects/jiemamy/svn/view?view=rev&revision=3702 Author: ashigeru Date: 2009-10-05 19:36:49 +0900 (Mon, 05 Oct 2009) Log Message: ----------- インターフェースから生成されるクラスのコンストラクタ呼び出しを検出しにくいため、そのような場合にInvocationPointcut.isTargetの第1引数にインターフェースが渡るようにした。 併せてInterfaceEnhancerTestを増強。 Modified Paths: -------------- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java Added Paths: ----------- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java 2009-10-05 00:53:40 UTC (rev 3701) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InterfaceEnhancer.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -18,6 +18,11 @@ */ 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; @@ -37,7 +42,6 @@ import org.jiemamy.utils.enhancer.helper.AspectList; import org.jiemamy.utils.enhancer.helper.EnhanceManager; -import org.jiemamy.utils.enhancer.helper.EnhanceManipulator; import org.jiemamy.utils.enhancer.helper.JavassistConverter; import org.jiemamy.utils.enhancer.helper.NewInstanceEnhancer; import org.jiemamy.utils.enhancer.reflection.ReflectionFactory; @@ -70,7 +74,7 @@ * </p> * <ul> * <li> - * ファクトリインターフェース、プロダクトインターフェース、プロダクトインターフェースの親クラスは、 + * ファクトリインターフェース、プロダクトインターフェース、およびプロダクトの親クラスは、 * いずれも{@code public}で宣言されなければならない。 * </li> * <li> @@ -117,12 +121,12 @@ * <li> {@code factoryInterface}は{@code public}の可視性を持つ </li> * <li> {@code factoryInterface}はインターフェース型を表現する </li> * <li> {@code factoryInterface}のすべてのメソッドは、インターフェース型の値を返す </li> - * <li> {@code factoryInterface}のそれぞれのメソッド返すインターフェースは、いずれも{@code public}で宣言される </li> + * <li> {@code factoryInterface}のそれぞれのメソッドが返すインターフェースは、いずれも{@code public}で宣言される </li> * <li> {@code productSuperClass}は{@code public}の可視性を持つ </li> * <li> {@code productSuperClass}は具象クラスである </li> * <li> {@code productSuperClass}は継承可能である </li> * <li> {@code productSuperClass}は(列挙でない)クラス型を表現する </li> - * <li> {@code productSuperClass}は引数をとらないコンストラクタを提供する </li> + * <li> {@code productSuperClass}は引数をとらない{@code public}コンストラクタを提供する </li> * </ul> * @param factoryInterface 実装を生成する対象のファクトリインターフェース * @param productSuperClass それぞれのプロダクトが実装する親クラス @@ -180,6 +184,11 @@ "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", @@ -211,6 +220,11 @@ "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); @@ -222,15 +236,14 @@ LOG.trace("Creating an implementation of factory: {}", factoryImplementation); Map<CtClass, CtClass> targetProducts = createProductMap(); - CtClass implementation = createFactoryImplementation(targetProducts); + CtClass implementation = createFactory(targetProducts); Map<CtClass, AspectList<CtMethod>> allProductAspects = - EnhanceManipulator.weavePointcutIntoAllProducts(enhanceManager, targetProducts); + weavePointcutIntoAllProducts(enhanceManager, targetProducts); AspectList<CtConstructor> factoryAspects = weavePointcutIntoFactory(implementation, targetProducts, allProductAspects); Class<?> installedFactory = - EnhanceManipulator - .install(converter, implementation, targetProducts, factoryAspects, allProductAspects); + install(converter, implementation, targetProducts, factoryAspects, allProductAspects); return installedFactory.asSubclass(factoryInterface); } @@ -253,17 +266,17 @@ CtClass baseClass = converter.loadCtClass(productSuperClass); for (Class<?> productInterface : productInterfaces) { CtClass baseInterface = converter.loadCtClass(productInterface); - CtClass productClass = EnhanceManipulator.createProductImplementation(baseInterface, baseClass); + CtClass productClass = createProductImplementation(baseInterface, baseClass); results.put(baseInterface, productClass); } LOG.debug("Product map: {}", results); return results; } - private CtClass createFactoryImplementation(Map<CtClass, CtClass> targetProducts) throws EnhanceException { + private CtClass createFactory(Map<CtClass, CtClass> targetProducts) throws EnhanceException { assert targetProducts != null; CtClass factory = converter.loadCtClass(factoryInterface); - CtClass factoryImpl = EnhanceManipulator.createFactoryImplementation(factory, targetProducts); + CtClass factoryImpl = createFactoryImplementation(factory, targetProducts); return factoryImpl; } Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java 2009-10-05 00:53:40 UTC (rev 3701) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/InvocationPointcut.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -31,7 +31,7 @@ * <p> * このインターフェースは、クライアントが実装してエンハンサに登録することができる。 * </p> - * @version $Date$ + * @version 0.2.0 * @author Suguru ARAKAWA (Gluegent, Inc.) * @see Pointcuts */ @@ -43,7 +43,11 @@ * {@code self}に渡される値は、対象のメソッドまたはコンストラクタを公開するクラスである。 * メソッドは継承によって子クラスで親クラスのメソッドを公開可能であるため、 * {@code behavior}に指定されるメンバを実際に宣言するクラスとは異なる場合がある。 - * なお、コンストラクタは継承できないため、{@code behavior}を宣言するクラスと必ず一致する。 + * コンストラクタの場合も同様に、対象のインスタンス生成対象がインターフェースであった場合、 + * インターフェースにコンストラクタは定義できないため、元のインターフェースが{@code self}に渡される。 + * 上記のため、 + * {@code CtBehavior#getDeclaringClass() behavior.getDeclaringClass()} + * の値を参考にせずに{@code self}を利用して対象を特定することが望ましい。 * </p> * <p> * メソッド呼び出しに対して検査を行う場合、引数の値は必ず Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-05 00:53:40 UTC (rev 3701) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/EnhanceManipulator.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -339,6 +339,13 @@ 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; } @@ -472,6 +479,11 @@ * } * </code></pre> * <p> + * このとき、バイパス対象がインターフェースメソッドである場合、親のメソッドを呼び出そうとすると + * リンクエラーになってしまう。 + * そのため、かわりに{@link AbstractMethodError}をスローする。 + * </p> + * <p> * なお、このメソッドでは拡張するクラスにバイパスメソッドを追加するだけで、 * アスペクトを処理するメソッドについては * {@link #createPointcutMethod(CtClass, CtMethod, CtField, int)} @@ -508,12 +520,14 @@ }); bypass.setModifiers(Modifier.PUBLIC); if (isDeclaredInClass(method, enhance.getSuperclass()) == false) { - LOG.debug("Bypass target {}{} is not declared in {}; this throws AbstractMethodError", new Object[] { + LOG.debug("Bypass target {}{} is not declared in {}; this throws {}", new Object[] { method.getName(), method.getSignature(), - enhance.getSuperclass().getName() + enhance.getSuperclass().getName(), + AbstractMethodError.class.getName() }); - bypass.setBody(String.format("throw new java.lang.AbstractMethodError(\"%s\");", method.getName())); + bypass.setBody(String.format("throw new %s(\"%s\");", AbstractMethodError.class.getName(), method + .getName())); } else { LOG.debug("Bypass target {}{} is in {}; this just invokes it", new Object[] { method.getName(), @@ -803,6 +817,7 @@ 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; Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java 2009-10-05 00:53:40 UTC (rev 3701) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/main/java/org/jiemamy/utils/enhancer/helper/NewInstanceEnhancer.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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,7 +182,7 @@ LOG.debug("Rewrite new[{}]: {} (at {}{}:{})", new Object[] { enhanceIndex, - declaring.getName(), + provider.getName(), expr.where().getName(), expr.where().getSignature(), expr.getLineNumber() @@ -169,16 +191,16 @@ // new Hoge(...)の部分を、アドバイスの実行に置き換える EnhanceManipulator.replaceToPointcut(expr, 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; + } } Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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; + } +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoDefaultConstructor.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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; + } +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNoPublicConstructor.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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 { + +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/ClassNotPublic.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Modified: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java 2009-10-05 00:53:40 UTC (rev 3701) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceEnhancerTest.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -18,14 +18,22 @@ */ 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 org.junit.Ignore; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtConstructor; + import org.junit.Test; import org.jiemamy.utils.enhancer.aspect.StringResultPointcut; @@ -39,26 +47,20 @@ public class InterfaceEnhancerTest { /** - * Test method for {@link org.jiemamy.utils.enhancer.InterfaceEnhancer#InterfaceEnhancer(java.lang.Class, java.lang.Class, java.util.List)}. - */ - @Ignore - @Test - public void testInterfaceEnhancer() { - fail("Not yet implemented"); - } - - /** - * Test method for {@link org.jiemamy.utils.enhancer.AbstractEnhancer#getFactory()}. + * 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(); @@ -73,7 +75,8 @@ * @throws Exception if occur */ @Test - public void testGetFactory_Enhanced() throws Exception { + public void testGetFactory_EnhanceMethod() throws Exception { + // Stringを返すメソッドをフックして、かわりに "Hello" を返す。 Enhance enhance = new Enhance(new StringResultPointcut(), new InvocationHandler() { public Object handle(Invocation invocation) { @@ -87,15 +90,98 @@ 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 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>( @@ -108,6 +194,127 @@ assertThat(product.getMessage(), is("Concrete")); } + /** + * 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); } Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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 { + +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceNotPublic.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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(); +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotInterface.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Added: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java =================================================================== --- leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java (rev 0) +++ leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java 2009-10-05 10:36:49 UTC (rev 3702) @@ -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(); +} Property changes on: leto/factory-enhancer/branches/interface-enhancer-20091004/src/test/java/org/jiemamy/utils/enhancer/InterfaceReturnsNotPublic.java ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native