Author: mbenson Date: Sat Dec 3 17:11:44 2011 New Revision: 1209953 URL: http://svn.apache.org/viewvc?rev=1209953&view=rev Log: [DIGESTER-153] Add constructor support to ObjectCreateRule - even when the constructor args come from nested elements
Added: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/RecordedInvocation.java - copied, changed from r1209949, commons/proper/proxy/branches/version-2.0-work/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml (with props) Modified: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java Modified: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java?rev=1209953&r1=1209952&r2=1209953&view=diff ============================================================================== --- commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java (original) +++ commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java Sat Dec 3 17:11:44 2011 @@ -21,16 +21,18 @@ package org.apache.commons.digester3; import static java.lang.String.format; import static java.util.Arrays.fill; -import static net.sf.cglib.proxy.Enhancer.isEnhanced; import static org.apache.commons.beanutils.ConstructorUtils.getAccessibleConstructor; +import static org.apache.commons.beanutils.ConvertUtils.convert; import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; -import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.Factory; -import net.sf.cglib.proxy.LazyLoader; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -42,6 +44,68 @@ import org.xml.sax.SAXException; public class ObjectCreateRule extends Rule { + private interface DeferredConstructionProxy + { + void finish(); + } + + private static class DeferredConstructionCallback implements MethodInterceptor + { + Constructor<?> constructor; + Object[] constructorArgs; + ArrayList<RecordedInvocation> invocations = new ArrayList<RecordedInvocation>(); + Object delegate; + + DeferredConstructionCallback(Constructor<?> constructor, Object[] constructorArgs) + { + super(); + this.constructor = constructor; + this.constructorArgs = constructorArgs; + } + + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable + { + boolean hasDelegate; + synchronized ( this ) { + hasDelegate = delegate != null; + if ( method.getDeclaringClass().equals( DeferredConstructionProxy.class ) ) + { + if ( !hasDelegate ) + { + establishDelegate(); + hasDelegate = true; + } + return null; + } + } + if ( hasDelegate ) { + return proxy.invoke( delegate, args ); + } + invocations.add( new RecordedInvocation( method, args ) ); + return proxy.invokeSuper( obj, args ); + } + + private void establishDelegate() throws Exception { + // this piece of code is adapted from CallMethodRule + for ( int i = 0; i < constructorArgs.length; i++ ) + { + // convert nulls and convert stringy parameters for non-stringy param types + if ( constructorArgs[i] == null + || ( constructorArgs[i] instanceof String && !String.class.isAssignableFrom( constructor.getParameterTypes()[i] ) ) ) + { + constructorArgs[i] = convert( (String) constructorArgs[i], constructor.getParameterTypes()[i] ); + } + } + delegate = constructor.newInstance( constructorArgs ); + for ( RecordedInvocation invocation : invocations ) + { + invocation.getInvokedMethod().invoke( delegate, invocation.getArguments() ); + } + constructor = null; + constructorArgs = null; + invocations = null; + } + } // ----------------------------------------------------------- Constructors @@ -205,16 +269,16 @@ public class ObjectCreateRule fill( constructorArguments, null ); getDigester().pushParams( constructorArguments ); - ObjectCreateRuleLazyLoader lazyLoader = new ObjectCreateRuleLazyLoader( constructor, - constructorArgumentsTypes, - constructorArguments ); + DeferredConstructionCallback callback = new DeferredConstructionCallback(constructor, constructorArguments); + if ( proxyFactory == null ) { synchronized ( this ) { // check again for null now that we're in the synchronized block: if ( proxyFactory == null ) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass( clazz ); - enhancer.setCallback( lazyLoader ); + enhancer.setInterfaces(new Class[] { DeferredConstructionProxy.class }); + enhancer.setCallback( callback ); enhancer.setClassLoader( getDigester().getClassLoader() ); Object result = enhancer.create(); proxyFactory = (Factory) result; @@ -222,7 +286,7 @@ public class ObjectCreateRule } } } - return proxyFactory.newInstance( lazyLoader ); + return proxyFactory.newInstance( callback ); } /** @@ -234,10 +298,10 @@ public class ObjectCreateRule { Object top = getDigester().pop(); - if ( isEnhanced( top.getClass() ) ) + if (top instanceof DeferredConstructionProxy) { - // do lazy load?!? getDigester().popParams(); + ((DeferredConstructionProxy) top).finish(); } if ( getDigester().getLogger().isDebugEnabled() ) Copied: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/RecordedInvocation.java (from r1209949, commons/proper/proxy/branches/version-2.0-work/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java) URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/RecordedInvocation.java?p2=commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/RecordedInvocation.java&p1=commons/proper/proxy/branches/version-2.0-work/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java&r1=1209949&r2=1209953&rev=1209953&view=diff ============================================================================== --- commons/proper/proxy/branches/version-2.0-work/core/src/main/java/org/apache/commons/proxy2/invoker/RecordedInvocation.java (original) +++ commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/RecordedInvocation.java Sat Dec 3 17:11:44 2011 @@ -15,14 +15,13 @@ * limitations under the License. */ -package org.apache.commons.proxy2.invoker; - -import org.apache.commons.proxy2.ProxyUtils; +package org.apache.commons.digester3; import java.lang.reflect.Method; /** * Detached representation of a method invocation. + * From Commons [proxy] v2 branch. * @author James Carman */ public class RecordedInvocation @@ -116,7 +115,7 @@ public class RecordedInvocation else { buffer.append("("); - buffer.append(ProxyUtils.getJavaClassName(input.getClass())); + buffer.append(input.getClass().getSimpleName()); buffer.append("){"); Object[] array = ( Object[] ) input; int count = array.length; Modified: commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java?rev=1209953&r1=1209952&r2=1209953&view=diff ============================================================================== --- commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java (original) +++ commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java Sat Dec 3 17:11:44 2011 @@ -62,6 +62,33 @@ public final class Digester153TestCase assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); } + @Test + public void constructorWithAttributeAndElement() + throws Exception + { + ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); + createRule.setConstructorArguments( boolean.class, double.class ); + + Digester digester = new Digester(); + digester.addRule( "toplevel/bean", createRule ); + digester.addCallParam( "toplevel/bean", 0, "boolean" ); + digester.addCallParam( "toplevel/bean/double", 1 ); + digester.addBeanPropertySetter("toplevel/bean/float", "floatProperty"); + + TestBean bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + + assertTrue( bean.getBooleanProperty() ); + assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); + assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); + + // do it again to exercise the cglib Factory: + bean = digester.parse( getClass().getResourceAsStream( "ConstructorWithAttributeAndElement.xml" ) ); + + assertTrue( bean.getBooleanProperty() ); + assertEquals( 9.99D, bean.getDoubleProperty(), 0 ); + assertEquals( Float.valueOf( 5.5f ), Float.valueOf( bean.getFloatProperty() ) ); + } + /* @Test public void basicConstructorViaBinder() Added: commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml?rev=1209953&view=auto ============================================================================== --- commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml (added) +++ commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml Sat Dec 3 17:11:44 2011 @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +--> +<toplevel> + <bean boolean="true"> + <double>9.99</double> + <float>5.5</float> + </bean> +</toplevel> Propchange: commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/ConstructorWithAttributeAndElement.xml ------------------------------------------------------------------------------ svn:eol-style = native