From 910380196a666767c9a81349c0b4c7fe54bb9406 Mon Sep 17 00:00:00 2001 From: "andrew.asa" Date: Tue, 6 Feb 2018 17:26:35 +0800 Subject: [PATCH] =?UTF-8?q?spring-cglib-repack=E6=BA=90=E7=A0=81=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=88=B0=E5=88=B0spring=E4=B8=AD=EF=BC=8C=E4=B9=8B?= =?UTF-8?q?=E5=89=8Dspring=E4=B8=BA=E4=BA=86=E8=A6=81=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E6=8E=89cglib=E8=87=AA=E5=B7=B1=E5=AE=9E=E7=8E=B0=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E9=81=8Dcglib=EF=BC=8C=E4=BD=86=E6=98=AF=E5=8D=95?= =?UTF-8?q?=E7=8B=AC=E6=89=93=E4=BA=86=E5=8C=85=E6=88=90=E4=B8=BAspring-cg?= =?UTF-8?q?lib-repack=EF=BC=8C=E6=BA=90=E7=A0=81=20https://github.com/adre?= =?UTF-8?q?wasa/spring-core-3.2-jre6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aop/framework/CglibAopProxy.java | 20 +- .../aop/framework/ObjenesisCglibAopProxy.java | 6 +- ...CglibSubclassingInstantiationStrategy.java | 14 +- .../cglib/beans/BeanCopier.java | 188 +++ .../cglib/beans/BeanGenerator.java | 156 +++ .../springframework/cglib/beans/BeanMap.java | 315 +++++ .../cglib/beans/BeanMapEmitter.java | 202 +++ .../springframework/cglib/beans/BulkBean.java | 142 ++ .../cglib/beans/BulkBeanEmitter.java | 162 +++ .../cglib/beans/BulkBeanException.java | 42 + .../cglib/beans/FixedKeySet.java | 38 + .../cglib/beans/ImmutableBean.java | 127 ++ .../cglib/core/AbstractClassGenerator.java | 242 ++++ .../springframework/cglib/core/Block.java | 50 + .../cglib/core/ClassEmitter.java | 284 ++++ .../cglib/core/ClassGenerator.java | 23 + .../springframework/cglib/core/ClassInfo.java | 46 + .../cglib/core/ClassNameReader.java | 67 + .../cglib/core/ClassesKey.java | 31 + .../cglib/core/CodeEmitter.java | 859 ++++++++++++ .../cglib/core/CodeGenerationException.java | 36 + .../cglib/core/CollectionUtils.java | 80 ++ .../springframework/cglib/core/Constants.java | 70 + .../springframework/cglib/core/Converter.java | 21 + .../cglib/core/Customizer.java | 23 + .../cglib/core/DebuggingClassWriter.java | 117 ++ .../cglib/core/DefaultGeneratorStrategy.java | 48 + .../cglib/core/DefaultNamingPolicy.java | 64 + .../cglib/core/DuplicatesPredicate.java | 28 + .../springframework/cglib/core/EmitUtils.java | 926 +++++++++++++ .../cglib/core/GeneratorStrategy.java | 44 + .../cglib/core/KeyFactory.java | 265 ++++ .../springframework/cglib/core/Local.java | 38 + .../cglib/core/LocalVariablesSorter.java | 159 +++ .../cglib/core/MethodInfo.java | 47 + .../cglib/core/MethodInfoTransformer.java | 38 + .../cglib/core/MethodWrapper.java | 47 + .../cglib/core/NamingPolicy.java | 41 + .../cglib/core/ObjectSwitchCallback.java | 25 + .../springframework/cglib/core/Predicate.java | 21 + .../cglib/core/ProcessArrayCallback.java | 23 + .../cglib/core/ProcessSwitchCallback.java | 24 + .../cglib/core/ReflectUtils.java | 479 +++++++ .../cglib/core/RejectModifierPredicate.java | 30 + .../springframework/cglib/core/Signature.java | 74 ++ .../cglib/core/SpringNamingPolicy.java | 3 - .../cglib/core/TinyBitSet.java | 78 ++ .../cglib/core/Transformer.java | 20 + .../springframework/cglib/core/TypeUtils.java | 420 ++++++ .../cglib/core/VisibilityPredicate.java | 47 + .../cglib/proxy/BridgeMethodResolver.java | 112 ++ .../springframework/cglib/proxy/Callback.java | 29 + .../cglib/proxy/CallbackFilter.java | 42 + .../cglib/proxy/CallbackGenerator.java | 38 + .../cglib/proxy/CallbackHelper.java | 99 ++ .../cglib/proxy/CallbackInfo.java | 105 ++ .../cglib/proxy/Dispatcher.java | 30 + .../cglib/proxy/DispatcherGenerator.java | 69 + .../springframework/cglib/proxy/Enhancer.java | 1092 +++++++++++++++ .../springframework/cglib/proxy/Factory.java | 80 ++ .../cglib/proxy/FixedValue.java | 35 + .../cglib/proxy/FixedValueGenerator.java | 44 + .../cglib/proxy/InterfaceMaker.java | 121 ++ .../cglib/proxy/InvocationHandler.java | 35 + .../proxy/InvocationHandlerGenerator.java | 66 + .../cglib/proxy/LazyLoader.java | 30 + .../cglib/proxy/LazyLoaderGenerator.java | 91 ++ .../cglib/proxy/MethodInterceptor.java | 42 + .../proxy/MethodInterceptorGenerator.java | 245 ++++ .../cglib/proxy/MethodProxy.java | 230 ++++ .../springframework/cglib/proxy/Mixin.java | 237 ++++ .../cglib/proxy/MixinBeanEmitter.java | 40 + .../cglib/proxy/MixinEmitter.java | 92 ++ .../cglib/proxy/MixinEverythingEmitter.java | 49 + .../springframework/cglib/proxy/NoOp.java | 28 + .../cglib/proxy/NoOpGenerator.java | 45 + .../springframework/cglib/proxy/Proxy.java | 104 ++ .../cglib/proxy/ProxyRefDispatcher.java | 31 + .../proxy/UndeclaredThrowableException.java | 38 + .../cglib/reflect/ConstructorDelegate.java | 124 ++ .../cglib/reflect/FastClass.java | 202 +++ .../cglib/reflect/FastClassEmitter.java | 228 ++++ .../cglib/reflect/FastConstructor.java | 46 + .../cglib/reflect/FastMember.java | 65 + .../cglib/reflect/FastMethod.java | 59 + .../cglib/reflect/MethodDelegate.java | 263 ++++ .../cglib/reflect/MulticastDelegate.java | 171 +++ .../AbstractClassFilterTransformer.java | 90 ++ .../cglib/transform/AbstractClassLoader.java | 117 ++ .../transform/AbstractClassTransformer.java | 30 + .../cglib/transform/AbstractProcessTask.java | 64 + .../transform/AbstractTransformTask.java | 269 ++++ .../cglib/transform/AnnotationVisitorTee.java | 62 + .../transform/ClassEmitterTransformer.java | 21 + .../cglib/transform/ClassFilter.java | 27 + .../transform/ClassFilterTransformer.java | 30 + .../cglib/transform/ClassReaderGenerator.java | 42 + .../cglib/transform/ClassTransformer.java | 30 + .../transform/ClassTransformerChain.java | 58 + .../transform/ClassTransformerFactory.java | 20 + .../cglib/transform/ClassTransformerTee.java | 33 + .../cglib/transform/ClassVisitorTee.java | 104 ++ .../cglib/transform/FieldVisitorTee.java | 48 + .../cglib/transform/MethodFilter.java | 22 + .../transform/MethodFilterTransformer.java | 45 + .../cglib/transform/MethodVisitorTee.java | 158 +++ .../transform/TransformingClassGenerator.java | 34 + .../transform/TransformingClassLoader.java | 33 + .../impl/AbstractInterceptFieldCallback.java | 42 + .../impl/AccessFieldTransformer.java | 61 + .../impl/AddDelegateTransformer.java | 126 ++ .../transform/impl/AddInitTransformer.java | 59 + .../impl/AddPropertyTransformer.java | 47 + .../impl/AddStaticInitTransformer.java | 50 + .../cglib/transform/impl/FieldProvider.java | 35 + .../impl/FieldProviderTransformer.java | 208 +++ .../impl/InterceptFieldCallback.java | 42 + .../transform/impl/InterceptFieldEnabled.java | 21 + .../transform/impl/InterceptFieldFilter.java | 24 + .../impl/InterceptFieldTransformer.java | 213 +++ .../impl/UndeclaredThrowableStrategy.java | 56 + .../impl/UndeclaredThrowableTransformer.java | 59 + .../cglib/util/ParallelSorter.java | 295 +++++ .../cglib/util/ParallelSorterEmitter.java | 100 ++ .../cglib/util/SorterTemplate.java | 172 +++ .../cglib/util/StringSwitcher.java | 158 +++ .../ConfigurationClassEnhancer.java | 39 +- .../classloading/ShadowingClassLoader.java | 2 +- .../support/ScriptFactoryPostProcessor.java | 4 +- .../AnnotationDrivenBeanDefinitionParser.java | 2 +- .../WebMvcConfigurationSupport.java | 1178 +++++++++-------- .../annotation/MvcUriComponentsBuilder.java | 12 +- 132 files changed, 14536 insertions(+), 613 deletions(-) create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BeanCopier.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BeanGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMap.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMapEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBean.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanException.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/FixedKeySet.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/beans/ImmutableBean.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/AbstractClassGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Block.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ClassEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ClassGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ClassInfo.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ClassNameReader.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ClassesKey.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/CodeEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/CodeGenerationException.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/CollectionUtils.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Constants.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Converter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Customizer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/DebuggingClassWriter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/DefaultGeneratorStrategy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/DefaultNamingPolicy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/DuplicatesPredicate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/EmitUtils.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/GeneratorStrategy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/KeyFactory.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Local.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/LocalVariablesSorter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfo.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfoTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/MethodWrapper.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/NamingPolicy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ObjectSwitchCallback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Predicate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ProcessArrayCallback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ProcessSwitchCallback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/ReflectUtils.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/RejectModifierPredicate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Signature.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/TinyBitSet.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/Transformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/TypeUtils.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/core/VisibilityPredicate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/BridgeMethodResolver.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Callback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackFilter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackHelper.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackInfo.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Dispatcher.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/DispatcherGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Enhancer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Factory.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValue.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValueGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/InterfaceMaker.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandler.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandlerGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoader.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoaderGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptor.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptorGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodProxy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Mixin.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinBeanEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEverythingEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOp.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOpGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/Proxy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/ProxyRefDispatcher.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/proxy/UndeclaredThrowableException.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/ConstructorDelegate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClass.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClassEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/FastConstructor.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMember.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMethod.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/MethodDelegate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/reflect/MulticastDelegate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassFilterTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassLoader.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractProcessTask.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractTransformTask.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/AnnotationVisitorTee.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassEmitterTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilterTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassReaderGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerChain.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerFactory.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerTee.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/ClassVisitorTee.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/FieldVisitorTee.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilterTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/MethodVisitorTee.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassGenerator.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassLoader.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AbstractInterceptFieldCallback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AccessFieldTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddDelegateTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddInitTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddPropertyTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddStaticInitTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProvider.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProviderTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldCallback.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldEnabled.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldFilter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableStrategy.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableTransformer.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorterEmitter.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/util/SorterTemplate.java create mode 100644 fine-spring/src/com/fr/third/springframework/cglib/util/StringSwitcher.java diff --git a/fine-spring/src/com/fr/third/springframework/aop/framework/CglibAopProxy.java b/fine-spring/src/com/fr/third/springframework/aop/framework/CglibAopProxy.java index f3297b891..52474f534 100644 --- a/fine-spring/src/com/fr/third/springframework/aop/framework/CglibAopProxy.java +++ b/fine-spring/src/com/fr/third/springframework/aop/framework/CglibAopProxy.java @@ -42,16 +42,16 @@ import com.fr.third.springframework.core.SmartClassLoader; import com.fr.third.springframework.util.Assert; import com.fr.third.springframework.util.ClassUtils; import com.fr.third.springframework.util.ObjectUtils; -import org.springframework.cglib.core.CodeGenerationException; -import org.springframework.cglib.proxy.Callback; -import org.springframework.cglib.proxy.CallbackFilter; -import org.springframework.cglib.proxy.Dispatcher; -import org.springframework.cglib.proxy.Enhancer; -import org.springframework.cglib.proxy.Factory; -import org.springframework.cglib.proxy.MethodInterceptor; -import org.springframework.cglib.proxy.MethodProxy; -import org.springframework.cglib.proxy.NoOp; -import org.springframework.cglib.transform.impl.UndeclaredThrowableStrategy; +import com.fr.third.springframework.cglib.core.CodeGenerationException; +import com.fr.third.springframework.cglib.proxy.Callback; +import com.fr.third.springframework.cglib.proxy.CallbackFilter; +import com.fr.third.springframework.cglib.proxy.Dispatcher; +import com.fr.third.springframework.cglib.proxy.Enhancer; +import com.fr.third.springframework.cglib.proxy.Factory; +import com.fr.third.springframework.cglib.proxy.MethodInterceptor; +import com.fr.third.springframework.cglib.proxy.MethodProxy; +import com.fr.third.springframework.cglib.proxy.NoOp; +import com.fr.third.springframework.cglib.transform.impl.UndeclaredThrowableStrategy; /** diff --git a/fine-spring/src/com/fr/third/springframework/aop/framework/ObjenesisCglibAopProxy.java b/fine-spring/src/com/fr/third/springframework/aop/framework/ObjenesisCglibAopProxy.java index 6013c580d..a1212394c 100644 --- a/fine-spring/src/com/fr/third/springframework/aop/framework/ObjenesisCglibAopProxy.java +++ b/fine-spring/src/com/fr/third/springframework/aop/framework/ObjenesisCglibAopProxy.java @@ -18,9 +18,9 @@ package com.fr.third.springframework.aop.framework; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.cglib.proxy.Callback; -import org.springframework.cglib.proxy.Enhancer; -import org.springframework.cglib.proxy.Factory; +import com.fr.third.springframework.cglib.proxy.Callback; +import com.fr.third.springframework.cglib.proxy.Enhancer; +import com.fr.third.springframework.cglib.proxy.Factory; import org.springframework.objenesis.ObjenesisException; import org.springframework.objenesis.ObjenesisStd; diff --git a/fine-spring/src/com/fr/third/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/fine-spring/src/com/fr/third/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 727b4698f..00c1a1aa9 100644 --- a/fine-spring/src/com/fr/third/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/fine-spring/src/com/fr/third/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -26,13 +26,13 @@ import com.fr.third.springframework.beans.BeanInstantiationException; import com.fr.third.springframework.beans.BeanUtils; import com.fr.third.springframework.beans.factory.BeanFactory; import com.fr.third.springframework.cglib.core.SpringNamingPolicy; -import org.springframework.cglib.proxy.Callback; -import org.springframework.cglib.proxy.CallbackFilter; -import org.springframework.cglib.proxy.Enhancer; -import org.springframework.cglib.proxy.Factory; -import org.springframework.cglib.proxy.MethodInterceptor; -import org.springframework.cglib.proxy.MethodProxy; -import org.springframework.cglib.proxy.NoOp; +import com.fr.third.springframework.cglib.proxy.Callback; +import com.fr.third.springframework.cglib.proxy.CallbackFilter; +import com.fr.third.springframework.cglib.proxy.Enhancer; +import com.fr.third.springframework.cglib.proxy.Factory; +import com.fr.third.springframework.cglib.proxy.MethodInterceptor; +import com.fr.third.springframework.cglib.proxy.MethodProxy; +import com.fr.third.springframework.cglib.proxy.NoOp; /** diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanCopier.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanCopier.java new file mode 100644 index 000000000..395229654 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanCopier.java @@ -0,0 +1,188 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.AbstractClassGenerator; +import com.fr.third.springframework.cglib.core.ClassEmitter; +import com.fr.third.springframework.cglib.core.CodeEmitter; +import com.fr.third.springframework.cglib.core.Constants; +import com.fr.third.springframework.cglib.core.Converter; +import com.fr.third.springframework.cglib.core.EmitUtils; +import com.fr.third.springframework.cglib.core.KeyFactory; +import com.fr.third.springframework.cglib.core.Local; +import com.fr.third.springframework.cglib.core.MethodInfo; +import com.fr.third.springframework.cglib.core.ReflectUtils; +import com.fr.third.springframework.cglib.core.Signature; +import com.fr.third.springframework.cglib.core.TypeUtils; + +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * @author Chris Nokleberg + */ +abstract public class BeanCopier +{ + private static final BeanCopierKey KEY_FACTORY = + (BeanCopierKey) KeyFactory.create(BeanCopierKey.class); + private static final Type CONVERTER = + TypeUtils.parseType("com.fr.third.springframework.cglib.core.Converter"); + //TypeUtils.parseType("net.sf.cglib.core.Converter"); + private static final Type BEAN_COPIER = + TypeUtils.parseType("com.fr.third.springframework.cglib.beans.BeanCopier"); + //TypeUtils.parseType("net.sf.cglib.beans.BeanCopier"); + private static final Signature COPY = + new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER }); + private static final Signature CONVERT = + TypeUtils.parseSignature("Object convert(Object, Class, Object)"); + + interface BeanCopierKey { + public Object newInstance(String source, String target, boolean useConverter); + } + + @SuppressWarnings("rawtypes") + public static BeanCopier create(Class source, Class target, boolean useConverter) { + Generator gen = new Generator(); + gen.setSource(source); + gen.setTarget(target); + gen.setUseConverter(useConverter); + return gen.create(); + } + + abstract public void copy(Object from, Object to, Converter converter); + + @SuppressWarnings("rawtypes") + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BeanCopier.class.getName()); + private Class source; + private Class target; + private boolean useConverter; + + public Generator() { + super(SOURCE); + } + + public void setSource(Class source) { + if(!Modifier.isPublic(source.getModifiers())){ + setNamePrefix(source.getName()); + } + this.source = source; + } + + public void setTarget(Class target) { + if(!Modifier.isPublic(target.getModifiers())){ + setNamePrefix(target.getName()); + } + + this.target = target; + } + + public void setUseConverter(boolean useConverter) { + this.useConverter = useConverter; + } + + protected ClassLoader getDefaultClassLoader() { + return source.getClassLoader(); + } + + public BeanCopier create() { + Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter); + return (BeanCopier)super.create(key); + } + + @SuppressWarnings("unchecked") + public void generateClass(ClassVisitor v) { + Type sourceType = Type.getType(source); + Type targetType = Type.getType(target); + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + BEAN_COPIER, + null, + Constants.SOURCE_FILE); + + EmitUtils.null_constructor(ce); + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null); + PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source); + PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target); + + Map names = new HashMap(); + for (int i = 0; i < getters.length; i++) { + names.put(getters[i].getName(), getters[i]); + } + Local targetLocal = e.make_local(); + Local sourceLocal = e.make_local(); + if (useConverter) { + e.load_arg(1); + e.checkcast(targetType); + e.store_local(targetLocal); + e.load_arg(0); + e.checkcast(sourceType); + e.store_local(sourceLocal); + } else { + e.load_arg(1); + e.checkcast(targetType); + e.load_arg(0); + e.checkcast(sourceType); + } + for (int i = 0; i < setters.length; i++) { + PropertyDescriptor setter = setters[i]; + PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName()); + if (getter != null) { + MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod()); + MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); + if (useConverter) { + Type setterType = write.getSignature().getArgumentTypes()[0]; + e.load_local(targetLocal); + e.load_arg(2); + e.load_local(sourceLocal); + e.invoke(read); + e.box(read.getSignature().getReturnType()); + EmitUtils.load_class(e, setterType); + e.push(write.getSignature().getName()); + e.invoke_interface(CONVERTER, CONVERT); + e.unbox_or_zero(setterType); + e.invoke(write); + } else if (compatible(getter, setter)) { + e.dup2(); + e.invoke(read); + e.invoke(write); + } + } + } + e.return_value(); + e.end_method(); + ce.end_class(); + } + + private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) { + return setter.getPropertyType().isAssignableFrom(getter.getPropertyType()); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanGenerator.java new file mode 100644 index 000000000..0a6abf9c3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanGenerator.java @@ -0,0 +1,156 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class BeanGenerator extends AbstractClassGenerator +{ + private static final Source SOURCE = new Source(BeanGenerator.class.getName()); + private static final BeanGeneratorKey KEY_FACTORY = + (BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class); + + interface BeanGeneratorKey { + @SuppressWarnings("rawtypes") + public Object newInstance(String superclass, Map props); + } + + @SuppressWarnings("rawtypes") + private Class superclass; + @SuppressWarnings("rawtypes") + private Map props = new HashMap(); + private boolean classOnly; + + public BeanGenerator() { + super(SOURCE); + } + + /** + * Set the class which the generated class will extend. The class + * must not be declared as final, and must have a non-private + * no-argument constructor. + * @param superclass class to extend, or null to extend Object + */ + @SuppressWarnings("rawtypes") + public void setSuperclass(Class superclass) { + if (superclass != null && superclass.equals(Object.class)) { + superclass = null; + } + this.superclass = superclass; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void addProperty(String name, Class type) { + if (props.containsKey(name)) { + throw new IllegalArgumentException("Duplicate property name \"" + name + "\""); + } + props.put(name, Type.getType(type)); + } + + protected ClassLoader getDefaultClassLoader() { + if (superclass != null) { + return superclass.getClassLoader(); + } else { + return null; + } + } + + public Object create() { + classOnly = false; + return createHelper(); + } + + public Object createClass() { + classOnly = true; + return createHelper(); + } + + private Object createHelper() { + if (superclass != null) { + setNamePrefix(superclass.getName()); + } + String superName = (superclass != null) ? superclass.getName() : "java.lang.Object"; + Object key = KEY_FACTORY.newInstance(superName, props); + return super.create(key); + } + + @SuppressWarnings("unchecked") + public void generateClass(ClassVisitor v) throws Exception { + int size = props.size(); + String[] names = (String[])props.keySet().toArray(new String[size]); + Type[] types = new Type[size]; + for (int i = 0; i < size; i++) { + types[i] = (Type)props.get(names[i]); + } + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT, + null, + null); + EmitUtils.null_constructor(ce); + EmitUtils.add_properties(ce, names, types); + ce.end_class(); + } + + @SuppressWarnings("rawtypes") + protected Object firstInstance(Class type) { + if (classOnly) { + return type; + } else { + return ReflectUtils.newInstance(type); + } + } + + @SuppressWarnings("rawtypes") + protected Object nextInstance(Object instance) { + Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass(); + if (classOnly) { + return protoclass; + } else { + return ReflectUtils.newInstance(protoclass); + } + } + + @SuppressWarnings("rawtypes") + public static void addProperties(BeanGenerator gen, Map props) { + for (Iterator it = props.keySet().iterator(); it.hasNext();) { + String name = (String)it.next(); + gen.addProperty(name, (Class)props.get(name)); + } + } + + @SuppressWarnings("rawtypes") + public static void addProperties(BeanGenerator gen, Class type) { + addProperties(gen, ReflectUtils.getBeanProperties(type)); + } + + public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) { + for (int i = 0; i < descriptors.length; i++) { + gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType()); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMap.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMap.java new file mode 100644 index 000000000..eaa258911 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMap.java @@ -0,0 +1,315 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +/** + * A Map-based view of a JavaBean. The default set of keys is the + * union of all property names (getters or setters). An attempt to set + * a read-only property will be ignored, and write-only properties will + * be returned as null. Removal of objects is not a + * supported (the key set is fixed). + * @author Chris Nokleberg + */ +@SuppressWarnings("rawtypes") +abstract public class BeanMap implements Map { + /** + * Limit the properties reflected in the key set of the map + * to readable properties. + * @see Generator#setRequire + */ + public static final int REQUIRE_GETTER = 1; + + /** + * Limit the properties reflected in the key set of the map + * to writable properties. + * @see Generator#setRequire + */ + public static final int REQUIRE_SETTER = 2; + + /** + * Helper method to create a new BeanMap. For finer + * control over the generated instance, use a new instance of + * BeanMap.Generator instead of this static method. + * @param bean the JavaBean underlying the map + * @return a new BeanMap instance + */ + public static BeanMap create(Object bean) { + Generator gen = new Generator(); + gen.setBean(bean); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BeanMap.class.getName()); + + private static final BeanMapKey KEY_FACTORY = + (BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME); + + interface BeanMapKey { + public Object newInstance(Class type, int require); + } + + private Object bean; + private Class beanClass; + private int require; + + public Generator() { + super(SOURCE); + } + + /** + * Set the bean that the generated map should reflect. The bean may be swapped + * out for another bean of the same type using {@link #setBean}. + * Calling this method overrides any value previously set using {@link #setBeanClass}. + * You must call either this method or {@link #setBeanClass} before {@link #create}. + * @param bean the initial bean + */ + public void setBean(Object bean) { + this.bean = bean; + if (bean != null) + beanClass = bean.getClass(); + } + + /** + * Set the class of the bean that the generated map should support. + * You must call either this method or {@link #setBeanClass} before {@link #create}. + * @param beanClass the class of the bean + */ + public void setBeanClass(Class beanClass) { + this.beanClass = beanClass; + } + + /** + * Limit the properties reflected by the generated map. + * @param require any combination of {@link #REQUIRE_GETTER} and + * {@link #REQUIRE_SETTER}; default is zero (any property allowed) + */ + public void setRequire(int require) { + this.require = require; + } + + protected ClassLoader getDefaultClassLoader() { + return beanClass.getClassLoader(); + } + + /** + * Create a new instance of the BeanMap. An existing + * generated class will be reused if possible. + */ + public BeanMap create() { + if (beanClass == null) + throw new IllegalArgumentException("Class of bean unknown"); + setNamePrefix(beanClass.getName()); + return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require)); + } + + public void generateClass(ClassVisitor v) throws Exception { + new BeanMapEmitter(v, getClassName(), beanClass, require); + } + + protected Object firstInstance(Class type) { + return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean); + } + + protected Object nextInstance(Object instance) { + return ((BeanMap)instance).newInstance(bean); + } + } + + /** + * Create a new BeanMap instance using the specified bean. + * This is faster than using the {@link #create} static method. + * @param bean the JavaBean underlying the map + * @return a new BeanMap instance + */ + abstract public BeanMap newInstance(Object bean); + + /** + * Get the type of a property. + * @param name the name of the JavaBean property + * @return the type of the property, or null if the property does not exist + */ + abstract public Class getPropertyType(String name); + + protected Object bean; + + protected BeanMap() { + } + + protected BeanMap(Object bean) { + setBean(bean); + } + + public Object get(Object key) { + return get(bean, key); + } + + public Object put(Object key, Object value) { + return put(bean, key, value); + } + + /** + * Get the property of a bean. This allows a BeanMap + * to be used statically for multiple beans--the bean instance tied to the + * map is ignored and the bean passed to this method is used instead. + * @param bean the bean to query; must be compatible with the type of + * this BeanMap + * @param key must be a String + * @return the current value, or null if there is no matching property + */ + abstract public Object get(Object bean, Object key); + + /** + * Set the property of a bean. This allows a BeanMap + * to be used statically for multiple beans--the bean instance tied to the + * map is ignored and the bean passed to this method is used instead. + * @param key must be a String + * @return the old value, if there was one, or null + */ + abstract public Object put(Object bean, Object key, Object value); + + /** + * Change the underlying bean this map should use. + * @param bean the new JavaBean + * @see #getBean + */ + public void setBean(Object bean) { + this.bean = bean; + } + + /** + * Return the bean currently in use by this map. + * @return the current JavaBean + * @see #setBean + */ + public Object getBean() { + return bean; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + return keySet().contains(key); + } + + public boolean containsValue(Object value) { + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object v = get(it.next()); + if (((value == null) && (v == null)) || value.equals(v)) + return true; + } + return false; + } + + public int size() { + return keySet().size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + for (Iterator it = t.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + put(key, t.get(key)); + } + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof Map)) { + return false; + } + Map other = (Map)o; + if (size() != other.size()) { + return false; + } + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + if (!other.containsKey(key)) { + return false; + } + Object v1 = get(key); + Object v2 = other.get(key); + if (!((v1 == null) ? v2 == null : v1.equals(v2))) { + return false; + } + } + return true; + } + + public int hashCode() { + int code = 0; + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + Object value = get(key); + code += ((key == null) ? 0 : key.hashCode()) ^ + ((value == null) ? 0 : value.hashCode()); + } + return code; + } + + @SuppressWarnings("unchecked") + public Set entrySet() { + HashMap copy = new HashMap(); + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + copy.put(key, get(key)); + } + return Collections.unmodifiableMap(copy).entrySet(); + } + + @SuppressWarnings("unchecked") + public Collection values() { + Set keys = keySet(); + List values = new ArrayList(keys.size()); + for (Iterator it = keys.iterator(); it.hasNext();) { + values.add(get(it.next())); + } + return Collections.unmodifiableCollection(values); + } + + /* + * @see java.util.AbstractMap#toString + */ + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append('{'); + for (Iterator it = keySet().iterator(); it.hasNext();) { + Object key = it.next(); + sb.append(key); + sb.append('='); + sb.append(get(key)); + if (it.hasNext()) { + sb.append(", "); + } + } + sb.append('}'); + return sb.toString(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMapEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMapEmitter.java new file mode 100644 index 000000000..aca9f7668 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BeanMapEmitter.java @@ -0,0 +1,202 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.beans.*; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +class BeanMapEmitter extends ClassEmitter { + private static final Type BEAN_MAP = + TypeUtils.parseType("com.fr.third.springframework.cglib.beans.BeanMap"); + //TypeUtils.parseType("net.sf.cglib.beans.BeanMap"); + private static final Type FIXED_KEY_SET = + TypeUtils.parseType("com.fr.third.springframework.cglib.beans.FixedKeySet"); + //TypeUtils.parseType("net.sf.cglib.beans.FixedKeySet"); + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseConstructor("Object"); + private static final Signature CSTRUCT_STRING_ARRAY = + TypeUtils.parseConstructor("String[]"); + private static final Signature BEAN_MAP_GET = + TypeUtils.parseSignature("Object get(Object, Object)"); + private static final Signature BEAN_MAP_PUT = + TypeUtils.parseSignature("Object put(Object, Object, Object)"); + private static final Signature KEY_SET = + TypeUtils.parseSignature("java.util.Set keySet()"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT }); + private static final Signature GET_PROPERTY_TYPE = + TypeUtils.parseSignature("Class getPropertyType(String)"); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) { + super(v); + + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + generateConstructor(); + + Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type)); + Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type)); + Map allProps = new HashMap(); + allProps.putAll(getters); + allProps.putAll(setters); + + if (require != 0) { + for (Iterator it = allProps.keySet().iterator(); it.hasNext();) { + String name = (String)it.next(); + if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) || + (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) { + it.remove(); + getters.remove(name); + setters.remove(name); + } + } + } + generateGet(type, getters); + generatePut(type, setters); + + String[] allNames = getNames(allProps); + generateKeySet(allNames); + generateGetPropertyType(allProps, allNames); + end_class(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Map makePropertyMap(PropertyDescriptor[] props) { + Map names = new HashMap(); + for (int i = 0; i < props.length; i++) { + names.put(((PropertyDescriptor)props[i]).getName(), props[i]); + } + return names; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private String[] getNames(Map propertyMap) { + return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]); + } + + private void generateConstructor() { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); + e.load_this(); + e.load_arg(0); + e.super_invoke_constructor(CSTRUCT_OBJECT); + e.return_value(); + e.end_method(); + } + + @SuppressWarnings("rawtypes") + private void generateGet(Class type, final Map getters) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null); + e.load_arg(0); + e.checkcast(Type.getType(type)); + e.load_arg(1); + e.checkcast(Constants.TYPE_STRING); + EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)getters.get(key); + MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod()); + e.invoke(method); + e.box(method.getSignature().getReturnType()); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }); + e.end_method(); + } + + @SuppressWarnings("rawtypes") + private void generatePut(Class type, final Map setters) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null); + e.load_arg(0); + e.checkcast(Type.getType(type)); + e.load_arg(1); + e.checkcast(Constants.TYPE_STRING); + EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)setters.get(key); + if (pd.getReadMethod() == null) { + e.aconst_null(); + } else { + MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod()); + e.dup(); + e.invoke(read); + e.box(read.getSignature().getReturnType()); + } + e.swap(); // move old value behind bean + e.load_arg(2); // new value + MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod()); + e.unbox(write.getSignature().getArgumentTypes()[0]); + e.invoke(write); + e.return_value(); + } + public void processDefault() { + // fall-through + } + }); + e.aconst_null(); + e.return_value(); + e.end_method(); + } + + private void generateKeySet(String[] allNames) { + // static initializer + declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null); + + CodeEmitter e = begin_static(); + e.new_instance(FIXED_KEY_SET); + e.dup(); + EmitUtils.push_array(e, allNames); + e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY); + e.putfield("keys"); + e.return_value(); + e.end_method(); + + // keySet + e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null); + e.load_this(); + e.getfield("keys"); + e.return_value(); + e.end_method(); + } + + @SuppressWarnings("rawtypes") + private void generateGetPropertyType(final Map allProps, String[] allNames) { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null); + e.load_arg(0); + EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key); + EmitUtils.load_class(e, Type.getType(pd.getPropertyType())); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }); + e.end_method(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBean.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBean.java new file mode 100644 index 000000000..0efda5b16 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBean.java @@ -0,0 +1,142 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +/** + * @author Juozas Baliuka + */ +abstract public class BulkBean +{ + private static final BulkBeanKey KEY_FACTORY = + (BulkBeanKey)KeyFactory.create(BulkBeanKey.class); + + interface BulkBeanKey { + public Object newInstance(String target, String[] getters, String[] setters, String[] types); + } + + @SuppressWarnings("rawtypes") + protected Class target; + protected String[] getters, setters; + @SuppressWarnings("rawtypes") + protected Class[] types; + + protected BulkBean() { } + + abstract public void getPropertyValues(Object bean, Object[] values); + abstract public void setPropertyValues(Object bean, Object[] values); + + public Object[] getPropertyValues(Object bean) { + Object[] values = new Object[getters.length]; + getPropertyValues(bean, values); + return values; + } + + @SuppressWarnings("rawtypes") + public Class[] getPropertyTypes() { + return (Class[])types.clone(); + } + + public String[] getGetters() { + return (String[])getters.clone(); + } + + public String[] getSetters() { + return (String[])setters.clone(); + } + + @SuppressWarnings("rawtypes") + public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) { + Generator gen = new Generator(); + gen.setTarget(target); + gen.setGetters(getters); + gen.setSetters(setters); + gen.setTypes(types); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(BulkBean.class.getName()); + @SuppressWarnings("rawtypes") + private Class target; + private String[] getters; + private String[] setters; + @SuppressWarnings("rawtypes") + private Class[] types; + + public Generator() { + super(SOURCE); + } + + @SuppressWarnings("rawtypes") + public void setTarget(Class target) { + this.target = target; + } + + public void setGetters(String[] getters) { + this.getters = getters; + } + + public void setSetters(String[] setters) { + this.setters = setters; + } + + @SuppressWarnings("rawtypes") + public void setTypes(Class[] types) { + this.types = types; + } + + protected ClassLoader getDefaultClassLoader() { + return target.getClassLoader(); + } + + public BulkBean create() { + setNamePrefix(target.getName()); + String targetClassName = target.getName(); + String[] typeClassNames = ReflectUtils.getNames(types); + Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames); + return (BulkBean)super.create(key); + } + + public void generateClass(ClassVisitor v) throws Exception { + new BulkBeanEmitter(v, getClassName(), target, getters, setters, types); + } + + @SuppressWarnings("rawtypes") + protected Object firstInstance(Class type) { + BulkBean instance = (BulkBean)ReflectUtils.newInstance(type); + instance.target = target; + + int length = getters.length; + instance.getters = new String[length]; + System.arraycopy(getters, 0, instance.getters, 0, length); + + instance.setters = new String[length]; + System.arraycopy(setters, 0, instance.setters, 0, length); + + instance.types = new Class[types.length]; + System.arraycopy(types, 0, instance.types, 0, types.length); + + return instance; + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanEmitter.java new file mode 100644 index 000000000..97ccab128 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanEmitter.java @@ -0,0 +1,162 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +class BulkBeanEmitter extends ClassEmitter { + private static final Signature GET_PROPERTY_VALUES = + TypeUtils.parseSignature("void getPropertyValues(Object, Object[])"); + private static final Signature SET_PROPERTY_VALUES = + TypeUtils.parseSignature("void setPropertyValues(Object, Object[])"); + private static final Signature CSTRUCT_EXCEPTION = + TypeUtils.parseConstructor("Throwable, int"); + private static final Type BULK_BEAN = + TypeUtils.parseType("com.fr.third.springframework.cglib.beans.BulkBean"); + //TypeUtils.parseType("net.sf.cglib.beans.BulkBean"); + private static final Type BULK_BEAN_EXCEPTION = + TypeUtils.parseType("com.fr.third.springframework.cglib.beans.BulkBeanException"); + //TypeUtils.parseType("net.sf.cglib.beans.BulkBeanException"); + + @SuppressWarnings("rawtypes") + public BulkBeanEmitter(ClassVisitor v, + String className, + Class target, + String[] getterNames, + String[] setterNames, + Class[] types) { + super(v); + + Method[] getters = new Method[getterNames.length]; + Method[] setters = new Method[setterNames.length]; + validate(target, getterNames, setterNames, types, getters, setters); + + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + generateGet(target, getters); + generateSet(target, setters); + end_class(); + } + + @SuppressWarnings("rawtypes") + private void generateGet(final Class target, final Method[] getters) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_VALUES, null); + if (getters.length >= 0) { + e.load_arg(0); + e.checkcast(Type.getType(target)); + Local bean = e.make_local(); + e.store_local(bean); + for (int i = 0; i < getters.length; i++) { + if (getters[i] != null) { + MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]); + e.load_arg(1); + e.push(i); + e.load_local(bean); + e.invoke(getter); + e.box(getter.getSignature().getReturnType()); + e.aastore(); + } + } + } + e.return_value(); + e.end_method(); + } + + @SuppressWarnings("rawtypes") + private void generateSet(final Class target, final Method[] setters) { + // setPropertyValues + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_PROPERTY_VALUES, null); + if (setters.length > 0) { + Local index = e.make_local(Type.INT_TYPE); + e.push(0); + e.store_local(index); + e.load_arg(0); + e.checkcast(Type.getType(target)); + e.load_arg(1); + Block handler = e.begin_block(); + int lastIndex = 0; + for (int i = 0; i < setters.length; i++) { + if (setters[i] != null) { + MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]); + int diff = i - lastIndex; + if (diff > 0) { + e.iinc(index, diff); + lastIndex = i; + } + e.dup2(); + e.aaload(i); + e.unbox(setter.getSignature().getArgumentTypes()[0]); + e.invoke(setter); + } + } + handler.end(); + e.return_value(); + e.catch_exception(handler, Constants.TYPE_THROWABLE); + e.new_instance(BULK_BEAN_EXCEPTION); + e.dup_x1(); + e.swap(); + e.load_local(index); + e.invoke_constructor(BULK_BEAN_EXCEPTION, CSTRUCT_EXCEPTION); + e.athrow(); + } else { + e.return_value(); + } + e.end_method(); + } + + @SuppressWarnings("rawtypes") + private static void validate(Class target, + String[] getters, + String[] setters, + Class[] types, + Method[] getters_out, + Method[] setters_out) { + int i = -1; + if (setters.length != types.length || getters.length != types.length) { + throw new BulkBeanException("accessor array length must be equal type array length", i); + } + try { + for (i = 0; i < types.length; i++) { + if (getters[i] != null) { + Method method = ReflectUtils.findDeclaredMethod(target, getters[i], null); + if (method.getReturnType() != types[i]) { + throw new BulkBeanException("Specified type " + types[i] + + " does not match declared type " + method.getReturnType(), i); + } + if (Modifier.isPrivate(method.getModifiers())) { + throw new BulkBeanException("Property is private", i); + } + getters_out[i] = method; + } + if (setters[i] != null) { + Method method = ReflectUtils.findDeclaredMethod(target, setters[i], new Class[]{ types[i] }); + if (Modifier.isPrivate(method.getModifiers()) ){ + throw new BulkBeanException("Property is private", i); + } + setters_out[i] = method; + } + } + } catch (NoSuchMethodException e) { + throw new BulkBeanException("Cannot find specified property", i); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanException.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanException.java new file mode 100644 index 000000000..9376be8b3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/BulkBeanException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +public class BulkBeanException extends RuntimeException +{ + private static final long serialVersionUID = 4577179525950683882L; + private int index; + private Throwable cause; + + public BulkBeanException(String message, int index) { + super(message); + this.index = index; + } + + public BulkBeanException(Throwable cause, int index) { + super(cause.getMessage()); + this.index = index; + this.cause = cause; + } + + public int getIndex() { + return index; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/FixedKeySet.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/FixedKeySet.java new file mode 100644 index 000000000..de8be7b05 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/FixedKeySet.java @@ -0,0 +1,38 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.util.*; + +@SuppressWarnings("rawtypes") +public /* need it for class loading */ class FixedKeySet extends AbstractSet { + private Set set; + private int size; + + @SuppressWarnings("unchecked") + public FixedKeySet(String[] keys) { + size = keys.length; + set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys))); + } + + public Iterator iterator() { + return set.iterator(); + } + + public int size() { + return size; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/beans/ImmutableBean.java b/fine-spring/src/com/fr/third/springframework/cglib/beans/ImmutableBean.java new file mode 100644 index 000000000..0adf4c916 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/beans/ImmutableBean.java @@ -0,0 +1,127 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.beans; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +/** + * @author Chris Nokleberg + */ +@SuppressWarnings("rawtypes") +public class ImmutableBean +{ + private static final Type ILLEGAL_STATE_EXCEPTION = + TypeUtils.parseType("IllegalStateException"); + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseConstructor("Object"); + private static final Class[] OBJECT_CLASSES = { Object.class }; + private static final String FIELD_NAME = "CGLIB$RWBean"; + + private ImmutableBean() { + } + + public static Object create(Object bean) { + Generator gen = new Generator(); + gen.setBean(bean); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ImmutableBean.class.getName()); + private Object bean; + private Class target; + + public Generator() { + super(SOURCE); + } + + public void setBean(Object bean) { + this.bean = bean; + target = bean.getClass(); + } + + protected ClassLoader getDefaultClassLoader() { + return target.getClassLoader(); + } + + public Object create() { + String name = target.getName(); + setNamePrefix(name); + return super.create(name); + } + + public void generateClass(ClassVisitor v) { + Type targetType = Type.getType(target); + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + targetType, + null, + Constants.SOURCE_FILE); + + ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null); + + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.checkcast(targetType); + e.putfield(FIELD_NAME); + e.return_value(); + e.end_method(); + + PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target); + Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false); + Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true); + + for (int i = 0; i < getters.length; i++) { + MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]); + e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC); + e.load_this(); + e.getfield(FIELD_NAME); + e.invoke(getter); + e.return_value(); + e.end_method(); + } + + for (int i = 0; i < setters.length; i++) { + MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]); + e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC); + e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable"); + e.end_method(); + } + + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean }); + } + + // optimize + protected Object nextInstance(Object instance) { + return firstInstance(instance.getClass()); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/AbstractClassGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/core/AbstractClassGenerator.java new file mode 100644 index 000000000..199a471b3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/AbstractClassGenerator.java @@ -0,0 +1,242 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.ClassReader; + +import java.util.*; +import java.lang.ref.*; + +/** + * Abstract class for all code-generating CGLIB utilities. + * In addition to caching generated classes for performance, it provides hooks for + * customizing the ClassLoader, name of the generated class, and transformations + * applied before generation. + */ +@SuppressWarnings("rawtypes") +abstract public class AbstractClassGenerator +implements ClassGenerator +{ + private static final Object NAME_KEY = new Object(); + private static final ThreadLocal CURRENT = new ThreadLocal(); + + private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; + private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE; + private Source source; + private ClassLoader classLoader; + private String namePrefix; + private Object key; + private boolean useCache = true; + private String className; + private boolean attemptLoad; + + protected static class Source { + String name; + Map cache = new WeakHashMap(); + public Source(String name) { + this.name = name; + } + } + + protected AbstractClassGenerator(Source source) { + this.source = source; + } + + protected void setNamePrefix(String namePrefix) { + this.namePrefix = namePrefix; + } + + final protected String getClassName() { + if (className == null) + className = getClassName(getClassLoader()); + return className; + } + + private String getClassName(final ClassLoader loader) { + final Set nameCache = getClassNameCache(loader); + return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() { + public boolean evaluate(Object arg) { + return nameCache.contains(arg); + } + }); + } + + private Set getClassNameCache(ClassLoader loader) { + return (Set)((Map)source.cache.get(loader)).get(NAME_KEY); + } + + /** + * Set the ClassLoader in which the class will be generated. + * Concrete subclasses of AbstractClassGenerator (such as Enhancer) + * will try to choose an appropriate default if this is unset. + *

+ * Classes are cached per-ClassLoader using a WeakHashMap, to allow + * the generated classes to be removed when the associated loader is garbage collected. + * @param classLoader the loader to generate the new class with, or null to use the default + */ + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + /** + * Override the default naming policy. + * @see DefaultNamingPolicy + * @param namingPolicy the custom policy, or null to use the default + */ + public void setNamingPolicy(NamingPolicy namingPolicy) { + if (namingPolicy == null) + namingPolicy = DefaultNamingPolicy.INSTANCE; + this.namingPolicy = namingPolicy; + } + + /** + * @see #setNamingPolicy + */ + public NamingPolicy getNamingPolicy() { + return namingPolicy; + } + + /** + * Whether use and update the static cache of generated classes + * for a class with the same properties. Default is true. + */ + public void setUseCache(boolean useCache) { + this.useCache = useCache; + } + + /** + * @see #setUseCache + */ + public boolean getUseCache() { + return useCache; + } + + /** + * If set, CGLIB will attempt to load classes from the specified + * ClassLoader before generating them. Because generated + * class names are not guaranteed to be unique, the default is false. + */ + public void setAttemptLoad(boolean attemptLoad) { + this.attemptLoad = attemptLoad; + } + + public boolean getAttemptLoad() { + return attemptLoad; + } + + /** + * Set the strategy to use to create the bytecode from this generator. + * By default an instance of {@see DefaultGeneratorStrategy} is used. + */ + public void setStrategy(GeneratorStrategy strategy) { + if (strategy == null) + strategy = DefaultGeneratorStrategy.INSTANCE; + this.strategy = strategy; + } + + /** + * @see #setStrategy + */ + public GeneratorStrategy getStrategy() { + return strategy; + } + + /** + * Used internally by CGLIB. Returns the AbstractClassGenerator + * that is being used to generate a class in the current thread. + */ + public static AbstractClassGenerator getCurrent() { + return (AbstractClassGenerator)CURRENT.get(); + } + + public ClassLoader getClassLoader() { + ClassLoader t = classLoader; + if (t == null) { + t = getDefaultClassLoader(); + } + if (t == null) { + t = getClass().getClassLoader(); + } + if (t == null) { + t = Thread.currentThread().getContextClassLoader(); + } + if (t == null) { + throw new IllegalStateException("Cannot determine classloader"); + } + return t; + } + + abstract protected ClassLoader getDefaultClassLoader(); + + @SuppressWarnings({ "unchecked" }) + protected Object create(Object key) { + try { + Class gen = null; + + synchronized (source) { + ClassLoader loader = getClassLoader(); + Map cache2 = null; + cache2 = (Map)source.cache.get(loader); + if (cache2 == null) { + cache2 = new HashMap(); + cache2.put(NAME_KEY, new HashSet()); + source.cache.put(loader, cache2); + } else if (useCache) { + Reference ref = (Reference)cache2.get(key); + gen = (Class) (( ref == null ) ? null : ref.get()); + } + if (gen == null) { + Object save = CURRENT.get(); + CURRENT.set(this); + try { + this.key = key; + + if (attemptLoad) { + try { + gen = loader.loadClass(getClassName()); + } catch (ClassNotFoundException e) { + // ignore + } + } + if (gen == null) { + byte[] b = strategy.generate(this); + String className = ClassNameReader.getClassName(new ClassReader(b)); + getClassNameCache(loader).add(className); + gen = ReflectUtils.defineClass(className, b, loader); + } + + if (useCache) { + cache2.put(key, new WeakReference(gen)); + } + return firstInstance(gen); + } finally { + CURRENT.set(save); + } + } + } + return firstInstance(gen); + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + abstract protected Object firstInstance(Class type) throws Exception; + abstract protected Object nextInstance(Object instance) throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Block.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Block.java new file mode 100644 index 000000000..e65e224bc --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Block.java @@ -0,0 +1,50 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Label; + +public class Block +{ + private CodeEmitter e; + private Label start; + private Label end; + + public Block(CodeEmitter e) { + this.e = e; + start = e.mark(); + } + + public CodeEmitter getCodeEmitter() { + return e; + } + + public void end() { + if (end != null) { + throw new IllegalStateException("end of label already set"); + } + end = e.mark(); + } + + public Label getStart() { + return start; + } + + public Label getEnd() { + return end; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ClassEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassEmitter.java new file mode 100644 index 000000000..ae7c6190a --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassEmitter.java @@ -0,0 +1,284 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.FieldVisitor; +import com.fr.third.springframework.asm.MethodVisitor; +import com.fr.third.springframework.asm.Opcodes; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.transform.ClassTransformer; + + + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +@SuppressWarnings("rawtypes") +public class ClassEmitter extends ClassTransformer { + private ClassInfo classInfo; + private Map fieldInfo; + + private static int hookCounter; + private MethodVisitor rawStaticInit; + private CodeEmitter staticInit; + private CodeEmitter staticHook; + private Signature staticHookSig; + + public ClassEmitter(ClassVisitor cv) { + setTarget(cv); + } + + public ClassEmitter() { + super(Opcodes.ASM4); + } + + public void setTarget(ClassVisitor cv) { + this.cv = cv; + fieldInfo = new HashMap(); + + // just to be safe + staticInit = staticHook = null; + staticHookSig = null; + } + + synchronized private static int getNextHook() { + return ++hookCounter; + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public void begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source) { + final Type classType = Type.getType("L" + className.replace('.', '/') + ";"); + classInfo = new ClassInfo() { + public Type getType() { + return classType; + } + public Type getSuperType() { + return (superType != null) ? superType : Constants.TYPE_OBJECT; + } + public Type[] getInterfaces() { + return interfaces; + } + public int getModifiers() { + return access; + } + }; + cv.visit(version, + access, + classInfo.getType().getInternalName(), + null, + classInfo.getSuperType().getInternalName(), + TypeUtils.toInternalNames(interfaces)); + if (source != null) + cv.visitSource(source, null); + init(); + } + + public CodeEmitter getStaticHook() { + if (TypeUtils.isInterface(getAccess())) { + throw new IllegalStateException("static hook is invalid for this class"); + } + if (staticHook == null) { + staticHookSig = new Signature("CGLIB$STATICHOOK" + getNextHook(), "()V"); + staticHook = begin_method(Constants.ACC_STATIC, + staticHookSig, + null); + if (staticInit != null) { + staticInit.invoke_static_this(staticHookSig); + } + } + return staticHook; + } + + protected void init() { + } + + public int getAccess() { + return classInfo.getModifiers(); + } + + public Type getClassType() { + return classInfo.getType(); + } + + public Type getSuperType() { + return classInfo.getSuperType(); + } + + public void end_class() { + if (staticHook != null && staticInit == null) { + // force creation of static init + begin_static(); + } + if (staticInit != null) { + staticHook.return_value(); + staticHook.end_method(); + rawStaticInit.visitInsn(Constants.RETURN); + rawStaticInit.visitMaxs(0, 0); + staticInit = staticHook = null; + staticHookSig = null; + } + cv.visitEnd(); + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + if (classInfo == null) + throw new IllegalStateException("classInfo is null! " + this); + MethodVisitor v = cv.visitMethod(access, + sig.getName(), + sig.getDescriptor(), + null, + TypeUtils.toInternalNames(exceptions)); + if (sig.equals(Constants.SIG_STATIC) && !TypeUtils.isInterface(getAccess())) { + rawStaticInit = v; + MethodVisitor wrapped = new MethodVisitor(Opcodes.ASM4, v) { + public void visitMaxs(int maxStack, int maxLocals) { + // ignore + } + public void visitInsn(int insn) { + if (insn != Constants.RETURN) { + super.visitInsn(insn); + } + } + }; + staticInit = new CodeEmitter(this, wrapped, access, sig, exceptions); + if (staticHook == null) { + // force static hook creation + getStaticHook(); + } else { + staticInit.invoke_static_this(staticHookSig); + } + return staticInit; + } else if (sig.equals(staticHookSig)) { + return new CodeEmitter(this, v, access, sig, exceptions) { + public boolean isStaticHook() { + return true; + } + }; + } else { + return new CodeEmitter(this, v, access, sig, exceptions); + } + } + + public CodeEmitter begin_static() { + return begin_method(Constants.ACC_STATIC, Constants.SIG_STATIC, null); + } + + @SuppressWarnings("unchecked") + public void declare_field(int access, String name, Type type, Object value) { + FieldInfo existing = (FieldInfo)fieldInfo.get(name); + FieldInfo info = new FieldInfo(access, name, type, value); + if (existing != null) { + if (!info.equals(existing)) { + throw new IllegalArgumentException("Field \"" + name + "\" has been declared differently"); + } + } else { + fieldInfo.put(name, info); + cv.visitField(access, name, type.getDescriptor(), null, value); + } + } + + boolean isFieldDeclared(String name) { + return fieldInfo.get(name) != null; + } + + FieldInfo getFieldInfo(String name) { + FieldInfo field = (FieldInfo)fieldInfo.get(name); + if (field == null) { + throw new IllegalArgumentException("Field " + name + " is not declared in " + getClassType().getClassName()); + } + return field; + } + + static class FieldInfo { + int access; + String name; + Type type; + Object value; + + public FieldInfo(int access, String name, Type type, Object value) { + this.access = access; + this.name = name; + this.type = type; + this.value = value; + } + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof FieldInfo)) + return false; + FieldInfo other = (FieldInfo)o; + if (access != other.access || + !name.equals(other.name) || + !type.equals(other.type)) { + return false; + } + if ((value == null) ^ (other.value == null)) + return false; + if (value != null && !value.equals(other.value)) + return false; + return true; + } + + public int hashCode() { + return access ^ name.hashCode() ^ type.hashCode() ^ ((value == null) ? 0 : value.hashCode()); + } + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + begin_class(version, + access, + name.replace('/', '.'), + TypeUtils.fromInternalName(superName), + TypeUtils.fromInternalNames(interfaces), + null); + } + + public void visitEnd() { + end_class(); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + declare_field(access, name, Type.getType(desc), value); + return null; + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return begin_method(access, + new Signature(name, desc), + TypeUtils.fromInternalNames(exceptions)); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ClassGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassGenerator.java new file mode 100644 index 000000000..f02303b20 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassGenerator.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.ClassVisitor; + +public interface ClassGenerator { + void generateClass(ClassVisitor v) throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ClassInfo.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassInfo.java new file mode 100644 index 000000000..e20575d54 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassInfo.java @@ -0,0 +1,46 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +abstract public class ClassInfo { + + protected ClassInfo() { + } + + abstract public Type getType(); + abstract public Type getSuperType(); + abstract public Type[] getInterfaces(); + abstract public int getModifiers(); + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof ClassInfo)) + return false; + return getType().equals(((ClassInfo)o).getType()); + } + + public int hashCode() { + return getType().hashCode(); + } + + public String toString() { + return getType().getClassName(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ClassNameReader.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassNameReader.java new file mode 100644 index 000000000..6e5aaa5aa --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassNameReader.java @@ -0,0 +1,67 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + + +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Opcodes; + +import java.util.*; + +@SuppressWarnings("rawtypes") +public class ClassNameReader { + private ClassNameReader() { + } + + private static final EarlyExitException EARLY_EXIT = new EarlyExitException(); + @SuppressWarnings("serial") + private static class EarlyExitException extends RuntimeException { } + + public static String getClassName(ClassReader r) { + + return getClassInfo(r)[0]; + + } + + @SuppressWarnings("unchecked") + public static String[] getClassInfo(ClassReader r) { + final List array = new ArrayList(); + try { + r.accept(new ClassVisitor(Opcodes.ASM4, null) { + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + array.add( name.replace('/', '.') ); + if(superName != null){ + array.add( superName.replace('/', '.') ); + } + for(int i = 0; i < interfaces.length; i++ ){ + array.add( interfaces[i].replace('/', '.') ); + } + + throw EARLY_EXIT; + } + }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } catch (EarlyExitException e) { } + + return (String[])array.toArray( new String[]{} ); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ClassesKey.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassesKey.java new file mode 100644 index 000000000..b88b673a0 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ClassesKey.java @@ -0,0 +1,31 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +public class ClassesKey { + private static final Key FACTORY = (Key)KeyFactory.create(Key.class, KeyFactory.OBJECT_BY_CLASS); + + interface Key { + Object newInstance(Object[] array); + } + + private ClassesKey() { + } + + public static Object create(Object[] array) { + return FACTORY.newInstance(array); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/CodeEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/core/CodeEmitter.java new file mode 100644 index 000000000..0f04cb065 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/CodeEmitter.java @@ -0,0 +1,859 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.util.*; +import com.fr.third.springframework.asm.*; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class CodeEmitter extends LocalVariablesSorter { + private static final Signature BOOLEAN_VALUE = + TypeUtils.parseSignature("boolean booleanValue()"); + private static final Signature CHAR_VALUE = + TypeUtils.parseSignature("char charValue()"); + private static final Signature LONG_VALUE = + TypeUtils.parseSignature("long longValue()"); + private static final Signature DOUBLE_VALUE = + TypeUtils.parseSignature("double doubleValue()"); + private static final Signature FLOAT_VALUE = + TypeUtils.parseSignature("float floatValue()"); + private static final Signature INT_VALUE = + TypeUtils.parseSignature("int intValue()"); + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature CSTRUCT_STRING = + TypeUtils.parseConstructor("String"); + + public static final int ADD = Constants.IADD; + public static final int MUL = Constants.IMUL; + public static final int XOR = Constants.IXOR; + public static final int USHR = Constants.IUSHR; + public static final int SUB = Constants.ISUB; + public static final int DIV = Constants.IDIV; + public static final int NEG = Constants.INEG; + public static final int REM = Constants.IREM; + public static final int AND = Constants.IAND; + public static final int OR = Constants.IOR; + + public static final int GT = Constants.IFGT; + public static final int LT = Constants.IFLT; + public static final int GE = Constants.IFGE; + public static final int LE = Constants.IFLE; + public static final int NE = Constants.IFNE; + public static final int EQ = Constants.IFEQ; + + private ClassEmitter ce; + private State state; + + private static class State + extends MethodInfo + { + ClassInfo classInfo; + int access; + Signature sig; + Type[] argumentTypes; + int localOffset; + Type[] exceptionTypes; + + State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) { + this.classInfo = classInfo; + this.access = access; + this.sig = sig; + this.exceptionTypes = exceptionTypes; + localOffset = TypeUtils.isStatic(access) ? 0 : 1; + argumentTypes = sig.getArgumentTypes(); + } + + public ClassInfo getClassInfo() { + return classInfo; + } + + public int getModifiers() { + return access; + } + + public Signature getSignature() { + return sig; + } + + public Type[] getExceptionTypes() { + return exceptionTypes; + } + + @SuppressWarnings("unused") + public Attribute getAttribute() { + return null; + } + } + + CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) { + super(access, sig.getDescriptor(), mv); + this.ce = ce; + state = new State(ce.getClassInfo(), access, sig, exceptionTypes); + } + + public CodeEmitter(CodeEmitter wrap) { + super(wrap); + this.ce = wrap.ce; + this.state = wrap.state; + } + + public boolean isStaticHook() { + return false; + } + + public Signature getSignature() { + return state.sig; + } + + public Type getReturnType() { + return state.sig.getReturnType(); + } + + public MethodInfo getMethodInfo() { + return state; + } + + public ClassEmitter getClassEmitter() { + return ce; + } + + public void end_method() { + visitMaxs(0, 0); + } + + public Block begin_block() { + return new Block(this); + } + + public void catch_exception(Block block, Type exception) { + if (block.getEnd() == null) { + throw new IllegalStateException("end of block is unset"); + } + mv.visitTryCatchBlock(block.getStart(), + block.getEnd(), + mark(), + exception.getInternalName()); + } + + public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); } + public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); } + public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); } + + public void if_jump(int mode, Label label) { + mv.visitJumpInsn(mode, label); + } + + public void if_icmp(int mode, Label label) { + if_cmp(Type.INT_TYPE, mode, label); + } + + public void if_cmp(Type type, int mode, Label label) { + int intOp = -1; + int jumpmode = mode; + switch (mode) { + case GE: jumpmode = LT; break; + case LE: jumpmode = GT; break; + } + switch (type.getSort()) { + case Type.LONG: + mv.visitInsn(Constants.LCMP); + break; + case Type.DOUBLE: + mv.visitInsn(Constants.DCMPG); + break; + case Type.FLOAT: + mv.visitInsn(Constants.FCMPG); + break; + case Type.ARRAY: + case Type.OBJECT: + switch (mode) { + case EQ: + mv.visitJumpInsn(Constants.IF_ACMPEQ, label); + return; + case NE: + mv.visitJumpInsn(Constants.IF_ACMPNE, label); + return; + } + throw new IllegalArgumentException("Bad comparison for type " + type); + default: + switch (mode) { + case EQ: intOp = Constants.IF_ICMPEQ; break; + case NE: intOp = Constants.IF_ICMPNE; break; + case GE: swap(); /* fall through */ + case LT: intOp = Constants.IF_ICMPLT; break; + case LE: swap(); /* fall through */ + case GT: intOp = Constants.IF_ICMPGT; break; + } + mv.visitJumpInsn(intOp, label); + return; + } + if_jump(jumpmode, label); + } + + public void pop() { mv.visitInsn(Constants.POP); } + public void pop2() { mv.visitInsn(Constants.POP2); } + public void dup() { mv.visitInsn(Constants.DUP); } + public void dup2() { mv.visitInsn(Constants.DUP2); } + public void dup_x1() { mv.visitInsn(Constants.DUP_X1); } + public void dup_x2() { mv.visitInsn(Constants.DUP_X2); } + public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); } + public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); } + public void swap() { mv.visitInsn(Constants.SWAP); } + public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); } + + public void swap(Type prev, Type type) { + if (type.getSize() == 1) { + if (prev.getSize() == 1) { + swap(); // same as dup_x1(), pop(); + } else { + dup_x2(); + pop(); + } + } else { + if (prev.getSize() == 1) { + dup2_x1(); + pop2(); + } else { + dup2_x2(); + pop2(); + } + } + } + + public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); } + public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); } + + public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); } + + public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); } + public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); } + + /** + * Casts from one primitive numeric type to another + */ + public void cast_numeric(Type from, Type to) { + if (from != to) { + if (from == Type.DOUBLE_TYPE) { + if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.D2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.D2L); + } else { + mv.visitInsn(Constants.D2I); + cast_numeric(Type.INT_TYPE, to); + } + } else if (from == Type.FLOAT_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.F2D); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.F2L); + } else { + mv.visitInsn(Constants.F2I); + cast_numeric(Type.INT_TYPE, to); + } + } else if (from == Type.LONG_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.L2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.L2F); + } else { + mv.visitInsn(Constants.L2I); + cast_numeric(Type.INT_TYPE, to); + } + } else { + if (to == Type.BYTE_TYPE) { + mv.visitInsn(Constants.I2B); + } else if (to == Type.CHAR_TYPE) { + mv.visitInsn(Constants.I2C); + } else if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Constants.I2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Constants.I2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Constants.I2L); + } else if (to == Type.SHORT_TYPE) { + mv.visitInsn(Constants.I2S); + } + } + } + } + + public void push(int i) { + if (i < -1) { + mv.visitLdcInsn(new Integer(i)); + } else if (i <= 5) { + mv.visitInsn(TypeUtils.ICONST(i)); + } else if (i <= Byte.MAX_VALUE) { + mv.visitIntInsn(Constants.BIPUSH, i); + } else if (i <= Short.MAX_VALUE) { + mv.visitIntInsn(Constants.SIPUSH, i); + } else { + mv.visitLdcInsn(new Integer(i)); + } + } + + public void push(long value) { + if (value == 0L || value == 1L) { + mv.visitInsn(TypeUtils.LCONST(value)); + } else { + mv.visitLdcInsn(new Long(value)); + } + } + + public void push(float value) { + if (value == 0f || value == 1f || value == 2f) { + mv.visitInsn(TypeUtils.FCONST(value)); + } else { + mv.visitLdcInsn(new Float(value)); + } + } + public void push(double value) { + if (value == 0d || value == 1d) { + mv.visitInsn(TypeUtils.DCONST(value)); + } else { + mv.visitLdcInsn(new Double(value)); + } + } + + public void push(String value) { + mv.visitLdcInsn(value); + } + + public void newarray() { + newarray(Constants.TYPE_OBJECT); + } + + public void newarray(Type type) { + if (TypeUtils.isPrimitive(type)) { + mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type)); + } else { + emit_type(Constants.ANEWARRAY, type); + } + } + + public void arraylength() { + mv.visitInsn(Constants.ARRAYLENGTH); + } + + public void load_this() { + if (TypeUtils.isStatic(state.access)) { + throw new IllegalStateException("no 'this' pointer within static method"); + } + mv.visitVarInsn(Constants.ALOAD, 0); + } + + /** + * Pushes all of the arguments of the current method onto the stack. + */ + public void load_args() { + load_args(0, state.argumentTypes.length); + } + + /** + * Pushes the specified argument of the current method onto the stack. + * @param index the zero-based index into the argument list + */ + public void load_arg(int index) { + load_local(state.argumentTypes[index], + state.localOffset + skipArgs(index)); + } + + // zero-based (see load_this) + public void load_args(int fromArg, int count) { + int pos = state.localOffset + skipArgs(fromArg); + for (int i = 0; i < count; i++) { + Type t = state.argumentTypes[fromArg + i]; + load_local(t, pos); + pos += t.getSize(); + } + } + + private int skipArgs(int numArgs) { + int amount = 0; + for (int i = 0; i < numArgs; i++) { + amount += state.argumentTypes[i].getSize(); + } + return amount; + } + + private void load_local(Type t, int pos) { + mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos); + } + + private void store_local(Type t, int pos) { + mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos); + } + + public void iinc(Local local, int amount) { + mv.visitIincInsn(local.getIndex(), amount); + } + + public void store_local(Local local) { + store_local(local.getType(), local.getIndex()); + } + + public void load_local(Local local) { + load_local(local.getType(), local.getIndex()); + } + + public void return_value() { + mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN)); + } + + public void getfield(String name) { + ClassEmitter.FieldInfo info = ce.getFieldInfo(name); + int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD; + emit_field(opcode, ce.getClassType(), name, info.type); + } + + public void putfield(String name) { + ClassEmitter.FieldInfo info = ce.getFieldInfo(name); + int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD; + emit_field(opcode, ce.getClassType(), name, info.type); + } + + public void super_getfield(String name, Type type) { + emit_field(Constants.GETFIELD, ce.getSuperType(), name, type); + } + + public void super_putfield(String name, Type type) { + emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type); + } + + public void super_getstatic(String name, Type type) { + emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type); + } + + public void super_putstatic(String name, Type type) { + emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type); + } + + public void getfield(Type owner, String name, Type type) { + emit_field(Constants.GETFIELD, owner, name, type); + } + + public void putfield(Type owner, String name, Type type) { + emit_field(Constants.PUTFIELD, owner, name, type); + } + + public void getstatic(Type owner, String name, Type type) { + emit_field(Constants.GETSTATIC, owner, name, type); + } + + public void putstatic(Type owner, String name, Type type) { + emit_field(Constants.PUTSTATIC, owner, name, type); + } + + // package-protected for EmitUtils, try to fix + void emit_field(int opcode, Type ctype, String name, Type ftype) { + mv.visitFieldInsn(opcode, + ctype.getInternalName(), + name, + ftype.getDescriptor()); + } + + public void super_invoke() { + super_invoke(state.sig); + } + + public void super_invoke(Signature sig) { + emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig); + } + + public void invoke_constructor(Type type) { + invoke_constructor(type, CSTRUCT_NULL); + } + + public void super_invoke_constructor() { + invoke_constructor(ce.getSuperType()); + } + + public void invoke_constructor_this() { + invoke_constructor(ce.getClassType()); + } + + private void emit_invoke(int opcode, Type type, Signature sig) { + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) && + ((opcode == Constants.INVOKEVIRTUAL) || + (opcode == Constants.INVOKESTATIC))) { + } + mv.visitMethodInsn(opcode, + type.getInternalName(), + sig.getName(), + sig.getDescriptor()); + } + + public void invoke_interface(Type owner, Signature sig) { + emit_invoke(Constants.INVOKEINTERFACE, owner, sig); + } + + public void invoke_virtual(Type owner, Signature sig) { + emit_invoke(Constants.INVOKEVIRTUAL, owner, sig); + } + + public void invoke_static(Type owner, Signature sig) { + emit_invoke(Constants.INVOKESTATIC, owner, sig); + } + + public void invoke_virtual_this(Signature sig) { + invoke_virtual(ce.getClassType(), sig); + } + + public void invoke_static_this(Signature sig) { + invoke_static(ce.getClassType(), sig); + } + + public void invoke_constructor(Type type, Signature sig) { + emit_invoke(Constants.INVOKESPECIAL, type, sig); + } + + public void invoke_constructor_this(Signature sig) { + invoke_constructor(ce.getClassType(), sig); + } + + public void super_invoke_constructor(Signature sig) { + invoke_constructor(ce.getSuperType(), sig); + } + + public void new_instance_this() { + new_instance(ce.getClassType()); + } + + public void new_instance(Type type) { + emit_type(Constants.NEW, type); + } + + private void emit_type(int opcode, Type type) { + String desc; + if (TypeUtils.isArray(type)) { + desc = type.getDescriptor(); + } else { + desc = type.getInternalName(); + } + mv.visitTypeInsn(opcode, desc); + } + + public void aaload(int index) { + push(index); + aaload(); + } + + public void aaload() { mv.visitInsn(Constants.AALOAD); } + public void aastore() { mv.visitInsn(Constants.AASTORE); } + public void athrow() { mv.visitInsn(Constants.ATHROW); } + + public Label make_label() { + return new Label(); + } + + public Local make_local() { + return make_local(Constants.TYPE_OBJECT); + } + + public Local make_local(Type type) { + return new Local(newLocal(type.getSize()), type); + } + + public void checkcast_this() { + checkcast(ce.getClassType()); + } + + public void checkcast(Type type) { + if (!type.equals(Constants.TYPE_OBJECT)) { + emit_type(Constants.CHECKCAST, type); + } + } + + public void instance_of(Type type) { + emit_type(Constants.INSTANCEOF, type); + } + + public void instance_of_this() { + instance_of(ce.getClassType()); + } + + public void process_switch(int[] keys, ProcessSwitchCallback callback) { + float density; + if (keys.length == 0) { + density = 0; + } else { + density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1); + } + process_switch(keys, callback, density >= 0.5f); + } + + public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) { + if (!isSorted(keys)) + throw new IllegalArgumentException("keys to switch must be sorted ascending"); + Label def = make_label(); + Label end = make_label(); + + try { + if (keys.length > 0) { + int len = keys.length; + int min = keys[0]; + int max = keys[len - 1]; + int range = max - min + 1; + + if (useTable) { + Label[] labels = new Label[range]; + Arrays.fill(labels, def); + for (int i = 0; i < len; i++) { + labels[keys[i] - min] = make_label(); + } + mv.visitTableSwitchInsn(min, max, def, labels); + for (int i = 0; i < range; i++) { + Label label = labels[i]; + if (label != def) { + mark(label); + callback.processCase(i + min, end); + } + } + } else { + Label[] labels = new Label[len]; + for (int i = 0; i < len; i++) { + labels[i] = make_label(); + } + mv.visitLookupSwitchInsn(def, keys, labels); + for (int i = 0; i < len; i++) { + mark(labels[i]); + callback.processCase(keys[i], end); + } + } + } + + mark(def); + callback.processDefault(); + mark(end); + + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + private static boolean isSorted(int[] keys) { + for (int i = 1; i < keys.length; i++) { + if (keys[i] < keys[i - 1]) + return false; + } + return true; + } + + public void mark(Label label) { + mv.visitLabel(label); + } + + Label mark() { + Label label = make_label(); + mv.visitLabel(label); + return label; + } + + public void push(boolean value) { + push(value ? 1 : 0); + } + + /** + * Toggles the integer on the top of the stack from 1 to 0 or vice versa + */ + public void not() { + push(1); + math(XOR, Type.INT_TYPE); + } + + public void throw_exception(Type type, String msg) { + new_instance(type); + dup(); + push(msg); + invoke_constructor(type, CSTRUCT_STRING); + athrow(); + } + + /** + * If the argument is a primitive class, replaces the primitive value + * on the top of the stack with the wrapped (Object) equivalent. For + * example, char -> Character. + * If the class is Void, a null is pushed onto the stack instead. + * @param type the class indicating the current type of the top stack value + */ + public void box(Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type == Type.VOID_TYPE) { + aconst_null(); + } else { + Type boxed = TypeUtils.getBoxedType(type); + new_instance(boxed); + if (type.getSize() == 2) { + // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o + dup_x2(); + dup_x2(); + pop(); + } else { + // p -> po -> opo -> oop -> o + dup_x1(); + swap(); + } + invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type })); + } + } + } + + /** + * If the argument is a primitive class, replaces the object + * on the top of the stack with the unwrapped (primitive) + * equivalent. For example, Character -> char. + * @param type the class indicating the desired type of the top stack value + * @return true if the value was unboxed + */ + public void unbox(Type type) { + Type t = Constants.TYPE_NUMBER; + Signature sig = null; + switch (type.getSort()) { + case Type.VOID: + return; + case Type.CHAR: + t = Constants.TYPE_CHARACTER; + sig = CHAR_VALUE; + break; + case Type.BOOLEAN: + t = Constants.TYPE_BOOLEAN; + sig = BOOLEAN_VALUE; + break; + case Type.DOUBLE: + sig = DOUBLE_VALUE; + break; + case Type.FLOAT: + sig = FLOAT_VALUE; + break; + case Type.LONG: + sig = LONG_VALUE; + break; + case Type.INT: + case Type.SHORT: + case Type.BYTE: + sig = INT_VALUE; + } + + if (sig == null) { + checkcast(type); + } else { + checkcast(t); + invoke_virtual(t, sig); + } + } + + /** + * Allocates and fills an Object[] array with the arguments to the + * current method. Primitive values are inserted as their boxed + * (Object) equivalents. + */ + public void create_arg_array() { + /* generates: + Object[] args = new Object[]{ arg1, new Integer(arg2) }; + */ + + push(state.argumentTypes.length); + newarray(); + for (int i = 0; i < state.argumentTypes.length; i++) { + dup(); + push(i); + load_arg(i); + box(state.argumentTypes[i]); + aastore(); + } + } + + + /** + * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise. + */ + public void zero_or_null(Type type) { + if (TypeUtils.isPrimitive(type)) { + switch (type.getSort()) { + case Type.DOUBLE: + push(0d); + break; + case Type.LONG: + push(0L); + break; + case Type.FLOAT: + push(0f); + break; + case Type.VOID: + aconst_null(); + default: + push(0); + } + } else { + aconst_null(); + } + } + + /** + * Unboxes the object on the top of the stack. If the object is null, the + * unboxed primitive value becomes zero. + */ + public void unbox_or_zero(Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type != Type.VOID_TYPE) { + Label nonNull = make_label(); + Label end = make_label(); + dup(); + ifnonnull(nonNull); + pop(); + zero_or_null(type); + goTo(end); + mark(nonNull); + unbox(type); + mark(end); + } + } else { + checkcast(type); + } + } + + public void visitMaxs(int maxStack, int maxLocals) { + if (!TypeUtils.isAbstract(state.access)) { + mv.visitMaxs(0, 0); + } + } + + public void invoke(MethodInfo method, Type virtualType) { + ClassInfo classInfo = method.getClassInfo(); + Type type = classInfo.getType(); + Signature sig = method.getSignature(); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + invoke_constructor(type, sig); + } else if (TypeUtils.isInterface(classInfo.getModifiers())) { + invoke_interface(type, sig); + } else if (TypeUtils.isStatic(method.getModifiers())) { + invoke_static(type, sig); + } else { + invoke_virtual(virtualType, sig); + } + } + + public void invoke(MethodInfo method) { + invoke(method, method.getClassInfo().getType()); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/CodeGenerationException.java b/fine-spring/src/com/fr/third/springframework/cglib/core/CodeGenerationException.java new file mode 100644 index 000000000..9e1efc2ae --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/CodeGenerationException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +/** + * @version $Id: CodeGenerationException.java,v 1.3 2004/06/24 21:15:21 herbyderby Exp $ + */ +public class CodeGenerationException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = -7501475398900264554L; + private Throwable cause; + + public CodeGenerationException(Throwable cause) { + super(cause.getClass().getName() + "-->" + cause.getMessage()); + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/CollectionUtils.java b/fine-spring/src/com/fr/third/springframework/cglib/core/CollectionUtils.java new file mode 100644 index 000000000..f3fbb2354 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/CollectionUtils.java @@ -0,0 +1,80 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.util.*; + +/** + * @author Chris Nokleberg + * @version $Id: CollectionUtils.java,v 1.7 2004/06/24 21:15:21 herbyderby Exp $ + */ +public class CollectionUtils { + private CollectionUtils() { } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static Map bucket(Collection c, Transformer t) { + Map buckets = new HashMap(); + for (Iterator it = c.iterator(); it.hasNext();) { + Object value = (Object)it.next(); + Object key = t.transform(value); + List bucket = (List)buckets.get(key); + if (bucket == null) { + buckets.put(key, bucket = new LinkedList()); + } + bucket.add(value); + } + return buckets; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void reverse(Map source, Map target) { + for (Iterator it = source.keySet().iterator(); it.hasNext();) { + Object key = it.next(); + target.put(source.get(key), key); + } + } + + @SuppressWarnings({ "rawtypes" }) + public static Collection filter(Collection c, Predicate p) { + Iterator it = c.iterator(); + while (it.hasNext()) { + if (!p.evaluate(it.next())) { + it.remove(); + } + } + return c; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List transform(Collection c, Transformer t) { + List result = new ArrayList(c.size()); + for (Iterator it = c.iterator(); it.hasNext();) { + result.add(t.transform(it.next())); + } + return result; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static Map getIndexMap(List list) { + Map indexes = new HashMap(); + int index = 0; + for (Iterator it = list.iterator(); it.hasNext();) { + indexes.put(it.next(), new Integer(index++)); + } + return indexes; + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Constants.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Constants.java new file mode 100644 index 000000000..521461a4d --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Constants.java @@ -0,0 +1,70 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Opcodes; +import com.fr.third.springframework.asm.Type; + +/** + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: Constants.java,v 1.21 2006/03/05 02:43:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes"}) +public interface Constants extends Opcodes { + public static final Class[] EMPTY_CLASS_ARRAY = {}; + public static final Type[] TYPES_EMPTY = {}; + + public static final Signature SIG_STATIC = + TypeUtils.parseSignature("void ()"); + + public static final Type TYPE_OBJECT_ARRAY = TypeUtils.parseType("Object[]"); + public static final Type TYPE_CLASS_ARRAY = TypeUtils.parseType("Class[]"); + public static final Type TYPE_STRING_ARRAY = TypeUtils.parseType("String[]"); + + public static final Type TYPE_OBJECT = TypeUtils.parseType("Object"); + public static final Type TYPE_CLASS = TypeUtils.parseType("Class"); + public static final Type TYPE_CLASS_LOADER = TypeUtils.parseType("ClassLoader"); + public static final Type TYPE_CHARACTER = TypeUtils.parseType("Character"); + public static final Type TYPE_BOOLEAN = TypeUtils.parseType("Boolean"); + public static final Type TYPE_DOUBLE = TypeUtils.parseType("Double"); + public static final Type TYPE_FLOAT = TypeUtils.parseType("Float"); + public static final Type TYPE_LONG = TypeUtils.parseType("Long"); + public static final Type TYPE_INTEGER = TypeUtils.parseType("Integer"); + public static final Type TYPE_SHORT = TypeUtils.parseType("Short"); + public static final Type TYPE_BYTE = TypeUtils.parseType("Byte"); + public static final Type TYPE_NUMBER = TypeUtils.parseType("Number"); + public static final Type TYPE_STRING = TypeUtils.parseType("String"); + public static final Type TYPE_THROWABLE = TypeUtils.parseType("Throwable"); + public static final Type TYPE_BIG_INTEGER = TypeUtils.parseType("java.math.BigInteger"); + public static final Type TYPE_BIG_DECIMAL = TypeUtils.parseType("java.math.BigDecimal"); + public static final Type TYPE_STRING_BUFFER = TypeUtils.parseType("StringBuffer"); + public static final Type TYPE_RUNTIME_EXCEPTION = TypeUtils.parseType("RuntimeException"); + public static final Type TYPE_ERROR = TypeUtils.parseType("Error"); + public static final Type TYPE_SYSTEM = TypeUtils.parseType("System"); + public static final Type TYPE_SIGNATURE = TypeUtils.parseType("com.fr.third.springframework.cglib.core.Signature"); + + public static final String CONSTRUCTOR_NAME = ""; + public static final String STATIC_NAME = ""; + public static final String SOURCE_FILE = ""; + public static final String SUID_FIELD_NAME = "serialVersionUID"; + + public static final int PRIVATE_FINAL_STATIC = ACC_PRIVATE | ACC_FINAL | ACC_STATIC; + + public static final int SWITCH_STYLE_TRIE = 0; + public static final int SWITCH_STYLE_HASH = 1; + public static final int SWITCH_STYLE_HASHONLY = 2; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Converter.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Converter.java new file mode 100644 index 000000000..5bd480065 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Converter.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +public interface Converter { + @SuppressWarnings("rawtypes") + Object convert(Object value, Class target, Object context); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Customizer.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Customizer.java new file mode 100644 index 000000000..2572393b5 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Customizer.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +public interface Customizer { + void customize(CodeEmitter e, Type type); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/DebuggingClassWriter.java b/fine-spring/src/com/fr/third/springframework/cglib/core/DebuggingClassWriter.java new file mode 100644 index 000000000..e2b618582 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/DebuggingClassWriter.java @@ -0,0 +1,117 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + + +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.ClassWriter; +import com.fr.third.springframework.asm.Opcodes; + +import java.io.*; +import java.lang.reflect.Constructor; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class DebuggingClassWriter extends ClassVisitor { + + public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation"; + + private static String debugLocation; + private static Constructor traceCtor; + + private String className; + private String superName; + + static { + debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY); + if (debugLocation != null) { + System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'"); + try { + Class clazz = Class.forName("org.objectweb.asm.util.TraceClassVisitor"); + traceCtor = clazz.getConstructor(new Class[]{ClassVisitor.class, PrintWriter.class}); + } catch (Throwable ignore) { + } + } + } + + public DebuggingClassWriter(int flags) { + super(Opcodes.ASM4, new ClassWriter(flags)); + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + className = name.replace('/', '.'); + this.superName = superName.replace('/', '.'); + super.visit(version, access, name, signature, superName, interfaces); + } + + public String getClassName() { + return className; + } + + public String getSuperName() { + return superName; + } + + public byte[] toByteArray() { + + return (byte[]) java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + + + byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray(); + if (debugLocation != null) { + String dirs = className.replace('.', File.separatorChar); + try { + new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs(); + + File file = new File(new File(debugLocation), dirs + ".class"); + OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); + try { + out.write(b); + } finally { + out.close(); + } + + if (traceCtor != null) { + file = new File(new File(debugLocation), dirs + ".asm"); + out = new BufferedOutputStream(new FileOutputStream(file)); + try { + ClassReader cr = new ClassReader(b); + PrintWriter pw = new PrintWriter(new OutputStreamWriter(out)); + ClassVisitor tcv = (ClassVisitor)traceCtor.newInstance(new Object[]{null, pw}); + cr.accept(tcv, 0); + pw.flush(); + } finally { + out.close(); + } + } + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + return b; + } + }); + + } + } diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultGeneratorStrategy.java b/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultGeneratorStrategy.java new file mode 100644 index 000000000..f337d3cda --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultGeneratorStrategy.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.ClassWriter; + +public class DefaultGeneratorStrategy implements GeneratorStrategy { + public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy(); + + public byte[] generate(ClassGenerator cg) throws Exception { + DebuggingClassWriter cw = getClassVisitor(); + transform(cg).generateClass(cw); + return transform(cw.toByteArray()); + } + + protected DebuggingClassWriter getClassVisitor() throws Exception { + return new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS); + } + + protected final ClassWriter getClassWriter() { + // Cause compile / runtime errors for people who implemented the old + // interface without using @Override + throw new UnsupportedOperationException("You are calling " + + "getClassWriter, which no longer exists in this cglib version."); + } + + protected byte[] transform(byte[] b) throws Exception { + return b; + } + + protected ClassGenerator transform(ClassGenerator cg) throws Exception { + return cg; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultNamingPolicy.java b/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultNamingPolicy.java new file mode 100644 index 000000000..1d60c9d79 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/DefaultNamingPolicy.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +/** + * The default policy used by {@link AbstractClassGenerator}. + * Generates names such as + *

net.sf.cglib.Foo$$EnhancerByCGLIB$$38272841

+ * This is composed of a prefix based on the name of the superclass, a fixed + * string incorporating the CGLIB class responsible for generation, and a + * hashcode derived from the parameters used to create the object. If the same + * name has been previously been used in the same ClassLoader, a + * suffix is added to ensure uniqueness. + */ +public class DefaultNamingPolicy implements NamingPolicy { + public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy(); + + public String getClassName(String prefix, String source, Object key, Predicate names) { + if (prefix == null) { + prefix = "net.sf.cglib.empty.Object"; + } else if (prefix.startsWith("java")) { + prefix = "$" + prefix; + } + String base = + prefix + "$$" + + source.substring(source.lastIndexOf('.') + 1) + + getTag() + "$$" + + Integer.toHexString(key.hashCode()); + String attempt = base; + int index = 2; + while (names.evaluate(attempt)) + attempt = base + "_" + index++; + return attempt; + } + + /** + * Returns a string which is incorporated into every generated class name. + * By default returns "ByCGLIB" + */ + protected String getTag() { + return "ByCGLIB"; + } + + public int hashCode() { + return getTag().hashCode(); + } + + public boolean equals(Object o) { + return (o instanceof DefaultNamingPolicy) && ((DefaultNamingPolicy) o).getTag().equals(getTag()); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/DuplicatesPredicate.java b/fine-spring/src/com/fr/third/springframework/cglib/core/DuplicatesPredicate.java new file mode 100644 index 000000000..541b53b5f --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/DuplicatesPredicate.java @@ -0,0 +1,28 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.lang.reflect.Method; +import java.util.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class DuplicatesPredicate implements Predicate { + private Set unique = new HashSet(); + + public boolean evaluate(Object arg) { + return unique.add(MethodWrapper.create((Method)arg)); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/EmitUtils.java b/fine-spring/src/com/fr/third/springframework/cglib/core/EmitUtils.java new file mode 100644 index 000000000..92d852e38 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/EmitUtils.java @@ -0,0 +1,926 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; + + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class EmitUtils { + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature CSTRUCT_THROWABLE = + TypeUtils.parseConstructor("Throwable"); + + private static final Signature GET_NAME = + TypeUtils.parseSignature("String getName()"); + private static final Signature HASH_CODE = + TypeUtils.parseSignature("int hashCode()"); + private static final Signature EQUALS = + TypeUtils.parseSignature("boolean equals(Object)"); + private static final Signature STRING_LENGTH = + TypeUtils.parseSignature("int length()"); + private static final Signature STRING_CHAR_AT = + TypeUtils.parseSignature("char charAt(int)"); + private static final Signature FOR_NAME = + TypeUtils.parseSignature("Class forName(String)"); + private static final Signature DOUBLE_TO_LONG_BITS = + TypeUtils.parseSignature("long doubleToLongBits(double)"); + private static final Signature FLOAT_TO_INT_BITS = + TypeUtils.parseSignature("int floatToIntBits(float)"); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature APPEND_STRING = + TypeUtils.parseSignature("StringBuffer append(String)"); + private static final Signature APPEND_INT = + TypeUtils.parseSignature("StringBuffer append(int)"); + private static final Signature APPEND_DOUBLE = + TypeUtils.parseSignature("StringBuffer append(double)"); + private static final Signature APPEND_FLOAT = + TypeUtils.parseSignature("StringBuffer append(float)"); + private static final Signature APPEND_CHAR = + TypeUtils.parseSignature("StringBuffer append(char)"); + private static final Signature APPEND_LONG = + TypeUtils.parseSignature("StringBuffer append(long)"); + private static final Signature APPEND_BOOLEAN = + TypeUtils.parseSignature("StringBuffer append(boolean)"); + private static final Signature LENGTH = + TypeUtils.parseSignature("int length()"); + private static final Signature SET_LENGTH = + TypeUtils.parseSignature("void setLength(int)"); + private static final Signature GET_DECLARED_METHOD = + TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])"); + + + + public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}"); + + private EmitUtils() { + } + + public static void factory_method(ClassEmitter ce, Signature sig) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null); + e.new_instance_this(); + e.dup(); + e.load_args(); + e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes())); + e.return_value(); + e.end_method(); + } + + public static void null_constructor(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null); + e.load_this(); + e.super_invoke_constructor(); + e.return_value(); + e.end_method(); + } + + /** + * Process an array on the stack. Assumes the top item on the stack + * is an array of the specified type. For each element in the array, + * puts the element on the stack and triggers the callback. + * @param type the type of the array (type.isArray() must be true) + * @param callback the callback triggered for each element + */ + @SuppressWarnings("static-access") + public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) { + Type componentType = TypeUtils.getComponentType(type); + Local array = e.make_local(); + Local loopvar = e.make_local(Type.INT_TYPE); + Label loopbody = e.make_label(); + Label checkloop = e.make_label(); + e.store_local(array); + e.push(0); + e.store_local(loopvar); + e.goTo(checkloop); + + e.mark(loopbody); + e.load_local(array); + e.load_local(loopvar); + e.array_load(componentType); + callback.processElement(componentType); + e.iinc(loopvar, 1); + + e.mark(checkloop); + e.load_local(loopvar); + e.load_local(array); + e.arraylength(); + e.if_icmp(e.LT, loopbody); + } + + /** + * Process two arrays on the stack in parallel. Assumes the top two items on the stack + * are arrays of the specified class. The arrays must be the same length. For each pair + * of elements in the arrays, puts the pair on the stack and triggers the callback. + * @param type the type of the arrays (type.isArray() must be true) + * @param callback the callback triggered for each pair of elements + */ + @SuppressWarnings("static-access") + public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) { + Type componentType = TypeUtils.getComponentType(type); + Local array1 = e.make_local(); + Local array2 = e.make_local(); + Local loopvar = e.make_local(Type.INT_TYPE); + Label loopbody = e.make_label(); + Label checkloop = e.make_label(); + e.store_local(array1); + e.store_local(array2); + e.push(0); + e.store_local(loopvar); + e.goTo(checkloop); + + e.mark(loopbody); + e.load_local(array1); + e.load_local(loopvar); + e.array_load(componentType); + e.load_local(array2); + e.load_local(loopvar); + e.array_load(componentType); + callback.processElement(componentType); + e.iinc(loopvar, 1); + + e.mark(checkloop); + e.load_local(loopvar); + e.load_local(array1); + e.arraylength(); + e.if_icmp(e.LT, loopbody); + } + + public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) { + try { + switch (switchStyle) { + case Constants.SWITCH_STYLE_TRIE: + string_switch_trie(e, strings, callback); + break; + case Constants.SWITCH_STYLE_HASH: + string_switch_hash(e, strings, callback, false); + break; + case Constants.SWITCH_STYLE_HASHONLY: + string_switch_hash(e, strings, callback, true); + break; + default: + throw new IllegalArgumentException("unknown switch style " + switchStyle); + } + } catch (RuntimeException ex) { + throw ex; + } catch (Error ex) { + throw ex; + } catch (Exception ex) { + throw new CodeGenerationException(ex); + } + } + + private static void string_switch_trie(final CodeEmitter e, + String[] strings, + final ObjectSwitchCallback callback) throws Exception { + final Label def = e.make_label(); + final Label end = e.make_label(); + final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { + public Object transform(Object value) { + return new Integer(((String)value).length()); + } + }); + e.dup(); + e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + stringSwitchHelper(e, bucket, callback, def, end, 0); + } + public void processDefault() { + e.goTo(def); + } + }); + e.mark(def); + e.pop(); + callback.processDefault(); + e.mark(end); + } + + private static void stringSwitchHelper(final CodeEmitter e, + List strings, + final ObjectSwitchCallback callback, + final Label def, + final Label end, + final int index) throws Exception { + final int len = ((String)strings.get(0)).length(); + final Map buckets = CollectionUtils.bucket(strings, new Transformer() { + public Object transform(Object value) { + return new Integer(((String)value).charAt(index)); + } + }); + e.dup(); + e.push(index); + e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + if (index + 1 == len) { + e.pop(); + callback.processCase(bucket.get(0), end); + } else { + stringSwitchHelper(e, bucket, callback, def, end, index + 1); + } + } + public void processDefault() { + e.goTo(def); + } + }); + } + + static int[] getSwitchKeys(Map buckets) { + int[] keys = new int[buckets.size()]; + int index = 0; + for (Iterator it = buckets.keySet().iterator(); it.hasNext();) { + keys[index++] = ((Integer)it.next()).intValue(); + } + Arrays.sort(keys); + return keys; + } + + private static void string_switch_hash(final CodeEmitter e, + final String[] strings, + final ObjectSwitchCallback callback, + final boolean skipEquals) throws Exception { + final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() { + public Object transform(Object value) { + return new Integer(value.hashCode()); + } + }); + final Label def = e.make_label(); + final Label end = e.make_label(); + e.dup(); + e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); + e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() { + @SuppressWarnings("static-access") + public void processCase(int key, Label ignore_end) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + Label next = null; + if (skipEquals && bucket.size() == 1) { + if (skipEquals) + e.pop(); + callback.processCase((String)bucket.get(0), end); + } else { + for (Iterator it = bucket.iterator(); it.hasNext();) { + String string = (String)it.next(); + if (next != null) { + e.mark(next); + } + if (it.hasNext()) { + e.dup(); + } + e.push(string); + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + if (it.hasNext()) { + e.if_jump(e.EQ, next = e.make_label()); + e.pop(); + } else { + e.if_jump(e.EQ, def); + } + callback.processCase(string, end); + } + } + } + public void processDefault() { + e.pop(); + } + }); + e.mark(def); + callback.processDefault(); + e.mark(end); + } + + public static void load_class_this(CodeEmitter e) { + load_class_helper(e, e.getClassEmitter().getClassType()); + } + + public static void load_class(CodeEmitter e, Type type) { + if (TypeUtils.isPrimitive(type)) { + if (type == Type.VOID_TYPE) { + throw new IllegalArgumentException("cannot load void type"); + } + e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS); + } else { + load_class_helper(e, type); + } + } + + private static void load_class_helper(CodeEmitter e, final Type type) { + if (e.isStaticHook()) { + e.push(TypeUtils.emulateClassGetName(type)); + e.invoke_static(Constants.TYPE_CLASS, FOR_NAME); + } else { + ClassEmitter ce = e.getClassEmitter(); + String typeName = TypeUtils.emulateClassGetName(type); + + String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName); + if (!ce.isFieldDeclared(fieldName)) { + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null); + CodeEmitter hook = ce.getStaticHook(); + hook.push(typeName); + hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME); + hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS); + } + e.getfield(fieldName); + } + } + + public static void push_array(CodeEmitter e, Object[] array) { + e.push(array.length); + e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType()))); + for (int i = 0; i < array.length; i++) { + e.dup(); + e.push(i); + push_object(e, array[i]); + e.aastore(); + } + } + + private static Class remapComponentType(Class componentType) { + if (componentType.equals(Type.class)) + return Class.class; + return componentType; + } + + public static void push_object(CodeEmitter e, Object obj) { + if (obj == null) { + e.aconst_null(); + } else { + Class type = obj.getClass(); + if (type.isArray()) { + push_array(e, (Object[])obj); + } else if (obj instanceof String) { + e.push((String)obj); + } else if (obj instanceof Type) { + load_class(e, (Type)obj); + } else if (obj instanceof Class) { + load_class(e, Type.getType((Class)obj)); + } else if (obj instanceof BigInteger) { + e.new_instance(Constants.TYPE_BIG_INTEGER); + e.dup(); + e.push(obj.toString()); + e.invoke_constructor(Constants.TYPE_BIG_INTEGER); + } else if (obj instanceof BigDecimal) { + e.new_instance(Constants.TYPE_BIG_DECIMAL); + e.dup(); + e.push(obj.toString()); + e.invoke_constructor(Constants.TYPE_BIG_DECIMAL); + } else { + throw new IllegalArgumentException("unknown type: " + obj.getClass()); + } + } + } + + @SuppressWarnings("static-access") + public static void hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer) { + if (TypeUtils.isArray(type)) { + hash_array(e, type, multiplier, customizer); + } else { + e.swap(Type.INT_TYPE, type); + e.push(multiplier); + e.math(e.MUL, Type.INT_TYPE); + e.swap(type, Type.INT_TYPE); + if (TypeUtils.isPrimitive(type)) { + hash_primitive(e, type); + } else { + hash_object(e, type, customizer); + } + e.math(e.ADD, Type.INT_TYPE); + } + } + + private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer) { + Label skip = e.make_label(); + Label end = e.make_label(); + e.dup(); + e.ifnull(skip); + EmitUtils.process_array(e, type, new ProcessArrayCallback() { + public void processElement(Type type) { + hash_code(e, type, multiplier, customizer); + } + }); + e.goTo(end); + e.mark(skip); + e.pop(); + e.mark(end); + } + + private static void hash_object(CodeEmitter e, Type type, Customizer customizer) { + // (f == null) ? 0 : f.hashCode(); + Label skip = e.make_label(); + Label end = e.make_label(); + e.dup(); + e.ifnull(skip); + if (customizer != null) { + customizer.customize(e, type); + } + e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE); + e.goTo(end); + e.mark(skip); + e.pop(); + e.push(0); + e.mark(end); + } + + @SuppressWarnings("static-access") + private static void hash_primitive(CodeEmitter e, Type type) { + switch (type.getSort()) { + case Type.BOOLEAN: + // f ? 0 : 1 + e.push(1); + e.math(e.XOR, Type.INT_TYPE); + break; + case Type.FLOAT: + // Float.floatToIntBits(f) + e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS); + break; + case Type.DOUBLE: + // Double.doubleToLongBits(f), hash_code(Long.TYPE) + e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS); + // fall through + case Type.LONG: + hash_long(e); + } + } + + @SuppressWarnings("static-access") + private static void hash_long(CodeEmitter e) { + // (int)(f ^ (f >>> 32)) + e.dup2(); + e.push(32); + e.math(e.USHR, Type.LONG_TYPE); + e.math(e.XOR, Type.LONG_TYPE); + e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE); + } + +// public static void not_equals(CodeEmitter e, Type type, Label notEquals) { +// not_equals(e, type, notEquals, null); +// } + + /** + * Branches to the specified label if the top two items on the stack + * are not equal. The items must both be of the specified + * class. Equality is determined by comparing primitive values + * directly and by invoking the equals method for + * Objects. Arrays are recursively processed in the same manner. + */ + public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) { + (new ProcessArrayCallback() { + public void processElement(Type type) { + not_equals_helper(e, type, notEquals, customizer, this); + } + }).processElement(type); + } + + @SuppressWarnings("static-access") + private static void not_equals_helper(CodeEmitter e, + Type type, + Label notEquals, + Customizer customizer, + ProcessArrayCallback callback) { + if (TypeUtils.isPrimitive(type)) { + e.if_cmp(type, e.NE, notEquals); + } else { + Label end = e.make_label(); + nullcmp(e, notEquals, end); + if (TypeUtils.isArray(type)) { + Label checkContents = e.make_label(); + e.dup2(); + e.arraylength(); + e.swap(); + e.arraylength(); + e.if_icmp(e.EQ, checkContents); + e.pop2(); + e.goTo(notEquals); + e.mark(checkContents); + EmitUtils.process_arrays(e, type, callback); + } else { + if (customizer != null) { + customizer.customize(e, type); + e.swap(); + customizer.customize(e, type); + } + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + e.if_jump(e.EQ, notEquals); + } + e.mark(end); + } + } + + /** + * If both objects on the top of the stack are non-null, does nothing. + * If one is null, or both are null, both are popped off and execution + * branches to the respective label. + * @param oneNull label to branch to if only one of the objects is null + * @param bothNull label to branch to if both of the objects are null + */ + private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) { + e.dup2(); + Label nonNull = e.make_label(); + Label oneNullHelper = e.make_label(); + Label end = e.make_label(); + e.ifnonnull(nonNull); + e.ifnonnull(oneNullHelper); + e.pop2(); + e.goTo(bothNull); + + e.mark(nonNull); + e.ifnull(oneNullHelper); + e.goTo(end); + + e.mark(oneNullHelper); + e.pop2(); + e.goTo(oneNull); + + e.mark(end); + } + + /* + public static void to_string(CodeEmitter e, + Type type, + ArrayDelimiters delims, + Customizer customizer) { + e.new_instance(Constants.TYPE_STRING_BUFFER); + e.dup(); + e.invoke_constructor(Constants.TYPE_STRING_BUFFER); + e.swap(); + append_string(e, type, delims, customizer); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING); + } + */ + + public static void append_string(final CodeEmitter e, + Type type, + final ArrayDelimiters delims, + final Customizer customizer) { + final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS; + ProcessArrayCallback callback = new ProcessArrayCallback() { + public void processElement(Type type) { + append_string_helper(e, type, d, customizer, this); + e.push(d.inside); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + }; + append_string_helper(e, type, d, customizer, callback); + } + + private static void append_string_helper(CodeEmitter e, + Type type, + ArrayDelimiters delims, + Customizer customizer, + ProcessArrayCallback callback) { + Label skip = e.make_label(); + Label end = e.make_label(); + if (TypeUtils.isPrimitive(type)) { + switch (type.getSort()) { + case Type.INT: + case Type.SHORT: + case Type.BYTE: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT); + break; + case Type.DOUBLE: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE); + break; + case Type.FLOAT: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT); + break; + case Type.LONG: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG); + break; + case Type.BOOLEAN: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN); + break; + case Type.CHAR: + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR); + break; + } + } else if (TypeUtils.isArray(type)) { + e.dup(); + e.ifnull(skip); + e.swap(); + if (delims != null && delims.before != null && !"".equals(delims.before)) { + e.push(delims.before); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + e.swap(); + } + EmitUtils.process_array(e, type, callback); + shrinkStringBuffer(e, 2); + if (delims != null && delims.after != null && !"".equals(delims.after)) { + e.push(delims.after); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + } else { + e.dup(); + e.ifnull(skip); + if (customizer != null) { + customizer.customize(e, type); + } + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + e.goTo(end); + e.mark(skip); + e.pop(); + e.push("null"); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + e.mark(end); + } + + @SuppressWarnings("static-access") + private static void shrinkStringBuffer(CodeEmitter e, int amt) { + e.dup(); + e.dup(); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH); + e.push(amt); + e.math(e.SUB, Type.INT_TYPE); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH); + } + + public static class ArrayDelimiters { + private String before; + private String inside; + private String after; + + public ArrayDelimiters(String before, String inside, String after) { + this.before = before; + this.inside = inside; + this.after = after; + } + } + + public static void load_method(CodeEmitter e, MethodInfo method) { + load_class(e, method.getClassInfo().getType()); + e.push(method.getSignature().getName()); + push_object(e, method.getSignature().getArgumentTypes()); + e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD); + } + + private interface ParameterTyper { + Type[] getParameterTypes(MethodInfo member); + } + + public static void method_switch(CodeEmitter e, + List methods, + ObjectSwitchCallback callback) { + member_switch_helper(e, methods, callback, true); + } + + public static void constructor_switch(CodeEmitter e, + List constructors, + ObjectSwitchCallback callback) { + member_switch_helper(e, constructors, callback, false); + } + + private static void member_switch_helper(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + boolean useName) { + try { + final Map cache = new HashMap(); + final ParameterTyper cached = new ParameterTyper() { + public Type[] getParameterTypes(MethodInfo member) { + Type[] types = (Type[])cache.get(member); + if (types == null) { + cache.put(member, types = member.getSignature().getArgumentTypes()); + } + return types; + } + }; + final Label def = e.make_label(); + final Label end = e.make_label(); + if (useName) { + e.swap(); + final Map buckets = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return ((MethodInfo)value).getSignature().getName(); + } + }); + String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label dontUseEnd) throws Exception { + member_helper_size(e, (List)buckets.get(key), callback, cached, def, end); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } else { + member_helper_size(e, members, callback, cached, def, end); + } + e.mark(def); + e.pop(); + callback.processDefault(); + e.mark(end); + } catch (RuntimeException ex) { + throw ex; + } catch (Error ex) { + throw ex; + } catch (Exception ex) { + throw new CodeGenerationException(ex); + } + } + + private static void member_helper_size(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + final ParameterTyper typer, + final Label def, + final Label end) throws Exception { + final Map buckets = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return new Integer(typer.getParameterTypes((MethodInfo)value).length); + } + }); + e.dup(); + e.arraylength(); + e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() { + public void processCase(int key, Label dontUseEnd) throws Exception { + List bucket = (List)buckets.get(new Integer(key)); + member_helper_type(e, bucket, callback, typer, def, end, new BitSet()); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } + + @SuppressWarnings("static-access") + private static void member_helper_type(final CodeEmitter e, + List members, + final ObjectSwitchCallback callback, + final ParameterTyper typer, + final Label def, + final Label end, + final BitSet checked) throws Exception { + if (members.size() == 1) { + MethodInfo member = (MethodInfo)members.get(0); + Type[] types = typer.getParameterTypes(member); + // need to check classes that have not already been checked via switches + for (int i = 0; i < types.length; i++) { + if (checked == null || !checked.get(i)) { + e.dup(); + e.aaload(i); + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + e.push(TypeUtils.emulateClassGetName(types[i])); + e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS); + e.if_jump(e.EQ, def); + } + } + e.pop(); + callback.processCase(member, end); + } else { + // choose the index that has the best chance of uniquely identifying member + Type[] example = typer.getParameterTypes((MethodInfo)members.get(0)); + Map buckets = null; + int index = -1; + for (int i = 0; i < example.length; i++) { + final int j = i; + Map test = CollectionUtils.bucket(members, new Transformer() { + public Object transform(Object value) { + return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]); + } + }); + if (buckets == null || test.size() > buckets.size()) { + buckets = test; + index = i; + } + } + if (buckets == null || buckets.size() == 1) { + // must have two methods with same name, types, and different return types + e.goTo(def); + } else { + checked.set(index); + + e.dup(); + e.aaload(index); + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + + final Map fbuckets = buckets; + String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label dontUseEnd) throws Exception { + member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked); + } + public void processDefault() throws Exception { + e.goTo(def); + } + }); + } + } + } + + public static void wrap_throwable(Block block, Type wrapper) { + CodeEmitter e = block.getCodeEmitter(); + e.catch_exception(block, Constants.TYPE_THROWABLE); + e.new_instance(wrapper); + e.dup_x1(); + e.swap(); + e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); + e.athrow(); + } + + public static void add_properties(ClassEmitter ce, String[] names, Type[] types) { + for (int i = 0; i < names.length; i++) { + String fieldName = "$cglib_prop_" + names[i]; + ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null); + EmitUtils.add_property(ce, names[i], types[i], fieldName); + } + } + + public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) { + String property = TypeUtils.upperFirst(name); + CodeEmitter e; + e = ce.begin_method(Constants.ACC_PUBLIC, + new Signature("get" + property, + type, + Constants.TYPES_EMPTY), + null); + e.load_this(); + e.getfield(fieldName); + e.return_value(); + e.end_method(); + + e = ce.begin_method(Constants.ACC_PUBLIC, + new Signature("set" + property, + Type.VOID_TYPE, + new Type[]{ type }), + null); + e.load_this(); + e.load_arg(0); + e.putfield(fieldName); + e.return_value(); + e.end_method(); + } + + /* generates: + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch ( e) { + throw e; + } catch (Throwable e) { + throw new (e); + } + */ + public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) { + Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions)); + + if (set.contains(Constants.TYPE_THROWABLE)) + return; + + boolean needThrow = exceptions != null; + if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) { + e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION); + needThrow = true; + } + if (!set.contains(Constants.TYPE_ERROR)) { + e.catch_exception(handler, Constants.TYPE_ERROR); + needThrow = true; + } + if (exceptions != null) { + for (int i = 0; i < exceptions.length; i++) { + e.catch_exception(handler, exceptions[i]); + } + } + if (needThrow) { + e.athrow(); + } + // e -> eo -> oeo -> ooe -> o + e.catch_exception(handler, Constants.TYPE_THROWABLE); + e.new_instance(wrapper); + e.dup_x1(); + e.swap(); + e.invoke_constructor(wrapper, CSTRUCT_THROWABLE); + e.athrow(); + } + + public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) { + return begin_method(e, method, method.getModifiers()); + } + + public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) { + return e.begin_method(access, + method.getSignature(), + method.getExceptionTypes()); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/GeneratorStrategy.java b/fine-spring/src/com/fr/third/springframework/cglib/core/GeneratorStrategy.java new file mode 100644 index 000000000..dc59724f2 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/GeneratorStrategy.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +/** + * The GeneratorStrategyClass. By providing your + * own strategy you may examine or modify the generated class before + * it is loaded. Typically this will be accomplished by subclassing + * {@link DefaultGeneratorStrategy} and overriding the appropriate + * protected method. + * @see AbstractClassGenerator#setStrategy + */ +public interface GeneratorStrategy { + /** + * Generate the class. + * @param cg a class generator on which you can call {@link ClassGenerator#generateClass} + * @return a byte array containing the bits of a valid Class + */ + byte[] generate(ClassGenerator cg) throws Exception; + + /** + * The GeneratorStrategy in use does not currently, but may + * in the future, affect the caching of classes generated by {@link + * AbstractClassGenerator}, so this is a reminder that you should + * correctly implement equals and hashCode + * to avoid generating too many classes. + */ + boolean equals(Object o); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/KeyFactory.java b/fine-spring/src/com/fr/third/springframework/cglib/core/KeyFactory.java new file mode 100644 index 000000000..42435d225 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/KeyFactory.java @@ -0,0 +1,265 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; + +import java.lang.reflect.Method; + + +/** + * Generates classes to handle multi-valued keys, for use in things such as Maps and Sets. + * Code for equals and hashCode methods follow the + * the rules laid out in Effective Java by Joshua Bloch. + *

+ * To generate a KeyFactory, you need to supply an interface which + * describes the structure of the key. The interface should have a + * single method named newInstance, which returns an + * Object. The arguments array can be + * anything--Objects, primitive values, or single or + * multi-dimension arrays of either. For example: + *

+ *     private interface IntStringKey {
+ *         public Object newInstance(int i, String s);
+ *     }
+ * 

+ * Once you have made a KeyFactory, you generate a new key by calling + * the newInstance method defined by your interface. + *

+ *     IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
+ *     Object key1 = factory.newInstance(4, "Hello");
+ *     Object key2 = factory.newInstance(4, "World");
+ * 

+ * Note: + * hashCode equality between two keys key1 and key2 is only guaranteed if + * key1.equals(key2) and the keys were produced by the same factory. + * + * @version $Id: KeyFactory.java,v 1.26 2006/03/05 02:43:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes"}) +abstract public class KeyFactory { + private static final Signature GET_NAME = + TypeUtils.parseSignature("String getName()"); + private static final Signature GET_CLASS = + TypeUtils.parseSignature("Class getClass()"); + private static final Signature HASH_CODE = + TypeUtils.parseSignature("int hashCode()"); + private static final Signature EQUALS = + TypeUtils.parseSignature("boolean equals(Object)"); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature APPEND_STRING = + TypeUtils.parseSignature("StringBuffer append(String)"); + private static final Type KEY_FACTORY = + TypeUtils.parseType("com.fr.third.springframework.cglib.core.KeyFactory"); + //TypeUtils.parseType("net.sf.cglib.core.KeyFactory"); + + //generated numbers: + private final static int PRIMES[] = { + 11, 73, 179, 331, + 521, 787, 1213, 1823, + 2609, 3691, 5189, 7247, + 10037, 13931, 19289, 26627, + 36683, 50441, 69403, 95401, + 131129, 180179, 247501, 340057, + 467063, 641371, 880603, 1209107, + 1660097, 2279161, 3129011, 4295723, + 5897291, 8095873, 11114263, 15257791, + 20946017, 28754629, 39474179, 54189869, + 74391461, 102123817, 140194277, 192456917, + 264202273, 362693231, 497900099, 683510293, + 938313161, 1288102441, 1768288259 }; + + + public static final Customizer CLASS_BY_NAME = new Customizer() { + public void customize(CodeEmitter e, Type type) { + if (type.equals(Constants.TYPE_CLASS)) { + e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME); + } + } + }; + + public static final Customizer OBJECT_BY_CLASS = new Customizer() { + public void customize(CodeEmitter e, Type type) { + e.invoke_virtual(Constants.TYPE_OBJECT, GET_CLASS); + } + }; + + protected KeyFactory() { + } + + public static KeyFactory create(Class keyInterface) { + return create(keyInterface, null); + } + + public static KeyFactory create(Class keyInterface, Customizer customizer) { + return create(keyInterface.getClassLoader(), keyInterface, customizer); + } + + public static KeyFactory create(ClassLoader loader, Class keyInterface, Customizer customizer) { + Generator gen = new Generator(); + gen.setInterface(keyInterface); + gen.setCustomizer(customizer); + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(KeyFactory.class.getName()); + private Class keyInterface; + private Customizer customizer; + private int constant; + private int multiplier; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return keyInterface.getClassLoader(); + } + + public void setCustomizer(Customizer customizer) { + this.customizer = customizer; + } + + public void setInterface(Class keyInterface) { + this.keyInterface = keyInterface; + } + + public KeyFactory create() { + setNamePrefix(keyInterface.getName()); + return (KeyFactory)super.create(keyInterface.getName()); + } + + public void setHashConstant(int constant) { + this.constant = constant; + } + + public void setHashMultiplier(int multiplier) { + this.multiplier = multiplier; + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + + @SuppressWarnings("static-access") + public void generateClass(ClassVisitor v) { + ClassEmitter ce = new ClassEmitter(v); + + Method newInstance = ReflectUtils.findNewInstance(keyInterface); + if (!newInstance.getReturnType().equals(Object.class)) { + throw new IllegalArgumentException("newInstance method must return Object"); + } + + Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes()); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + KEY_FACTORY, + new Type[]{ Type.getType(keyInterface) }, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance)); + + int seed = 0; + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, + TypeUtils.parseConstructor(parameterTypes), + null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + for (int i = 0; i < parameterTypes.length; i++) { + seed += parameterTypes[i].hashCode(); + ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL, + getFieldName(i), + parameterTypes[i], + null); + e.dup(); + e.load_arg(i); + e.putfield(getFieldName(i)); + } + e.return_value(); + e.end_method(); + + // hash code + e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null); + int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)]; + int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)]; + e.push(hc); + for (int i = 0; i < parameterTypes.length; i++) { + e.load_this(); + e.getfield(getFieldName(i)); + EmitUtils.hash_code(e, parameterTypes[i], hm, customizer); + } + e.return_value(); + e.end_method(); + + // equals + e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null); + Label fail = e.make_label(); + e.load_arg(0); + e.instance_of_this(); + e.if_jump(e.EQ, fail); + for (int i = 0; i < parameterTypes.length; i++) { + e.load_this(); + e.getfield(getFieldName(i)); + e.load_arg(0); + e.checkcast_this(); + e.getfield(getFieldName(i)); + EmitUtils.not_equals(e, parameterTypes[i], fail, customizer); + } + e.push(1); + e.return_value(); + e.mark(fail); + e.push(0); + e.return_value(); + e.end_method(); + + // toString + e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null); + e.new_instance(Constants.TYPE_STRING_BUFFER); + e.dup(); + e.invoke_constructor(Constants.TYPE_STRING_BUFFER); + for (int i = 0; i < parameterTypes.length; i++) { + if (i > 0) { + e.push(", "); + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING); + } + e.load_this(); + e.getfield(getFieldName(i)); + EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizer); + } + e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + + private String getFieldName(int arg) { + return "FIELD_" + arg; + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Local.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Local.java new file mode 100644 index 000000000..195ec47e7 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Local.java @@ -0,0 +1,38 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +public class Local +{ + private Type type; + private int index; + + public Local(int index, Type type) { + this.type = type; + this.index = index; + } + + public int getIndex() { + return index; + } + + public Type getType() { + return type; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/LocalVariablesSorter.java b/fine-spring/src/com/fr/third/springframework/cglib/core/LocalVariablesSorter.java new file mode 100644 index 000000000..11ac678a7 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/LocalVariablesSorter.java @@ -0,0 +1,159 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.MethodVisitor; +import com.fr.third.springframework.asm.Opcodes; +import com.fr.third.springframework.asm.Type; + +/** + * A {@link MethodVisitor} that renumbers local variables in their order of + * appearance. This adapter allows one to easily add new local variables to a + * method. + * + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class LocalVariablesSorter extends MethodVisitor { + + /** + * Mapping from old to new local variable indexes. A local variable at index + * i of size 1 is remapped to 'mapping[2*i]', while a local variable at + * index i of size 2 is remapped to 'mapping[2*i+1]'. + */ + private static class State + { + int[] mapping = new int[40]; + int nextLocal; + } + + protected final int firstLocal; + private final State state; + + public LocalVariablesSorter( + final int access, + final String desc, + final MethodVisitor mv) + { + super(Opcodes.ASM4, mv); + state = new State(); + Type[] args = Type.getArgumentTypes(desc); + state.nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1; + for (int i = 0; i < args.length; i++) { + state.nextLocal += args[i].getSize(); + } + firstLocal = state.nextLocal; + } + + public LocalVariablesSorter(LocalVariablesSorter lvs) { + super(Opcodes.ASM4, lvs.mv); + state = lvs.state; + firstLocal = lvs.firstLocal; + } + + public void visitVarInsn(final int opcode, final int var) { + int size; + switch (opcode) { + case Opcodes.LLOAD: + case Opcodes.LSTORE: + case Opcodes.DLOAD: + case Opcodes.DSTORE: + size = 2; + break; + default: + size = 1; + } + mv.visitVarInsn(opcode, remap(var, size)); + } + + public void visitIincInsn(final int var, final int increment) { + mv.visitIincInsn(remap(var, 1), increment); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + mv.visitMaxs(maxStack, state.nextLocal); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + mv.visitLocalVariable(name, desc, signature, start, end, remap(index)); + } + + // ------------- + + protected int newLocal(final int size) { + int var = state.nextLocal; + state.nextLocal += size; + return var; + } + + private int remap(final int var, final int size) { + if (var < firstLocal) { + return var; + } + int key = 2 * var + size - 1; + int length = state.mapping.length; + if (key >= length) { + int[] newMapping = new int[Math.max(2 * length, key + 1)]; + System.arraycopy(state.mapping, 0, newMapping, 0, length); + state.mapping = newMapping; + } + int value = state.mapping[key]; + if (value == 0) { + value = state.nextLocal + 1; + state.mapping[key] = value; + state.nextLocal += size; + } + return value - 1; + } + + private int remap(final int var) { + if (var < firstLocal) { + return var; + } + int key = 2 * var; + int value = key < state.mapping.length ? state.mapping[key] : 0; + if (value == 0) { + value = key + 1 < state.mapping.length ? state.mapping[key + 1] : 0; + } + if (value == 0) { + throw new IllegalStateException("Unknown local variable " + var); + } + return value - 1; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfo.java b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfo.java new file mode 100644 index 000000000..699385123 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfo.java @@ -0,0 +1,47 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +abstract public class MethodInfo { + + protected MethodInfo() { + } + + abstract public ClassInfo getClassInfo(); + abstract public int getModifiers(); + abstract public Signature getSignature(); + abstract public Type[] getExceptionTypes(); + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof MethodInfo)) + return false; + return getSignature().equals(((MethodInfo)o).getSignature()); + } + + public int hashCode() { + return getSignature().hashCode(); + } + + public String toString() { + // include modifiers, exceptions + return getSignature().toString(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfoTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfoTransformer.java new file mode 100644 index 000000000..f63e7902d --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodInfoTransformer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.lang.reflect.*; + +public class MethodInfoTransformer implements Transformer +{ + private static final MethodInfoTransformer INSTANCE = new MethodInfoTransformer(); + + public static MethodInfoTransformer getInstance() { + return INSTANCE; + } + + @SuppressWarnings("rawtypes") + public Object transform(Object value) { + if (value instanceof Method) { + return ReflectUtils.getMethodInfo((Method)value); + } else if (value instanceof Constructor) { + return ReflectUtils.getMethodInfo((Constructor)value); + } else { + throw new IllegalArgumentException("cannot get method info for " + value); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/MethodWrapper.java b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodWrapper.java new file mode 100644 index 000000000..20cb84e29 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/MethodWrapper.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.lang.reflect.Method; +import java.util.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class MethodWrapper { + private static final MethodWrapperKey KEY_FACTORY = + (MethodWrapperKey)KeyFactory.create(MethodWrapperKey.class); + + /** Internal interface, only public due to ClassLoader issues. */ + public interface MethodWrapperKey { + public Object newInstance(String name, String[] parameterTypes, String returnType); + } + + private MethodWrapper() { + } + + public static Object create(Method method) { + return KEY_FACTORY.newInstance(method.getName(), + ReflectUtils.getNames(method.getParameterTypes()), + method.getReturnType().getName()); + } + + public static Set createSet(Collection methods) { + Set set = new HashSet(); + for (Iterator it = methods.iterator(); it.hasNext();) { + set.add(create((Method)it.next())); + } + return set; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/NamingPolicy.java b/fine-spring/src/com/fr/third/springframework/cglib/core/NamingPolicy.java new file mode 100644 index 000000000..76a6c18e1 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/NamingPolicy.java @@ -0,0 +1,41 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +/** + * Customize the generated class name for {@link AbstractClassGenerator}-based utilities. + */ +public interface NamingPolicy { + /** + * Choose a name for a generated class. + * @param prefix a dotted-name chosen by the generating class (possibly to put the generated class in a particular package) + * @param source the fully-qualified class name of the generating class (for example "net.sf.cglib.Enhancer") + * @param key A key object representing the state of the parameters; for caching to work properly, equal keys should result + * in the same generated class name. The default policy incorporates key.hashCode() into the class name. + * @param names a predicate that returns true if the given classname has already been used in the same ClassLoader. + * @return the fully-qualified class name + */ + String getClassName(String prefix, String source, Object key, Predicate names); + + /** + * The NamingPolicy in use does not currently, but may + * in the future, affect the caching of classes generated by {@link + * AbstractClassGenerator}, so this is a reminder that you should + * correctly implement equals and hashCode + * to avoid generating too many classes. + */ + boolean equals(Object o); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ObjectSwitchCallback.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ObjectSwitchCallback.java new file mode 100644 index 000000000..df232cf62 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ObjectSwitchCallback.java @@ -0,0 +1,25 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Label; + +public interface ObjectSwitchCallback { + void processCase(Object key, Label end) throws Exception; + void processDefault() throws Exception; +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Predicate.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Predicate.java new file mode 100644 index 000000000..079a5b62d --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Predicate.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +public interface Predicate { + boolean evaluate(Object arg); +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessArrayCallback.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessArrayCallback.java new file mode 100644 index 000000000..2aa072e12 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessArrayCallback.java @@ -0,0 +1,23 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +public interface ProcessArrayCallback { + void processElement(Type type); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessSwitchCallback.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessSwitchCallback.java new file mode 100644 index 000000000..c4c8cf92e --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ProcessSwitchCallback.java @@ -0,0 +1,24 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Label; + +public interface ProcessSwitchCallback { + void processCase(int key, Label end) throws Exception; + void processDefault() throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/ReflectUtils.java b/fine-spring/src/com/fr/third/springframework/cglib/core/ReflectUtils.java new file mode 100644 index 000000000..df216c099 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/ReflectUtils.java @@ -0,0 +1,479 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.Type; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +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 java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class ReflectUtils { + private ReflectUtils() { } + + private static final Map primitives = new HashMap(8); + private static final Map transforms = new HashMap(8); + private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader(); + private static Method DEFINE_CLASS; + private static final ProtectionDomain PROTECTION_DOMAIN; + + static { + PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return ReflectUtils.class.getProtectionDomain(); + } + }); + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this + DEFINE_CLASS = loader.getDeclaredMethod("defineClass", + new Class[]{ String.class, + byte[].class, + Integer.TYPE, + Integer.TYPE, + ProtectionDomain.class }); + DEFINE_CLASS.setAccessible(true); + } catch (ClassNotFoundException e) { + throw new CodeGenerationException(e); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + return null; + } + }); + } + + private static final String[] CGLIB_PACKAGES = { + "java.lang", + }; + + static { + primitives.put("byte", Byte.TYPE); + primitives.put("char", Character.TYPE); + primitives.put("double", Double.TYPE); + primitives.put("float", Float.TYPE); + primitives.put("int", Integer.TYPE); + primitives.put("long", Long.TYPE); + primitives.put("short", Short.TYPE); + primitives.put("boolean", Boolean.TYPE); + + transforms.put("byte", "B"); + transforms.put("char", "C"); + transforms.put("double", "D"); + transforms.put("float", "F"); + transforms.put("int", "I"); + transforms.put("long", "J"); + transforms.put("short", "S"); + transforms.put("boolean", "Z"); + } + + public static Type[] getExceptionTypes(Member member) { + if (member instanceof Method) { + return TypeUtils.getTypes(((Method)member).getExceptionTypes()); + } else if (member instanceof Constructor) { + return TypeUtils.getTypes(((Constructor)member).getExceptionTypes()); + } else { + throw new IllegalArgumentException("Cannot get exception types of a field"); + } + } + + public static Signature getSignature(Member member) { + if (member instanceof Method) { + return new Signature(member.getName(), Type.getMethodDescriptor((Method)member)); + } else if (member instanceof Constructor) { + Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes()); + return new Signature(Constants.CONSTRUCTOR_NAME, + Type.getMethodDescriptor(Type.VOID_TYPE, types)); + + } else { + throw new IllegalArgumentException("Cannot get signature of a field"); + } + } + + public static Constructor findConstructor(String desc) { + return findConstructor(desc, defaultLoader); + } + + public static Constructor findConstructor(String desc, ClassLoader loader) { + try { + int lparen = desc.indexOf('('); + String className = desc.substring(0, lparen).trim(); + return getClass(className, loader).getConstructor(parseTypes(desc, loader)); + } catch (ClassNotFoundException e) { + throw new CodeGenerationException(e); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public static Method findMethod(String desc) { + return findMethod(desc, defaultLoader); + } + + public static Method findMethod(String desc, ClassLoader loader) { + try { + int lparen = desc.indexOf('('); + int dot = desc.lastIndexOf('.', lparen); + String className = desc.substring(0, dot).trim(); + String methodName = desc.substring(dot + 1, lparen).trim(); + return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader)); + } catch (ClassNotFoundException e) { + throw new CodeGenerationException(e); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException { + int lparen = desc.indexOf('('); + int rparen = desc.indexOf(')', lparen); + List params = new ArrayList(); + int start = lparen + 1; + for (;;) { + int comma = desc.indexOf(',', start); + if (comma < 0) { + break; + } + params.add(desc.substring(start, comma).trim()); + start = comma + 1; + } + if (start < rparen) { + params.add(desc.substring(start, rparen).trim()); + } + Class[] types = new Class[params.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = getClass((String)params.get(i), loader); + } + return types; + } + + private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException { + return getClass(className, loader, CGLIB_PACKAGES); + } + + private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException { + String save = className; + int dimensions = 0; + int index = 0; + while ((index = className.indexOf("[]", index) + 1) > 0) { + dimensions++; + } + StringBuffer brackets = new StringBuffer(className.length() - dimensions); + for (int i = 0; i < dimensions; i++) { + brackets.append('['); + } + className = className.substring(0, className.length() - 2 * dimensions); + + String prefix = (dimensions > 0) ? brackets + "L" : ""; + String suffix = (dimensions > 0) ? ";" : ""; + try { + return Class.forName(prefix + className + suffix, false, loader); + } catch (ClassNotFoundException ignore) { } + for (int i = 0; i < packages.length; i++) { + try { + return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader); + } catch (ClassNotFoundException ignore) { } + } + if (dimensions == 0) { + Class c = (Class)primitives.get(className); + if (c != null) { + return c; + } + } else { + String transform = (String)transforms.get(className); + if (transform != null) { + try { + return Class.forName(brackets + transform, false, loader); + } catch (ClassNotFoundException ignore) { } + } + } + throw new ClassNotFoundException(save); + } + + + public static Object newInstance(Class type) { + return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null); + } + + public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) { + return newInstance(getConstructor(type, parameterTypes), args); + } + + public static Object newInstance(final Constructor cstruct, final Object[] args) { + + boolean flag = cstruct.isAccessible(); + try { + cstruct.setAccessible(true); + Object result = cstruct.newInstance(args); + return result; + } catch (InstantiationException e) { + throw new CodeGenerationException(e); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e.getTargetException()); + } finally { + cstruct.setAccessible(flag); + } + + } + + public static Constructor getConstructor(Class type, Class[] parameterTypes) { + try { + Constructor constructor = type.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public static String[] getNames(Class[] classes) + { + if (classes == null) + return null; + String[] names = new String[classes.length]; + for (int i = 0; i < names.length; i++) { + names[i] = classes[i].getName(); + } + return names; + } + + public static Class[] getClasses(Object[] objects) { + Class[] classes = new Class[objects.length]; + for (int i = 0; i < objects.length; i++) { + classes[i] = objects[i].getClass(); + } + return classes; + } + + public static Method findNewInstance(Class iface) { + Method m = findInterfaceMethod(iface); + if (!m.getName().equals("newInstance")) { + throw new IllegalArgumentException(iface + " missing newInstance method"); + } + return m; + } + + public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) { + Set methods = new HashSet(); + for (int i = 0; i < properties.length; i++) { + PropertyDescriptor pd = properties[i]; + if (read) { + methods.add(pd.getReadMethod()); + } + if (write) { + methods.add(pd.getWriteMethod()); + } + } + methods.remove(null); + return (Method[])methods.toArray(new Method[methods.size()]); + } + + public static PropertyDescriptor[] getBeanProperties(Class type) { + return getPropertiesHelper(type, true, true); + } + + public static PropertyDescriptor[] getBeanGetters(Class type) { + return getPropertiesHelper(type, true, false); + } + + public static PropertyDescriptor[] getBeanSetters(Class type) { + return getPropertiesHelper(type, false, true); + } + + private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { + try { + BeanInfo info = Introspector.getBeanInfo(type, Object.class); + PropertyDescriptor[] all = info.getPropertyDescriptors(); + if (read && write) { + return all; + } + List properties = new ArrayList(all.length); + for (int i = 0; i < all.length; i++) { + PropertyDescriptor pd = all[i]; + if ((read && pd.getReadMethod() != null) || + (write && pd.getWriteMethod() != null)) { + properties.add(pd); + } + } + return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]); + } catch (IntrospectionException e) { + throw new CodeGenerationException(e); + } + } + + + + public static Method findDeclaredMethod(final Class type, + final String methodName, final Class[] parameterTypes) + throws NoSuchMethodException { + + Class cl = type; + while (cl != null) { + try { + return cl.getDeclaredMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) { + cl = cl.getSuperclass(); + } + } + throw new NoSuchMethodException(methodName); + + } + + public static List addAllMethods(final Class type, final List list) { + + + list.addAll(java.util.Arrays.asList(type.getDeclaredMethods())); + Class superclass = type.getSuperclass(); + if (superclass != null) { + addAllMethods(superclass, list); + } + Class[] interfaces = type.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + addAllMethods(interfaces[i], list); + } + + return list; + } + + public static List addAllInterfaces(Class type, List list) { + Class superclass = type.getSuperclass(); + if (superclass != null) { + list.addAll(Arrays.asList(type.getInterfaces())); + addAllInterfaces(superclass, list); + } + return list; + } + + + public static Method findInterfaceMethod(Class iface) { + if (!iface.isInterface()) { + throw new IllegalArgumentException(iface + " is not an interface"); + } + Method[] methods = iface.getDeclaredMethods(); + if (methods.length != 1) { + throw new IllegalArgumentException("expecting exactly 1 method in " + iface); + } + return methods[0]; + } + + public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { + Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), PROTECTION_DOMAIN }; + Class c = (Class)DEFINE_CLASS.invoke(loader, args); + // Force static initializers to run. + Class.forName(className, true, loader); + return c; + } + + public static int findPackageProtected(Class[] classes) { + for (int i = 0; i < classes.length; i++) { + if (!Modifier.isPublic(classes[i].getModifiers())) { + return i; + } + } + return 0; + } + + public static MethodInfo getMethodInfo(final Member member, final int modifiers) { + final Signature sig = getSignature(member); + return new MethodInfo() { + private ClassInfo ci; + public ClassInfo getClassInfo() { + if (ci == null) + ci = ReflectUtils.getClassInfo(member.getDeclaringClass()); + return ci; + } + public int getModifiers() { + return modifiers; + } + public Signature getSignature() { + return sig; + } + public Type[] getExceptionTypes() { + return ReflectUtils.getExceptionTypes(member); + } + @SuppressWarnings("unused") + public Attribute getAttribute() { + return null; + } + }; + } + + public static MethodInfo getMethodInfo(Member member) { + return getMethodInfo(member, member.getModifiers()); + } + + public static ClassInfo getClassInfo(final Class clazz) { + final Type type = Type.getType(clazz); + final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass()); + return new ClassInfo() { + public Type getType() { + return type; + } + public Type getSuperType() { + return sc; + } + public Type[] getInterfaces() { + return TypeUtils.getTypes(clazz.getInterfaces()); + } + public int getModifiers() { + return clazz.getModifiers(); + } + }; + } + + // used by MethodInterceptorGenerated generated code + public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) + { + Map map = new HashMap(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + map.put(method.getName() + Type.getMethodDescriptor(method), method); + } + Method[] result = new Method[namesAndDescriptors.length / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]); + if (result[i] == null) { + } + } + return result; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/RejectModifierPredicate.java b/fine-spring/src/com/fr/third/springframework/cglib/core/RejectModifierPredicate.java new file mode 100644 index 000000000..b1ce83db5 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/RejectModifierPredicate.java @@ -0,0 +1,30 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import java.lang.reflect.*; + +public class RejectModifierPredicate implements Predicate { + private int rejectMask; + + public RejectModifierPredicate(int rejectMask) { + this.rejectMask = rejectMask; + } + + public boolean evaluate(Object arg) { + return (((Member)arg).getModifiers() & rejectMask) == 0; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Signature.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Signature.java new file mode 100644 index 000000000..48f50fbe3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Signature.java @@ -0,0 +1,74 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +/** + * A representation of a method signature, containing the method name, + * return type, and parameter types. + */ +public class Signature { + private String name; + private String desc; + + public Signature(String name, String desc) { + // better error checking + if (name.indexOf('(') >= 0) { + throw new IllegalArgumentException("Name '" + name + "' is invalid"); + } + this.name = name; + this.desc = desc; + } + + public Signature(String name, Type returnType, Type[] argumentTypes) { + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return desc; + } + + public Type getReturnType() { + return Type.getReturnType(desc); + } + + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(desc); + } + + public String toString() { + return name + desc; + } + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof Signature)) + return false; + Signature other = (Signature)o; + return name.equals(other.name) && desc.equals(other.desc); + } + + public int hashCode() { + return name.hashCode() ^ desc.hashCode(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/SpringNamingPolicy.java b/fine-spring/src/com/fr/third/springframework/cglib/core/SpringNamingPolicy.java index 0c14904fc..e36cbe478 100644 --- a/fine-spring/src/com/fr/third/springframework/cglib/core/SpringNamingPolicy.java +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/SpringNamingPolicy.java @@ -16,9 +16,6 @@ package com.fr.third.springframework.cglib.core; - -import org.springframework.cglib.core.DefaultNamingPolicy; - /** * Custom extension of CGLIB's {@link DefaultNamingPolicy}, modifying * the tag in generated class names from "ByCGLIB" to "BySpringCGLIB". diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/TinyBitSet.java b/fine-spring/src/com/fr/third/springframework/cglib/core/TinyBitSet.java new file mode 100644 index 000000000..f2252fda2 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/TinyBitSet.java @@ -0,0 +1,78 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +public class TinyBitSet { + private static int[] T = new int[256]; + private int value = 0; + + private static int gcount(int x) { + int c = 0; + while (x != 0) { + c++; + x &= (x - 1); + } + return c; + } + + static { + for(int j = 0; j < 256; j++) { + T[j] = gcount(j); + } + } + + private static int topbit(int i) { + int j; + for (j = 0; i != 0; i ^= j) { + j = i & -i; + } + return j; + } + + private static int log2(int i) { + int j = 0; + for (j = 0; i != 0; i >>= 1) { + j++; + } + return j; + } + + public int length() { + return log2(topbit(value)); + } + + public int cardinality() { + int w = value; + int c = 0; + while (w != 0) { + c += T[w & 255]; + w >>= 8; + } + return c; + } + + public boolean get(int index) { + return (value & (1 << index)) != 0; + } + + public void set(int index) { + value |= (1 << index); + } + + public void clear(int index) { + value &= ~(1 << index); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/Transformer.java b/fine-spring/src/com/fr/third/springframework/cglib/core/Transformer.java new file mode 100644 index 000000000..a88418a00 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/Transformer.java @@ -0,0 +1,20 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +public interface Transformer { + Object transform(Object value); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/TypeUtils.java b/fine-spring/src/com/fr/third/springframework/cglib/core/TypeUtils.java new file mode 100644 index 000000000..864f4d9c8 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/TypeUtils.java @@ -0,0 +1,420 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + +import com.fr.third.springframework.asm.Type; + +import java.util.*; +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class TypeUtils { + private static final Map transforms = new HashMap(); + private static final Map rtransforms = new HashMap(); + + private TypeUtils() { + } + + static { + transforms.put("void", "V"); + transforms.put("byte", "B"); + transforms.put("char", "C"); + transforms.put("double", "D"); + transforms.put("float", "F"); + transforms.put("int", "I"); + transforms.put("long", "J"); + transforms.put("short", "S"); + transforms.put("boolean", "Z"); + + CollectionUtils.reverse(transforms, rtransforms); + } + + public static Type getType(String className) { + return Type.getType("L" + className.replace('.', '/') + ";"); + } + + public static boolean isFinal(int access) { + return (Constants.ACC_FINAL & access) != 0; + } + + public static boolean isStatic(int access) { + return (Constants.ACC_STATIC & access) != 0; + } + + public static boolean isProtected(int access) { + return (Constants.ACC_PROTECTED & access) != 0; + } + + public static boolean isPublic(int access) { + return (Constants.ACC_PUBLIC & access) != 0; + } + + public static boolean isAbstract(int access) { + return (Constants.ACC_ABSTRACT & access) != 0; + } + + public static boolean isInterface(int access) { + return (Constants.ACC_INTERFACE & access) != 0; + } + + public static boolean isPrivate(int access) { + return (Constants.ACC_PRIVATE & access) != 0; + } + + public static boolean isSynthetic(int access) { + return (Constants.ACC_SYNTHETIC & access) != 0; + } + + public static boolean isBridge(int access) { + return (Constants.ACC_BRIDGE & access) != 0; + } + + // getPackage returns null on JDK 1.2 + public static String getPackageName(Type type) { + return getPackageName(getClassName(type)); + } + + public static String getPackageName(String className) { + int idx = className.lastIndexOf('.'); + return (idx < 0) ? "" : className.substring(0, idx); + } + + public static String upperFirst(String s) { + if (s == null || s.length() == 0) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + public static String getClassName(Type type) { + if (isPrimitive(type)) { + return (String)rtransforms.get(type.getDescriptor()); + } else if (isArray(type)) { + return getClassName(getComponentType(type)) + "[]"; + } else { + return type.getClassName(); + } + } + + public static Type[] add(Type[] types, Type extra) { + if (types == null) { + return new Type[]{ extra }; + } else { + List list = Arrays.asList(types); + if (list.contains(extra)) { + return types; + } + Type[] copy = new Type[types.length + 1]; + System.arraycopy(types, 0, copy, 0, types.length); + copy[types.length] = extra; + return copy; + } + } + + public static Type[] add(Type[] t1, Type[] t2) { + Type[] all = new Type[t1.length + t2.length]; + System.arraycopy(t1, 0, all, 0, t1.length); + System.arraycopy(t2, 0, all, t1.length, t2.length); + return all; + } + + public static Type fromInternalName(String name) { + return Type.getType("L" + name + ";"); + } + + public static Type[] fromInternalNames(String[] names) { + if (names == null) { + return null; + } + Type[] types = new Type[names.length]; + for (int i = 0; i < names.length; i++) { + types[i] = fromInternalName(names[i]); + } + return types; + } + + public static int getStackSize(Type[] types) { + int size = 0; + for (int i = 0; i < types.length; i++) { + size += types[i].getSize(); + } + return size; + } + + public static String[] toInternalNames(Type[] types) { + if (types == null) { + return null; + } + String[] names = new String[types.length]; + for (int i = 0; i < types.length; i++) { + names[i] = types[i].getInternalName(); + } + return names; + } + + public static Signature parseSignature(String s) { + int space = s.indexOf(' '); + int lparen = s.indexOf('(', space); + int rparen = s.indexOf(')', lparen); + String returnType = s.substring(0, space); + String methodName = s.substring(space + 1, lparen); + StringBuffer sb = new StringBuffer(); + sb.append('('); + for (Iterator it = parseTypes(s, lparen + 1, rparen).iterator(); it.hasNext();) { + sb.append(it.next()); + } + sb.append(')'); + sb.append(map(returnType)); + return new Signature(methodName, sb.toString()); + } + + public static Type parseType(String s) { + return Type.getType(map(s)); + } + + public static Type[] parseTypes(String s) { + List names = parseTypes(s, 0, s.length()); + Type[] types = new Type[names.size()]; + for (int i = 0; i < types.length; i++) { + types[i] = Type.getType((String)names.get(i)); + } + return types; + } + + public static Signature parseConstructor(Type[] types) { + StringBuffer sb = new StringBuffer(); + sb.append("("); + for (int i = 0; i < types.length; i++) { + sb.append(types[i].getDescriptor()); + } + sb.append(")"); + sb.append("V"); + return new Signature(Constants.CONSTRUCTOR_NAME, sb.toString()); + } + + public static Signature parseConstructor(String sig) { + return parseSignature("void (" + sig + ")"); + } + + private static List parseTypes(String s, int mark, int end) { + List types = new ArrayList(5); + for (;;) { + int next = s.indexOf(',', mark); + if (next < 0) { + break; + } + types.add(map(s.substring(mark, next).trim())); + mark = next + 1; + } + types.add(map(s.substring(mark, end).trim())); + return types; + } + + private static String map(String type) { + if (type.equals("")) { + return type; + } + String t = (String)transforms.get(type); + if (t != null) { + return t; + } else if (type.indexOf('.') < 0) { + return map("java.lang." + type); + } else { + StringBuffer sb = new StringBuffer(); + int index = 0; + while ((index = type.indexOf("[]", index) + 1) > 0) { + sb.append('['); + } + type = type.substring(0, type.length() - sb.length() * 2); + sb.append('L').append(type.replace('.', '/')).append(';'); + return sb.toString(); + } + } + + public static Type getBoxedType(Type type) { + switch (type.getSort()) { + case Type.CHAR: + return Constants.TYPE_CHARACTER; + case Type.BOOLEAN: + return Constants.TYPE_BOOLEAN; + case Type.DOUBLE: + return Constants.TYPE_DOUBLE; + case Type.FLOAT: + return Constants.TYPE_FLOAT; + case Type.LONG: + return Constants.TYPE_LONG; + case Type.INT: + return Constants.TYPE_INTEGER; + case Type.SHORT: + return Constants.TYPE_SHORT; + case Type.BYTE: + return Constants.TYPE_BYTE; + default: + return type; + } + } + + public static Type getUnboxedType(Type type) { + if (Constants.TYPE_INTEGER.equals(type)) { + return Type.INT_TYPE; + } else if (Constants.TYPE_BOOLEAN.equals(type)) { + return Type.BOOLEAN_TYPE; + } else if (Constants.TYPE_DOUBLE.equals(type)) { + return Type.DOUBLE_TYPE; + } else if (Constants.TYPE_LONG.equals(type)) { + return Type.LONG_TYPE; + } else if (Constants.TYPE_CHARACTER.equals(type)) { + return Type.CHAR_TYPE; + } else if (Constants.TYPE_BYTE.equals(type)) { + return Type.BYTE_TYPE; + } else if (Constants.TYPE_FLOAT.equals(type)) { + return Type.FLOAT_TYPE; + } else if (Constants.TYPE_SHORT.equals(type)) { + return Type.SHORT_TYPE; + } else { + return type; + } + } + + public static boolean isArray(Type type) { + return type.getSort() == Type.ARRAY; + } + + public static Type getComponentType(Type type) { + if (!isArray(type)) { + throw new IllegalArgumentException("Type " + type + " is not an array"); + } + return Type.getType(type.getDescriptor().substring(1)); + } + + public static boolean isPrimitive(Type type) { + switch (type.getSort()) { + case Type.ARRAY: + case Type.OBJECT: + return false; + default: + return true; + } + } + + public static String emulateClassGetName(Type type) { + if (isArray(type)) { + return type.getDescriptor().replace('/', '.'); + } else { + return getClassName(type); + } + } + + public static boolean isConstructor(MethodInfo method) { + return method.getSignature().getName().equals(Constants.CONSTRUCTOR_NAME); + } + + public static Type[] getTypes(Class[] classes) { + if (classes == null) { + return null; + } + Type[] types = new Type[classes.length]; + for (int i = 0; i < classes.length; i++) { + types[i] = Type.getType(classes[i]); + } + return types; + } + + public static int ICONST(int value) { + switch (value) { + case -1: return Constants.ICONST_M1; + case 0: return Constants.ICONST_0; + case 1: return Constants.ICONST_1; + case 2: return Constants.ICONST_2; + case 3: return Constants.ICONST_3; + case 4: return Constants.ICONST_4; + case 5: return Constants.ICONST_5; + } + return -1; // error + } + + public static int LCONST(long value) { + if (value == 0L) { + return Constants.LCONST_0; + } else if (value == 1L) { + return Constants.LCONST_1; + } else { + return -1; // error + } + } + + public static int FCONST(float value) { + if (value == 0f) { + return Constants.FCONST_0; + } else if (value == 1f) { + return Constants.FCONST_1; + } else if (value == 2f) { + return Constants.FCONST_2; + } else { + return -1; // error + } + } + + public static int DCONST(double value) { + if (value == 0d) { + return Constants.DCONST_0; + } else if (value == 1d) { + return Constants.DCONST_1; + } else { + return -1; // error + } + } + + public static int NEWARRAY(Type type) { + switch (type.getSort()) { + case Type.BYTE: + return Constants.T_BYTE; + case Type.CHAR: + return Constants.T_CHAR; + case Type.DOUBLE: + return Constants.T_DOUBLE; + case Type.FLOAT: + return Constants.T_FLOAT; + case Type.INT: + return Constants.T_INT; + case Type.LONG: + return Constants.T_LONG; + case Type.SHORT: + return Constants.T_SHORT; + case Type.BOOLEAN: + return Constants.T_BOOLEAN; + default: + return -1; // error + } + } + + public static String escapeType(String s) { + StringBuffer sb = new StringBuffer(); + for (int i = 0, len = s.length(); i < len; i++) { + char c = s.charAt(i); + switch (c) { + case '$': sb.append("$24"); break; + case '.': sb.append("$2E"); break; + case '[': sb.append("$5B"); break; + case ';': sb.append("$3B"); break; + case '(': sb.append("$28"); break; + case ')': sb.append("$29"); break; + case '/': sb.append("$2F"); break; + default: + sb.append(c); + } + } + return sb.toString(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/core/VisibilityPredicate.java b/fine-spring/src/com/fr/third/springframework/cglib/core/VisibilityPredicate.java new file mode 100644 index 000000000..2710d91c1 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/core/VisibilityPredicate.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.core; + + +import com.fr.third.springframework.asm.Type; + +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; + +public class VisibilityPredicate implements Predicate { + private boolean protectedOk; + private String pkg; + + @SuppressWarnings("rawtypes") + public VisibilityPredicate(Class source, boolean protectedOk) { + this.protectedOk = protectedOk; + pkg = TypeUtils.getPackageName(Type.getType(source)); + } + + public boolean evaluate(Object arg) { + int mod = (arg instanceof Member) ? ((Member)arg).getModifiers() : ((Integer)arg).intValue(); + if (Modifier.isPrivate(mod)) { + return false; + } else if (Modifier.isPublic(mod)) { + return true; + } else if (Modifier.isProtected(mod)) { + return protectedOk; + } else { + return pkg.equals(TypeUtils.getPackageName(Type.getType(((Member)arg).getDeclaringClass()))); + } + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/BridgeMethodResolver.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/BridgeMethodResolver.java new file mode 100644 index 000000000..5f7e1e229 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/BridgeMethodResolver.java @@ -0,0 +1,112 @@ +/* + * Copyright 2011 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.MethodVisitor; +import com.fr.third.springframework.asm.Opcodes; +import com.fr.third.springframework.cglib.core.*; + + + +/** + * Uses bytecode reflection to figure out the targets of all bridge methods + * that use invokespecial, so that we can later rewrite them to use invokevirtual. + * + * @author sberlin@gmail.com (Sam Berlin) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +class BridgeMethodResolver { + + private final Map/* */declToBridge; + + public BridgeMethodResolver(Map declToBridge) { + this.declToBridge = declToBridge; + } + + /** + * Finds all bridge methods that are being called with invokespecial & + * returns them. + */ + public Map/**/resolveAll() { + Map resolved = new HashMap(); + for (Iterator entryIter = declToBridge.entrySet().iterator(); entryIter.hasNext(); ) { + Map.Entry entry = (Map.Entry)entryIter.next(); + Class owner = (Class)entry.getKey(); + Set bridges = (Set)entry.getValue(); + try { + new ClassReader(owner.getName()) + .accept(new BridgedFinder(bridges, resolved), + ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); + } catch(IOException ignored) {} + } + return resolved; + } + + private static class BridgedFinder extends ClassVisitor { + private Map/**/ resolved; + private Set/**/ eligableMethods; + + private Signature currentMethod = null; + + BridgedFinder(Set eligableMethods, Map resolved) { + super(Opcodes.ASM4); + this.resolved = resolved; + this.eligableMethods = eligableMethods; + } + + public void visit(int version, int access, String name, + String signature, String superName, String[] interfaces) { + } + + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + Signature sig = new Signature(name, desc); + if (eligableMethods.remove(sig)) { + currentMethod = sig; + return new MethodVisitor(Opcodes.ASM4) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (opcode == Opcodes.INVOKESPECIAL && currentMethod != null) { + Signature target = new Signature(name, desc); + // If the target signature is the same as the current, + // we shouldn't change our bridge becaues invokespecial + // is the only way to make progress (otherwise we'll + // get infinite recursion). This would typically + // only happen when a bridge method is created to widen + // the visibility of a superclass' method. + if (!target.equals(currentMethod)) { + resolved.put(currentMethod, target); + } + currentMethod = null; + } + } + }; + } else { + return null; + } + } + } + +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Callback.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Callback.java new file mode 100644 index 000000000..77ba427fe --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Callback.java @@ -0,0 +1,29 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * All callback interfaces used by {@link Enhancer} extend this interface. + * @see MethodInterceptor + * @see NoOp + * @see LazyLoader + * @see Dispatcher + * @see InvocationHandler + * @see FixedValue + */ +public interface Callback +{ +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackFilter.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackFilter.java new file mode 100644 index 000000000..eab6f18a0 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackFilter.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; + +/** + * Map methods of subclasses generated by {@link Enhancer} to a particular + * callback. The type of the callbacks chosen for each method affects + * the bytecode generated for that method in the subclass, and cannot + * change for the life of the class. + */ +public interface CallbackFilter { + /** + * Map a method to a callback. + * @param method the intercepted method + * @return the index into the array of callbacks (as specified by {@link Enhancer#setCallbacks}) to use for the method, + */ + int accept(Method method); + + /** + * The CallbackFilter in use affects which cached class + * the Enhancer will use, so this is a reminder that + * you should correctly implement equals and + * hashCode for custom CallbackFilter + * implementations in order to improve performance. + */ + boolean equals(Object o); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackGenerator.java new file mode 100644 index 000000000..fba9ff32f --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackGenerator.java @@ -0,0 +1,38 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.List; +import com.fr.third.springframework.cglib.core.*; + +interface CallbackGenerator +{ + @SuppressWarnings("rawtypes") + void generate(ClassEmitter ce, Context context, List methods) throws Exception; + @SuppressWarnings("rawtypes") + void generateStatic(CodeEmitter e, Context context, List methods) throws Exception; + + interface Context + { + ClassLoader getClassLoader(); + CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method); + int getOriginalModifiers(MethodInfo method); + int getIndex(MethodInfo method); + void emitCallback(CodeEmitter ce, int index); + Signature getImplSignature(MethodInfo method); + void emitInvoke(CodeEmitter e, MethodInfo method); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackHelper.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackHelper.java new file mode 100644 index 000000000..73cb9cc30 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackHelper.java @@ -0,0 +1,99 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import com.fr.third.springframework.cglib.core.*; +import java.lang.reflect.Method; +import java.util.*; + +/** + * @version $Id: CallbackHelper.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class CallbackHelper +implements CallbackFilter +{ + private Map methodMap = new HashMap(); + private List callbacks = new ArrayList(); + + public CallbackHelper(Class superclass, Class[] interfaces) + { + List methods = new ArrayList(); + Enhancer.getMethods(superclass, interfaces, methods); + Map indexes = new HashMap(); + for (int i = 0, size = methods.size(); i < size; i++) { + Method method = (Method)methods.get(i); + Object callback = getCallback(method); + if (callback == null) + throw new IllegalStateException("getCallback cannot return null"); + boolean isCallback = callback instanceof Callback; + if (!(isCallback || (callback instanceof Class))) + throw new IllegalStateException("getCallback must return a Callback or a Class"); + if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback)) + throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method"); + Integer index = (Integer)indexes.get(callback); + if (index == null) { + index = new Integer(callbacks.size()); + indexes.put(callback, index); + } + methodMap.put(method, index); + callbacks.add(callback); + } + } + + abstract protected Object getCallback(Method method); + + public Callback[] getCallbacks() + { + if (callbacks.size() == 0) + return new Callback[0]; + if (callbacks.get(0) instanceof Callback) { + return (Callback[])callbacks.toArray(new Callback[callbacks.size()]); + } else { + throw new IllegalStateException("getCallback returned classes, not callbacks; call getCallbackTypes instead"); + } + } + + public Class[] getCallbackTypes() + { + if (callbacks.size() == 0) + return new Class[0]; + if (callbacks.get(0) instanceof Callback) { + return ReflectUtils.getClasses(getCallbacks()); + } else { + return (Class[])callbacks.toArray(new Class[callbacks.size()]); + } + } + + public int accept(Method method) + { + return ((Integer)methodMap.get(method)).intValue(); + } + + public int hashCode() + { + return methodMap.hashCode(); + } + + public boolean equals(Object o) + { + if (o == null) + return false; + if (!(o instanceof CallbackHelper)) + return false; + return methodMap.equals(((CallbackHelper)o).methodMap); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackInfo.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackInfo.java new file mode 100644 index 000000000..e118cd0db --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/CallbackInfo.java @@ -0,0 +1,105 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + + +import com.fr.third.springframework.asm.Type; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +class CallbackInfo +{ + public static Type[] determineTypes(Class[] callbackTypes) { + Type[] types = new Type[callbackTypes.length]; + for (int i = 0; i < types.length; i++) { + types[i] = determineType(callbackTypes[i]); + } + return types; + } + + public static Type[] determineTypes(Callback[] callbacks) { + Type[] types = new Type[callbacks.length]; + for (int i = 0; i < types.length; i++) { + types[i] = determineType(callbacks[i]); + } + return types; + } + + public static CallbackGenerator[] getGenerators(Type[] callbackTypes) { + CallbackGenerator[] generators = new CallbackGenerator[callbackTypes.length]; + for (int i = 0; i < generators.length; i++) { + generators[i] = getGenerator(callbackTypes[i]); + } + return generators; + } + + //////////////////// PRIVATE //////////////////// + + private Class cls; + private CallbackGenerator generator; + private Type type; + + private static final CallbackInfo[] CALLBACKS = { + new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE), + new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE), + new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE), + new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE), + new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE), + new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE), + new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE), + }; + + private CallbackInfo(Class cls, CallbackGenerator generator) { + this.cls = cls; + this.generator = generator; + type = Type.getType(cls); + } + + private static Type determineType(Callback callback) { + if (callback == null) { + throw new IllegalStateException("Callback is null"); + } + return determineType(callback.getClass()); + } + + private static Type determineType(Class callbackType) { + Class cur = null; + for (int i = 0; i < CALLBACKS.length; i++) { + CallbackInfo info = CALLBACKS[i]; + if (info.cls.isAssignableFrom(callbackType)) { + if (cur != null) { + throw new IllegalStateException("Callback implements both " + cur + " and " + info.cls); + } + cur = info.cls; + } + } + if (cur == null) { + throw new IllegalStateException("Unknown callback type " + callbackType); + } + return Type.getType(cur); + } + + private static CallbackGenerator getGenerator(Type callbackType) { + for (int i = 0; i < CALLBACKS.length; i++) { + CallbackInfo info = CALLBACKS[i]; + if (info.type.equals(callbackType)) { + return info.generator; + } + } + throw new IllegalStateException("Unknown callback type " + callbackType); + } +} + + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Dispatcher.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Dispatcher.java new file mode 100644 index 000000000..d895a6378 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Dispatcher.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * Dispatching {@link Enhancer} callback. This is identical to the + * {@link LazyLoader} interface but needs to be separate so that Enhancer + * knows which type of code to generate. + */ +public interface Dispatcher extends Callback { + /** + * Return the object which the original method invocation should + * be dispatched. This method is called for every method invocation. + * @return an object that can invoke the method + */ + Object loadObject() throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/DispatcherGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/DispatcherGenerator.java new file mode 100644 index 000000000..7d695b5d3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/DispatcherGenerator.java @@ -0,0 +1,69 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.*; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes" }) +class DispatcherGenerator implements CallbackGenerator { + public static final DispatcherGenerator INSTANCE = + new DispatcherGenerator(false); + public static final DispatcherGenerator PROXY_REF_INSTANCE = + new DispatcherGenerator(true); + + private static final Type DISPATCHER = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.Dispatcher"); + //TypeUtils.parseType("net.sf.cglib.proxy.Dispatcher"); + private static final Type PROXY_REF_DISPATCHER = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.ProxyRefDispatcher"); + //TypeUtils.parseType("net.sf.cglib.proxy.ProxyRefDispatcher"); + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + private static final Signature PROXY_REF_LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject(Object)"); + + private boolean proxyRef; + + private DispatcherGenerator(boolean proxyRef) { + this.proxyRef = proxyRef; + } + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (!TypeUtils.isProtected(method.getModifiers())) { + CodeEmitter e = context.beginMethod(ce, method); + context.emitCallback(e, context.getIndex(method)); + if (proxyRef) { + e.load_this(); + e.invoke_interface(PROXY_REF_DISPATCHER, PROXY_REF_LOAD_OBJECT); + } else { + e.invoke_interface(DISPATCHER, LOAD_OBJECT); + } + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Enhancer.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Enhancer.java new file mode 100644 index 000000000..9c92cf411 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Enhancer.java @@ -0,0 +1,1092 @@ +/* + * Copyright 2002,2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +/** + * Generates dynamic subclasses to enable method interception. This + * class started as a substitute for the standard Dynamic Proxy support + * included with JDK 1.3, but one that allowed the proxies to extend a + * concrete base class, in addition to implementing interfaces. The dynamically + * generated subclasses override the non-final methods of the superclass and + * have hooks which callback to user-defined interceptor + * implementations. + *

+ * The original and most general callback type is the {@link MethodInterceptor}, which + * in AOP terms enables "around advice"--that is, you can invoke custom code both before + * and after the invocation of the "super" method. In addition you can modify the + * arguments before calling the super method, or not call it at all. + *

+ * Although MethodInterceptor is generic enough to meet any + * interception need, it is often overkill. For simplicity and performance, additional + * specialized callback types, such as {@link LazyLoader} are also available. + * Often a single callback will be used per enhanced class, but you can control + * which callback is used on a per-method basis with a {@link CallbackFilter}. + *

+ * The most common uses of this class are embodied in the static helper methods. For + * advanced needs, such as customizing the ClassLoader to use, you should create + * a new instance of Enhancer. Other classes within CGLIB follow a similar pattern. + *

+ * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is + * used to explicitly disable this feature. The Factory interface provides an API + * to change the callbacks of an existing object, as well as a faster and easier way to create + * new instances of the same type. + *

+ * For an almost drop-in replacement for + * java.lang.reflect.Proxy, see the {@link Proxy} class. + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class Enhancer extends AbstractClassGenerator +{ + private static final CallbackFilter ALL_ZERO = new CallbackFilter(){ + public int accept(Method method) { + return 0; + } + }; + + private static final Source SOURCE = new Source(Enhancer.class.getName()); + private static final EnhancerKey KEY_FACTORY = + (EnhancerKey)KeyFactory.create(EnhancerKey.class); + + private static final String BOUND_FIELD = "CGLIB$BOUND"; + private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS"; + private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS"; + private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS"; + private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS"; + private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED"; + + private static final Type FACTORY = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.Factory"); + private static final Type ILLEGAL_STATE_EXCEPTION = + TypeUtils.parseType("IllegalStateException"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Type THREAD_LOCAL = + TypeUtils.parseType("ThreadLocal"); + private static final Type CALLBACK = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.Callback"); + //TypeUtils.parseType("net.sf.cglib.proxy.Callback"); + private static final Type CALLBACK_ARRAY = + Type.getType(Callback[].class); + private static final Signature CSTRUCT_NULL = + TypeUtils.parseConstructor(""); + private static final Signature SET_THREAD_CALLBACKS = + new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature SET_STATIC_CALLBACKS = + new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY }); + private static final Signature MULTIARG_NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ + Constants.TYPE_CLASS_ARRAY, + Constants.TYPE_OBJECT_ARRAY, + CALLBACK_ARRAY, + }); + private static final Signature SINGLE_NEW_INSTANCE = + new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK }); + private static final Signature SET_CALLBACK = + new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK }); + private static final Signature GET_CALLBACK = + new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE }); + private static final Signature SET_CALLBACKS = + new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY }); + private static final Signature GET_CALLBACKS = + new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]); + private static final Signature THREAD_LOCAL_GET = + TypeUtils.parseSignature("Object get()"); + private static final Signature THREAD_LOCAL_SET = + TypeUtils.parseSignature("void set(Object)"); + private static final Signature BIND_CALLBACKS = + TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)"); + + /** Internal interface, only public due to ClassLoader issues. */ + public interface EnhancerKey { + public Object newInstance(String type, + String[] interfaces, + CallbackFilter filter, + Type[] callbackTypes, + boolean useFactory, + boolean interceptDuringConstruction, + Long serialVersionUID); + } + + private Class[] interfaces; + private CallbackFilter filter; + private Callback[] callbacks; + private Type[] callbackTypes; + private boolean classOnly; + private Class superclass; + private Class[] argumentTypes; + private Object[] arguments; + private boolean useFactory = true; + private Long serialVersionUID; + private boolean interceptDuringConstruction = true; + + /** + * Create a new Enhancer. A new Enhancer + * object should be used for each generated object, and should not + * be shared across threads. To create additional instances of a + * generated class, use the Factory interface. + * @see Factory + */ + public Enhancer() { + super(SOURCE); + } + + /** + * Set the class which the generated class will extend. As a convenience, + * if the supplied superclass is actually an interface, setInterfaces + * will be called with the appropriate argument instead. + * A non-interface argument must not be declared as final, and must have an + * accessible constructor. + * @param superclass class to extend or interface to implement + * @see #setInterfaces(Class[]) + */ + public void setSuperclass(Class superclass) { + if (superclass != null && superclass.isInterface()) { + setInterfaces(new Class[]{ superclass }); + } else if (superclass != null && superclass.equals(Object.class)) { + // affects choice of ClassLoader + this.superclass = null; + } else { + this.superclass = superclass; + } + } + + /** + * Set the interfaces to implement. The Factory interface will + * always be implemented regardless of what is specified here. + * @param interfaces array of interfaces to implement, or null + * @see Factory + */ + public void setInterfaces(Class[] interfaces) { + this.interfaces = interfaces; + } + + /** + * Set the {@link CallbackFilter} used to map the generated class' methods + * to a particular callback index. + * New object instances will always use the same mapping, but may use different + * actual callback objects. + * @param filter the callback filter to use when generating a new class + * @see #setCallbacks + */ + public void setCallbackFilter(CallbackFilter filter) { + this.filter = filter; + } + + + /** + * Set the single {@link Callback} to use. + * Ignored if you use {@link #createClass}. + * @param callback the callback to use for all methods + * @see #setCallbacks + */ + public void setCallback(final Callback callback) { + setCallbacks(new Callback[]{ callback }); + } + + /** + * Set the array of callbacks to use. + * Ignored if you use {@link #createClass}. + * You must use a {@link CallbackFilter} to specify the index into this + * array for each method in the proxied class. + * @param callbacks the callback array + * @see #setCallbackFilter + * @see #setCallback + */ + public void setCallbacks(Callback[] callbacks) { + if (callbacks != null && callbacks.length == 0) { + throw new IllegalArgumentException("Array cannot be empty"); + } + this.callbacks = callbacks; + } + + /** + * Set whether the enhanced object instances should implement + * the {@link Factory} interface. + * This was added for tools that need for proxies to be more + * indistinguishable from their targets. Also, in some cases it may + * be necessary to disable the Factory interface to + * prevent code from changing the underlying callbacks. + * @param useFactory whether to implement Factory; default is true + */ + public void setUseFactory(boolean useFactory) { + this.useFactory = useFactory; + } + + /** + * Set whether methods called from within the proxy's constructer + * will be intercepted. The default value is true. Unintercepted methods + * will call the method of the proxy's base class, if it exists. + * @param interceptDuringConstruction whether to intercept methods called from the constructor + */ + public void setInterceptDuringConstruction(boolean interceptDuringConstruction) { + this.interceptDuringConstruction = interceptDuringConstruction; + } + + /** + * Set the single type of {@link Callback} to use. + * This may be used instead of {@link #setCallback} when calling + * {@link #createClass}, since it may not be possible to have + * an array of actual callback instances. + * @param callbackType the type of callback to use for all methods + * @see #setCallbackTypes + */ + public void setCallbackType(Class callbackType) { + setCallbackTypes(new Class[]{ callbackType }); + } + + /** + * Set the array of callback types to use. + * This may be used instead of {@link #setCallbacks} when calling + * {@link #createClass}, since it may not be possible to have + * an array of actual callback instances. + * You must use a {@link CallbackFilter} to specify the index into this + * array for each method in the proxied class. + * @param callbackTypes the array of callback types + */ + public void setCallbackTypes(Class[] callbackTypes) { + if (callbackTypes != null && callbackTypes.length == 0) { + throw new IllegalArgumentException("Array cannot be empty"); + } + this.callbackTypes = CallbackInfo.determineTypes(callbackTypes); + } + + /** + * Generate a new class if necessary and uses the specified + * callbacks (if any) to create a new object instance. + * Uses the no-arg constructor of the superclass. + * @return a new instance + */ + public Object create() { + classOnly = false; + argumentTypes = null; + return createHelper(); + } + + /** + * Generate a new class if necessary and uses the specified + * callbacks (if any) to create a new object instance. + * Uses the constructor of the superclass matching the argumentTypes + * parameter, with the given arguments. + * @param argumentTypes constructor signature + * @param arguments compatible wrapped arguments to pass to constructor + * @return a new instance + */ + public Object create(Class[] argumentTypes, Object[] arguments) { + classOnly = false; + if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) { + throw new IllegalArgumentException("Arguments must be non-null and of equal length"); + } + this.argumentTypes = argumentTypes; + this.arguments = arguments; + return createHelper(); + } + + /** + * Generate a new class if necessary and return it without creating a new instance. + * This ignores any callbacks that have been set. + * To create a new instance you will have to use reflection, and methods + * called during the constructor will not be intercepted. To avoid this problem, + * use the multi-arg create method. + * @see #create(Class[], Object[]) + */ + public Class createClass() { + classOnly = true; + return (Class)createHelper(); + } + + /** + * Insert a static serialVersionUID field into the generated class. + * @param sUID the field value, or null to avoid generating field. + */ + public void setSerialVersionUID(Long sUID) { + serialVersionUID = sUID; + } + + private void validate() { + if (classOnly ^ (callbacks == null)) { + if (classOnly) { + throw new IllegalStateException("createClass does not accept callbacks"); + } else { + throw new IllegalStateException("Callbacks are required"); + } + } + if (classOnly && (callbackTypes == null)) { + throw new IllegalStateException("Callback types are required"); + } + if (callbacks != null && callbackTypes != null) { + if (callbacks.length != callbackTypes.length) { + throw new IllegalStateException("Lengths of callback and callback types array must be the same"); + } + Type[] check = CallbackInfo.determineTypes(callbacks); + for (int i = 0; i < check.length; i++) { + if (!check[i].equals(callbackTypes[i])) { + throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]); + } + } + } else if (callbacks != null) { + callbackTypes = CallbackInfo.determineTypes(callbacks); + } + if (filter == null) { + if (callbackTypes.length > 1) { + throw new IllegalStateException("Multiple callback types possible but no filter specified"); + } + filter = ALL_ZERO; + } + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i] == null) { + throw new IllegalStateException("Interfaces cannot be null"); + } + if (!interfaces[i].isInterface()) { + throw new IllegalStateException(interfaces[i] + " is not an interface"); + } + } + } + } + + private Object createHelper() { + validate(); + if (superclass != null) { + setNamePrefix(superclass.getName()); + } else if (interfaces != null) { + setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName()); + } + return super.create(KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null, + ReflectUtils.getNames(interfaces), + filter, + callbackTypes, + useFactory, + interceptDuringConstruction, + serialVersionUID)); + } + + protected ClassLoader getDefaultClassLoader() { + if (superclass != null) { + return superclass.getClassLoader(); + } else if (interfaces != null) { + return interfaces[0].getClassLoader(); + } else { + return null; + } + } + + private Signature rename(Signature sig, int index) { + return new Signature("CGLIB$" + sig.getName() + "$" + index, + sig.getDescriptor()); + } + + /** + * Finds all of the methods that will be extended by an + * Enhancer-generated class using the specified superclass and + * interfaces. This can be useful in building a list of Callback + * objects. The methods are added to the end of the given list. Due + * to the subclassing nature of the classes generated by Enhancer, + * the methods are guaranteed to be non-static, non-final, and + * non-private. Each method signature will only occur once, even if + * it occurs in multiple classes. + * @param superclass the class that will be extended, or null + * @param interfaces the list of interfaces that will be implemented, or null + * @param methods the list into which to copy the applicable methods + */ + public static void getMethods(Class superclass, Class[] interfaces, List methods) + { + getMethods(superclass, interfaces, methods, null, null); + } + + private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic) + { + ReflectUtils.addAllMethods(superclass, methods); + List target = (interfaceMethods != null) ? interfaceMethods : methods; + if (interfaces != null) { + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i] != Factory.class) { + ReflectUtils.addAllMethods(interfaces[i], target); + } + } + } + if (interfaceMethods != null) { + if (forcePublic != null) { + forcePublic.addAll(MethodWrapper.createSet(interfaceMethods)); + } + methods.addAll(interfaceMethods); + } + CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC)); + CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true)); + CollectionUtils.filter(methods, new DuplicatesPredicate()); + CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL)); + } + + public void generateClass(ClassVisitor v) throws Exception { + Class sc = (superclass == null) ? Object.class : superclass; + + if (TypeUtils.isFinal(sc.getModifiers())) + throw new IllegalArgumentException("Cannot subclass final class " + sc); + List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors())); + filterConstructors(sc, constructors); + + // Order is very important: must add superclass, then + // its superclass chain, then each interface and + // its superinterfaces. + List actualMethods = new ArrayList(); + List interfaceMethods = new ArrayList(); + final Set forcePublic = new HashSet(); + getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic); + + List methods = CollectionUtils.transform(actualMethods, new Transformer() { + public Object transform(Object value) { + Method method = (Method)value; + int modifiers = Constants.ACC_FINAL + | (method.getModifiers() + & ~Constants.ACC_ABSTRACT + & ~Constants.ACC_NATIVE + & ~Constants.ACC_SYNCHRONIZED); + if (forcePublic.contains(MethodWrapper.create(method))) { + modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC; + } + return ReflectUtils.getMethodInfo(method, modifiers); + } + }); + + ClassEmitter e = new ClassEmitter(v); + e.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + Type.getType(sc), + (useFactory ? + TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) : + TypeUtils.getTypes(interfaces)), + Constants.SOURCE_FILE); + List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance()); + + e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null); + if (!interceptDuringConstruction) { + e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null); + } + e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null); + e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null); + if (serialVersionUID != null) { + e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID); + } + + for (int i = 0; i < callbackTypes.length; i++) { + e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null); + } + + emitMethods(e, methods, actualMethods); + emitConstructors(e, constructorInfo); + emitSetThreadCallbacks(e); + emitSetStaticCallbacks(e); + emitBindCallbacks(e); + + if (useFactory) { + int[] keys = getCallbackKeys(); + emitNewInstanceCallbacks(e); + emitNewInstanceCallback(e); + emitNewInstanceMultiarg(e, constructorInfo); + emitGetCallback(e, keys); + emitSetCallback(e, keys); + emitGetCallbacks(e); + emitSetCallbacks(e); + } + + e.end_class(); + } + + /** + * Filter the list of constructors from the superclass. The + * constructors which remain will be included in the generated + * class. The default implementation is to filter out all private + * constructors, but subclasses may extend Enhancer to override this + * behavior. + * @param sc the superclass + * @param constructors the list of all declared constructors from the superclass + * @throws IllegalArgumentException if there are no non-private constructors + */ + protected void filterConstructors(Class sc, List constructors) { + CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true)); + if (constructors.size() == 0) + throw new IllegalArgumentException("No visible constructors in " + sc); + } + + protected Object firstInstance(Class type) throws Exception { + if (classOnly) { + return type; + } else { + return createUsingReflection(type); + } + } + + protected Object nextInstance(Object instance) { + Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass(); + if (classOnly) { + return protoclass; + } else if (instance instanceof Factory) { + if (argumentTypes != null) { + return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks); + } else { + return ((Factory)instance).newInstance(callbacks); + } + } else { + return createUsingReflection(protoclass); + } + } + + /** + * Call this method to register the {@link Callback} array to use before + * creating a new instance of the generated class via reflection. If you are using + * an instance of Enhancer or the {@link Factory} interface to create + * new instances, this method is unnecessary. Its primary use is for when you want to + * cache and reuse a generated class yourself, and the generated class does + * not implement the {@link Factory} interface. + *

+ * Note that this method only registers the callbacks on the current thread. + * If you want to register callbacks for instances created by multiple threads, + * use {@link #registerStaticCallbacks}. + *

+ * The registered callbacks are overwritten and subsequently cleared + * when calling any of the create methods (such as + * {@link #create}), or any {@link Factory} newInstance method. + * Otherwise they are not cleared, and you should be careful to set them + * back to null after creating new instances via reflection if + * memory leakage is a concern. + * @param generatedClass a class previously created by {@link Enhancer} + * @param callbacks the array of callbacks to use when instances of the generated + * class are created + * @see #setUseFactory + */ + public static void registerCallbacks(Class generatedClass, Callback[] callbacks) { + setThreadCallbacks(generatedClass, callbacks); + } + + /** + * Similar to {@link #registerCallbacks}, but suitable for use + * when multiple threads will be creating instances of the generated class. + * The thread-level callbacks will always override the static callbacks. + * Static callbacks are never cleared. + * @param generatedClass a class previously created by {@link Enhancer} + * @param callbacks the array of callbacks to use when instances of the generated + * class are created + */ + public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) { + setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME); + } + + /** + * Determine if a class was generated using Enhancer. + * @param type any class + * @return whether the class was generated using Enhancer + */ + public static boolean isEnhanced(Class type) { + try { + getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + private static void setThreadCallbacks(Class type, Callback[] callbacks) { + setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME); + } + + private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) { + try { + Method setter = getCallbacksSetter(type, methodName); + setter.invoke(null, new Object[]{ callbacks }); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException(type + " is not an enhanced class"); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e); + } + } + + private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException { + return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class }); + } + + private Object createUsingReflection(Class type) { + setThreadCallbacks(type, callbacks); + try{ + + if (argumentTypes != null) { + + return ReflectUtils.newInstance(type, argumentTypes, arguments); + + } else { + + return ReflectUtils.newInstance(type); + + } + }finally{ + // clear thread callbacks to allow them to be gc'd + setThreadCallbacks(type, null); + } + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param type class to extend or interface to implement + * @param callback the callback to use for all methods + */ + public static Object create(Class type, Callback callback) { + Enhancer e = new Enhancer(); + e.setSuperclass(type); + e.setCallback(callback); + return e.create(); + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param type class to extend or interface to implement + * @param interfaces array of interfaces to implement, or null + * @param callback the callback to use for all methods + */ + public static Object create(Class superclass, Class interfaces[], Callback callback) { + Enhancer e = new Enhancer(); + e.setSuperclass(superclass); + e.setInterfaces(interfaces); + e.setCallback(callback); + return e.create(); + } + + /** + * Helper method to create an intercepted object. + * For finer control over the generated instance, use a new instance of Enhancer + * instead of this static method. + * @param type class to extend or interface to implement + * @param interfaces array of interfaces to implement, or null + * @param filter the callback filter to use when generating a new class + * @param callbacks callback implementations to use for the enhanced object + */ + public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) { + Enhancer e = new Enhancer(); + e.setSuperclass(superclass); + e.setInterfaces(interfaces); + e.setCallbackFilter(filter); + e.setCallbacks(callbacks); + return e.create(); + } + + private void emitConstructors(ClassEmitter ce, List constructors) { + boolean seenNull = false; + for (Iterator it = constructors.iterator(); it.hasNext();) { + MethodInfo constructor = (MethodInfo)it.next(); + CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC); + e.load_this(); + e.dup(); + e.load_args(); + Signature sig = constructor.getSignature(); + seenNull = seenNull || sig.getDescriptor().equals("()V"); + e.super_invoke_constructor(sig); + e.invoke_static_this(BIND_CALLBACKS); + if (!interceptDuringConstruction) { + e.load_this(); + e.push(1); + e.putfield(CONSTRUCTED_FIELD); + } + e.return_value(); + e.end_method(); + } + if (!classOnly && !seenNull && arguments == null) + throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given"); + } + + private int[] getCallbackKeys() { + int[] keys = new int[callbackTypes.length]; + for (int i = 0; i < callbackTypes.length; i++) { + keys[i] = i; + } + return keys; + } + + private void emitGetCallback(ClassEmitter ce, int[] keys) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null); + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.load_arg(0); + e.process_switch(keys, new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + e.getfield(getCallbackField(key)); + e.goTo(end); + } + public void processDefault() { + e.pop(); // stack height + e.aconst_null(); + } + }); + e.return_value(); + e.end_method(); + } + + private void emitSetCallback(ClassEmitter ce, int[] keys) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null); + e.load_arg(0); + e.process_switch(keys, new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + e.load_this(); + e.load_arg(1); + e.checkcast(callbackTypes[key]); + e.putfield(getCallbackField(key)); + e.goTo(end); + } + public void processDefault() { + } + }); + e.return_value(); + e.end_method(); + } + + private void emitSetCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null); + e.load_this(); + e.load_arg(0); + for (int i = 0; i < callbackTypes.length; i++) { + e.dup2(); + e.aaload(i); + e.checkcast(callbackTypes[i]); + e.putfield(getCallbackField(i)); + } + e.return_value(); + e.end_method(); + } + + private void emitGetCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null); + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.push(callbackTypes.length); + e.newarray(CALLBACK); + for (int i = 0; i < callbackTypes.length; i++) { + e.dup(); + e.push(i); + e.load_this(); + e.getfield(getCallbackField(i)); + e.aastore(); + } + e.return_value(); + e.end_method(); + } + + private void emitNewInstanceCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + e.load_arg(0); + e.invoke_static_this(SET_THREAD_CALLBACKS); + emitCommonNewInstance(e); + } + + private void emitCommonNewInstance(CodeEmitter e) { + e.new_instance_this(); + e.dup(); + e.invoke_constructor_this(); + e.aconst_null(); + e.invoke_static_this(SET_THREAD_CALLBACKS); + e.return_value(); + e.end_method(); + } + + private void emitNewInstanceCallback(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null); + switch (callbackTypes.length) { + case 0: + break; + case 1: + e.push(1); + e.newarray(CALLBACK); + e.dup(); + e.push(0); + e.load_arg(0); + e.aastore(); + e.invoke_static_this(SET_THREAD_CALLBACKS); + break; + default: + e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required"); + } + emitCommonNewInstance(e); + } + + private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null); + e.load_arg(2); + e.invoke_static_this(SET_THREAD_CALLBACKS); + e.new_instance_this(); + e.dup(); + e.load_arg(0); + EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + MethodInfo constructor = (MethodInfo)key; + Type types[] = constructor.getSignature().getArgumentTypes(); + for (int i = 0; i < types.length; i++) { + e.load_arg(1); + e.push(i); + e.aaload(); + e.unbox(types[i]); + } + e.invoke_constructor_this(constructor.getSignature()); + e.goTo(end); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found"); + } + }); + e.aconst_null(); + e.invoke_static_this(SET_THREAD_CALLBACKS); + e.return_value(); + e.end_method(); + } + + @SuppressWarnings("unused") + private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) { + CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes); + + Map groups = new HashMap(); + final Map indexes = new HashMap(); + final Map originalModifiers = new HashMap(); + final Map positions = CollectionUtils.getIndexMap(methods); + final Map declToBridge = new HashMap(); + + Iterator it1 = methods.iterator(); + Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null; + + while (it1.hasNext()) { + MethodInfo method = (MethodInfo)it1.next(); + Method actualMethod = (it2 != null) ? (Method)it2.next() : null; + int index = filter.accept(actualMethod); + if (index >= callbackTypes.length) { + throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index); + } + originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers())); + indexes.put(method, new Integer(index)); + List group = (List)groups.get(generators[index]); + if (group == null) { + groups.put(generators[index], group = new ArrayList(methods.size())); + } + group.add(method); + + // Optimization: build up a map of Class -> bridge methods in class + // so that we can look up all the bridge methods in one pass for a class. + if (TypeUtils.isBridge(actualMethod.getModifiers())) { + Set bridges = (Set)declToBridge.get(actualMethod.getDeclaringClass()); + if (bridges == null) { + bridges = new HashSet(); + declToBridge.put(actualMethod.getDeclaringClass(), bridges); + } + bridges.add(method.getSignature()); + } + } + + final Map bridgeToTarget = new BridgeMethodResolver(declToBridge).resolveAll(); + + Set seenGen = new HashSet(); + CodeEmitter se = ce.getStaticHook(); + se.new_instance(THREAD_LOCAL); + se.dup(); + se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL); + se.putfield(THREAD_CALLBACKS_FIELD); + + final Object[] state = new Object[1]; + CallbackGenerator.Context context = new CallbackGenerator.Context() { + public ClassLoader getClassLoader() { + return Enhancer.this.getClassLoader(); + } + public int getOriginalModifiers(MethodInfo method) { + return ((Integer)originalModifiers.get(method)).intValue(); + } + public int getIndex(MethodInfo method) { + return ((Integer)indexes.get(method)).intValue(); + } + public void emitCallback(CodeEmitter e, int index) { + emitCurrentCallback(e, index); + } + public Signature getImplSignature(MethodInfo method) { + return rename(method.getSignature(), ((Integer)positions.get(method)).intValue()); + } + public void emitInvoke(CodeEmitter e, MethodInfo method) { + // If this is a bridge and we know the target was called from invokespecial, + // then we need to invoke_virtual w/ the bridge target instead of doing + // a super, because super may itself be using super, which would bypass + // any proxies on the target. + Signature bridgeTarget = (Signature)bridgeToTarget.get(method.getSignature()); + if (bridgeTarget != null) { + // parameters than the current. + // In reality this should always be true because otherwise we wouldn't + // have had a bridge doing an invokespecial. + // If it isn't true, we would need to checkcast each argument + // against the target's argument types + e.invoke_virtual_this(bridgeTarget); + + Type retType = method.getSignature().getReturnType(); + // Not necessary to cast if the target & bridge have + // the same return type. + // (This conveniently includes void and primitive types, + // which would fail if casted. It's not possible to + // covariant from boxed to unbox (or vice versa), so no having + // to box/unbox for bridges). + // assignable from the target. (This would happen if a subclass + // used covariant returns to narrow the return type within a bridge + // method.) + if (!retType.equals(bridgeTarget.getReturnType())) { + e.checkcast(retType); + } + } else { + e.super_invoke(method.getSignature()); + } + } + @SuppressWarnings("static-access") + public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) { + CodeEmitter e = EmitUtils.begin_method(ce, method); + if (!interceptDuringConstruction && + !TypeUtils.isAbstract(method.getModifiers())) { + Label constructed = e.make_label(); + e.load_this(); + e.getfield(CONSTRUCTED_FIELD); + e.if_jump(e.NE, constructed); + e.load_this(); + e.load_args(); + e.super_invoke(); + e.return_value(); + e.mark(constructed); + } + return e; + } + }; + for (int i = 0; i < callbackTypes.length; i++) { + CallbackGenerator gen = generators[i]; + if (!seenGen.contains(gen)) { + seenGen.add(gen); + final List fmethods = (List)groups.get(gen); + if (fmethods != null) { + try { + gen.generate(ce, context, fmethods); + gen.generateStatic(se, context, fmethods); + } catch (RuntimeException x) { + throw x; + } catch (Exception x) { + throw new CodeGenerationException(x); + } + } + } + } + se.return_value(); + se.end_method(); + } + + private void emitSetThreadCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + SET_THREAD_CALLBACKS, + null); + e.getfield(THREAD_CALLBACKS_FIELD); + e.load_arg(0); + e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET); + e.return_value(); + e.end_method(); + } + + private void emitSetStaticCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + SET_STATIC_CALLBACKS, + null); + e.load_arg(0); + e.putfield(STATIC_CALLBACKS_FIELD); + e.return_value(); + e.end_method(); + } + + private void emitCurrentCallback(CodeEmitter e, int index) { + e.load_this(); + e.getfield(getCallbackField(index)); + e.dup(); + Label end = e.make_label(); + e.ifnonnull(end); + e.pop(); // stack height + e.load_this(); + e.invoke_static_this(BIND_CALLBACKS); + e.load_this(); + e.getfield(getCallbackField(index)); + e.mark(end); + } + + @SuppressWarnings("static-access") + private void emitBindCallbacks(ClassEmitter ce) { + CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC, + BIND_CALLBACKS, + null); + Local me = e.make_local(); + e.load_arg(0); + e.checkcast_this(); + e.store_local(me); + + Label end = e.make_label(); + e.load_local(me); + e.getfield(BOUND_FIELD); + e.if_jump(e.NE, end); + e.load_local(me); + e.push(1); + e.putfield(BOUND_FIELD); + + e.getfield(THREAD_CALLBACKS_FIELD); + e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET); + e.dup(); + Label found_callback = e.make_label(); + e.ifnonnull(found_callback); + e.pop(); + + e.getfield(STATIC_CALLBACKS_FIELD); + e.dup(); + e.ifnonnull(found_callback); + e.pop(); + e.goTo(end); + + e.mark(found_callback); + e.checkcast(CALLBACK_ARRAY); + e.load_local(me); + e.swap(); + for (int i = callbackTypes.length - 1; i >= 0; i--) { + if (i != 0) { + e.dup2(); + } + e.aaload(i); + e.checkcast(callbackTypes[i]); + e.putfield(getCallbackField(i)); + } + + e.mark(end); + e.return_value(); + e.end_method(); + } + + private static String getCallbackField(int index) { + return "CGLIB$CALLBACK_" + index; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Factory.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Factory.java new file mode 100644 index 000000000..fe2e1b9e3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Factory.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * All enhanced instances returned by the {@link Enhancer} class implement this interface. + * Using this interface for new instances is faster than going through the Enhancer + * interface or using reflection. In addition, to intercept methods called during + * object construction you must use these methods instead of reflection. + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: Factory.java,v 1.13 2004/06/24 21:15:20 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes" }) +public interface Factory { + /** + * Creates new instance of the same type, using the no-arg constructor. + * The class of this object must have been created using a single Callback type. + * If multiple callbacks are required an exception will be thrown. + * @param callback the new interceptor to use + * @return new instance of the same type + */ + Object newInstance(Callback callback); + + /** + * Creates new instance of the same type, using the no-arg constructor. + * @param callbacks the new callbacks(s) to use + * @return new instance of the same type + */ + Object newInstance(Callback[] callbacks); + + /** + * Creates a new instance of the same type, using the constructor + * matching the given signature. + * @param types the constructor argument types + * @param args the constructor arguments + * @param callbacks the new interceptor(s) to use + * @return new instance of the same type + */ + Object newInstance(Class[] types, Object[] args, Callback[] callbacks); + + /** + * Return the Callback implementation at the specified index. + * @param index the callback index + * @return the callback implementation + */ + Callback getCallback(int index); + + /** + * Set the callback for this object for the given type. + * @param index the callback index to replace + * @param callback the new callback + */ + void setCallback(int index, Callback callback); + + /** + * Replace all of the callbacks for this object at once. + * @param callbacks the new callbacks(s) to use + */ + void setCallbacks(Callback[] callbacks); + + /** + * Get the current set of callbacks for ths object. + * @return a new array instance + */ + Callback[] getCallbacks(); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValue.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValue.java new file mode 100644 index 000000000..593d68034 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValue.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * {@link Enhancer} callback that simply returns the value to return + * from the proxied method. No information about what method + * is being called is available to the callback, and the type of + * the returned object must be compatible with the return type of + * the proxied method. This makes this callback primarily useful + * for forcing a particular method (through the use of a {@link CallbackFilter} + * to return a fixed value with little overhead. + */ +public interface FixedValue extends Callback { + /** + * Return the object which the original method invocation should + * return. This method is called for every method invocation. + * @return an object matching the type of the return value for every + * method this callback is mapped to + */ + Object loadObject() throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValueGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValueGenerator.java new file mode 100644 index 000000000..71250b9a4 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/FixedValueGenerator.java @@ -0,0 +1,44 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.*; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +@SuppressWarnings({ "rawtypes" }) +class FixedValueGenerator implements CallbackGenerator { + public static final FixedValueGenerator INSTANCE = new FixedValueGenerator(); + private static final Type FIXED_VALUE = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.FixedValue"); + //TypeUtils.parseType("net.sf.cglib.proxy.FixedValue"); + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + CodeEmitter e = context.beginMethod(ce, method); + context.emitCallback(e, context.getIndex(method)); + e.invoke_interface(FIXED_VALUE, LOAD_OBJECT); + e.unbox_or_zero(e.getReturnType()); + e.return_value(); + e.end_method(); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/InterfaceMaker.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InterfaceMaker.java new file mode 100644 index 000000000..a558dbfbc --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InterfaceMaker.java @@ -0,0 +1,121 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +/** + * Generates new interfaces at runtime. + * By passing a generated interface to the Enhancer's list of interfaces to + * implement, you can make your enhanced classes handle an arbitrary set + * of method signatures. + * @author Chris Nokleberg + * @version $Id: InterfaceMaker.java,v 1.4 2006/03/05 02:43:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class InterfaceMaker extends AbstractClassGenerator +{ + private static final Source SOURCE = new Source(InterfaceMaker.class.getName()); + private Map signatures = new HashMap(); + + /** + * Create a new InterfaceMaker. A new InterfaceMaker + * object should be used for each generated interface, and should not + * be shared across threads. + */ + public InterfaceMaker() { + super(SOURCE); + } + + /** + * Add a method signature to the interface. + * @param sig the method signature to add to the interface + * @param exceptions an array of exception types to declare for the method + */ + public void add(Signature sig, Type[] exceptions) { + signatures.put(sig, exceptions); + } + + /** + * Add a method signature to the interface. The method modifiers are ignored, + * since interface methods are by definition abstract and public. + * @param method the method to add to the interface + */ + public void add(Method method) { + add(ReflectUtils.getSignature(method), + ReflectUtils.getExceptionTypes(method)); + } + + /** + * Add all the public methods in the specified class. + * Methods from superclasses are included, except for methods declared in the base + * Object class (e.g. getClass, equals, hashCode). + * @param class the class containing the methods to add to the interface + */ + public void add(Class clazz) { + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (!m.getDeclaringClass().getName().equals("java.lang.Object")) { + add(m); + } + } + } + + /** + * Create an interface using the current set of method signatures. + */ + public Class create() { + setUseCache(false); + return (Class)super.create(this); + } + + protected ClassLoader getDefaultClassLoader() { + return null; + } + + protected Object firstInstance(Class type) { + return type; + } + + protected Object nextInstance(Object instance) { + throw new IllegalStateException("InterfaceMaker does not cache"); + } + + public void generateClass(ClassVisitor v) throws Exception { + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC | Constants.ACC_INTERFACE, + getClassName(), + null, + null, + Constants.SOURCE_FILE); + for (Iterator it = signatures.keySet().iterator(); it.hasNext();) { + Signature sig = (Signature)it.next(); + Type[] exceptions = (Type[])signatures.get(sig); + ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT, + sig, + exceptions).end_method(); + } + ce.end_class(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandler.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandler.java new file mode 100644 index 000000000..92445346f --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; + +/** + * {@link java.lang.reflect.InvocationHandler} replacement (unavailable under JDK 1.2). + * This callback type is primarily for use by the {@link Proxy} class but + * may be used with {@link Enhancer} as well. + * @author Neeme Praks neeme@apache.org + * @version $Id: InvocationHandler.java,v 1.3 2004/06/24 21:15:20 herbyderby Exp $ + */ +public interface InvocationHandler +extends Callback +{ + /** + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; + +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandlerGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandlerGenerator.java new file mode 100644 index 000000000..c996e53c4 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/InvocationHandlerGenerator.java @@ -0,0 +1,66 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import java.util.*; +@SuppressWarnings({ "rawtypes"}) +class InvocationHandlerGenerator +implements CallbackGenerator +{ + public static final InvocationHandlerGenerator INSTANCE = new InvocationHandlerGenerator(); + + private static final Type INVOCATION_HANDLER = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.InvocationHandler"); + //TypeUtils.parseType("net.sf.cglib.proxy.InvocationHandler"); + private static final Type UNDECLARED_THROWABLE_EXCEPTION = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.UndeclaredThrowableException"); + //TypeUtils.parseType("net.sf.cglib.proxy.UndeclaredThrowableException"); + private static final Type METHOD = + TypeUtils.parseType("java.lang.reflect.Method"); + private static final Signature INVOKE = + TypeUtils.parseSignature("Object invoke(Object, java.lang.reflect.Method, Object[])"); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + Signature impl = context.getImplSignature(method); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, impl.getName(), METHOD, null); + + CodeEmitter e = context.beginMethod(ce, method); + Block handler = e.begin_block(); + context.emitCallback(e, context.getIndex(method)); + e.load_this(); + e.getfield(impl.getName()); + e.create_arg_array(); + e.invoke_interface(INVOCATION_HANDLER, INVOKE); + e.unbox(method.getSignature().getReturnType()); + e.return_value(); + handler.end(); + EmitUtils.wrap_undeclared_throwable(e, handler, method.getExceptionTypes(), UNDECLARED_THROWABLE_EXCEPTION); + e.end_method(); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + EmitUtils.load_method(e, method); + e.putfield(context.getImplSignature(method).getName()); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoader.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoader.java new file mode 100644 index 000000000..07257006c --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoader.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * Lazy-loading {@link Enhancer} callback. + */ +public interface LazyLoader extends Callback { + /** + * Return the object which the original method invocation should be + * dispatched. Called as soon as the first lazily-loaded method in + * the enhanced instance is invoked. The same object is then used + * for every future method call to the proxy instance. + * @return an object that can invoke the method + */ + Object loadObject() throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoaderGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoaderGenerator.java new file mode 100644 index 000000000..278526f5b --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/LazyLoaderGenerator.java @@ -0,0 +1,91 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.*; + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +class LazyLoaderGenerator implements CallbackGenerator { + public static final LazyLoaderGenerator INSTANCE = new LazyLoaderGenerator(); + + private static final Signature LOAD_OBJECT = + TypeUtils.parseSignature("Object loadObject()"); + private static final Type LAZY_LOADER = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.LazyLoader"); + //TypeUtils.parseType("net.sf.cglib.proxy.LazyLoader"); + + public void generate(ClassEmitter ce, Context context, List methods) { + Set indexes = new HashSet(); + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (TypeUtils.isProtected(method.getModifiers())) { + // ignore protected methods + } else { + int index = context.getIndex(method); + indexes.add(new Integer(index)); + CodeEmitter e = context.beginMethod(ce, method); + e.load_this(); + e.dup(); + e.invoke_virtual_this(loadMethod(index)); + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + + for (Iterator it = indexes.iterator(); it.hasNext();) { + int index = ((Integer)it.next()).intValue(); + + String delegate = "CGLIB$LAZY_LOADER_" + index; + ce.declare_field(Constants.ACC_PRIVATE, delegate, Constants.TYPE_OBJECT, null); + + CodeEmitter e = ce.begin_method(Constants.ACC_PRIVATE | + Constants.ACC_SYNCHRONIZED | + Constants.ACC_FINAL, + loadMethod(index), + null); + e.load_this(); + e.getfield(delegate); + e.dup(); + Label end = e.make_label(); + e.ifnonnull(end); + e.pop(); + e.load_this(); + context.emitCallback(e, index); + e.invoke_interface(LAZY_LOADER, LOAD_OBJECT); + e.dup_x1(); + e.putfield(delegate); + e.mark(end); + e.return_value(); + e.end_method(); + + } + } + + private Signature loadMethod(int index) { + return new Signature("CGLIB$LOAD_PRIVATE_" + index, + Constants.TYPE_OBJECT, + Constants.TYPES_EMPTY); + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptor.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptor.java new file mode 100644 index 000000000..9bb0e39bc --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * General-purpose {@link Enhancer} callback which provides for "around advice". + * @author Juozas Baliuka baliuka@mwm.lt + * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $ + */ +public interface MethodInterceptor +extends Callback +{ + /** + * All generated proxied methods call this method instead of the original method. + * The original method may either be invoked by normal reflection using the Method object, + * or by using the MethodProxy (faster). + * @param obj "this", the enhanced object + * @param method intercepted Method + * @param args argument array; primitive types are wrapped + * @param proxy used to invoke super (non-intercepted method); may be called + * as many times as needed + * @throws Throwable any exception may be thrown; if so, super method will not be invoked + * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value. + * @see MethodProxy + */ + public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, + MethodProxy proxy) throws Throwable; + +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptorGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptorGenerator.java new file mode 100644 index 000000000..78a55f04d --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodInterceptorGenerator.java @@ -0,0 +1,245 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.*; + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +class MethodInterceptorGenerator +implements CallbackGenerator +{ + public static final MethodInterceptorGenerator INSTANCE = new MethodInterceptorGenerator(); + + static final String EMPTY_ARGS_NAME = "CGLIB$emptyArgs"; + static final String FIND_PROXY_NAME = "CGLIB$findMethodProxy"; + static final Class[] FIND_PROXY_TYPES = { Signature.class }; + + private static final Type ABSTRACT_METHOD_ERROR = + TypeUtils.parseType("AbstractMethodError"); + private static final Type METHOD = + TypeUtils.parseType("java.lang.reflect.Method"); + private static final Type REFLECT_UTILS = + TypeUtils.parseType("com.fr.third.springframework.cglib.core.ReflectUtils"); + //TypeUtils.parseType("net.sf.cglib.core.ReflectUtils"); + private static final Type METHOD_PROXY = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.MethodProxy"); + //TypeUtils.parseType("net.sf.cglib.proxy.MethodProxy"); + private static final Type METHOD_INTERCEPTOR = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.MethodInterceptor"); + //TypeUtils.parseType("net.sf.cglib.proxy.MethodInterceptor"); + private static final Signature GET_DECLARED_METHODS = + TypeUtils.parseSignature("java.lang.reflect.Method[] getDeclaredMethods()"); + @SuppressWarnings("unused") + private static final Signature GET_DECLARING_CLASS = + TypeUtils.parseSignature("Class getDeclaringClass()"); + private static final Signature FIND_METHODS = + TypeUtils.parseSignature("java.lang.reflect.Method[] findMethods(String[], java.lang.reflect.Method[])"); + private static final Signature MAKE_PROXY = + new Signature("create", METHOD_PROXY, new Type[]{ + Constants.TYPE_CLASS, + Constants.TYPE_CLASS, + Constants.TYPE_STRING, + Constants.TYPE_STRING, + Constants.TYPE_STRING + }); + private static final Signature INTERCEPT = + new Signature("intercept", Constants.TYPE_OBJECT, new Type[]{ + Constants.TYPE_OBJECT, + METHOD, + Constants.TYPE_OBJECT_ARRAY, + METHOD_PROXY + }); + private static final Signature FIND_PROXY = + new Signature(FIND_PROXY_NAME, METHOD_PROXY, new Type[]{ Constants.TYPE_SIGNATURE }); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Transformer METHOD_TO_CLASS = new Transformer(){ + public Object transform(Object value) { + return ((MethodInfo)value).getClassInfo(); + } + }; + @SuppressWarnings("unused") + private static final Signature CSTRUCT_SIGNATURE = + TypeUtils.parseConstructor("String, String"); + + private String getMethodField(Signature impl) { + return impl.getName() + "$Method"; + } + private String getMethodProxyField(Signature impl) { + return impl.getName() + "$Proxy"; + } + + public void generate(ClassEmitter ce, Context context, List methods) { + Map sigMap = new HashMap(); + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + Signature sig = method.getSignature(); + Signature impl = context.getImplSignature(method); + + String methodField = getMethodField(impl); + String methodProxyField = getMethodProxyField(impl); + + sigMap.put(sig.toString(), methodProxyField); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodField, METHOD, null); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, methodProxyField, METHOD_PROXY, null); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, EMPTY_ARGS_NAME, Constants.TYPE_OBJECT_ARRAY, null); + CodeEmitter e; + + // access method + e = ce.begin_method(Constants.ACC_FINAL, + impl, + method.getExceptionTypes()); + superHelper(e, method, context); + e.return_value(); + e.end_method(); + + // around method + e = context.beginMethod(ce, method); + Label nullInterceptor = e.make_label(); + context.emitCallback(e, context.getIndex(method)); + e.dup(); + e.ifnull(nullInterceptor); + + e.load_this(); + e.getfield(methodField); + + if (sig.getArgumentTypes().length == 0) { + e.getfield(EMPTY_ARGS_NAME); + } else { + e.create_arg_array(); + } + + e.getfield(methodProxyField); + e.invoke_interface(METHOD_INTERCEPTOR, INTERCEPT); + e.unbox_or_zero(sig.getReturnType()); + e.return_value(); + + e.mark(nullInterceptor); + superHelper(e, method, context); + e.return_value(); + e.end_method(); + } + generateFindProxy(ce, sigMap); + } + + private static void superHelper(CodeEmitter e, MethodInfo method, Context context) + { + if (TypeUtils.isAbstract(method.getModifiers())) { + e.throw_exception(ABSTRACT_METHOD_ERROR, method.toString() + " is abstract" ); + } else { + e.load_this(); + e.load_args(); + context.emitInvoke(e, method); + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) throws Exception { + /* generates: + static { + Class thisClass = Class.forName("NameOfThisClass"); + Class cls = Class.forName("java.lang.Object"); + String[] sigs = new String[]{ "toString", "()Ljava/lang/String;", ... }; + Method[] methods = cls.getDeclaredMethods(); + methods = ReflectUtils.findMethods(sigs, methods); + METHOD_0 = methods[0]; + CGLIB$ACCESS_0 = MethodProxy.create(cls, thisClass, "()Ljava/lang/String;", "toString", "CGLIB$ACCESS_0"); + ... + } + */ + + e.push(0); + e.newarray(); + e.putfield(EMPTY_ARGS_NAME); + + Local thisclass = e.make_local(); + Local declaringclass = e.make_local(); + EmitUtils.load_class_this(e); + e.store_local(thisclass); + + Map methodsByClass = CollectionUtils.bucket(methods, METHOD_TO_CLASS); + for (Iterator i = methodsByClass.keySet().iterator(); i.hasNext();) { + ClassInfo classInfo = (ClassInfo)i.next(); + + List classMethods = (List)methodsByClass.get(classInfo); + e.push(2 * classMethods.size()); + e.newarray(Constants.TYPE_STRING); + for (int index = 0; index < classMethods.size(); index++) { + MethodInfo method = (MethodInfo)classMethods.get(index); + Signature sig = method.getSignature(); + e.dup(); + e.push(2 * index); + e.push(sig.getName()); + e.aastore(); + e.dup(); + e.push(2 * index + 1); + e.push(sig.getDescriptor()); + e.aastore(); + } + + EmitUtils.load_class(e, classInfo.getType()); + e.dup(); + e.store_local(declaringclass); + e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHODS); + e.invoke_static(REFLECT_UTILS, FIND_METHODS); + + for (int index = 0; index < classMethods.size(); index++) { + MethodInfo method = (MethodInfo)classMethods.get(index); + Signature sig = method.getSignature(); + Signature impl = context.getImplSignature(method); + e.dup(); + e.push(index); + e.array_load(METHOD); + e.putfield(getMethodField(impl)); + + e.load_local(declaringclass); + e.load_local(thisclass); + e.push(sig.getDescriptor()); + e.push(sig.getName()); + e.push(impl.getName()); + e.invoke_static(METHOD_PROXY, MAKE_PROXY); + e.putfield(getMethodProxyField(impl)); + } + e.pop(); + } + } + + public void generateFindProxy(ClassEmitter ce, final Map sigMap) { + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC, + FIND_PROXY, + null); + e.load_arg(0); + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + ObjectSwitchCallback callback = new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + e.getfield((String)sigMap.get(key)); + e.return_value(); + } + public void processDefault() { + e.aconst_null(); + e.return_value(); + } + }; + EmitUtils.string_switch(e, + (String[])sigMap.keySet().toArray(new String[0]), + Constants.SWITCH_STYLE_HASH, + callback); + e.end_method(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodProxy.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodProxy.java new file mode 100644 index 000000000..2ae272d69 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MethodProxy.java @@ -0,0 +1,230 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.reflect.FastClass; + +/** + * Classes generated by {@link Enhancer} pass this object to the + * registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can + * be used to either invoke the original method, or call the same method on a different + * object of the same type. + * @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class MethodProxy { + private Signature sig1; + private Signature sig2; + private CreateInfo createInfo; + + private final Object initLock = new Object(); + private volatile FastClassInfo fastClassInfo; + + /** + * For internal use by {@link Enhancer} only; see the {@link net.sf.cglib.reflect.FastMethod} class + * for similar functionality. + */ + public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) { + MethodProxy proxy = new MethodProxy(); + proxy.sig1 = new Signature(name1, desc); + proxy.sig2 = new Signature(name2, desc); + proxy.createInfo = new CreateInfo(c1, c2); + return proxy; + } + + private void init() + { + /* + * Using a volatile invariant allows us to initialize the FastClass and + * method index pairs atomically. + * + * Double-checked locking is safe with volatile in Java 5. Before 1.5 this + * code could allow fastClassInfo to be instantiated more than once, which + * appears to be benign. + */ + if (fastClassInfo == null) + { + synchronized (initLock) + { + if (fastClassInfo == null) + { + CreateInfo ci = createInfo; + + FastClassInfo fci = new FastClassInfo(); + fci.f1 = helper(ci, ci.c1); + fci.f2 = helper(ci, ci.c2); + fci.i1 = fci.f1.getIndex(sig1); + fci.i2 = fci.f2.getIndex(sig2); + fastClassInfo = fci; + createInfo = null; + } + } + } + } + + private static class FastClassInfo + { + FastClass f1; + FastClass f2; + int i1; + int i2; + } + + private static class CreateInfo + { + Class c1; + Class c2; + NamingPolicy namingPolicy; + GeneratorStrategy strategy; + boolean attemptLoad; + + public CreateInfo(Class c1, Class c2) + { + this.c1 = c1; + this.c2 = c2; + AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent(); + if (fromEnhancer != null) { + namingPolicy = fromEnhancer.getNamingPolicy(); + strategy = fromEnhancer.getStrategy(); + attemptLoad = fromEnhancer.getAttemptLoad(); + } + } + } + + private static FastClass helper(CreateInfo ci, Class type) { + FastClass.Generator g = new FastClass.Generator(); + g.setType(type); + g.setClassLoader(ci.c2.getClassLoader()); + g.setNamingPolicy(ci.namingPolicy); + g.setStrategy(ci.strategy); + g.setAttemptLoad(ci.attemptLoad); + return g.create(); + } + + private MethodProxy() { + } + + /** + * Return the signature of the proxied method. + */ + public Signature getSignature() { + return sig1; + } + + /** + * Return the name of the synthetic method created by CGLIB which is + * used by {@link #invokeSuper} to invoke the superclass + * (non-intercepted) method implementation. The parameter types are + * the same as the proxied method. + */ + public String getSuperName() { + return sig2.getName(); + } + + /** + * Return the {@link net.sf.cglib.reflect.FastClass} method index + * for the method used by {@link #invokeSuper}. This index uniquely + * identifies the method within the generated proxy, and therefore + * can be useful to reference external metadata. + * @see #getSuperName + */ + public int getSuperIndex() { + init(); + return fastClassInfo.i2; + } + + // For testing + FastClass getFastClass() { + init(); + return fastClassInfo.f1; + } + + // For testing + FastClass getSuperFastClass() { + init(); + return fastClassInfo.f2; + } + + /** + * Return the MethodProxy used when intercepting the method + * matching the given signature. + * @param type the class generated by Enhancer + * @param sig the signature to match + * @return the MethodProxy instance, or null if no applicable matching method is found + * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor + */ + public static MethodProxy find(Class type, Signature sig) { + try { + Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME, + MethodInterceptorGenerator.FIND_PROXY_TYPES); + return (MethodProxy)m.invoke(null, new Object[]{ sig }); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor"); + } catch (IllegalAccessException e) { + throw new CodeGenerationException(e); + } catch (InvocationTargetException e) { + throw new CodeGenerationException(e); + } + } + + /** + * Invoke the original method, on a different object of the same type. + * @param obj the compatible object; recursion will result if you use the object passed as the first + * argument to the MethodInterceptor (usually not what you want) + * @param args the arguments passed to the intercepted method; you may substitute a different + * argument array as long as the types are compatible + * @see MethodInterceptor#intercept + * @throws Throwable the bare exceptions thrown by the called method are passed through + * without wrapping in an InvocationTargetException + */ + public Object invoke(Object obj, Object[] args) throws Throwable { + try { + init(); + FastClassInfo fci = fastClassInfo; + return fci.f1.invoke(fci.i1, obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (IllegalArgumentException e) { + if (fastClassInfo.i1 < 0) + throw new IllegalArgumentException("Protected method: " + sig1); + throw e; + } + } + + /** + * Invoke the original (super) method on the specified object. + * @param obj the enhanced object, must be the object passed as the first + * argument to the MethodInterceptor + * @param args the arguments passed to the intercepted method; you may substitute a different + * argument array as long as the types are compatible + * @see MethodInterceptor#intercept + * @throws Throwable the bare exceptions thrown by the called method are passed through + * without wrapping in an InvocationTargetException + */ + public Object invokeSuper(Object obj, Object[] args) throws Throwable { + try { + init(); + FastClassInfo fci = fastClassInfo; + return fci.f2.invoke(fci.i2, obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Mixin.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Mixin.java new file mode 100644 index 000000000..cf369b661 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Mixin.java @@ -0,0 +1,237 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + + + +/** + * Mixin allows + * multiple objects to be combined into a single larger object. The + * methods in the generated object simply call the original methods in the + * underlying "delegate" objects. + * @author Chris Nokleberg + * @version $Id: Mixin.java,v 1.7 2005/09/27 11:42:27 baliuka Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class Mixin { + private static final MixinKey KEY_FACTORY = + (MixinKey)KeyFactory.create(MixinKey.class, KeyFactory.CLASS_BY_NAME); + private static final Map ROUTE_CACHE = Collections.synchronizedMap(new HashMap()); + + public static final int STYLE_INTERFACES = 0; + public static final int STYLE_BEANS = 1; + public static final int STYLE_EVERYTHING = 2; + + interface MixinKey { + public Object newInstance(int style, String[] classes, int[] route); + } + + abstract public Mixin newInstance(Object[] delegates); + + /** + * Helper method to create an interface mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * + */ + public static Mixin create(Object[] delegates) { + Generator gen = new Generator(); + gen.setDelegates(delegates); + return gen.create(); + } + + /** + * Helper method to create an interface mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * + */ + public static Mixin create(Class[] interfaces, Object[] delegates) { + Generator gen = new Generator(); + gen.setClasses(interfaces); + gen.setDelegates(delegates); + return gen.create(); + } + + + public static Mixin createBean(Object[] beans) { + + return createBean(null, beans); + + } + /** + * Helper method to create a bean mixin. For finer control over the + * generated instance, use a new instance of Mixin + * instead of this static method. + * + */ + public static Mixin createBean(ClassLoader loader,Object[] beans) { + Generator gen = new Generator(); + gen.setStyle(STYLE_BEANS); + gen.setDelegates(beans); + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(Mixin.class.getName()); + + private Class[] classes; + private Object[] delegates; + private int style = STYLE_INTERFACES; + + private int[] route; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return classes[0].getClassLoader(); // is this right? + } + + public void setStyle(int style) { + switch (style) { + case STYLE_INTERFACES: + case STYLE_BEANS: + case STYLE_EVERYTHING: + this.style = style; + break; + default: + throw new IllegalArgumentException("Unknown mixin style: " + style); + } + } + + public void setClasses(Class[] classes) { + this.classes = classes; + } + + public void setDelegates(Object[] delegates) { + this.delegates = delegates; + } + + public Mixin create() { + if (classes == null && delegates == null) { + throw new IllegalStateException("Either classes or delegates must be set"); + } + switch (style) { + case STYLE_INTERFACES: + if (classes == null) { + Route r = route(delegates); + classes = r.classes; + route = r.route; + } + break; + case STYLE_BEANS: + // fall-through + case STYLE_EVERYTHING: + if (classes == null) { + classes = ReflectUtils.getClasses(delegates); + } else { + if (delegates != null) { + Class[] temp = ReflectUtils.getClasses(delegates); + if (classes.length != temp.length) { + throw new IllegalStateException("Specified classes are incompatible with delegates"); + } + for (int i = 0; i < classes.length; i++) { + if (!classes[i].isAssignableFrom(temp[i])) { + throw new IllegalStateException("Specified class " + classes[i] + " is incompatible with delegate class " + temp[i] + " (index " + i + ")"); + } + } + } + } + } + setNamePrefix(classes[ReflectUtils.findPackageProtected(classes)].getName()); + + return (Mixin)super.create(KEY_FACTORY.newInstance(style, ReflectUtils.getNames( classes ), route)); + } + + public void generateClass(ClassVisitor v) { + switch (style) { + case STYLE_INTERFACES: + new MixinEmitter(v, getClassName(), classes, route); + break; + case STYLE_BEANS: + new MixinBeanEmitter(v, getClassName(), classes); + break; + case STYLE_EVERYTHING: + new MixinEverythingEmitter(v, getClassName(), classes); + break; + } + } + + protected Object firstInstance(Class type) { + return ((Mixin)ReflectUtils.newInstance(type)).newInstance(delegates); + } + + protected Object nextInstance(Object instance) { + return ((Mixin)instance).newInstance(delegates); + } + } + + public static Class[] getClasses(Object[] delegates) { + return (Class[])route(delegates).classes.clone(); + } + +// public static int[] getRoute(Object[] delegates) { +// return (int[])route(delegates).route.clone(); +// } + + private static Route route(Object[] delegates) { + Object key = ClassesKey.create(delegates); + Route route = (Route)ROUTE_CACHE.get(key); + if (route == null) { + ROUTE_CACHE.put(key, route = new Route(delegates)); + } + return route; + } + + private static class Route + { + private Class[] classes; + private int[] route; + + Route(Object[] delegates) { + Map map = new HashMap(); + ArrayList collect = new ArrayList(); + for (int i = 0; i < delegates.length; i++) { + Class delegate = delegates[i].getClass(); + collect.clear(); + ReflectUtils.addAllInterfaces(delegate, collect); + for (Iterator it = collect.iterator(); it.hasNext();) { + Class iface = (Class)it.next(); + if (!map.containsKey(iface)) { + map.put(iface, new Integer(i)); + } + } + } + classes = new Class[map.size()]; + route = new int[map.size()]; + int index = 0; + for (Iterator it = map.keySet().iterator(); it.hasNext();) { + Class key = (Class)it.next(); + classes[index] = key; + route[index] = ((Integer)map.get(key)).intValue(); + index++; + } + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinBeanEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinBeanEmitter.java new file mode 100644 index 000000000..2290cfb24 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinBeanEmitter.java @@ -0,0 +1,40 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +/** + * @author Chris Nokleberg + * @version $Id: MixinBeanEmitter.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes" }) +class MixinBeanEmitter extends MixinEmitter { + public MixinBeanEmitter(ClassVisitor v, String className, Class[] classes) { + super(v, className, classes, null); + } + + protected Class[] getInterfaces(Class[] classes) { + return null; + } + + protected Method[] getMethods(Class type) { + return ReflectUtils.getPropertyMethods(ReflectUtils.getBeanProperties(type), true, true); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEmitter.java new file mode 100644 index 000000000..6a70ef687 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEmitter.java @@ -0,0 +1,92 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +/** + * @author Chris Nokleberg + * @version $Id: MixinEmitter.java,v 1.9 2006/08/27 21:04:37 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +class MixinEmitter extends ClassEmitter { + private static final String FIELD_NAME = "CGLIB$DELEGATES"; + private static final Signature CSTRUCT_OBJECT_ARRAY = + TypeUtils.parseConstructor("Object[]"); + private static final Type MIXIN = + TypeUtils.parseType("com.fr.third.springframework.cglib.proxy.Mixin"); + //TypeUtils.parseType("net.sf.cglib.proxy.Mixin"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", MIXIN, new Type[]{ Constants.TYPE_OBJECT_ARRAY }); + + public MixinEmitter(ClassVisitor v, String className, Class[] classes, int[] route) { + super(v); + + begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + className, + MIXIN, + TypeUtils.getTypes(getInterfaces(classes)), + Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + + declare_field(Constants.ACC_PRIVATE, FIELD_NAME, Constants.TYPE_OBJECT_ARRAY, null); + + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.putfield(FIELD_NAME); + e.return_value(); + e.end_method(); + + Set unique = new HashSet(); + for (int i = 0; i < classes.length; i++) { + Method[] methods = getMethods(classes[i]); + for (int j = 0; j < methods.length; j++) { + if (unique.add(MethodWrapper.create(methods[j]))) { + MethodInfo method = ReflectUtils.getMethodInfo(methods[j]); + e = EmitUtils.begin_method(this, method, Constants.ACC_PUBLIC); + e.load_this(); + e.getfield(FIELD_NAME); + e.aaload((route != null) ? route[i] : i); + e.checkcast(method.getClassInfo().getType()); + e.load_args(); + e.invoke(method); + e.return_value(); + e.end_method(); + } + } + } + + end_class(); + } + + protected Class[] getInterfaces(Class[] classes) { + return classes; + } + + protected Method[] getMethods(Class type) { + return type.getMethods(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEverythingEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEverythingEmitter.java new file mode 100644 index 000000000..8b52bf444 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/MixinEverythingEmitter.java @@ -0,0 +1,49 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +/** + * @author Chris Nokleberg + * @version $Id: MixinEverythingEmitter.java,v 1.3 2004/06/24 21:15:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +class MixinEverythingEmitter extends MixinEmitter { + + public MixinEverythingEmitter(ClassVisitor v, String className, Class[] classes) { + super(v, className, classes, null); + } + + protected Class[] getInterfaces(Class[] classes) { + List list = new ArrayList(); + for (int i = 0; i < classes.length; i++) { + ReflectUtils.addAllInterfaces(classes[i], list); + } + return (Class[])list.toArray(new Class[list.size()]); + } + + protected Method[] getMethods(Class type) { + List methods = new ArrayList(Arrays.asList(type.getMethods())); + CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.FINAL | Modifier.STATIC)); + return (Method[])methods.toArray(new Method[methods.size()]); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOp.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOp.java new file mode 100644 index 000000000..8623fe3a7 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOp.java @@ -0,0 +1,28 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * Methods using this {@link Enhancer} callback will delegate directly to the + * default (super) implementation in the base class. + */ +public interface NoOp extends Callback +{ + /** + * A thread-safe singleton instance of the NoOp callback. + */ + public static final NoOp INSTANCE = new NoOp() { }; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOpGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOpGenerator.java new file mode 100644 index 000000000..aedff45b7 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/NoOpGenerator.java @@ -0,0 +1,45 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.util.Iterator; +import java.util.List; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes" }) +class NoOpGenerator +implements CallbackGenerator +{ + public static final NoOpGenerator INSTANCE = new NoOpGenerator(); + + public void generate(ClassEmitter ce, Context context, List methods) { + for (Iterator it = methods.iterator(); it.hasNext();) { + MethodInfo method = (MethodInfo)it.next(); + if (TypeUtils.isBridge(method.getModifiers()) || ( + TypeUtils.isProtected(context.getOriginalModifiers(method)) && + TypeUtils.isPublic(method.getModifiers()))) { + CodeEmitter e = EmitUtils.begin_method(ce, method); + e.load_this(); + e.load_args(); + context.emitInvoke(e, method); + e.return_value(); + e.end_method(); + } + } + } + + public void generateStatic(CodeEmitter e, Context context, List methods) { } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/Proxy.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Proxy.java new file mode 100644 index 000000000..90bda197e --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/Proxy.java @@ -0,0 +1,104 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import java.io.Serializable; +import java.lang.reflect.Method; +import com.fr.third.springframework.cglib.core.*; + +/** + * This class is meant to be used as replacement for + * java.lang.reflect.Proxy under JDK 1.2. There are some known + * subtle differences: + *

+ *

+ * @version $Id: Proxy.java,v 1.6 2004/06/24 21:15:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class Proxy implements Serializable { + private static final long serialVersionUID = -4924795121305349506L; + + protected InvocationHandler h; + + private static final CallbackFilter BAD_OBJECT_METHOD_FILTER = new CallbackFilter() { + public int accept(Method method) { + if (method.getDeclaringClass().getName().equals("java.lang.Object")) { + String name = method.getName(); + if (!(name.equals("hashCode") || + name.equals("equals") || + name.equals("toString"))) { + return 1; + } + } + return 0; + } + }; + + protected Proxy(InvocationHandler h) { + Enhancer.registerCallbacks(getClass(), new Callback[]{ h, null }); + this.h = h; + } + + // private for security of isProxyClass + @SuppressWarnings("serial") + private static class ProxyImpl extends Proxy { + protected ProxyImpl(InvocationHandler h) { + super(h); + } + } + + public static InvocationHandler getInvocationHandler(Object proxy) { + if (!(proxy instanceof ProxyImpl)) { + throw new IllegalArgumentException("Object is not a proxy"); + } + return ((Proxy)proxy).h; + } + + public static Class getProxyClass(ClassLoader loader, Class[] interfaces) { + Enhancer e = new Enhancer(); + e.setSuperclass(ProxyImpl.class); + e.setInterfaces(interfaces); + e.setCallbackTypes(new Class[]{ + InvocationHandler.class, + NoOp.class, + }); + e.setCallbackFilter(BAD_OBJECT_METHOD_FILTER); + e.setUseFactory(false); + return e.createClass(); + } + + public static boolean isProxyClass(Class cl) { + return cl.getSuperclass().equals(ProxyImpl.class); + } + + public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) { + try { + Class clazz = getProxyClass(loader, interfaces); + return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h }); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/ProxyRefDispatcher.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/ProxyRefDispatcher.java new file mode 100644 index 000000000..2ca44d274 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/ProxyRefDispatcher.java @@ -0,0 +1,31 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +/** + * Dispatching {@link Enhancer} callback. This is the same as the + * {@link Dispatcher} except for the addition of an argument + * which references the proxy object. + */ +public interface ProxyRefDispatcher extends Callback { + /** + * Return the object which the original method invocation should + * be dispatched. This method is called for every method invocation. + * @param proxy a reference to the proxy (generated) object + * @return an object that can invoke the method + */ + Object loadObject(Object proxy) throws Exception; +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/proxy/UndeclaredThrowableException.java b/fine-spring/src/com/fr/third/springframework/cglib/proxy/UndeclaredThrowableException.java new file mode 100644 index 000000000..f57cef2f3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/proxy/UndeclaredThrowableException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002,2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.proxy; + +import com.fr.third.springframework.cglib.core.*; + +/** + * Used by {@link Proxy} as a replacement for java.lang.reflect.UndeclaredThrowableException. + * @author Juozas Baliuka + */ +public class UndeclaredThrowableException extends CodeGenerationException { + private static final long serialVersionUID = 1649412612498743276L; + + /** + * Creates a new instance of UndeclaredThrowableException without detail message. + */ + public UndeclaredThrowableException(Throwable t) { + super(t); + } + + public Throwable getUndeclaredThrowable() { + return getCause(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/ConstructorDelegate.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/ConstructorDelegate.java new file mode 100644 index 000000000..7f3461063 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/ConstructorDelegate.java @@ -0,0 +1,124 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + + +/** + * @author Chris Nokleberg + * @version $Id: ConstructorDelegate.java,v 1.20 2006/03/05 02:43:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class ConstructorDelegate { + private static final ConstructorKey KEY_FACTORY = + (ConstructorKey)KeyFactory.create(ConstructorKey.class, KeyFactory.CLASS_BY_NAME); + + interface ConstructorKey { + public Object newInstance(String declaring, String iface); + } + + protected ConstructorDelegate() { + } + + public static ConstructorDelegate create(Class targetClass, Class iface) { + Generator gen = new Generator(); + gen.setTargetClass(targetClass); + gen.setInterface(iface); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ConstructorDelegate.class.getName()); + private static final Type CONSTRUCTOR_DELEGATE = + TypeUtils.parseType("com.fr.third.springframework.cglib.reflect.ConstructorDelegate"); + //TypeUtils.parseType("net.sf.cglib.reflect.ConstructorDelegate"); + + private Class iface; + private Class targetClass; + + public Generator() { + super(SOURCE); + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + public ConstructorDelegate create() { + setNamePrefix(targetClass.getName()); + Object key = KEY_FACTORY.newInstance(iface.getName(), targetClass.getName()); + return (ConstructorDelegate)super.create(key); + } + + protected ClassLoader getDefaultClassLoader() { + return targetClass.getClassLoader(); + } + + public void generateClass(ClassVisitor v) { + setNamePrefix(targetClass.getName()); + + final Method newInstance = ReflectUtils.findNewInstance(iface); + if (!newInstance.getReturnType().isAssignableFrom(targetClass)) { + throw new IllegalArgumentException("incompatible return type"); + } + final Constructor constructor; + try { + constructor = targetClass.getDeclaredConstructor(newInstance.getParameterTypes()); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("interface does not match any known constructor"); + } + + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + CONSTRUCTOR_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + Type declaring = Type.getType(constructor.getDeclaringClass()); + EmitUtils.null_constructor(ce); + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, + ReflectUtils.getSignature(newInstance), + ReflectUtils.getExceptionTypes(newInstance)); + e.new_instance(declaring); + e.dup(); + e.load_args(); + e.invoke_constructor(declaring, ReflectUtils.getSignature(constructor)); + e.return_value(); + e.end_method(); + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClass.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClass.java new file mode 100644 index 000000000..45b9e2d2f --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClass.java @@ -0,0 +1,202 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class FastClass +{ + private Class type; + + protected FastClass() { + throw new Error("Using the FastClass empty constructor--please report to the cglib-devel mailing list"); + } + + protected FastClass(Class type) { + this.type = type; + } + + public static FastClass create(Class type) { + + return create(type.getClassLoader(),type); + + } + public static FastClass create(ClassLoader loader, Class type) { + Generator gen = new Generator(); + gen.setType(type); + gen.setClassLoader(loader); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator + { + private static final Source SOURCE = new Source(FastClass.class.getName()); + private Class type; + + public Generator() { + super(SOURCE); + } + + public void setType(Class type) { + this.type = type; + } + + public FastClass create() { + setNamePrefix(type.getName()); + return (FastClass)super.create(type.getName()); + } + + protected ClassLoader getDefaultClassLoader() { + return type.getClassLoader(); + } + + public void generateClass(ClassVisitor v) throws Exception { + new FastClassEmitter(v, getClassName(), type); + } + + protected Object firstInstance(Class type) { + return ReflectUtils.newInstance(type, + new Class[]{ Class.class }, + new Object[]{ this.type }); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } + + public Object invoke(String name, Class[] parameterTypes, Object obj, Object[] args) throws InvocationTargetException { + return invoke(getIndex(name, parameterTypes), obj, args); + } + + public Object newInstance() throws InvocationTargetException { + return newInstance(getIndex(Constants.EMPTY_CLASS_ARRAY), null); + } + + public Object newInstance(Class[] parameterTypes, Object[] args) throws InvocationTargetException { + return newInstance(getIndex(parameterTypes), args); + } + + public FastMethod getMethod(Method method) { + return new FastMethod(this, method); + } + + public FastConstructor getConstructor(Constructor constructor) { + return new FastConstructor(this, constructor); + } + + public FastMethod getMethod(String name, Class[] parameterTypes) { + try { + return getMethod(type.getMethod(name, parameterTypes)); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + public FastConstructor getConstructor(Class[] parameterTypes) { + try { + return getConstructor(type.getConstructor(parameterTypes)); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + public String getName() { + return type.getName(); + } + + public Class getJavaClass() { + return type; + } + + public String toString() { + return type.toString(); + } + + public int hashCode() { + return type.hashCode(); + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof FastClass)) { + return false; + } + return type.equals(((FastClass)o).type); + } + + /** + * Return the index of the matching method. The index may be used + * later to invoke the method with less overhead. If more than one + * method matches (i.e. they differ by return type only), one is + * chosen arbitrarily. + * @see #invoke(int, Object, Object[]) + * @param name the method name + * @param parameterTypes the parameter array + * @return the index, or -1 if none is found. + */ + abstract public int getIndex(String name, Class[] parameterTypes); + + /** + * Return the index of the matching constructor. The index may be used + * later to create a new instance with less overhead. + * @see #newInstance(int, Object[]) + * @param parameterTypes the parameter array + * @return the constructor index, or -1 if none is found. + */ + abstract public int getIndex(Class[] parameterTypes); + + /** + * Invoke the method with the specified index. + * @see getIndex(name, Class[]) + * @param index the method index + * @param obj the object the underlying method is invoked from + * @param args the arguments used for the method call + * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception + */ + abstract public Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException; + + /** + * Create a new instance using the specified constructor index and arguments. + * @see getIndex(Class[]) + * @param index the constructor index + * @param args the arguments passed to the constructor + * @throws java.lang.reflect.InvocationTargetException if the constructor throws an exception + */ + abstract public Object newInstance(int index, Object[] args) throws InvocationTargetException; + + abstract public int getIndex(Signature sig); + + /** + * Returns the maximum method index for this class. + */ + abstract public int getMaxIndex(); + + protected static String getSignatureWithoutReturnType(String name, Class[] parameterTypes) { + StringBuffer sb = new StringBuffer(); + sb.append(name); + sb.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + sb.append(Type.getDescriptor(parameterTypes[i])); + } + sb.append(')'); + return sb.toString(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClassEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClassEmitter.java new file mode 100644 index 000000000..ee380a0bf --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastClassEmitter.java @@ -0,0 +1,228 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import java.lang.reflect.*; +import java.util.*; + +import com.fr.third.springframework.asm.*; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +class FastClassEmitter extends ClassEmitter { + private static final Signature CSTRUCT_CLASS = + TypeUtils.parseConstructor("Class"); + private static final Signature METHOD_GET_INDEX = + TypeUtils.parseSignature("int getIndex(String, Class[])"); + private static final Signature SIGNATURE_GET_INDEX = + new Signature("getIndex", Type.INT_TYPE, new Type[]{ Constants.TYPE_SIGNATURE }); + private static final Signature TO_STRING = + TypeUtils.parseSignature("String toString()"); + private static final Signature CONSTRUCTOR_GET_INDEX = + TypeUtils.parseSignature("int getIndex(Class[])"); + private static final Signature INVOKE = + TypeUtils.parseSignature("Object invoke(int, Object, Object[])"); + private static final Signature NEW_INSTANCE = + TypeUtils.parseSignature("Object newInstance(int, Object[])"); + private static final Signature GET_MAX_INDEX = + TypeUtils.parseSignature("int getMaxIndex()"); + private static final Signature GET_SIGNATURE_WITHOUT_RETURN_TYPE = + TypeUtils.parseSignature("String getSignatureWithoutReturnType(String, Class[])"); + private static final Type FAST_CLASS = + TypeUtils.parseType("com.fr.third.springframework.cglib.reflect.FastClass"); + //TypeUtils.parseType("net.sf.cglib.reflect.FastClass"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Type INVOCATION_TARGET_EXCEPTION = + TypeUtils.parseType("java.lang.reflect.InvocationTargetException"); + private static final Type[] INVOCATION_TARGET_EXCEPTION_ARRAY = { INVOCATION_TARGET_EXCEPTION }; + + public FastClassEmitter(ClassVisitor v, String className, Class type) { + super(v); + + Type base = Type.getType(type); + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE); + + // constructor + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_CLASS, null); + e.load_this(); + e.load_args(); + e.super_invoke_constructor(CSTRUCT_CLASS); + e.return_value(); + e.end_method(); + + VisibilityPredicate vp = new VisibilityPredicate(type, false); + List methods = ReflectUtils.addAllMethods(type, new ArrayList()); + CollectionUtils.filter(methods, vp); + CollectionUtils.filter(methods, new DuplicatesPredicate()); + List constructors = new ArrayList(Arrays.asList(type.getDeclaredConstructors())); + CollectionUtils.filter(constructors, vp); + + // getIndex(String) + emitIndexBySignature(methods); + + // getIndex(String, Class[]) + emitIndexByClassArray(methods); + + // getIndex(Class[]) + e = begin_method(Constants.ACC_PUBLIC, CONSTRUCTOR_GET_INDEX, null); + e.load_args(); + List info = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance()); + EmitUtils.constructor_switch(e, info, new GetIndexCallback(e, info)); + e.end_method(); + + // invoke(int, Object, Object[]) + e = begin_method(Constants.ACC_PUBLIC, INVOKE, INVOCATION_TARGET_EXCEPTION_ARRAY); + e.load_arg(1); + e.checkcast(base); + e.load_arg(0); + invokeSwitchHelper(e, methods, 2, base); + e.end_method(); + + // newInstance(int, Object[]) + e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, INVOCATION_TARGET_EXCEPTION_ARRAY); + e.new_instance(base); + e.dup(); + e.load_arg(0); + invokeSwitchHelper(e, constructors, 1, base); + e.end_method(); + + // getMaxIndex() + e = begin_method(Constants.ACC_PUBLIC, GET_MAX_INDEX, null); + e.push(methods.size() - 1); + e.return_value(); + e.end_method(); + + end_class(); + } + + // support constructor indices ("") + private void emitIndexBySignature(List methods) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SIGNATURE_GET_INDEX, null); + List signatures = CollectionUtils.transform(methods, new Transformer() { + public Object transform(Object obj) { + return ReflectUtils.getSignature((Method)obj).toString(); + } + }); + e.load_arg(0); + e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING); + signatureSwitchHelper(e, signatures); + e.end_method(); + } + + private static final int TOO_MANY_METHODS = 100; + private void emitIndexByClassArray(List methods) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, METHOD_GET_INDEX, null); + if (methods.size() > TOO_MANY_METHODS) { + // hack for big classes + List signatures = CollectionUtils.transform(methods, new Transformer() { + public Object transform(Object obj) { + String s = ReflectUtils.getSignature((Method)obj).toString(); + return s.substring(0, s.lastIndexOf(')') + 1); + } + }); + e.load_args(); + e.invoke_static(FAST_CLASS, GET_SIGNATURE_WITHOUT_RETURN_TYPE); + signatureSwitchHelper(e, signatures); + } else { + e.load_args(); + List info = CollectionUtils.transform(methods, MethodInfoTransformer.getInstance()); + EmitUtils.method_switch(e, info, new GetIndexCallback(e, info)); + } + e.end_method(); + } + + private void signatureSwitchHelper(final CodeEmitter e, final List signatures) { + ObjectSwitchCallback callback = new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + // remove linear indexOf + e.push(signatures.indexOf(key)); + e.return_value(); + } + public void processDefault() { + e.push(-1); + e.return_value(); + } + }; + EmitUtils.string_switch(e, + (String[])signatures.toArray(new String[signatures.size()]), + Constants.SWITCH_STYLE_HASH, + callback); + } + + private static void invokeSwitchHelper(final CodeEmitter e, List members, final int arg, final Type base) { + final List info = CollectionUtils.transform(members, MethodInfoTransformer.getInstance()); + final Label illegalArg = e.make_label(); + Block block = e.begin_block(); + e.process_switch(getIntRange(info.size()), new ProcessSwitchCallback() { + public void processCase(int key, Label end) { + MethodInfo method = (MethodInfo)info.get(key); + Type[] types = method.getSignature().getArgumentTypes(); + for (int i = 0; i < types.length; i++) { + e.load_arg(arg); + e.aaload(i); + e.unbox(types[i]); + } + // change method lookup process so MethodInfo will already reference base + // instead of superclass when superclass method is inaccessible + e.invoke(method, base); + if (!TypeUtils.isConstructor(method)) { + e.box(method.getSignature().getReturnType()); + } + e.return_value(); + } + public void processDefault() { + e.goTo(illegalArg); + } + }); + block.end(); + EmitUtils.wrap_throwable(block, INVOCATION_TARGET_EXCEPTION); + e.mark(illegalArg); + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Cannot find matching method/constructor"); + } + + private static class GetIndexCallback implements ObjectSwitchCallback { + private CodeEmitter e; + private Map indexes = new HashMap(); + + public GetIndexCallback(CodeEmitter e, List methods) { + this.e = e; + int index = 0; + for (Iterator it = methods.iterator(); it.hasNext();) { + indexes.put(it.next(), new Integer(index++)); + } + } + + public void processCase(Object key, Label end) { + e.push(((Integer)indexes.get(key)).intValue()); + e.return_value(); + } + + public void processDefault() { + e.push(-1); + e.return_value(); + } + } + + private static int[] getIntRange(int length) { + int[] range = new int[length]; + for (int i = 0; i < length; i++) { + range[i] = i; + } + return range; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastConstructor.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastConstructor.java new file mode 100644 index 000000000..276bebe8f --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastConstructor.java @@ -0,0 +1,46 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +@SuppressWarnings({ "rawtypes" }) +public class FastConstructor extends FastMember +{ + FastConstructor(FastClass fc, Constructor constructor) { + super(fc, constructor, fc.getIndex(constructor.getParameterTypes())); + } + + public Class[] getParameterTypes() { + return ((Constructor)member).getParameterTypes(); + } + + public Class[] getExceptionTypes() { + return ((Constructor)member).getExceptionTypes(); + } + + public Object newInstance() throws InvocationTargetException { + return fc.newInstance(index, null); + } + + public Object newInstance(Object[] args) throws InvocationTargetException { + return fc.newInstance(index, args); + } + + public Constructor getJavaConstructor() { + return (Constructor)member; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMember.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMember.java new file mode 100644 index 000000000..2ea9ce102 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMember.java @@ -0,0 +1,65 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import java.lang.reflect.Member; +@SuppressWarnings({ "rawtypes" }) +abstract public class FastMember +{ + protected FastClass fc; + protected Member member; + protected int index; + + protected FastMember(FastClass fc, Member member, int index) { + this.fc = fc; + this.member = member; + this.index = index; + } + + abstract public Class[] getParameterTypes(); + abstract public Class[] getExceptionTypes(); + + public int getIndex() { + return index; + } + + public String getName() { + return member.getName(); + } + + public Class getDeclaringClass() { + return fc.getJavaClass(); + } + + public int getModifiers() { + return member.getModifiers(); + } + + public String toString() { + return member.toString(); + } + + public int hashCode() { + return member.hashCode(); + } + + public boolean equals(Object o) { + if (o == null || !(o instanceof FastMember)) { + return false; + } + return member.equals(((FastMember)o).member); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMethod.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMethod.java new file mode 100644 index 000000000..a8c027ecd --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/FastMethod.java @@ -0,0 +1,59 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +@SuppressWarnings({ "rawtypes" }) +public class FastMethod extends FastMember +{ + FastMethod(FastClass fc, Method method) { + super(fc, method, helper(fc, method)); + } + + private static int helper(FastClass fc, Method method) { + int index = fc.getIndex(method.getName(), method.getParameterTypes()); + if (index < 0) { + Class[] types = method.getParameterTypes(); + System.err.println("hash=" + method.getName().hashCode() + " size=" + types.length); + for (int i = 0; i < types.length; i++) { + System.err.println(" types[" + i + "]=" + types[i].getName()); + } + throw new IllegalArgumentException("Cannot find method " + method); + } + return index; + } + + public Class getReturnType() { + return ((Method)member).getReturnType(); + } + + public Class[] getParameterTypes() { + return ((Method)member).getParameterTypes(); + } + + public Class[] getExceptionTypes() { + return ((Method)member).getExceptionTypes(); + } + + public Object invoke(Object obj, Object[] args) throws InvocationTargetException { + return fc.invoke(index, obj, args); + } + + public Method getJavaMethod() { + return (Method)member; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/MethodDelegate.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/MethodDelegate.java new file mode 100644 index 000000000..96ae3e626 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/MethodDelegate.java @@ -0,0 +1,263 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +import java.lang.reflect.Method; + + +// don't require exact match for return type + +/** + * DOCUMENTATION FROM APACHE AVALON DELEGATE CLASS + * + *

+ * Delegates are a typesafe pointer to another method. Since Java does not + * have language support for such a construct, this utility will construct + * a proxy that forwards method calls to any method with the same signature. + * This utility is inspired in part by the C# delegate mechanism. We + * implemented it in a Java-centric manner. + *

+ * + *

Delegate

+ *

+ * Any interface with one method can become the interface for a delegate. + * Consider the example below: + *

+ * + *
+ *   public interface MainDelegate {
+ *       int main(String[] args);
+ *   }
+ * 
+ * + *

+ * The interface above is an example of an interface that can become a + * delegate. It has only one method, and the interface is public. In + * order to create a delegate for that method, all we have to do is + * call MethodDelegate.create(this, "alternateMain", MainDelegate.class). + * The following program will show how to use it: + *

+ * + *
+ *   public class Main {
+ *       public static int main( String[] args ) {
+ *           Main newMain = new Main();
+ *           MainDelegate start = (MainDelegate)
+ *               MethodDelegate.create(newMain, "alternateMain", MainDelegate.class);
+ *           return start.main( args );
+ *       }
+ *
+ *       public int alternateMain( String[] args ) {
+ *           for (int i = 0; i < args.length; i++) {
+ *               System.out.println( args[i] );
+ *           }
+ *           return args.length;
+ *       }
+ *   }
+ * 
+ * + *

+ * By themselves, delegates don't do much. Their true power lies in the fact that + * they can be treated like objects, and passed to other methods. In fact that is + * one of the key building blocks of building Intelligent Agents which in tern are + * the foundation of artificial intelligence. In the above program, we could have + * easily created the delegate to match the static main method by + * substituting the delegate creation call with this: + * MethodDelegate.createStatic(getClass(), "main", MainDelegate.class). + *

+ *

+ * Another key use for Delegates is to register event listeners. It is much easier + * to have all the code for your events separated out into methods instead of individual + * classes. One of the ways Java gets around that is to create anonymous classes. + * They are particularly troublesome because many Debuggers do not know what to do + * with them. Anonymous classes tend to duplicate alot of code as well. We can + * use any interface with one declared method to forward events to any method that + * matches the signature (although the method name can be different). + *

+ * + *

Equality

+ * The criteria that we use to test if two delegates are equal are: + * + * + * @version $Id: MethodDelegate.java,v 1.25 2006/03/05 02:43:19 herbyderby Exp $ + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class MethodDelegate { + private static final MethodDelegateKey KEY_FACTORY = + (MethodDelegateKey)KeyFactory.create(MethodDelegateKey.class, KeyFactory.CLASS_BY_NAME); + + protected Object target; + protected String eqMethod; + + interface MethodDelegateKey { + Object newInstance(Class delegateClass, String methodName, Class iface); + } + + public static MethodDelegate createStatic(Class targetClass, String methodName, Class iface) { + Generator gen = new Generator(); + gen.setTargetClass(targetClass); + gen.setMethodName(methodName); + gen.setInterface(iface); + return gen.create(); + } + + public static MethodDelegate create(Object target, String methodName, Class iface) { + Generator gen = new Generator(); + gen.setTarget(target); + gen.setMethodName(methodName); + gen.setInterface(iface); + return gen.create(); + } + + public boolean equals(Object obj) { + MethodDelegate other = (MethodDelegate)obj; + return target == other.target && eqMethod.equals(other.eqMethod); + } + + public int hashCode() { + return target.hashCode() ^ eqMethod.hashCode(); + } + + public Object getTarget() { + return target; + } + + abstract public MethodDelegate newInstance(Object target); + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(MethodDelegate.class.getName()); + private static final Type METHOD_DELEGATE = + TypeUtils.parseType("com.fr.third.springframework.cglib.reflect.MethodDelegate"); + //TypeUtils.parseType("net.sf.cglib.reflect.MethodDelegate"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", METHOD_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + + private Object target; + private Class targetClass; + private String methodName; + private Class iface; + + public Generator() { + super(SOURCE); + } + + public void setTarget(Object target) { + this.target = target; + this.targetClass = target.getClass(); + } + + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + protected ClassLoader getDefaultClassLoader() { + return targetClass.getClassLoader(); + } + + public MethodDelegate create() { + setNamePrefix(targetClass.getName()); + Object key = KEY_FACTORY.newInstance(targetClass, methodName, iface); + return (MethodDelegate)super.create(key); + } + + protected Object firstInstance(Class type) { + return ((MethodDelegate)ReflectUtils.newInstance(type)).newInstance(target); + } + + protected Object nextInstance(Object instance) { + return ((MethodDelegate)instance).newInstance(target); + } + + public void generateClass(ClassVisitor v) throws NoSuchMethodException { + Method proxy = ReflectUtils.findInterfaceMethod(iface); + final Method method = targetClass.getMethod(methodName, proxy.getParameterTypes()); + if (!proxy.getReturnType().isAssignableFrom(method.getReturnType())) { + throw new IllegalArgumentException("incompatible return types"); + } + + MethodInfo methodInfo = ReflectUtils.getMethodInfo(method); + + boolean isStatic = TypeUtils.isStatic(methodInfo.getModifiers()); + if ((target == null) ^ isStatic) { + throw new IllegalArgumentException("Static method " + (isStatic ? "not " : "") + "expected"); + } + + ClassEmitter ce = new ClassEmitter(v); + CodeEmitter e; + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + METHOD_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + ce.declare_field(Constants.PRIVATE_FINAL_STATIC, "eqMethod", Constants.TYPE_STRING, null); + EmitUtils.null_constructor(ce); + + // generate proxied method + MethodInfo proxied = ReflectUtils.getMethodInfo(iface.getDeclaredMethods()[0]); + e = EmitUtils.begin_method(ce, proxied, Constants.ACC_PUBLIC); + e.load_this(); + e.super_getfield("target", Constants.TYPE_OBJECT); + e.checkcast(methodInfo.getClassInfo().getType()); + e.load_args(); + e.invoke(methodInfo); + e.return_value(); + e.end_method(); + + // newInstance + e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + e.new_instance_this(); + e.dup(); + e.dup2(); + e.invoke_constructor_this(); + e.getfield("eqMethod"); + e.super_putfield("eqMethod", Constants.TYPE_STRING); + e.load_arg(0); + e.super_putfield("target", Constants.TYPE_OBJECT); + e.return_value(); + e.end_method(); + + // static initializer + e = ce.begin_static(); + e.push(methodInfo.getSignature().toString()); + e.putfield("eqMethod"); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/reflect/MulticastDelegate.java b/fine-spring/src/com/fr/third/springframework/cglib/reflect/MulticastDelegate.java new file mode 100644 index 000000000..2c0558390 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/reflect/MulticastDelegate.java @@ -0,0 +1,171 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.reflect; + +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class MulticastDelegate implements Cloneable { + protected Object[] targets = {}; + + protected MulticastDelegate() { + } + + public List getTargets() { + return new ArrayList(Arrays.asList(targets)); + } + + abstract public MulticastDelegate add(Object target); + + protected MulticastDelegate addHelper(Object target) { + MulticastDelegate copy = newInstance(); + copy.targets = new Object[targets.length + 1]; + System.arraycopy(targets, 0, copy.targets, 0, targets.length); + copy.targets[targets.length] = target; + return copy; + } + + public MulticastDelegate remove(Object target) { + for (int i = targets.length - 1; i >= 0; i--) { + if (targets[i].equals(target)) { + MulticastDelegate copy = newInstance(); + copy.targets = new Object[targets.length - 1]; + System.arraycopy(targets, 0, copy.targets, 0, i); + System.arraycopy(targets, i + 1, copy.targets, i, targets.length - i - 1); + return copy; + } + } + return this; + } + + abstract public MulticastDelegate newInstance(); + + public static MulticastDelegate create(Class iface) { + Generator gen = new Generator(); + gen.setInterface(iface); + return gen.create(); + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(MulticastDelegate.class.getName()); + private static final Type MULTICAST_DELEGATE = + TypeUtils.parseType("com.fr.third.springframework.cglib.reflect.MulticastDelegate"); + //TypeUtils.parseType("net.sf.cglib.reflect.MulticastDelegate"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", MULTICAST_DELEGATE, new Type[0]); + private static final Signature ADD_DELEGATE = + new Signature("add", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + private static final Signature ADD_HELPER = + new Signature("addHelper", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); + + private Class iface; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return iface.getClassLoader(); + } + + public void setInterface(Class iface) { + this.iface = iface; + } + + public MulticastDelegate create() { + setNamePrefix(MulticastDelegate.class.getName()); + return (MulticastDelegate)super.create(iface.getName()); + } + + public void generateClass(ClassVisitor cv) { + final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface)); + + ClassEmitter ce = new ClassEmitter(cv); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + MULTICAST_DELEGATE, + new Type[]{ Type.getType(iface) }, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + + // generate proxied method + emitProxy(ce, method); + + // newInstance + CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); + e.new_instance_this(); + e.dup(); + e.invoke_constructor_this(); + e.return_value(); + e.end_method(); + + // add + e = ce.begin_method(Constants.ACC_PUBLIC, ADD_DELEGATE, null); + e.load_this(); + e.load_arg(0); + e.checkcast(Type.getType(iface)); + e.invoke_virtual_this(ADD_HELPER); + e.return_value(); + e.end_method(); + + ce.end_class(); + } + + private void emitProxy(ClassEmitter ce, final MethodInfo method) { + final CodeEmitter e = EmitUtils.begin_method(ce, method, Constants.ACC_PUBLIC); + Type returnType = method.getSignature().getReturnType(); + final boolean returns = returnType != Type.VOID_TYPE; + Local result = null; + if (returns) { + result = e.make_local(returnType); + e.zero_or_null(returnType); + e.store_local(result); + } + e.load_this(); + e.super_getfield("targets", Constants.TYPE_OBJECT_ARRAY); + final Local result2 = result; + EmitUtils.process_array(e, Constants.TYPE_OBJECT_ARRAY, new ProcessArrayCallback() { + public void processElement(Type type) { + e.checkcast(Type.getType(iface)); + e.load_args(); + e.invoke(method); + if (returns) { + e.store_local(result2); + } + } + }); + if (returns) { + e.load_local(result); + } + e.return_value(); + e.end_method(); + } + + protected Object firstInstance(Class type) { + // make a new instance in case first object is used with a long list of targets + return ((MulticastDelegate)ReflectUtils.newInstance(type)).newInstance(); + } + + protected Object nextInstance(Object instance) { + return ((MulticastDelegate)instance).newInstance(); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassFilterTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassFilterTransformer.java new file mode 100644 index 000000000..a5a72b924 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassFilterTransformer.java @@ -0,0 +1,90 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.AnnotationVisitor; +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.FieldVisitor; +import com.fr.third.springframework.asm.MethodVisitor; + +abstract public class AbstractClassFilterTransformer extends AbstractClassTransformer { + private ClassTransformer pass; + private ClassVisitor target; + + public void setTarget(ClassVisitor target) { + super.setTarget(target); + pass.setTarget(target); + } + + protected AbstractClassFilterTransformer(ClassTransformer pass) { + this.pass = pass; + } + + abstract protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces); + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + target = accept(version, access, name, signature, superName, interfaces) ? pass : cv; + target.visit(version, access, name, signature, superName, interfaces); + } + + public void visitSource(String source, String debug) { + target.visitSource(source, debug); + } + + public void visitOuterClass(String owner, String name, String desc) { + target.visitOuterClass(owner, name, desc); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return target.visitAnnotation(desc, visible); + } + + public void visitAttribute(Attribute attr) { + target.visitAttribute(attr); + } + + public void visitInnerClass(String name, String outerName, String innerName, int access) { + target.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + return target.visitField(access, name, desc, signature, value); + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return target.visitMethod(access, name, desc, signature, exceptions); + } + + public void visitEnd() { + target.visitEnd(); + target = null; // just to be safe + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassLoader.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassLoader.java new file mode 100644 index 000000000..5a1b884f8 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassLoader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.asm.ClassWriter; +import com.fr.third.springframework.cglib.core.*; + + +import java.io.IOException; +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class AbstractClassLoader extends ClassLoader { + private ClassFilter filter; + private ClassLoader classPath; + private static java.security.ProtectionDomain DOMAIN ; + + static{ + + DOMAIN = (java.security.ProtectionDomain) + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + return AbstractClassLoader.class.getProtectionDomain(); + } + }); + } + + protected AbstractClassLoader(ClassLoader parent, ClassLoader classPath, ClassFilter filter) { + super(parent); + this.filter = filter; + this.classPath = classPath; + } + + public Class loadClass(String name) throws ClassNotFoundException { + + Class loaded = findLoadedClass(name); + + if( loaded != null ){ + if( loaded.getClassLoader() == this ){ + return loaded; + }//else reload with this class loader + } + + if (!filter.accept(name)) { + return super.loadClass(name); + } + ClassReader r; + try { + + java.io.InputStream is = classPath.getResourceAsStream( + name.replace('.','/') + ".class" + ); + + if (is == null) { + + throw new ClassNotFoundException(name); + + } + try { + + r = new ClassReader(is); + + } finally { + + is.close(); + + } + } catch (IOException e) { + throw new ClassNotFoundException(name + ":" + e.getMessage()); + } + + try { + DebuggingClassWriter w = + new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS); + getGenerator(r).generateClass(w); + byte[] b = w.toByteArray(); + Class c = super.defineClass(name, b, 0, b.length, DOMAIN); + postProcess(c); + return c; + } catch (RuntimeException e) { + throw e; + } catch (Error e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + + protected ClassGenerator getGenerator(ClassReader r) { + return (ClassGenerator) new ClassReaderGenerator(r, attributes(), getFlags()); + } + + protected int getFlags() { + return 0; + } + + protected Attribute[] attributes() { + return null; + } + + protected void postProcess(Class c) { + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassTransformer.java new file mode 100644 index 000000000..cc235c546 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractClassTransformer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Opcodes; + +abstract public class AbstractClassTransformer extends ClassTransformer { + protected AbstractClassTransformer() { + super(Opcodes.ASM4); + } + + public void setTarget(ClassVisitor target) { + cv = target; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractProcessTask.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractProcessTask.java new file mode 100644 index 000000000..5fe5dfdbc --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractProcessTask.java @@ -0,0 +1,64 @@ +///* +// * Copyright 2003 The Apache Software Foundation +// * +// * 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 com.fr.third.springframework.cglib.transform; +// +//import java.io.File; +//import java.util.*; +//import org.apache.tools.ant.BuildException; +//import org.apache.tools.ant.DirectoryScanner; +//import org.apache.tools.ant.Project; +//import org.apache.tools.ant.Task; +//import org.apache.tools.ant.types.FileSet; +//@SuppressWarnings({ "rawtypes", "unchecked" }) +//abstract public class AbstractProcessTask extends Task { +// private Vector filesets = new Vector(); +// +// public void addFileset(FileSet set) { +// filesets.addElement(set); +// } +// +// protected Collection getFiles() { +// Map fileMap = new HashMap(); +// Project p = getProject(); +// for (int i = 0; i < filesets.size(); i++) { +// FileSet fs = (FileSet)filesets.elementAt(i); +// DirectoryScanner ds = fs.getDirectoryScanner(p); +// String[] srcFiles = ds.getIncludedFiles(); +// File dir = fs.getDir(p); +// for (int j = 0; j < srcFiles.length; j++) { +// File src = new File(dir, srcFiles[j]); +// fileMap.put(src.getAbsolutePath(), src); +// } +// } +// return fileMap.values(); +// } +// +// +// +// public void execute() throws BuildException { +// beforeExecute(); +// for (Iterator it = getFiles().iterator(); it.hasNext();) { +// try { +// processFile((File)it.next()); +// } catch (Exception e) { +// throw new BuildException(e); +// } +// } +// } +// +// protected void beforeExecute() throws BuildException { } +// abstract protected void processFile(File file) throws Exception; +//} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractTransformTask.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractTransformTask.java new file mode 100644 index 000000000..c61d61dce --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AbstractTransformTask.java @@ -0,0 +1,269 @@ +///* +// * Copyright 2003,2004 The Apache Software Foundation +// * +// * 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 com.fr.third.springframework.cglib.transform; +// +//import java.io.*; +//import java.net.MalformedURLException; +//import java.util.zip.*; +// +//import com.fr.third.springframework.asm.Attribute; +//import com.fr.third.springframework.asm.ClassReader; +//import com.fr.third.springframework.asm.ClassWriter; +//import com.fr.third.springframework.cglib.core.*; +//import org.apache.tools.ant.*; +// +//abstract public class AbstractTransformTask extends AbstractProcessTask { +// private static final int ZIP_MAGIC = 0x504B0304; +// +// private static final int CLASS_MAGIC = 0xCAFEBABE; +// +// private boolean verbose; +// +// public void setVerbose(boolean verbose) { +// this.verbose = verbose; +// } +// +// /** +// * returns transformation for source class +// * +// * @param classInfo +// * class information +// * class name := classInfo[ 0 ] +// * super class name := classInfo[ 1 ] +// * interfaces := classInfo[ >1 ] +// */ +// abstract protected ClassTransformer getClassTransformer(String[] classInfo); +// +// protected Attribute[] attributes() { +// return null; +// } +// +// protected void processFile(File file) throws Exception { +// +// if (isClassFile(file)) { +// +// processClassFile(file); +// +// } else if (isJarFile(file)) { +// +// processJarFile(file); +// +// } else { +// +// log("ignoring " + file.toURI(), Project.MSG_WARN); +// +// } +// } +// +// /** +// * @param file +// * @throws Exception +// * @throws FileNotFoundException +// * @throws IOException +// * @throws MalformedURLException +// */ +// private void processClassFile(File file) throws Exception, +// FileNotFoundException, IOException, MalformedURLException { +// +// ClassReader reader = getClassReader(file); +// String name[] = ClassNameReader.getClassInfo(reader); +// DebuggingClassWriter w = +// new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS); +// ClassTransformer t = getClassTransformer(name); +// if (t != null) { +// +// if (verbose) { +// log("processing " + file.toURI()); +// } +// new TransformingClassGenerator(new ClassReaderGenerator( +// getClassReader(file), attributes(), getFlags()), t) +// .generateClass(w); +// FileOutputStream fos = new FileOutputStream(file); +// try { +// fos.write(w.toByteArray()); +// } finally { +// fos.close(); +// } +// +// } +// +// } +// +// protected int getFlags() { +// return 0; +// } +// +// private static ClassReader getClassReader(File file) throws Exception { +// InputStream in = new BufferedInputStream(new FileInputStream(file)); +// try { +// ClassReader r = new ClassReader(in); +// return r; +// } finally { +// in.close(); +// } +// +// } +// +// protected boolean isClassFile(File file) throws IOException { +// +// return checkMagic(file, CLASS_MAGIC); +// +// } +// +// protected void processJarFile(File file) throws Exception { +// +// if (verbose) { +// log("processing " + file.toURI()); +// } +// +// File tempFile = File.createTempFile(file.getName(), null, new File(file +// .getAbsoluteFile().getParent())); +// try{ +// +// ZipInputStream zip = new ZipInputStream(new FileInputStream(file)); +// try { +// FileOutputStream fout = new FileOutputStream(tempFile); +// try{ +// ZipOutputStream out = new ZipOutputStream(fout); +// +// ZipEntry entry; +// while ((entry = zip.getNextEntry()) != null) { +// +// +// byte bytes[] = getBytes(zip); +// +// if (!entry.isDirectory()) { +// +// DataInputStream din = new DataInputStream( +// new ByteArrayInputStream(bytes) +// ); +// +// if (din.readInt() == CLASS_MAGIC) { +// +// bytes = process(bytes); +// +// } else { +// if (verbose) { +// log("ignoring " + entry.toString()); +// } +// } +// } +// +// ZipEntry outEntry = new ZipEntry(entry.getName()); +// outEntry.setMethod(entry.getMethod()); +// outEntry.setComment(entry.getComment()); +// outEntry.setSize(bytes.length); +// +// +// if(outEntry.getMethod() == ZipEntry.STORED){ +// CRC32 crc = new CRC32(); +// crc.update(bytes); +// outEntry.setCrc( crc.getValue() ); +// outEntry.setCompressedSize(bytes.length); +// } +// out.putNextEntry(outEntry); +// out.write(bytes); +// out.closeEntry(); +// zip.closeEntry(); +// +// } +// out.close(); +// }finally{ +// fout.close(); +// } +// } finally { +// zip.close(); +// } +// +// +// if(file.delete()){ +// +// File newFile = new File(tempFile.getAbsolutePath()); +// +// if(!newFile.renameTo(file)){ +// throw new IOException("can not rename " + tempFile + " to " + file); +// } +// +// }else{ +// throw new IOException("can not delete " + file); +// } +// +// }finally{ +// +// tempFile.delete(); +// +// } +// +// } +// +// /** +// * @param bytes +// * @return +// * @throws IOException +// * @throws Exception +// */ +// private byte[] process(byte[] bytes) throws Exception { +// +// ClassReader reader = new ClassReader(new ByteArrayInputStream(bytes)); +// String name[] = ClassNameReader.getClassInfo(reader); +// DebuggingClassWriter w = +// new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS); +// ClassTransformer t = getClassTransformer(name); +// if (t != null) { +// if (verbose) { +// log("processing " + name[0]); +// } +// new TransformingClassGenerator(new ClassReaderGenerator( +// new ClassReader(new ByteArrayInputStream(bytes)), +// attributes(), getFlags()), t).generateClass(w); +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// out.write(w.toByteArray()); +// return out.toByteArray(); +// } +// return bytes; +// } +// +// /** +// * @param zip +// * @return +// * @throws IOException +// */ +// private byte[] getBytes(ZipInputStream zip) throws IOException { +// +// ByteArrayOutputStream bout = new ByteArrayOutputStream(); +// InputStream in = new BufferedInputStream(zip); +// int b; +// while ((b = in.read()) != -1) { +// bout.write(b); +// } +// return bout.toByteArray(); +// } +// +// private boolean checkMagic(File file, long magic) throws IOException { +// DataInputStream in = new DataInputStream(new FileInputStream(file)); +// try { +// int m = in.readInt(); +// return magic == m; +// } finally { +// in.close(); +// } +// } +// +// protected boolean isJarFile(File file) throws IOException { +// return checkMagic(file, ZIP_MAGIC); +// } +// +//} \ No newline at end of file diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/AnnotationVisitorTee.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/AnnotationVisitorTee.java new file mode 100644 index 000000000..b66e3a0c3 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/AnnotationVisitorTee.java @@ -0,0 +1,62 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.AnnotationVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public class AnnotationVisitorTee extends AnnotationVisitor { + private AnnotationVisitor av1, av2; + + public static AnnotationVisitor getInstance(AnnotationVisitor av1, AnnotationVisitor av2) { + if (av1 == null) + return av2; + if (av2 == null) + return av1; + return new AnnotationVisitorTee(av1, av2); + } + + public AnnotationVisitorTee(AnnotationVisitor av1, AnnotationVisitor av2) { + super(Opcodes.ASM4); + this.av1 = av1; + this.av2 = av2; + } + + public void visit(String name, Object value) { + av2.visit(name, value); + av2.visit(name, value); + } + + public void visitEnum(String name, String desc, String value) { + av1.visitEnum(name, desc, value); + av2.visitEnum(name, desc, value); + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + return getInstance(av1.visitAnnotation(name, desc), + av2.visitAnnotation(name, desc)); + } + + public AnnotationVisitor visitArray(String name) { + return getInstance(av1.visitArray(name), av2.visitArray(name)); + } + + public void visitEnd() { + av1.visitEnd(); + av2.visitEnd(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassEmitterTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassEmitterTransformer.java new file mode 100644 index 000000000..71705274e --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassEmitterTransformer.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.cglib.core.*; + +abstract public class ClassEmitterTransformer extends ClassEmitter { +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilter.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilter.java new file mode 100644 index 000000000..3942d8f2a --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilter.java @@ -0,0 +1,27 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +/** + * + * @author baliuka + */ +public interface ClassFilter { + + boolean accept(String className); + +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilterTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilterTransformer.java new file mode 100644 index 000000000..00f50ae14 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassFilterTransformer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +public class ClassFilterTransformer extends AbstractClassFilterTransformer { + private ClassFilter filter; + + public ClassFilterTransformer(ClassFilter filter, ClassTransformer pass) { + super(pass); + this.filter = filter; + } + + protected boolean accept(int version, int access, String name, String signature, String superName, String[] interfaces) { + return filter.accept(name.replace('/', '.')); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassReaderGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassReaderGenerator.java new file mode 100644 index 000000000..92207e60c --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassReaderGenerator.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + + +public class ClassReaderGenerator implements ClassGenerator { + private final ClassReader r; + private final Attribute[] attrs; + private final int flags; + + public ClassReaderGenerator(ClassReader r, int flags) { + this(r, null, flags); + } + + public ClassReaderGenerator(ClassReader r, Attribute[] attrs, int flags) { + this.r = r; + this.attrs = (attrs != null) ? attrs : new Attribute[0]; + this.flags = flags; + } + + public void generateClass(ClassVisitor v) { + r.accept(v, attrs, flags); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformer.java new file mode 100644 index 000000000..0aa496f23 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public abstract class ClassTransformer extends ClassVisitor { + public ClassTransformer() { + super(Opcodes.ASM4); + } + public ClassTransformer(int opcode) { + super(opcode); + } + public abstract void setTarget(ClassVisitor target); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerChain.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerChain.java new file mode 100644 index 000000000..fcda158fd --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerChain.java @@ -0,0 +1,58 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.MethodVisitor; + +public class ClassTransformerChain extends AbstractClassTransformer { + private ClassTransformer[] chain; + + public ClassTransformerChain(ClassTransformer[] chain) { + this.chain = (ClassTransformer[])chain.clone(); + } + + public void setTarget(ClassVisitor v) { + super.setTarget(chain[0]); + ClassVisitor next = v; + for (int i = chain.length - 1; i >= 0; i--) { + chain[i].setTarget(next); + next = chain[i]; + } + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return cv.visitMethod(access, name, desc, signature, exceptions); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("ClassTransformerChain{"); + for (int i = 0; i < chain.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(chain[i].toString()); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerFactory.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerFactory.java new file mode 100644 index 000000000..c569a74b9 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerFactory.java @@ -0,0 +1,20 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +public interface ClassTransformerFactory { + ClassTransformer newInstance(); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerTee.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerTee.java new file mode 100644 index 000000000..f7c679d9a --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassTransformerTee.java @@ -0,0 +1,33 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public class ClassTransformerTee extends ClassTransformer { + private ClassVisitor branch; + + public ClassTransformerTee(ClassVisitor branch) { + super(Opcodes.ASM4); + this.branch = branch; + } + + public void setTarget(ClassVisitor target) { + cv = new ClassVisitorTee(branch, target); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassVisitorTee.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassVisitorTee.java new file mode 100644 index 000000000..00852b6ec --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/ClassVisitorTee.java @@ -0,0 +1,104 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.AnnotationVisitor; +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.FieldVisitor; +import com.fr.third.springframework.asm.MethodVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public class ClassVisitorTee extends ClassVisitor { + private ClassVisitor cv1, cv2; + + public ClassVisitorTee(ClassVisitor cv1, ClassVisitor cv2) { + super(Opcodes.ASM4); + this.cv1 = cv1; + this.cv2 = cv2; + } + + public void visit(int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + cv1.visit(version, access, name, signature, superName, interfaces); + cv2.visit(version, access, name, signature, superName, interfaces); + } + + public void visitEnd() { + cv1.visitEnd(); + cv2.visitEnd(); + cv1 = cv2 = null; + } + + public void visitInnerClass(String name, String outerName, String innerName, int access) { + cv1.visitInnerClass(name, outerName, innerName, access); + cv2.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField(int access, + String name, + String desc, + String signature, + Object value) { + FieldVisitor fv1 = cv1.visitField(access, name, desc, signature, value); + FieldVisitor fv2 = cv2.visitField(access, name, desc, signature, value); + if (fv1 == null) + return fv2; + if (fv2 == null) + return fv1; + return new FieldVisitorTee(fv1, fv2); + } + + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + MethodVisitor mv1 = cv1.visitMethod(access, name, desc, signature, exceptions); + MethodVisitor mv2 = cv2.visitMethod(access, name, desc, signature, exceptions); + if (mv1 == null) + return mv2; + if (mv2 == null) + return mv1; + return new MethodVisitorTee(mv1, mv2); + } + + public void visitSource(String source, String debug) { + cv1.visitSource(source, debug); + cv2.visitSource(source, debug); + } + + public void visitOuterClass(String owner, String name, String desc) { + cv1.visitOuterClass(owner, name, desc); + cv2.visitOuterClass(owner, name, desc); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(cv1.visitAnnotation(desc, visible), + cv2.visitAnnotation(desc, visible)); + } + + public void visitAttribute(Attribute attrs) { + cv1.visitAttribute(attrs); + cv2.visitAttribute(attrs); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/FieldVisitorTee.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/FieldVisitorTee.java new file mode 100644 index 000000000..543f48522 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/FieldVisitorTee.java @@ -0,0 +1,48 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.AnnotationVisitor; +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.FieldVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public class FieldVisitorTee extends FieldVisitor { + private FieldVisitor fv1, fv2; + + public FieldVisitorTee(FieldVisitor fv1, FieldVisitor fv2) { + super(Opcodes.ASM4); + this.fv1 = fv1; + this.fv2 = fv2; + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(fv1.visitAnnotation(desc, visible), + fv2.visitAnnotation(desc, visible)); + } + + public void visitAttribute(Attribute attr) { + fv1.visitAttribute(attr); + fv2.visitAttribute(attr); + } + + public void visitEnd() { + fv1.visitEnd(); + fv2.visitEnd(); + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilter.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilter.java new file mode 100644 index 000000000..0d6bf30a0 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilter.java @@ -0,0 +1,22 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +public interface MethodFilter { + // pass class name too? + boolean accept(int access, String name, String desc, String signature, String[] exceptions); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilterTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilterTransformer.java new file mode 100644 index 000000000..61bc5ba27 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodFilterTransformer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.MethodVisitor; + +public class MethodFilterTransformer extends AbstractClassTransformer { + private MethodFilter filter; + private ClassTransformer pass; + private ClassVisitor direct; + + public MethodFilterTransformer(MethodFilter filter, ClassTransformer pass) { + this.filter = filter; + this.pass = pass; + super.setTarget(pass); + } + + public MethodVisitor visitMethod(int access, + String name, + String desc, + String signature, + String[] exceptions) { + return (filter.accept(access, name, desc, signature, exceptions) ? pass : direct).visitMethod(access, name, desc, signature, exceptions); + } + + public void setTarget(ClassVisitor target) { + pass.setTarget(target); + direct = target; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodVisitorTee.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodVisitorTee.java new file mode 100644 index 000000000..8eb4ffdd8 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/MethodVisitorTee.java @@ -0,0 +1,158 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.asm.AnnotationVisitor; +import com.fr.third.springframework.asm.Attribute; +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.MethodVisitor; +import com.fr.third.springframework.asm.Opcodes; + +public class MethodVisitorTee extends MethodVisitor { + private final MethodVisitor mv1; + private final MethodVisitor mv2; + + public MethodVisitorTee(MethodVisitor mv1, MethodVisitor mv2) { + super(Opcodes.ASM4); + this.mv1 = mv1; + this.mv2 = mv2; + } + + public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { + mv1.visitFrame(type, nLocal, local, nStack, stack); + mv2.visitFrame(type, nLocal, local, nStack, stack); + } + + public AnnotationVisitor visitAnnotationDefault() { + return AnnotationVisitorTee.getInstance(mv1.visitAnnotationDefault(), + mv2.visitAnnotationDefault()); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitAnnotation(desc, visible), + mv2.visitAnnotation(desc, visible)); + } + + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, + boolean visible) { + return AnnotationVisitorTee.getInstance(mv1.visitParameterAnnotation(parameter, desc, visible), + mv2.visitParameterAnnotation(parameter, desc, visible)); + } + + public void visitAttribute(Attribute attr) { + mv1.visitAttribute(attr); + mv2.visitAttribute(attr); + } + + public void visitCode() { + mv1.visitCode(); + mv2.visitCode(); + } + + public void visitInsn(int opcode) { + mv1.visitInsn(opcode); + mv2.visitInsn(opcode); + } + + public void visitIntInsn(int opcode, int operand) { + mv1.visitIntInsn(opcode, operand); + mv2.visitIntInsn(opcode, operand); + } + + public void visitVarInsn(int opcode, int var) { + mv1.visitVarInsn(opcode, var); + mv2.visitVarInsn(opcode, var); + } + + public void visitTypeInsn(int opcode, String desc) { + mv1.visitTypeInsn(opcode, desc); + mv2.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + mv1.visitFieldInsn(opcode, owner, name, desc); + mv2.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + mv1.visitMethodInsn(opcode, owner, name, desc); + mv2.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitJumpInsn(int opcode, Label label) { + mv1.visitJumpInsn(opcode, label); + mv2.visitJumpInsn(opcode, label); + } + + public void visitLabel(Label label) { + mv1.visitLabel(label); + mv2.visitLabel(label); + } + + public void visitLdcInsn(Object cst) { + mv1.visitLdcInsn(cst); + mv2.visitLdcInsn(cst); + } + + public void visitIincInsn(int var, int increment) { + mv1.visitIincInsn(var, increment); + mv2.visitIincInsn(var, increment); + } + + + // 代码改了。如果报错改回来 最后1个参数 labers[] TODO + public void visitTableSwitchInsn (int min, int max, Label dflt, Label labels) { + mv1.visitTableSwitchInsn(min, max, dflt, labels); + mv2.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) { + mv1.visitLookupSwitchInsn(dflt, keys, labels); + mv2.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn(String desc, int dims) { + mv1.visitMultiANewArrayInsn(desc, dims); + mv2.visitMultiANewArrayInsn(desc, dims); + } + + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + mv1.visitTryCatchBlock(start, end, handler, type); + mv2.visitTryCatchBlock(start, end, handler, type); + } + + public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { + mv1.visitLocalVariable(name, desc, signature, start, end, index); + mv2.visitLocalVariable(name, desc, signature, start, end, index); + } + + public void visitLineNumber(int line, Label start) { + mv1.visitLineNumber(line, start); + mv2.visitLineNumber(line, start); + } + + public void visitMaxs(int maxStack, int maxLocals) { + mv1.visitMaxs(maxStack, maxLocals); + mv2.visitMaxs(maxStack, maxLocals); + } + + public void visitEnd() { + mv1.visitEnd(); + mv2.visitEnd(); + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassGenerator.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassGenerator.java new file mode 100644 index 000000000..22e5d5042 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassGenerator.java @@ -0,0 +1,34 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +public class TransformingClassGenerator implements ClassGenerator { + private ClassGenerator gen; + private ClassTransformer t; + + public TransformingClassGenerator(ClassGenerator gen, ClassTransformer t) { + this.gen = gen; + this.t = t; + } + + public void generateClass(ClassVisitor v) throws Exception { + t.setTarget(v); + gen.generateClass(t); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassLoader.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassLoader.java new file mode 100644 index 000000000..2d8300ff8 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/TransformingClassLoader.java @@ -0,0 +1,33 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform; + +import com.fr.third.springframework.asm.ClassReader; +import com.fr.third.springframework.cglib.core.*; + +public class TransformingClassLoader extends AbstractClassLoader { + private ClassTransformerFactory t; + + public TransformingClassLoader(ClassLoader parent, ClassFilter filter, ClassTransformerFactory t) { + super(parent, parent, filter); + this.t = t; + } + + protected ClassGenerator getGenerator(ClassReader r) { + ClassTransformer t2 = (ClassTransformer)t.newInstance(); + return new TransformingClassGenerator(super.getGenerator(r), t2); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AbstractInterceptFieldCallback.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AbstractInterceptFieldCallback.java new file mode 100644 index 000000000..cfd728571 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AbstractInterceptFieldCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +/** + * @author Chris Nokleberg + */ +public class AbstractInterceptFieldCallback implements InterceptFieldCallback { + + public int writeInt(Object obj, String name, int oldValue, int newValue) { return newValue; } + public char writeChar(Object obj, String name, char oldValue, char newValue) { return newValue; } + public byte writeByte(Object obj, String name, byte oldValue, byte newValue) { return newValue; } + public boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue) { return newValue; } + public short writeShort(Object obj, String name, short oldValue, short newValue) { return newValue; } + public float writeFloat(Object obj, String name, float oldValue, float newValue) { return newValue; } + public double writeDouble(Object obj, String name, double oldValue, double newValue) { return newValue; } + public long writeLong(Object obj, String name, long oldValue, long newValue) { return newValue; } + public Object writeObject(Object obj, String name, Object oldValue, Object newValue) { return newValue; } + + public int readInt(Object obj, String name, int oldValue) { return oldValue; } + public char readChar(Object obj, String name, char oldValue) { return oldValue; } + public byte readByte(Object obj, String name, byte oldValue) { return oldValue; } + public boolean readBoolean(Object obj, String name, boolean oldValue) { return oldValue; } + public short readShort(Object obj, String name, short oldValue) { return oldValue; } + public float readFloat(Object obj, String name, float oldValue) { return oldValue; } + public double readDouble(Object obj, String name, double oldValue) { return oldValue; } + public long readLong(Object obj, String name, long oldValue) { return oldValue; } + public Object readObject(Object obj, String name, Object oldValue) { return oldValue; } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AccessFieldTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AccessFieldTransformer.java new file mode 100644 index 000000000..88adb6a97 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AccessFieldTransformer.java @@ -0,0 +1,61 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.transform.*; +import com.fr.third.springframework.cglib.core.*; + +public class AccessFieldTransformer extends ClassEmitterTransformer { + private Callback callback; + + public AccessFieldTransformer(Callback callback) { + this.callback = callback; + } + + public interface Callback { + String getPropertyName(Type owner, String fieldName); + } + + public void declare_field(int access, final String name, Type type, Object value) { + super.declare_field(access, name, type, value); + + String property = TypeUtils.upperFirst(callback.getPropertyName(getClassType(), name)); + if (property != null) { + CodeEmitter e; + e = begin_method(Constants.ACC_PUBLIC, + new Signature("get" + property, + type, + Constants.TYPES_EMPTY), + null); + e.load_this(); + e.getfield(name); + e.return_value(); + e.end_method(); + + e = begin_method(Constants.ACC_PUBLIC, + new Signature("set" + property, + Type.VOID_TYPE, + new Type[]{ type }), + null); + e.load_this(); + e.load_arg(0); + e.putfield(name); + e.return_value(); + e.end_method(); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddDelegateTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddDelegateTransformer.java new file mode 100644 index 000000000..af6506cfc --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddDelegateTransformer.java @@ -0,0 +1,126 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.CodeEmitter; +import com.fr.third.springframework.cglib.core.CodeGenerationException; +import com.fr.third.springframework.cglib.core.Constants; +import com.fr.third.springframework.cglib.core.ReflectUtils; +import com.fr.third.springframework.cglib.core.Signature; +import com.fr.third.springframework.cglib.core.TypeUtils; +import com.fr.third.springframework.cglib.transform.ClassEmitterTransformer; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * @author Juozas Baliuka + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class AddDelegateTransformer extends ClassEmitterTransformer { + private static final String DELEGATE = "$CGLIB_DELEGATE"; + private static final Signature CSTRUCT_OBJECT = + TypeUtils.parseSignature("void (Object)"); + + private Class[] delegateIf; + private Class delegateImpl; + private Type delegateType; + + /** Creates a new instance of AddDelegateTransformer */ + public AddDelegateTransformer(Class delegateIf[], Class delegateImpl) { + try { + delegateImpl.getConstructor(new Class[]{ Object.class }); + this.delegateIf = delegateIf; + this.delegateImpl = delegateImpl; + delegateType = Type.getType(delegateImpl); + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + } + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + + if(!TypeUtils.isInterface(access)){ + + Type[] all = TypeUtils.add(interfaces, TypeUtils.getTypes(delegateIf)); + super.begin_class(version, access, className, superType, all, sourceFile); + + declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT, + DELEGATE, + delegateType, + null); + for (int i = 0; i < delegateIf.length; i++) { + Method[] methods = delegateIf[i].getMethods(); + for (int j = 0; j < methods.length; j++) { + if (Modifier.isAbstract(methods[j].getModifiers())) { + addDelegate(methods[j]); + } + } + } + }else{ + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + final CodeEmitter e = super.begin_method(access, sig, exceptions); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + return new CodeEmitter(e) { + private boolean transformInit = true; + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + super.visitMethodInsn(opcode, owner, name, desc); + if (transformInit && opcode == Constants.INVOKESPECIAL) { + load_this(); + new_instance(delegateType); + dup(); + load_this(); + invoke_constructor(delegateType, CSTRUCT_OBJECT); + putfield(DELEGATE); + transformInit = false; + } + } + }; + } + return e; + } + + private void addDelegate(Method m) { + Method delegate; + try { + delegate = delegateImpl.getMethod(m.getName(), m.getParameterTypes()); + if (!delegate.getReturnType().getName().equals(m.getReturnType().getName())){ + throw new IllegalArgumentException("Invalid delegate signature " + delegate); + } + } catch (NoSuchMethodException e) { + throw new CodeGenerationException(e); + } + + final Signature sig = ReflectUtils.getSignature(m); + Type[] exceptions = TypeUtils.getTypes(m.getExceptionTypes()); + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, sig, exceptions); + e.load_this(); + e.getfield(DELEGATE); + e.load_args(); + e.invoke_virtual(delegateType, sig); + e.return_value(); + e.end_method(); + } +} + + + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddInitTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddInitTransformer.java new file mode 100644 index 000000000..9345a5353 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddInitTransformer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import java.lang.reflect.Method; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; + + +/** + * @author Mark Hobson + */ + +public class AddInitTransformer extends ClassEmitterTransformer { + private MethodInfo info; + + public AddInitTransformer(Method method) { + info = ReflectUtils.getMethodInfo(method); + + Type[] types = info.getSignature().getArgumentTypes(); + if (types.length != 1 || + !types[0].equals(Constants.TYPE_OBJECT) || + !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) { + throw new IllegalArgumentException(method + " illegal signature"); + } + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + final CodeEmitter emitter = super.begin_method(access, sig, exceptions); + if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) { + return new CodeEmitter(emitter) { + public void visitInsn(int opcode) { + if (opcode == Constants.RETURN) { + load_this(); + invoke(info); + } + super.visitInsn(opcode); + } + }; + } + return emitter; + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddPropertyTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddPropertyTransformer.java new file mode 100644 index 000000000..93be87ad7 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddPropertyTransformer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; +import java.util.*; +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class AddPropertyTransformer extends ClassEmitterTransformer { + private final String[] names; + private final Type[] types; + + public AddPropertyTransformer(Map props) { + int size = props.size(); + names = (String[])props.keySet().toArray(new String[size]); + types = new Type[size]; + for (int i = 0; i < size; i++) { + types[i] = (Type)props.get(names[i]); + } + } + + public AddPropertyTransformer(String[] names, Type[] types) { + this.names = names; + this.types = types; + } + + public void end_class() { + if (!TypeUtils.isAbstract(getAccess())) { + EmitUtils.add_properties(this, names, types); + } + super.end_class(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddStaticInitTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddStaticInitTransformer.java new file mode 100644 index 000000000..001af52c2 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/AddStaticInitTransformer.java @@ -0,0 +1,50 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import java.lang.reflect.Method; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class AddStaticInitTransformer extends ClassEmitterTransformer { + private MethodInfo info; + + public AddStaticInitTransformer(Method classInit) { + info = ReflectUtils.getMethodInfo(classInit); + if (!TypeUtils.isStatic(info.getModifiers())) { + throw new IllegalArgumentException(classInit + " is not static"); + } + Type[] types = info.getSignature().getArgumentTypes(); + if (types.length != 1 || + !types[0].equals(Constants.TYPE_CLASS) || + !info.getSignature().getReturnType().equals(Type.VOID_TYPE)) { + throw new IllegalArgumentException(classInit + " illegal signature"); + } + } + + protected void init() { + if (!TypeUtils.isInterface(getAccess())) { + CodeEmitter e = getStaticHook(); + EmitUtils.load_class_this(e); + e.invoke(info); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProvider.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProvider.java new file mode 100644 index 000000000..d1327b331 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +public interface FieldProvider { + + String[] getFieldNames(); + + @SuppressWarnings("rawtypes") + Class[] getFieldTypes(); + + void setField(int index, Object value); + + Object getField(int index); + + + void setField(String name, Object value); + + Object getField(String name); + + +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProviderTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProviderTransformer.java new file mode 100644 index 000000000..baf6fbbcf --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/FieldProviderTransformer.java @@ -0,0 +1,208 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; +import java.util.*; + +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class FieldProviderTransformer extends ClassEmitterTransformer { + + private static final String FIELD_NAMES = "CGLIB$FIELD_NAMES"; + private static final String FIELD_TYPES = "CGLIB$FIELD_TYPES"; + + private static final Type FIELD_PROVIDER = + TypeUtils.parseType("com.fr.third.springframework.cglib.transform.impl.FieldProvider"); + private static final Type ILLEGAL_ARGUMENT_EXCEPTION = + TypeUtils.parseType("IllegalArgumentException"); + private static final Signature PROVIDER_GET = + TypeUtils.parseSignature("Object getField(String)"); + private static final Signature PROVIDER_SET = + TypeUtils.parseSignature("void setField(String, Object)"); + private static final Signature PROVIDER_SET_BY_INDEX = + TypeUtils.parseSignature("void setField(int, Object)"); + private static final Signature PROVIDER_GET_BY_INDEX = + TypeUtils.parseSignature("Object getField(int)"); + private static final Signature PROVIDER_GET_TYPES = + TypeUtils.parseSignature("Class[] getFieldTypes()"); + private static final Signature PROVIDER_GET_NAMES = + TypeUtils.parseSignature("String[] getFieldNames()"); + + private int access; + private Map fields; + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + if (!TypeUtils.isAbstract(access)) { + interfaces = TypeUtils.add(interfaces, FIELD_PROVIDER); + } + this.access = access; + fields = new HashMap(); + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + + public void declare_field(int access, String name, Type type, Object value) { + super.declare_field(access, name, type, value); + + if (!TypeUtils.isStatic(access)) { + fields.put(name, type); + } + } + + public void end_class() { + if (!TypeUtils.isInterface(access)) { + try { + generate(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new CodeGenerationException(e); + } + } + super.end_class(); + } + + private void generate() throws Exception { + final String[] names = (String[])fields.keySet().toArray(new String[fields.size()]); + + int indexes[] = new int[names.length]; + for (int i = 0; i < indexes.length; i++) { + indexes[i] = i; + } + + super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_NAMES, Constants.TYPE_STRING_ARRAY, null); + super.declare_field(Constants.PRIVATE_FINAL_STATIC, FIELD_TYPES, Constants.TYPE_CLASS_ARRAY, null); + + // use separate methods here because each process switch inner class needs a final CodeEmitter + initFieldProvider(names); + getNames(); + getTypes(); + getField(names); + setField(names); + setByIndex(names, indexes); + getByIndex(names, indexes); + } + + private void initFieldProvider(String[] names) { + CodeEmitter e = getStaticHook(); + EmitUtils.push_object(e, names); + e.putstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); + + e.push(names.length); + e.newarray(Constants.TYPE_CLASS); + e.dup(); + for(int i = 0; i < names.length; i++ ){ + e.dup(); + e.push(i); + Type type = (Type)fields.get(names[i]); + EmitUtils.load_class(e, type); + e.aastore(); + } + e.putstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); + } + + private void getNames() { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_NAMES, null); + e.getstatic(getClassType(), FIELD_NAMES, Constants.TYPE_STRING_ARRAY); + e.return_value(); + e.end_method(); + } + + private void getTypes() { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_TYPES, null); + e.getstatic(getClassType(), FIELD_TYPES, Constants.TYPE_CLASS_ARRAY); + e.return_value(); + e.end_method(); + } + + private void setByIndex(final String[] names, final int[] indexes) throws Exception { + final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_SET_BY_INDEX, null); + e.load_this(); + e.load_arg(1); + e.load_arg(0); + e.process_switch(indexes, new ProcessSwitchCallback() { + public void processCase(int key, Label end) throws Exception { + Type type = (Type)fields.get(names[key]); + e.unbox(type); + e.putfield(names[key]); + e.return_value(); + } + public void processDefault() throws Exception { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); + } + }); + e.end_method(); + } + + private void getByIndex(final String[] names, final int[] indexes) throws Exception { + final CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, PROVIDER_GET_BY_INDEX, null); + e.load_this(); + e.load_arg(0); + e.process_switch(indexes, new ProcessSwitchCallback() { + public void processCase(int key, Label end) throws Exception { + Type type = (Type)fields.get(names[key]); + e.getfield(names[key]); + e.box(type); + e.return_value(); + } + public void processDefault() throws Exception { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field index"); + } + }); + e.end_method(); + } + + // if this is used to enhance class files SWITCH_STYLE_TRIE should be used + // to avoid JVM hashcode implementation incompatibilities + private void getField(String[] names) throws Exception { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_GET, null); + e.load_this(); + e.load_arg(0); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + Type type = (Type)fields.get(key); + e.getfield((String)key); + e.box(type); + e.return_value(); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); + } + }); + e.end_method(); + } + + private void setField(String[] names) throws Exception { + final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, PROVIDER_SET, null); + e.load_this(); + e.load_arg(1); + e.load_arg(0); + EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + Type type = (Type)fields.get(key); + e.unbox(type); + e.putfield((String)key); + e.return_value(); + } + public void processDefault() { + e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Unknown field name"); + } + }); + e.end_method(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldCallback.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldCallback.java new file mode 100644 index 000000000..19962888e --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldCallback.java @@ -0,0 +1,42 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +/** + * @author Juozas Baliuka + */ +public interface InterceptFieldCallback { + + int writeInt(Object obj, String name, int oldValue, int newValue); + char writeChar(Object obj, String name, char oldValue, char newValue); + byte writeByte(Object obj, String name, byte oldValue, byte newValue); + boolean writeBoolean(Object obj, String name, boolean oldValue, boolean newValue); + short writeShort(Object obj, String name, short oldValue, short newValue); + float writeFloat(Object obj, String name, float oldValue, float newValue); + double writeDouble(Object obj, String name, double oldValue, double newValue); + long writeLong(Object obj, String name, long oldValue, long newValue); + Object writeObject(Object obj, String name, Object oldValue, Object newValue); + + int readInt(Object obj, String name, int oldValue); + char readChar(Object obj, String name, char oldValue); + byte readByte(Object obj, String name, byte oldValue); + boolean readBoolean(Object obj, String name, boolean oldValue); + short readShort(Object obj, String name, short oldValue); + float readFloat(Object obj, String name, float oldValue); + double readDouble(Object obj, String name, double oldValue); + long readLong(Object obj, String name, long oldValue); + Object readObject(Object obj, String name, Object oldValue); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldEnabled.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldEnabled.java new file mode 100644 index 000000000..492f05a67 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldEnabled.java @@ -0,0 +1,21 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +public interface InterceptFieldEnabled { + void setInterceptFieldCallback(InterceptFieldCallback callback); + InterceptFieldCallback getInterceptFieldCallback(); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldFilter.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldFilter.java new file mode 100644 index 000000000..796b3cd0e --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldFilter.java @@ -0,0 +1,24 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + + +import com.fr.third.springframework.asm.Type; + +public interface InterceptFieldFilter { + boolean acceptRead(Type owner, String name); + boolean acceptWrite(Type owner, String name); +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldTransformer.java new file mode 100644 index 000000000..4c48d3614 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/InterceptFieldTransformer.java @@ -0,0 +1,213 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; + + +/** + * @author Juozas Baliuka, Chris Nokleberg + */ +public class InterceptFieldTransformer extends ClassEmitterTransformer { + private static final String CALLBACK_FIELD = "$CGLIB_READ_WRITE_CALLBACK"; + private static final Type CALLBACK = + TypeUtils.parseType("com.fr.third.springframework.cglib.transform.impl.InterceptFieldCallback"); + //TypeUtils.parseType("net.sf.cglib.transform.impl.InterceptFieldCallback"); + private static final Type ENABLED = + TypeUtils.parseType("com.fr.third.springframework.cglib.transform.impl.InterceptFieldEnabled"); + //TypeUtils.parseType("net.sf.cglib.transform.impl.InterceptFieldEnabled"); + private static final Signature ENABLED_SET = + new Signature("setInterceptFieldCallback", Type.VOID_TYPE, new Type[]{ CALLBACK }); + private static final Signature ENABLED_GET = + new Signature("getInterceptFieldCallback", CALLBACK, new Type[0]); + + private InterceptFieldFilter filter; + + public InterceptFieldTransformer(InterceptFieldFilter filter) { + this.filter = filter; + } + + public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String sourceFile) { + if (!TypeUtils.isInterface(access)) { + super.begin_class(version, access, className, superType, TypeUtils.add(interfaces, ENABLED), sourceFile); + + super.declare_field(Constants.ACC_PRIVATE | Constants.ACC_TRANSIENT, + CALLBACK_FIELD, + CALLBACK, + null); + + CodeEmitter e; + e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_GET, null); + e.load_this(); + e.getfield(CALLBACK_FIELD); + e.return_value(); + e.end_method(); + + e = super.begin_method(Constants.ACC_PUBLIC, ENABLED_SET, null); + e.load_this(); + e.load_arg(0); + e.putfield(CALLBACK_FIELD); + e.return_value(); + e.end_method(); + } else { + super.begin_class(version, access, className, superType, interfaces, sourceFile); + } + } + + public void declare_field(int access, String name, Type type, Object value) { + super.declare_field(access, name, type, value); + if (!TypeUtils.isStatic(access)) { + if (filter.acceptRead(getClassType(), name)) { + addReadMethod(name, type); + } + if (filter.acceptWrite(getClassType(), name)) { + addWriteMethod(name, type); + } + } + } + + private void addReadMethod(String name, Type type) { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, + readMethodSig(name, type.getDescriptor()), + null); + e.load_this(); + e.getfield(name); + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + Label intercept = e.make_label(); + e.ifnonnull(intercept); + e.return_value(); + + e.mark(intercept); + Local result = e.make_local(type); + e.store_local(result); + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + e.load_this(); + e.push(name); + e.load_local(result); + e.invoke_interface(CALLBACK, readCallbackSig(type)); + if (!TypeUtils.isPrimitive(type)) { + e.checkcast(type); + } + e.return_value(); + e.end_method(); + } + + private void addWriteMethod(String name, Type type) { + CodeEmitter e = super.begin_method(Constants.ACC_PUBLIC, + writeMethodSig(name, type.getDescriptor()), + null); + e.load_this(); + e.dup(); + e.invoke_interface(ENABLED,ENABLED_GET); + Label skip = e.make_label(); + e.ifnull(skip); + + e.load_this(); + e.invoke_interface(ENABLED,ENABLED_GET); + e.load_this(); + e.push(name); + e.load_this(); + e.getfield(name); + e.load_arg(0); + e.invoke_interface(CALLBACK, writeCallbackSig(type)); + if (!TypeUtils.isPrimitive(type)) { + e.checkcast(type); + } + Label go = e.make_label(); + e.goTo(go); + e.mark(skip); + e.load_arg(0); + e.mark(go); + e.putfield(name); + e.return_value(); + e.end_method(); + } + + public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) { + return new CodeEmitter(super.begin_method(access, sig, exceptions)) { + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + Type towner = TypeUtils.fromInternalName(owner); + switch (opcode) { + case Constants.GETFIELD: + if (filter.acceptRead(towner, name)) { + helper(towner, readMethodSig(name, desc)); + return; + } + break; + case Constants.PUTFIELD: + if (filter.acceptWrite(towner, name)) { + helper(towner, writeMethodSig(name, desc)); + return; + } + break; + } + super.visitFieldInsn(opcode, owner, name, desc); + } + + private void helper(Type owner, Signature sig) { + invoke_virtual(owner, sig); + } + }; + } + + private static Signature readMethodSig(String name, String desc) { + return new Signature("$cglib_read_" + name, "()" + desc); + } + + private static Signature writeMethodSig(String name, String desc) { + return new Signature("$cglib_write_" + name, "(" + desc + ")V"); + } + + private static Signature readCallbackSig(Type type) { + Type remap = remap(type); + return new Signature("read" + callbackName(remap), + remap, + new Type[]{ Constants.TYPE_OBJECT, + Constants.TYPE_STRING, + remap }); + } + + private static Signature writeCallbackSig(Type type) { + Type remap = remap(type); + return new Signature("write" + callbackName(remap), + remap, + new Type[]{ Constants.TYPE_OBJECT, + Constants.TYPE_STRING, + remap, + remap }); + } + + private static Type remap(Type type) { + switch (type.getSort()) { + case Type.OBJECT: + case Type.ARRAY: + return Constants.TYPE_OBJECT; + default: + return type; + } + } + + private static String callbackName(Type type) { + return (type == Constants.TYPE_OBJECT) ? + "Object" : + TypeUtils.upperFirst(TypeUtils.getClassName(type)); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableStrategy.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableStrategy.java new file mode 100644 index 000000000..32b036186 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableStrategy.java @@ -0,0 +1,56 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; + +/** + * A {@link GeneratorStrategy} suitable for use with {@link com.fr.third.springframework.cglib.Enhancer} which + * causes all undeclared exceptions thrown from within a proxied method to be wrapped + * in an alternative exception of your choice. + */ +@SuppressWarnings({ "rawtypes"}) +public class UndeclaredThrowableStrategy extends DefaultGeneratorStrategy { + + + private Class wrapper; + + /** + * Create a new instance of this strategy. + * @param wrapper a class which extends either directly or + * indirectly from Throwable and which has at least one + * constructor that takes a single argument of type + * Throwable, for example + * java.lang.reflect.UndeclaredThrowableException.class + */ + public UndeclaredThrowableStrategy(Class wrapper) { + this.wrapper = wrapper; + } + + private static final MethodFilter TRANSFORM_FILTER = new MethodFilter() { + public boolean accept(int access, String name, String desc, String signature, String[] exceptions) { + return !TypeUtils.isPrivate(access) && name.indexOf('$') < 0; + } + }; + + protected ClassGenerator transform(ClassGenerator cg) throws Exception { + ClassTransformer tr = new UndeclaredThrowableTransformer(wrapper); + tr = new MethodFilterTransformer(TRANSFORM_FILTER, tr); + return new TransformingClassGenerator(cg, tr); + } +} + diff --git a/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableTransformer.java b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableTransformer.java new file mode 100644 index 000000000..db31664a8 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/transform/impl/UndeclaredThrowableTransformer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.transform.impl; + +import java.lang.reflect.Constructor; + +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; +import com.fr.third.springframework.cglib.transform.*; +@SuppressWarnings({ "rawtypes" }) +public class UndeclaredThrowableTransformer extends ClassEmitterTransformer { + private Type wrapper; + + public UndeclaredThrowableTransformer(Class wrapper) { + this.wrapper = Type.getType(wrapper); + boolean found = false; + Constructor[] cstructs = wrapper.getConstructors(); + for (int i = 0; i < cstructs.length; i++) { + Class[] types = cstructs[i].getParameterTypes(); + if (types.length == 1 && types[0].equals(Throwable.class)) { + found = true; + break; + } + } + if (!found) + throw new IllegalArgumentException(wrapper + " does not have a single-arg constructor that takes a Throwable"); + } + + public CodeEmitter begin_method(int access, final Signature sig, final Type[] exceptions) { + CodeEmitter e = super.begin_method(access, sig, exceptions); + if (TypeUtils.isAbstract(access) || sig.equals(Constants.SIG_STATIC)) { + return e; + } + return new CodeEmitter(e) { + private Block handler; + /* init */ { + handler = begin_block(); + } + public void visitMaxs(int maxStack, int maxLocals) { + handler.end(); + EmitUtils.wrap_undeclared_throwable(this, handler, exceptions, wrapper); + super.visitMaxs(maxStack, maxLocals); + } + }; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorter.java b/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorter.java new file mode 100644 index 000000000..cc2572610 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorter.java @@ -0,0 +1,295 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.util; + +import java.util.Comparator; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.cglib.core.*; + +/** + * For the efficient sorting of multiple arrays in parallel. + *

+ * Given two arrays of equal length and varying types, the standard + * technique for sorting them in parallel is to create a new temporary + * object for each row, store the objects in a temporary array, sort the + * array using a custom comparator, and the extract the original values + * back into their respective arrays. This is wasteful in both time and + * memory. + *

+ * This class generates bytecode customized to the particular set of + * arrays you need to sort, in such a way that both arrays are sorted + * in-place, simultaneously. + *

+ * Two sorting algorithms are provided. + * Quicksort is best when you only need to sort by a single column, as + * it requires very few comparisons and swaps. Mergesort is best used + * when sorting multiple columns, as it is a "stable" sort--that is, it + * does not affect the relative order of equal objects from previous sorts. + *

+ * The mergesort algorithm here is an "in-place" variant, which while + * slower, does not require a temporary array. + * + * @author Chris Nokleberg + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +abstract public class ParallelSorter extends SorterTemplate { + protected Object[] a; + private Comparer comparer; + + protected ParallelSorter() { + } + + abstract public ParallelSorter newInstance(Object[] arrays); + + /** + * Create a new ParallelSorter object for a set of arrays. You may + * sort the arrays multiple times via the same ParallelSorter object. + * @param arrays An array of arrays to sort. The arrays may be a mix + * of primitive and non-primitive types, but should all be the same + * length. + * @param loader ClassLoader for generated class, uses "current" if null + */ + public static ParallelSorter create(Object[] arrays) { + Generator gen = new Generator(); + gen.setArrays(arrays); + return gen.create(); + } + + private int len() { + return ((Object[])a[0]).length; + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + */ + public void quickSort(int index) { + quickSort(index, 0, len(), null); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void quickSort(int index, int lo, int hi) { + quickSort(index, lo, hi, null); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void quickSort(int index, Comparator cmp) { + quickSort(index, 0, len(), cmp); + } + + /** + * Sort the arrays using the quicksort algorithm. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void quickSort(int index, int lo, int hi, Comparator cmp) { + chooseComparer(index, cmp); + super.quickSort(lo, hi - 1); + } + + /** + * @param index array (column) to sort by + */ + public void mergeSort(int index) { + mergeSort(index, 0, len(), null); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void mergeSort(int index, int lo, int hi) { + mergeSort(index, lo, hi, null); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + */ + public void mergeSort(int index, Comparator cmp) { + mergeSort(index, 0, len(), cmp); + } + + /** + * Sort the arrays using an in-place merge sort. + * @param index array (column) to sort by + * @param lo starting array index (row), inclusive + * @param hi ending array index (row), exclusive + * @param cmp Comparator to use if the specified column is non-primitive + */ + public void mergeSort(int index, int lo, int hi, Comparator cmp) { + chooseComparer(index, cmp); + super.mergeSort(lo, hi - 1); + } + + private void chooseComparer(int index, Comparator cmp) { + Object array = a[index]; + Class type = array.getClass().getComponentType(); + if (type.equals(Integer.TYPE)) { + comparer = new IntComparer((int[])array); + } else if (type.equals(Long.TYPE)) { + comparer = new LongComparer((long[])array); + } else if (type.equals(Double.TYPE)) { + comparer = new DoubleComparer((double[])array); + } else if (type.equals(Float.TYPE)) { + comparer = new FloatComparer((float[])array); + } else if (type.equals(Short.TYPE)) { + comparer = new ShortComparer((short[])array); + } else if (type.equals(Byte.TYPE)) { + comparer = new ByteComparer((byte[])array); + } else if (cmp != null) { + comparer = new ComparatorComparer((Object[])array, cmp); + } else { + comparer = new ObjectComparer((Object[])array); + } + } + + protected int compare(int i, int j) { + return comparer.compare(i, j); + } + + interface Comparer { + int compare(int i, int j); + } + + static class ComparatorComparer implements Comparer { + private Object[] a; + private Comparator cmp; + + public ComparatorComparer(Object[] a, Comparator cmp) { + this.a = a; + this.cmp = cmp; + } + + public int compare(int i, int j) { + return cmp.compare(a[i], a[j]); + } + } + + static class ObjectComparer implements Comparer { + private Object[] a; + public ObjectComparer(Object[] a) { this.a = a; } + public int compare(int i, int j) { + return ((Comparable)a[i]).compareTo(a[j]); + } + } + + static class IntComparer implements Comparer { + private int[] a; + public IntComparer(int[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + static class LongComparer implements Comparer { + private long[] a; + public LongComparer(long[] a) { this.a = a; } + public int compare(int i, int j) { + long vi = a[i]; + long vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class FloatComparer implements Comparer { + private float[] a; + public FloatComparer(float[] a) { this.a = a; } + public int compare(int i, int j) { + float vi = a[i]; + float vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class DoubleComparer implements Comparer { + private double[] a; + public DoubleComparer(double[] a) { this.a = a; } + public int compare(int i, int j) { + double vi = a[i]; + double vj = a[j]; + return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; + } + } + + static class ShortComparer implements Comparer { + private short[] a; + public ShortComparer(short[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + static class ByteComparer implements Comparer { + private byte[] a; + public ByteComparer(byte[] a) { this.a = a; } + public int compare(int i, int j) { return a[i] - a[j]; } + } + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(ParallelSorter.class.getName()); + + private Object[] arrays; + + public Generator() { + super(SOURCE); + } + + protected ClassLoader getDefaultClassLoader() { + return null; + } + + public void setArrays(Object[] arrays) { + this.arrays = arrays; + } + + public ParallelSorter create() { + return (ParallelSorter)super.create(ClassesKey.create(arrays)); + } + + public void generateClass(ClassVisitor v) throws Exception { + if (arrays.length == 0) { + throw new IllegalArgumentException("No arrays specified to sort"); + } + for (int i = 0; i < arrays.length; i++) { + if (!arrays[i].getClass().isArray()) { + throw new IllegalArgumentException(arrays[i].getClass() + " is not an array"); + } + } + new ParallelSorterEmitter(v, getClassName(), arrays); + } + + protected Object firstInstance(Class type) { + return ((ParallelSorter)ReflectUtils.newInstance(type)).newInstance(arrays); + } + + protected Object nextInstance(Object instance) { + return ((ParallelSorter)instance).newInstance(arrays); + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorterEmitter.java b/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorterEmitter.java new file mode 100644 index 000000000..4de370b12 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/util/ParallelSorterEmitter.java @@ -0,0 +1,100 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.util; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +class ParallelSorterEmitter extends ClassEmitter { + private static final Type PARALLEL_SORTER = + TypeUtils.parseType("com.fr.third.springframework.cglib.util.ParallelSorter"); + //TypeUtils.parseType("net.sf.cglib.util.ParallelSorter"); + private static final Signature CSTRUCT_OBJECT_ARRAY = + TypeUtils.parseConstructor("Object[]"); + private static final Signature NEW_INSTANCE = + new Signature("newInstance", PARALLEL_SORTER, new Type[]{ Constants.TYPE_OBJECT_ARRAY }); + private static final Signature SWAP = + TypeUtils.parseSignature("void swap(int, int)"); + + public ParallelSorterEmitter(ClassVisitor v, String className, Object[] arrays) { + super(v); + begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, PARALLEL_SORTER, null, Constants.SOURCE_FILE); + EmitUtils.null_constructor(this); + EmitUtils.factory_method(this, NEW_INSTANCE); + generateConstructor(arrays); + generateSwap(arrays); + end_class(); + } + + private String getFieldName(int index) { + return "FIELD_" + index; + } + + private void generateConstructor(Object[] arrays) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT_ARRAY, null); + e.load_this(); + e.super_invoke_constructor(); + e.load_this(); + e.load_arg(0); + e.super_putfield("a", Constants.TYPE_OBJECT_ARRAY); + for (int i = 0; i < arrays.length; i++) { + Type type = Type.getType(arrays[i].getClass()); + declare_field(Constants.ACC_PRIVATE, getFieldName(i), type, null); + e.load_this(); + e.load_arg(0); + e.push(i); + e.aaload(); + e.checkcast(type); + e.putfield(getFieldName(i)); + } + e.return_value(); + e.end_method(); + } + + private void generateSwap(final Object[] arrays) { + CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SWAP, null); + for (int i = 0; i < arrays.length; i++) { + Type type = Type.getType(arrays[i].getClass()); + Type component = TypeUtils.getComponentType(type); + Local T = e.make_local(type); + + e.load_this(); + e.getfield(getFieldName(i)); + e.store_local(T); + + e.load_local(T); + e.load_arg(0); + + e.load_local(T); + e.load_arg(1); + e.array_load(component); + + e.load_local(T); + e.load_arg(1); + + e.load_local(T); + e.load_arg(0); + e.array_load(component); + + e.array_store(component); + e.array_store(component); + } + e.return_value(); + e.end_method(); + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/util/SorterTemplate.java b/fine-spring/src/com/fr/third/springframework/cglib/util/SorterTemplate.java new file mode 100644 index 000000000..3d9e8bb43 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/util/SorterTemplate.java @@ -0,0 +1,172 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.util; + + +abstract class SorterTemplate { + private static final int MERGESORT_THRESHOLD = 12; + private static final int QUICKSORT_THRESHOLD = 7; + + abstract protected void swap(int i, int j); + abstract protected int compare(int i, int j); + + protected void quickSort(int lo, int hi) { + quickSortHelper(lo, hi); + insertionSort(lo, hi); + } + + private void quickSortHelper(int lo, int hi) { + for (;;) { + int diff = hi - lo; + if (diff <= QUICKSORT_THRESHOLD) { + break; + } + int i = (hi + lo) / 2; + if (compare(lo, i) > 0) { + swap(lo, i); + } + if (compare(lo, hi) > 0) { + swap(lo, hi); + } + if (compare(i, hi) > 0) { + swap(i, hi); + } + int j = hi - 1; + swap(i, j); + i = lo; + int v = j; + for (;;) { + while (compare(++i, v) < 0) { + /* nothing */; + } + while (compare(--j, v) > 0) { + /* nothing */; + } + if (j < i) { + break; + } + swap(i, j); + } + swap(i, hi - 1); + if (j - lo <= hi - i + 1) { + quickSortHelper(lo, j); + lo = i + 1; + } else { + quickSortHelper(i + 1, hi); + hi = j; + } + } + } + + private void insertionSort(int lo, int hi) { + for (int i = lo + 1 ; i <= hi; i++) { + for (int j = i; j > lo; j--) { + if (compare(j - 1, j) > 0) { + swap(j - 1, j); + } else { + break; + } + } + } + } + + protected void mergeSort(int lo, int hi) { + int diff = hi - lo; + if (diff <= MERGESORT_THRESHOLD) { + insertionSort(lo, hi); + return; + } + int mid = lo + diff / 2; + mergeSort(lo, mid); + mergeSort(mid, hi); + merge(lo, mid, hi, mid - lo, hi - mid); + } + + private void merge(int lo, int pivot, int hi, int len1, int len2) { + if (len1 == 0 || len2 == 0) { + return; + } + if (len1 + len2 == 2) { + if (compare(pivot, lo) < 0) { + swap(pivot, lo); + } + return; + } + int first_cut, second_cut; + int len11, len22; + if (len1 > len2) { + len11 = len1 / 2; + first_cut = lo + len11; + second_cut = lower(pivot, hi, first_cut); + len22 = second_cut - pivot; + } else { + len22 = len2 / 2; + second_cut = pivot + len22; + first_cut = upper(lo, pivot, second_cut); + len11 = first_cut - lo; + } + rotate(first_cut, pivot, second_cut); + int new_mid = first_cut + len22; + merge(lo, first_cut, new_mid, len11, len22); + merge(new_mid, second_cut, hi, len1 - len11, len2 - len22); + } + + private void rotate(int lo, int mid, int hi) { + int lot = lo; + int hit = mid - 1; + while (lot < hit) { + swap(lot++, hit--); + } + lot = mid; hit = hi - 1; + while (lot < hit) { + swap(lot++, hit--); + } + lot = lo; hit = hi - 1; + while (lot < hit) { + swap(lot++, hit--); + } + } + + private int lower(int lo, int hi, int val) { + int len = hi - lo; + while (len > 0) { + int half = len / 2; + int mid= lo + half; + if (compare(mid, val) < 0) { + lo = mid + 1; + len = len - half -1; + } else { + len = half; + } + } + return lo; + } + + private int upper(int lo, int hi, int val) { + int len = hi - lo; + while (len > 0) { + int half = len / 2; + int mid = lo + half; + if (compare(val, mid) < 0) { + len = half; + } else { + lo = mid + 1; + len = len - half -1; + } + } + return lo; + } +} diff --git a/fine-spring/src/com/fr/third/springframework/cglib/util/StringSwitcher.java b/fine-spring/src/com/fr/third/springframework/cglib/util/StringSwitcher.java new file mode 100644 index 000000000..22bdfef00 --- /dev/null +++ b/fine-spring/src/com/fr/third/springframework/cglib/util/StringSwitcher.java @@ -0,0 +1,158 @@ +/* + * Copyright 2003 The Apache Software Foundation + * + * 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 com.fr.third.springframework.cglib.util; + +import java.util.*; + +import com.fr.third.springframework.asm.ClassVisitor; +import com.fr.third.springframework.asm.Label; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.*; + + +/** + * This class implements a simple String->int mapping for a fixed set of keys. + */ +@SuppressWarnings({ "rawtypes" }) +abstract public class StringSwitcher { + private static final Type STRING_SWITCHER = + TypeUtils.parseType("com.fr.third.springframework.cglib.util.StringSwitcher"); + //TypeUtils.parseType("net.sf.cglib.util.StringSwitcher"); + private static final Signature INT_VALUE = + TypeUtils.parseSignature("int intValue(String)"); + private static final StringSwitcherKey KEY_FACTORY = + (StringSwitcherKey)KeyFactory.create(StringSwitcherKey.class); + + interface StringSwitcherKey { + public Object newInstance(String[] strings, int[] ints, boolean fixedInput); + } + + /** + * Helper method to create a StringSwitcher. + * For finer control over the generated instance, use a new instance of StringSwitcher.Generator + * instead of this static method. + * @param strings the array of String keys; must be the same length as the value array + * @param ints the array of integer results; must be the same length as the key array + * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as -1; if true, + * the result will be undefined, and the resulting code will be faster + */ + public static StringSwitcher create(String[] strings, int[] ints, boolean fixedInput) { + Generator gen = new Generator(); + gen.setStrings(strings); + gen.setInts(ints); + gen.setFixedInput(fixedInput); + return gen.create(); + } + + protected StringSwitcher() { + } + + /** + * Return the integer associated with the given key. + * @param s the key + * @return the associated integer value, or -1 if the key is unknown (unless + * fixedInput was specified when this StringSwitcher was created, + * in which case the return value for an unknown key is undefined) + */ + abstract public int intValue(String s); + + public static class Generator extends AbstractClassGenerator { + private static final Source SOURCE = new Source(StringSwitcher.class.getName()); + + private String[] strings; + private int[] ints; + private boolean fixedInput; + + public Generator() { + super(SOURCE); + } + + /** + * Set the array of recognized Strings. + * @param strings the array of String keys; must be the same length as the value array + * @see #setInts + */ + public void setStrings(String[] strings) { + this.strings = strings; + } + + /** + * Set the array of integer results. + * @param ints the array of integer results; must be the same length as the key array + * @see #setStrings + */ + public void setInts(int[] ints) { + this.ints = ints; + } + + /** + * Configure how unknown String keys will be handled. + * @param fixedInput if false, an unknown key will be returned from {@link #intValue} as -1; if true, + * the result will be undefined, and the resulting code will be faster + */ + public void setFixedInput(boolean fixedInput) { + this.fixedInput = fixedInput; + } + + protected ClassLoader getDefaultClassLoader() { + return getClass().getClassLoader(); + } + + /** + * Generate the StringSwitcher. + */ + public StringSwitcher create() { + setNamePrefix(StringSwitcher.class.getName()); + Object key = KEY_FACTORY.newInstance(strings, ints, fixedInput); + return (StringSwitcher)super.create(key); + } + + public void generateClass(ClassVisitor v) throws Exception { + ClassEmitter ce = new ClassEmitter(v); + ce.begin_class(Constants.V1_2, + Constants.ACC_PUBLIC, + getClassName(), + STRING_SWITCHER, + null, + Constants.SOURCE_FILE); + EmitUtils.null_constructor(ce); + final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, INT_VALUE, null); + e.load_arg(0); + final List stringList = Arrays.asList(strings); + int style = fixedInput ? Constants.SWITCH_STYLE_HASHONLY : Constants.SWITCH_STYLE_HASH; + EmitUtils.string_switch(e, strings, style, new ObjectSwitchCallback() { + public void processCase(Object key, Label end) { + e.push(ints[stringList.indexOf(key)]); + e.return_value(); + } + public void processDefault() { + e.push(-1); + e.return_value(); + } + }); + e.end_method(); + ce.end_class(); + } + + protected Object firstInstance(Class type) { + return (StringSwitcher)ReflectUtils.newInstance(type); + } + + protected Object nextInstance(Object instance) { + return instance; + } + } +} diff --git a/fine-spring/src/com/fr/third/springframework/context/annotation/ConfigurationClassEnhancer.java b/fine-spring/src/com/fr/third/springframework/context/annotation/ConfigurationClassEnhancer.java index f2134a0da..e3f53d4f3 100644 --- a/fine-spring/src/com/fr/third/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/fine-spring/src/com/fr/third/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -20,6 +20,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; +import com.fr.third.springframework.asm.Type; +import com.fr.third.springframework.cglib.core.Constants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,16 +36,16 @@ import com.fr.third.springframework.cglib.core.SpringNamingPolicy; import com.fr.third.springframework.core.annotation.AnnotationUtils; import com.fr.third.springframework.util.Assert; import com.fr.third.springframework.util.ReflectionUtils; -import org.springframework.cglib.core.ClassGenerator; -import org.springframework.cglib.core.DefaultGeneratorStrategy; -import org.springframework.cglib.proxy.Callback; -import org.springframework.cglib.proxy.CallbackFilter; -import org.springframework.cglib.proxy.Enhancer; -import org.springframework.cglib.proxy.MethodInterceptor; -import org.springframework.cglib.proxy.MethodProxy; -import org.springframework.cglib.proxy.NoOp; -import org.springframework.cglib.transform.ClassEmitterTransformer; -import org.springframework.cglib.transform.TransformingClassGenerator; +import com.fr.third.springframework.cglib.core.ClassGenerator; +import com.fr.third.springframework.cglib.core.DefaultGeneratorStrategy; +import com.fr.third.springframework.cglib.proxy.Callback; +import com.fr.third.springframework.cglib.proxy.CallbackFilter; +import com.fr.third.springframework.cglib.proxy.Enhancer; +import com.fr.third.springframework.cglib.proxy.MethodInterceptor; +import com.fr.third.springframework.cglib.proxy.MethodProxy; +import com.fr.third.springframework.cglib.proxy.NoOp; +import com.fr.third.springframework.cglib.transform.ClassEmitterTransformer; +import com.fr.third.springframework.cglib.transform.TransformingClassGenerator; /** * Enhances {@link Configuration} classes by generating a CGLIB subclass which @@ -200,15 +202,14 @@ class ConfigurationClassEnhancer { @Override protected ClassGenerator transform(ClassGenerator cg) throws Exception { - //ClassEmitterTransformer transformer = new ClassEmitterTransformer() { - // @Override - // public void end_class() { - // //declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null); - // super.end_class(); - // } - //}; - //return new TransformingClassGenerator(cg, transformer); - return super.transform(cg); + ClassEmitterTransformer transformer = new ClassEmitterTransformer() { + @Override + public void end_class() { + declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null); + super.end_class(); + } + }; + return new TransformingClassGenerator(cg, transformer); } } diff --git a/fine-spring/src/com/fr/third/springframework/instrument/classloading/ShadowingClassLoader.java b/fine-spring/src/com/fr/third/springframework/instrument/classloading/ShadowingClassLoader.java index d7e0db463..a3f5dc94b 100644 --- a/fine-spring/src/com/fr/third/springframework/instrument/classloading/ShadowingClassLoader.java +++ b/fine-spring/src/com/fr/third/springframework/instrument/classloading/ShadowingClassLoader.java @@ -48,7 +48,7 @@ public class ShadowingClassLoader extends DecoratingClassLoader { /** Packages that are excluded by default */ public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] {"java.", "javax.", "sun.", "oracle.", "com.sun.", "com.ibm.", "COM.ibm.", - "org.w3c.", "org.xml.", "org.dom4j.", "org.eclipse", "org.aspectj.", "net.sf.cglib", + "org.w3c.", "org.xml.", "org.dom4j.", "org.eclipse", "org.aspectj.", "com.fr.third.springframework.cglib", "com.fr.third.springframework.cglib", "org.apache.xerces.", "org.apache.commons.logging."}; diff --git a/fine-spring/src/com/fr/third/springframework/scripting/support/ScriptFactoryPostProcessor.java b/fine-spring/src/com/fr/third/springframework/scripting/support/ScriptFactoryPostProcessor.java index 0be2f527f..f98f8ce0f 100644 --- a/fine-spring/src/com/fr/third/springframework/scripting/support/ScriptFactoryPostProcessor.java +++ b/fine-spring/src/com/fr/third/springframework/scripting/support/ScriptFactoryPostProcessor.java @@ -47,7 +47,7 @@ import com.fr.third.springframework.beans.factory.support.DefaultListableBeanFac import com.fr.third.springframework.beans.factory.support.GenericBeanDefinition; import com.fr.third.springframework.context.ResourceLoaderAware; import com.fr.third.springframework.core.Conventions; -import org.springframework.cglib.core.Signature; +import com.fr.third.springframework.cglib.core.Signature; import com.fr.third.springframework.core.Ordered; import com.fr.third.springframework.core.io.DefaultResourceLoader; import com.fr.third.springframework.core.io.ResourceLoader; @@ -56,7 +56,7 @@ import com.fr.third.springframework.scripting.ScriptSource; import com.fr.third.springframework.util.ClassUtils; import com.fr.third.springframework.util.ObjectUtils; import com.fr.third.springframework.util.StringUtils; -import org.springframework.cglib.proxy.InterfaceMaker; +import com.fr.third.springframework.cglib.proxy.InterfaceMaker; /** diff --git a/fine-spring/src/com/fr/third/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/fine-spring/src/com/fr/third/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java index 29226c947..de0abbde7 100644 --- a/fine-spring/src/com/fr/third/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java +++ b/fine-spring/src/com/fr/third/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java @@ -128,7 +128,7 @@ import com.fr.third.springframework.web.servlet.mvc.support.DefaultHandlerExcept * @author Brian Clozel * @since 3.0 */ -class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { +public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { private static final boolean javaxValidationPresent = ClassUtils.isPresent( "javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()); diff --git a/fine-spring/src/com/fr/third/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/fine-spring/src/com/fr/third/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 5c0e544a0..ce35a6e69 100644 --- a/fine-spring/src/com/fr/third/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/fine-spring/src/com/fr/third/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -37,6 +37,7 @@ import com.fr.third.springframework.format.support.DefaultFormattingConversionSe import com.fr.third.springframework.format.support.FormattingConversionService; import com.fr.third.springframework.http.MediaType; import com.fr.third.springframework.http.converter.ByteArrayHttpMessageConverter; +import com.fr.third.springframework.http.converter.GenericHttpMessageConverter; import com.fr.third.springframework.http.converter.HttpMessageConverter; import com.fr.third.springframework.http.converter.ResourceHttpMessageConverter; import com.fr.third.springframework.http.converter.StringHttpMessageConverter; @@ -75,6 +76,7 @@ import com.fr.third.springframework.web.servlet.mvc.method.annotation.ExceptionH import com.fr.third.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import com.fr.third.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import com.fr.third.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; +import com.sun.prism.impl.BaseContext; /** * This is the main class providing the configuration behind the MVC Java config. @@ -84,7 +86,7 @@ import com.fr.third.springframework.web.servlet.mvc.support.DefaultHandlerExcept * necessary remembering to add {@link Configuration @Configuration} to the * subclass and {@link Bean @Bean} to overridden {@link Bean @Bean} methods. * For more details see the Javadoc of {@link EnableWebMvc @EnableWebMvc}. - * + *

*

This class registers the following {@link HandlerMapping}s:

* - * + *

*

Registers these {@link HandlerAdapter}s: *

- * + *

*

Registers a {@link HandlerExceptionResolverComposite} with this chain of * exception resolvers: *

- * + *

*

Both the {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver} are configured with default * instances of the following by default: @@ -135,568 +137,622 @@ import com.fr.third.springframework.web.servlet.mvc.support.DefaultHandlerExcept * @author Rossen Stoyanchev * @author Brian Clozel * @author Sebastien Deleuze - * @since 3.1 * @see EnableWebMvc * @see WebMvcConfigurer * @see WebMvcConfigurerAdapter + * @since 3.1 */ public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { - private static boolean romePresent = - ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", WebMvcConfigurationSupport.class.getClassLoader()); - - private static final boolean jaxb2Present = - ClassUtils.isPresent("javax.xml.bind.Binder", WebMvcConfigurationSupport.class.getClassLoader()); - - private static final boolean jackson2Present = - ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && - ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); - - private static final boolean jacksonPresent = - ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && - ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); - - - private ApplicationContext applicationContext; - - private ServletContext servletContext; - - private List interceptors; - - private ContentNegotiationManager contentNegotiationManager; - - private List> messageConverters; - - - /** - * Set the Spring {@link ApplicationContext}, e.g. for resource loading. - */ - @Override - public void setApplicationContext(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - /** - * Set the {@link javax.servlet.ServletContext}, e.g. for resource handling, - * looking up file extensions, etc. - */ - @Override - public void setServletContext(ServletContext servletContext) { - this.servletContext = servletContext; - } - - - /** - * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping - * requests to annotated controllers. - */ - @Bean - public RequestMappingHandlerMapping requestMappingHandlerMapping() { - RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); - handlerMapping.setOrder(0); - handlerMapping.setInterceptors(getInterceptors()); - handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager()); - - PathMatchConfigurer configurer = new PathMatchConfigurer(); - configurePathMatch(configurer); - if (configurer.isUseSuffixPatternMatch() != null) { - handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch()); - } - if (configurer.isUseRegisteredSuffixPatternMatch() != null) { - handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch()); - } - if (configurer.isUseTrailingSlashMatch() != null) { - handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch()); - } - if (configurer.getPathMatcher() != null) { - handlerMapping.setPathMatcher(configurer.getPathMatcher()); - } - if (configurer.getUrlPathHelper() != null) { - handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper()); - } - - return handlerMapping; - } - - /** - * Provide access to the shared handler interceptors used to configure - * {@link HandlerMapping} instances with. This method cannot be overridden, - * use {@link #addInterceptors(InterceptorRegistry)} instead. - */ - protected final Object[] getInterceptors() { - if (this.interceptors == null) { - InterceptorRegistry registry = new InterceptorRegistry(); - addInterceptors(registry); - registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); - this.interceptors = registry.getInterceptors(); - } - return this.interceptors.toArray(); - } - - /** - * Override this method to add Spring MVC interceptors for - * pre- and post-processing of controller invocation. - * @see InterceptorRegistry - */ - protected void addInterceptors(InterceptorRegistry registry) { - } - - /** - * Override this method to configure path matching options. - * @see PathMatchConfigurer - * @since 4.0.3 - */ - public void configurePathMatch(PathMatchConfigurer configurer) { - } - - /** - * Return a {@link ContentNegotiationManager} instance to use to determine - * requested {@linkplain MediaType media types} in a given request. - */ - @Bean - public ContentNegotiationManager mvcContentNegotiationManager() { - if (this.contentNegotiationManager == null) { - ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext); - configurer.mediaTypes(getDefaultMediaTypes()); - configureContentNegotiation(configurer); - try { - this.contentNegotiationManager = configurer.getContentNegotiationManager(); - } - catch (Exception ex) { - throw new BeanInitializationException("Could not create ContentNegotiationManager", ex); - } - } - return this.contentNegotiationManager; - } - - protected Map getDefaultMediaTypes() { - Map map = new HashMap(); - if (romePresent) { - map.put("atom", MediaType.APPLICATION_ATOM_XML); - map.put("rss", MediaType.valueOf("application/rss+xml")); - } - if (jaxb2Present) { - map.put("xml", MediaType.APPLICATION_XML); - } - if (jackson2Present || jacksonPresent) { - map.put("json", MediaType.APPLICATION_JSON); - } - return map; - } - - /** - * Override this method to configure content negotiation. - * @see DefaultServletHandlerConfigurer - */ - protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { - } - - /** - * Return a handler mapping ordered at 1 to map URL paths directly to - * view names. To configure view controllers, override - * {@link #addViewControllers}. - */ - @Bean - public HandlerMapping viewControllerHandlerMapping() { - ViewControllerRegistry registry = new ViewControllerRegistry(); - addViewControllers(registry); - - AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); - handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); - handlerMapping.setInterceptors(getInterceptors()); - return handlerMapping; - } - - /** - * Override this method to add view controllers. - * @see ViewControllerRegistry - */ - protected void addViewControllers(ViewControllerRegistry registry) { - } - - /** - * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL - * paths to controller bean names. - */ - @Bean - public BeanNameUrlHandlerMapping beanNameHandlerMapping() { - BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); - mapping.setOrder(2); - mapping.setInterceptors(getInterceptors()); - return mapping; - } - - /** - * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped - * resource handlers. To configure resource handling, override - * {@link #addResourceHandlers}. - */ - @Bean - public HandlerMapping resourceHandlerMapping() { - ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext); - addResourceHandlers(registry); - - AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); - handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); - return handlerMapping; - } - - /** - * Override this method to add resource handlers for serving static resources. - * @see ResourceHandlerRegistry - */ - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - } - - /** - * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped - * default servlet handler. To configure "default" Servlet handling, - * override {@link #configureDefaultServletHandling}. - */ - @Bean - public HandlerMapping defaultServletHandlerMapping() { - DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); - configureDefaultServletHandling(configurer); - AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping(); - handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); - return handlerMapping; - } - - /** - * Override this method to configure "default" Servlet handling. - * @see DefaultServletHandlerConfigurer - */ - protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - } - - /** - * Returns a {@link RequestMappingHandlerAdapter} for processing requests - * through annotated controller methods. Consider overriding one of these - * other more fine-grained methods: - *
    - *
  • {@link #addArgumentResolvers} for adding custom argument resolvers. - *
  • {@link #addReturnValueHandlers} for adding custom return value handlers. - *
  • {@link #configureMessageConverters} for adding custom message converters. - *
- */ - @Bean - public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { - List argumentResolvers = new ArrayList(); - addArgumentResolvers(argumentResolvers); - - List returnValueHandlers = new ArrayList(); - addReturnValueHandlers(returnValueHandlers); - - RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); - adapter.setContentNegotiationManager(mvcContentNegotiationManager()); - adapter.setMessageConverters(getMessageConverters()); - adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); - adapter.setCustomArgumentResolvers(argumentResolvers); - adapter.setCustomReturnValueHandlers(returnValueHandlers); - - AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); - configureAsyncSupport(configurer); - - if (configurer.getTaskExecutor() != null) { - adapter.setTaskExecutor(configurer.getTaskExecutor()); - } - if (configurer.getTimeout() != null) { - adapter.setAsyncRequestTimeout(configurer.getTimeout()); - } - adapter.setCallableInterceptors(configurer.getCallableInterceptors()); - adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); - - return adapter; - } - - /** - * Return the {@link ConfigurableWebBindingInitializer} to use for - * initializing all {@link WebDataBinder} instances. - */ - protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { - ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); - initializer.setConversionService(mvcConversionService()); - initializer.setValidator(mvcValidator()); - initializer.setMessageCodesResolver(getMessageCodesResolver()); - return initializer; - } - - /** - * Return a {@link FormattingConversionService} for use with annotated - * controller methods and the {@code spring:eval} JSP tag. - * Also see {@link #addFormatters} as an alternative to overriding this method. - */ - @Bean - public FormattingConversionService mvcConversionService() { - FormattingConversionService conversionService = new DefaultFormattingConversionService(); - addFormatters(conversionService); - return conversionService; - } - - /** - * Return a global {@link Validator} instance for example for validating - * {@code @ModelAttribute} and {@code @RequestBody} method arguments. - * Delegates to {@link #getValidator()} first and if that returns {@code null} - * checks the classpath for the presence of a JSR-303 implementations - * before creating a {@code OptionalValidatorFactoryBean}.If a JSR-303 - * implementation is not available, a no-op {@link Validator} is returned. - */ - @Bean - public Validator mvcValidator() { - Validator validator = getValidator(); - if (validator == null) { - if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) { - Class clazz; - try { - String className = "com.fr.third.springframework.validation.beanvalidation.OptionalValidatorFactoryBean"; - clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader()); - } - catch (ClassNotFoundException ex) { - throw new BeanInitializationException("Could not find default validator class", ex); - } - catch (LinkageError ex) { - throw new BeanInitializationException("Could not load default validator class", ex); - } - validator = (Validator) BeanUtils.instantiate(clazz); - } - else { - validator = new NoOpValidator(); - } - } - return validator; - } - - /** - * Override this method to provide a custom {@link Validator}. - */ - protected Validator getValidator() { - return null; - } - - /** - * Override this method to provide a custom {@link MessageCodesResolver}. - */ - protected MessageCodesResolver getMessageCodesResolver() { - return null; - } - - /** - * Add custom {@link HandlerMethodArgumentResolver}s to use in addition to - * the ones registered by default. - *

Custom argument resolvers are invoked before built-in resolvers - * except for those that rely on the presence of annotations (e.g. - * {@code @RequestParameter}, {@code @PathVariable}, etc.). - * The latter can be customized by configuring the - * {@link RequestMappingHandlerAdapter} directly. - * @param argumentResolvers the list of custom converters; - * initially an empty list. - */ - protected void addArgumentResolvers(List argumentResolvers) { - } - - /** - * Add custom {@link HandlerMethodReturnValueHandler}s in addition to the - * ones registered by default. - *

Custom return value handlers are invoked before built-in ones except - * for those that rely on the presence of annotations (e.g. - * {@code @ResponseBody}, {@code @ModelAttribute}, etc.). - * The latter can be customized by configuring the - * {@link RequestMappingHandlerAdapter} directly. - * @param returnValueHandlers the list of custom handlers; - * initially an empty list. - */ - protected void addReturnValueHandlers(List returnValueHandlers) { - } - - /** - * Provides access to the shared {@link HttpMessageConverter}s used by the - * {@link RequestMappingHandlerAdapter} and the - * {@link ExceptionHandlerExceptionResolver}. - * This method cannot be overridden. - * Use {@link #configureMessageConverters(List)} instead. - * Also see {@link #addDefaultHttpMessageConverters(List)} that can be - * used to add default message converters. - */ - protected final List> getMessageConverters() { - if (this.messageConverters == null) { - this.messageConverters = new ArrayList>(); - configureMessageConverters(this.messageConverters); - if (this.messageConverters.isEmpty()) { - addDefaultHttpMessageConverters(this.messageConverters); - } - } - return this.messageConverters; - } - - /** - * Override this method to add custom {@link HttpMessageConverter}s to use - * with the {@link RequestMappingHandlerAdapter} and the - * {@link ExceptionHandlerExceptionResolver}. Adding converters to the - * list turns off the default converters that would otherwise be registered - * by default. Also see {@link #addDefaultHttpMessageConverters(List)} that - * can be used to add default message converters. - * @param converters a list to add message converters to; - * initially an empty list. - */ - protected void configureMessageConverters(List> converters) { - } - - /** - * Adds a set of default HttpMessageConverter instances to the given list. - * Subclasses can call this method from {@link #configureMessageConverters(List)}. - * @param messageConverters the list to add the default message converters to - */ - @SuppressWarnings("deprecation") - protected final void addDefaultHttpMessageConverters(List> messageConverters) { - StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); - stringConverter.setWriteAcceptCharset(false); - - messageConverters.add(new ByteArrayHttpMessageConverter()); - messageConverters.add(stringConverter); - messageConverters.add(new ResourceHttpMessageConverter()); - messageConverters.add(new SourceHttpMessageConverter()); - messageConverters.add(new AllEncompassingFormHttpMessageConverter()); - - if (romePresent) { - messageConverters.add(new AtomFeedHttpMessageConverter()); - messageConverters.add(new RssChannelHttpMessageConverter()); - } - if (jaxb2Present) { - messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); - } - if (jackson2Present) { - messageConverters.add(new MappingJackson2HttpMessageConverter()); - } - else if (jacksonPresent) { - messageConverters.add(new com.fr.third.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); - } - } - - /** - * Override this method to add custom {@link Converter}s and {@link Formatter}s. - */ - protected void addFormatters(FormatterRegistry registry) { - } - - /** - * Override this method to configure asynchronous request processing options. - * @see AsyncSupportConfigurer - */ - public void configureAsyncSupport(AsyncSupportConfigurer configurer) { - } - - /** - * Return an instance of {@link CompositeUriComponentsContributor} for use with - * {@link com.fr.third.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}. - */ - @Bean - public CompositeUriComponentsContributor mvcUriComponentsContributor() { - return new CompositeUriComponentsContributor( - requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService()); - } - - /** - * Returns a {@link HttpRequestHandlerAdapter} for processing requests - * with {@link HttpRequestHandler}s. - */ - @Bean - public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { - return new HttpRequestHandlerAdapter(); - } - - /** - * Returns a {@link SimpleControllerHandlerAdapter} for processing requests - * with interface-based controllers. - */ - @Bean - public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { - return new SimpleControllerHandlerAdapter(); - } - - /** - * Returns a {@link HandlerExceptionResolverComposite} containing a list - * of exception resolvers obtained either through - * {@link #configureHandlerExceptionResolvers(List)} or through - * {@link #addDefaultHandlerExceptionResolvers(List)}. - *

Note: This method cannot be made final due to CGLib - * constraints. Rather than overriding it, consider overriding - * {@link #configureHandlerExceptionResolvers(List)}, which allows - * providing a list of resolvers. - */ - @Bean - public HandlerExceptionResolver handlerExceptionResolver() { - List exceptionResolvers = new ArrayList(); - configureHandlerExceptionResolvers(exceptionResolvers); - - if (exceptionResolvers.isEmpty()) { - addDefaultHandlerExceptionResolvers(exceptionResolvers); - } - - HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); - composite.setOrder(0); - composite.setExceptionResolvers(exceptionResolvers); - return composite; - } - - /** - * Override this method to configure the list of - * {@link HandlerExceptionResolver}s to use. Adding resolvers to the list - * turns off the default resolvers that would otherwise be registered by - * default. Also see {@link #addDefaultHandlerExceptionResolvers(List)} - * that can be used to add the default exception resolvers. - * @param exceptionResolvers a list to add exception resolvers to; - * initially an empty list. - */ - protected void configureHandlerExceptionResolvers(List exceptionResolvers) { - } - - /** - * A method available to subclasses for adding default {@link HandlerExceptionResolver}s. - *

Adds the following exception resolvers: - *

    - *
  • {@link ExceptionHandlerExceptionResolver} - * for handling exceptions through @{@link ExceptionHandler} methods. - *
  • {@link ResponseStatusExceptionResolver} - * for exceptions annotated with @{@link ResponseStatus}. - *
  • {@link DefaultHandlerExceptionResolver} - * for resolving known Spring exception types - *
- */ - protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers) { - ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver(); - exceptionHandlerExceptionResolver.setContentNegotiationManager(mvcContentNegotiationManager()); - exceptionHandlerExceptionResolver.setMessageConverters(getMessageConverters()); - exceptionHandlerExceptionResolver.setApplicationContext(this.applicationContext); - exceptionHandlerExceptionResolver.afterPropertiesSet(); - exceptionResolvers.add(exceptionHandlerExceptionResolver); - - ResponseStatusExceptionResolver responseStatusExceptionResolver = new ResponseStatusExceptionResolver(); - responseStatusExceptionResolver.setMessageSource(this.applicationContext); - exceptionResolvers.add(responseStatusExceptionResolver); - - exceptionResolvers.add(new DefaultHandlerExceptionResolver()); - } - - - private static final class EmptyHandlerMapping extends AbstractHandlerMapping { - - @Override - protected Object getHandlerInternal(HttpServletRequest request) { - return null; - } - } - - - private static final class NoOpValidator implements Validator { - - @Override - public boolean supports(Class clazz) { - return false; - } - - @Override - public void validate(Object target, Errors errors) { - } - } + private static boolean romePresent = + ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", WebMvcConfigurationSupport.class.getClassLoader()); + + private static final boolean jaxb2Present = + ClassUtils.isPresent("javax.xml.bind.Binder", WebMvcConfigurationSupport.class.getClassLoader()); + + private static final boolean jackson2Present = + ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && + ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); + + private static final boolean jacksonPresent = + ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", WebMvcConfigurationSupport.class.getClassLoader()) && + ClassUtils.isPresent("org.codehaus.jackson.JsonGenerator", WebMvcConfigurationSupport.class.getClassLoader()); + + + private ApplicationContext applicationContext; + + private ServletContext servletContext; + + private List interceptors; + + private ContentNegotiationManager contentNegotiationManager; + + private List> messageConverters; + + + /** + * Set the Spring {@link ApplicationContext}, e.g. for resource loading. + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + + this.applicationContext = applicationContext; + } + + /** + * Set the {@link javax.servlet.ServletContext}, e.g. for resource handling, + * looking up file extensions, etc. + */ + @Override + public void setServletContext(ServletContext servletContext) { + + this.servletContext = servletContext; + } + + + /** + * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping + * requests to annotated controllers. + */ + @Bean + public RequestMappingHandlerMapping requestMappingHandlerMapping() { + + RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); + handlerMapping.setOrder(0); + handlerMapping.setInterceptors(getInterceptors()); + handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager()); + + PathMatchConfigurer configurer = new PathMatchConfigurer(); + configurePathMatch(configurer); + if (configurer.isUseSuffixPatternMatch() != null) { + handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch()); + } + if (configurer.isUseRegisteredSuffixPatternMatch() != null) { + handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch()); + } + if (configurer.isUseTrailingSlashMatch() != null) { + handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch()); + } + if (configurer.getPathMatcher() != null) { + handlerMapping.setPathMatcher(configurer.getPathMatcher()); + } + if (configurer.getUrlPathHelper() != null) { + handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper()); + } + + return handlerMapping; + } + + /** + * Provide access to the shared handler interceptors used to configure + * {@link HandlerMapping} instances with. This method cannot be overridden, + * use {@link #addInterceptors(InterceptorRegistry)} instead. + */ + protected final Object[] getInterceptors() { + + if (this.interceptors == null) { + InterceptorRegistry registry = new InterceptorRegistry(); + addInterceptors(registry); + registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); + this.interceptors = registry.getInterceptors(); + } + return this.interceptors.toArray(); + } + + /** + * Override this method to add Spring MVC interceptors for + * pre- and post-processing of controller invocation. + * + * @see InterceptorRegistry + */ + protected void addInterceptors(InterceptorRegistry registry) { + + } + + /** + * Override this method to configure path matching options. + * + * @see PathMatchConfigurer + * @since 4.0.3 + */ + public void configurePathMatch(PathMatchConfigurer configurer) { + + } + + /** + * Return a {@link ContentNegotiationManager} instance to use to determine + * requested {@linkplain MediaType media types} in a given request. + */ + @Bean + public ContentNegotiationManager mvcContentNegotiationManager() { + + if (this.contentNegotiationManager == null) { + ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext); + configurer.mediaTypes(getDefaultMediaTypes()); + configureContentNegotiation(configurer); + try { + this.contentNegotiationManager = configurer.getContentNegotiationManager(); + } catch (Exception ex) { + throw new BeanInitializationException("Could not create ContentNegotiationManager", ex); + } + } + return this.contentNegotiationManager; + } + + protected Map getDefaultMediaTypes() { + + Map map = new HashMap(); + if (romePresent) { + map.put("atom", MediaType.APPLICATION_ATOM_XML); + map.put("rss", MediaType.valueOf("application/rss+xml")); + } + if (jaxb2Present) { + map.put("xml", MediaType.APPLICATION_XML); + } + if (jackson2Present || jacksonPresent) { + map.put("json", MediaType.APPLICATION_JSON); + } + return map; + } + + /** + * Override this method to configure content negotiation. + * + * @see DefaultServletHandlerConfigurer + */ + protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + + } + + /** + * Return a handler mapping ordered at 1 to map URL paths directly to + * view names. To configure view controllers, override + * {@link #addViewControllers}. + */ + @Bean + public HandlerMapping viewControllerHandlerMapping() { + + ViewControllerRegistry registry = new ViewControllerRegistry(); + addViewControllers(registry); + + AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); + handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); + handlerMapping.setInterceptors(getInterceptors()); + return handlerMapping; + } + + /** + * Override this method to add view controllers. + * + * @see ViewControllerRegistry + */ + protected void addViewControllers(ViewControllerRegistry registry) { + + } + + /** + * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL + * paths to controller bean names. + */ + @Bean + public BeanNameUrlHandlerMapping beanNameHandlerMapping() { + + BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); + mapping.setOrder(2); + mapping.setInterceptors(getInterceptors()); + return mapping; + } + + /** + * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped + * resource handlers. To configure resource handling, override + * {@link #addResourceHandlers}. + */ + @Bean + public HandlerMapping resourceHandlerMapping() { + + ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext); + addResourceHandlers(registry); + + AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); + handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); + return handlerMapping; + } + + /** + * Override this method to add resource handlers for serving static resources. + * + * @see ResourceHandlerRegistry + */ + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + + } + + /** + * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped + * default servlet handler. To configure "default" Servlet handling, + * override {@link #configureDefaultServletHandling}. + */ + @Bean + public HandlerMapping defaultServletHandlerMapping() { + + DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); + configureDefaultServletHandling(configurer); + AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping(); + handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); + return handlerMapping; + } + + /** + * Override this method to configure "default" Servlet handling. + * + * @see DefaultServletHandlerConfigurer + */ + protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + + } + + /** + * Returns a {@link RequestMappingHandlerAdapter} for processing requests + * through annotated controller methods. Consider overriding one of these + * other more fine-grained methods: + *
    + *
  • {@link #addArgumentResolvers} for adding custom argument resolvers. + *
  • {@link #addReturnValueHandlers} for adding custom return value handlers. + *
  • {@link #configureMessageConverters} for adding custom message converters. + *
+ */ + @Bean + public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + + List argumentResolvers = new ArrayList(); + addArgumentResolvers(argumentResolvers); + + List returnValueHandlers = new ArrayList(); + addReturnValueHandlers(returnValueHandlers); + + RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); + adapter.setContentNegotiationManager(mvcContentNegotiationManager()); + adapter.setMessageConverters(getMessageConverters()); + adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); + adapter.setCustomArgumentResolvers(argumentResolvers); + adapter.setCustomReturnValueHandlers(returnValueHandlers); + + AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); + configureAsyncSupport(configurer); + + if (configurer.getTaskExecutor() != null) { + adapter.setTaskExecutor(configurer.getTaskExecutor()); + } + if (configurer.getTimeout() != null) { + adapter.setAsyncRequestTimeout(configurer.getTimeout()); + } + adapter.setCallableInterceptors(configurer.getCallableInterceptors()); + adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); + + return adapter; + } + + /** + * Return the {@link ConfigurableWebBindingInitializer} to use for + * initializing all {@link WebDataBinder} instances. + */ + protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { + + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(mvcConversionService()); + initializer.setValidator(mvcValidator()); + initializer.setMessageCodesResolver(getMessageCodesResolver()); + return initializer; + } + + /** + * Return a {@link FormattingConversionService} for use with annotated + * controller methods and the {@code spring:eval} JSP tag. + * Also see {@link #addFormatters} as an alternative to overriding this method. + */ + @Bean + public FormattingConversionService mvcConversionService() { + + FormattingConversionService conversionService = new DefaultFormattingConversionService(); + addFormatters(conversionService); + return conversionService; + } + + /** + * Return a global {@link Validator} instance for example for validating + * {@code @ModelAttribute} and {@code @RequestBody} method arguments. + * Delegates to {@link #getValidator()} first and if that returns {@code null} + * checks the classpath for the presence of a JSR-303 implementations + * before creating a {@code OptionalValidatorFactoryBean}.If a JSR-303 + * implementation is not available, a no-op {@link Validator} is returned. + */ + @Bean + public Validator mvcValidator() { + + Validator validator = getValidator(); + if (validator == null) { + if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) { + Class clazz; + try { + String className = "com.fr.third.springframework.validation.beanvalidation.OptionalValidatorFactoryBean"; + clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + throw new BeanInitializationException("Could not find default validator class", ex); + } catch (LinkageError ex) { + throw new BeanInitializationException("Could not load default validator class", ex); + } + validator = (Validator) BeanUtils.instantiate(clazz); + } else { + validator = new NoOpValidator(); + } + } + return validator; + } + + /** + * Override this method to provide a custom {@link Validator}. + */ + protected Validator getValidator() { + + return null; + } + + /** + * Override this method to provide a custom {@link MessageCodesResolver}. + */ + protected MessageCodesResolver getMessageCodesResolver() { + + return null; + } + + /** + * Add custom {@link HandlerMethodArgumentResolver}s to use in addition to + * the ones registered by default. + *

Custom argument resolvers are invoked before built-in resolvers + * except for those that rely on the presence of annotations (e.g. + * {@code @RequestParameter}, {@code @PathVariable}, etc.). + * The latter can be customized by configuring the + * {@link RequestMappingHandlerAdapter} directly. + * + * @param argumentResolvers the list of custom converters; + * initially an empty list. + */ + protected void addArgumentResolvers(List argumentResolvers) { + + } + + /** + * Add custom {@link HandlerMethodReturnValueHandler}s in addition to the + * ones registered by default. + *

Custom return value handlers are invoked before built-in ones except + * for those that rely on the presence of annotations (e.g. + * {@code @ResponseBody}, {@code @ModelAttribute}, etc.). + * The latter can be customized by configuring the + * {@link RequestMappingHandlerAdapter} directly. + * + * @param returnValueHandlers the list of custom handlers; + * initially an empty list. + */ + protected void addReturnValueHandlers(List returnValueHandlers) { + + } + + /** + * Provides access to the shared {@link HttpMessageConverter}s used by the + * {@link RequestMappingHandlerAdapter} and the + * {@link ExceptionHandlerExceptionResolver}. + * This method cannot be overridden. + * Use {@link #configureMessageConverters(List)} instead. + * Also see {@link #addDefaultHttpMessageConverters(List)} that can be + * used to add default message converters. + */ + protected final List> getMessageConverters() { + + if (this.messageConverters == null) { + this.messageConverters = new ArrayList>(); + configureMessageConverters(this.messageConverters); + if (this.messageConverters.isEmpty()) { + addDefaultHttpMessageConverters(this.messageConverters); + } + } + return this.messageConverters; + } + + /** + * Override this method to add custom {@link HttpMessageConverter}s to use + * with the {@link RequestMappingHandlerAdapter} and the + * {@link ExceptionHandlerExceptionResolver}. Adding converters to the + * list turns off the default converters that would otherwise be registered + * by default. Also see {@link #addDefaultHttpMessageConverters(List)} that + * can be used to add default message converters. + * + * @param converters a list to add message converters to; + * initially an empty list. + */ + protected void configureMessageConverters(List> converters) { + + } + + /** + * Adds a set of default HttpMessageConverter instances to the given list. + * Subclasses can call this method from {@link #configureMessageConverters(List)}. + * + * @param messageConverters the list to add the default message converters to + */ + @SuppressWarnings("deprecation") + protected final void addDefaultHttpMessageConverters(List> messageConverters) { + + StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); + stringConverter.setWriteAcceptCharset(false); + + messageConverters.add(new ByteArrayHttpMessageConverter()); + messageConverters.add(stringConverter); + messageConverters.add(new ResourceHttpMessageConverter()); + messageConverters.add(new SourceHttpMessageConverter()); + messageConverters.add(new AllEncompassingFormHttpMessageConverter()); + + if (romePresent) { + messageConverters.add(new AtomFeedHttpMessageConverter()); + messageConverters.add(new RssChannelHttpMessageConverter()); + } + if (jaxb2Present) { + messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); + } + if (jackson2Present) { + messageConverters.add(new MappingJackson2HttpMessageConverter()); + } else if (jacksonPresent) { + messageConverters.add(new com.fr.third.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); + } else { + try { + + String cls = "com.finebi.base.annotation.MappingJackson2HttpMessageConverter"; + GenericHttpMessageConverter mc = (MappingJackson2HttpMessageConverter) WebMvcConfigurationSupport.class.getClassLoader().loadClass(cls).newInstance(); + messageConverters.add(mc); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * Override this method to add custom {@link Converter}s and {@link Formatter}s. + */ + protected void addFormatters(FormatterRegistry registry) { + + } + + /** + * Override this method to configure asynchronous request processing options. + * + * @see AsyncSupportConfigurer + */ + public void configureAsyncSupport(AsyncSupportConfigurer configurer) { + + } + + /** + * Return an instance of {@link CompositeUriComponentsContributor} for use with + * {@link com.fr.third.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}. + */ + @Bean + public CompositeUriComponentsContributor mvcUriComponentsContributor() { + + return new CompositeUriComponentsContributor( + requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService()); + } + + /** + * Returns a {@link HttpRequestHandlerAdapter} for processing requests + * with {@link HttpRequestHandler}s. + */ + @Bean + public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { + + return new HttpRequestHandlerAdapter(); + } + + /** + * Returns a {@link SimpleControllerHandlerAdapter} for processing requests + * with interface-based controllers. + */ + @Bean + public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { + + return new SimpleControllerHandlerAdapter(); + } + + /** + * Returns a {@link HandlerExceptionResolverComposite} containing a list + * of exception resolvers obtained either through + * {@link #configureHandlerExceptionResolvers(List)} or through + * {@link #addDefaultHandlerExceptionResolvers(List)}. + *

Note: This method cannot be made final due to CGLib + * constraints. Rather than overriding it, consider overriding + * {@link #configureHandlerExceptionResolvers(List)}, which allows + * providing a list of resolvers. + */ + @Bean + public HandlerExceptionResolver handlerExceptionResolver() { + + List exceptionResolvers = new ArrayList(); + configureHandlerExceptionResolvers(exceptionResolvers); + + if (exceptionResolvers.isEmpty()) { + addDefaultHandlerExceptionResolvers(exceptionResolvers); + } + + HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); + composite.setOrder(0); + composite.setExceptionResolvers(exceptionResolvers); + return composite; + } + + /** + * Override this method to configure the list of + * {@link HandlerExceptionResolver}s to use. Adding resolvers to the list + * turns off the default resolvers that would otherwise be registered by + * default. Also see {@link #addDefaultHandlerExceptionResolvers(List)} + * that can be used to add the default exception resolvers. + * + * @param exceptionResolvers a list to add exception resolvers to; + * initially an empty list. + */ + protected void configureHandlerExceptionResolvers(List exceptionResolvers) { + + } + + /** + * A method available to subclasses for adding default {@link HandlerExceptionResolver}s. + *

Adds the following exception resolvers: + *

    + *
  • {@link ExceptionHandlerExceptionResolver} + * for handling exceptions through @{@link ExceptionHandler} methods. + *
  • {@link ResponseStatusExceptionResolver} + * for exceptions annotated with @{@link ResponseStatus}. + *
  • {@link DefaultHandlerExceptionResolver} + * for resolving known Spring exception types + *
+ */ + protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers) { + + ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver(); + exceptionHandlerExceptionResolver.setContentNegotiationManager(mvcContentNegotiationManager()); + exceptionHandlerExceptionResolver.setMessageConverters(getMessageConverters()); + exceptionHandlerExceptionResolver.setApplicationContext(this.applicationContext); + exceptionHandlerExceptionResolver.afterPropertiesSet(); + exceptionResolvers.add(exceptionHandlerExceptionResolver); + + ResponseStatusExceptionResolver responseStatusExceptionResolver = new ResponseStatusExceptionResolver(); + responseStatusExceptionResolver.setMessageSource(this.applicationContext); + exceptionResolvers.add(responseStatusExceptionResolver); + + exceptionResolvers.add(new DefaultHandlerExceptionResolver()); + } + + + private static final class EmptyHandlerMapping extends AbstractHandlerMapping { + + @Override + protected Object getHandlerInternal(HttpServletRequest request) { + + return null; + } + } + + + private static final class NoOpValidator implements Validator { + + @Override + public boolean supports(Class clazz) { + + return false; + } + + @Override + public void validate(Object target, Errors errors) { + + } + } } diff --git a/fine-spring/src/com/fr/third/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/fine-spring/src/com/fr/third/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index ba80b7096..18748101f 100644 --- a/fine-spring/src/com/fr/third/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/fine-spring/src/com/fr/third/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -51,10 +51,10 @@ import com.fr.third.springframework.web.servlet.DispatcherServlet; import com.fr.third.springframework.web.servlet.support.ServletUriComponentsBuilder; import com.fr.third.springframework.web.util.UriComponents; import com.fr.third.springframework.web.util.UriComponentsBuilder; -import org.springframework.cglib.proxy.Callback; -import org.springframework.cglib.proxy.Enhancer; -import org.springframework.cglib.proxy.Factory; -import org.springframework.cglib.proxy.MethodProxy; +import com.fr.third.springframework.cglib.proxy.Callback; +import com.fr.third.springframework.cglib.proxy.Enhancer; +import com.fr.third.springframework.cglib.proxy.Factory; +import com.fr.third.springframework.cglib.proxy.MethodProxy; import org.springframework.objenesis.ObjenesisStd; /** @@ -340,7 +340,7 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder { enhancer.setSuperclass(type); enhancer.setInterfaces(new Class[] {MethodInvocationInfo.class}); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); - enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class); + enhancer.setCallbackType(com.fr.third.springframework.cglib.proxy.MethodInterceptor.class); Factory factory = (Factory) objenesis.newInstance(enhancer.createClass()); factory.setCallbacks(new Callback[] {interceptor}); return (T) factory; @@ -349,7 +349,7 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder { private static class ControllerMethodInvocationInterceptor - implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor { + implements com.fr.third.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor { private static final Method getControllerMethod = ReflectionUtils.findMethod(MethodInvocationInfo.class, "getControllerMethod");