Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 7F1707E1B for ; Mon, 5 Dec 2011 21:03:28 +0000 (UTC) Received: (qmail 24177 invoked by uid 500); 5 Dec 2011 21:03:28 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 24099 invoked by uid 500); 5 Dec 2011 21:03:28 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 24092 invoked by uid 99); 5 Dec 2011 21:03:28 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 05 Dec 2011 21:03:28 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 05 Dec 2011 21:03:22 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 0B8D923889E0 for ; Mon, 5 Dec 2011 21:03:00 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1210631 - in /commons/proper/digester/trunk/src: main/java/org/apache/commons/digester3/ main/java/org/apache/commons/digester3/binder/ main/java/org/apache/commons/digester3/xmlrules/ main/resources/org/apache/commons/digester3/xmlrules/ ... Date: Mon, 05 Dec 2011 21:02:59 -0000 To: commits@commons.apache.org From: simonetripodi@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20111205210300.0B8D923889E0@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: simonetripodi Date: Mon Dec 5 21:02:59 2011 New Revision: 1210631 URL: http://svn.apache.org/viewvc?rev=1210631&view=rev Log: enhanced [DIGESTER-153] by appliyng the patch kindly provided by Matt Benson Modified: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/ObjectCreateRule.java commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/xmlrules/ObjectCreateRule.java commons/proper/digester/trunk/src/main/resources/org/apache/commons/digester3/xmlrules/digester-rules.dtd commons/proper/digester/trunk/src/test/java/org/apache/commons/digester3/Digester153TestCase.java commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/xmlrules/constructor-testrules.xml 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=1210631&r1=1210630&r2=1210631&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 Mon Dec 5 21:02:59 2011 @@ -19,8 +19,8 @@ package org.apache.commons.digester3; * under the License. */ +import static java.lang.System.arraycopy; import static java.lang.String.format; -import static java.util.Arrays.fill; import static org.apache.commons.beanutils.ConstructorUtils.getAccessibleConstructor; import static org.apache.commons.beanutils.ConvertUtils.convert; @@ -29,6 +29,7 @@ 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.MethodInterceptor; @@ -44,11 +45,6 @@ import org.xml.sax.SAXException; public class ObjectCreateRule extends Rule { - private interface DeferredConstructionProxy - { - void finish(); - } - private static class DeferredConstructionCallback implements MethodInterceptor { Constructor constructor; @@ -56,46 +52,34 @@ public class ObjectCreateRule ArrayList invocations = new ArrayList(); Object delegate; - DeferredConstructionCallback(Constructor constructor, Object[] constructorArgs) + 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 + 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 ) { - if ( !hasDelegate ) - { - establishDelegate(); - hasDelegate = true; - } - return null; + invocations.add( new RecordedInvocation( method, args ) ); } } 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] ); - } - } + synchronized void establishDelegate() + throws Exception + { + convertTo( constructor.getParameterTypes(), constructorArgs ); delegate = constructor.newInstance( constructorArgs ); for ( RecordedInvocation invocation : invocations ) { @@ -107,6 +91,106 @@ public class ObjectCreateRule } } + private static class ProxyManager + { + private final Class clazz; + private final Constructor constructor; + private final Object[] templateConstructorArguments; + private final Digester digester; + private final boolean hasDefaultConstructor; + private Factory factory; + + ProxyManager( Class clazz, Constructor constructor, Object[] constructorArguments, Digester digester ) + { + this.clazz = clazz; + hasDefaultConstructor = getAccessibleConstructor(clazz, new Class[0]) != null; + this.constructor = constructor; + Class[] argTypes = constructor.getParameterTypes(); + templateConstructorArguments = new Object[argTypes.length]; + if ( constructorArguments == null ) + { + for ( int i = 0; i < templateConstructorArguments.length; i++ ) + { + if ( argTypes[i].equals(boolean.class) ) + { + templateConstructorArguments[i] = Boolean.FALSE; + continue; + } + if ( argTypes[i].isPrimitive() ) + { + templateConstructorArguments[i] = convert("0", argTypes[i]); + continue; + } + templateConstructorArguments[i] = null; + } + } + else + { + if ( constructorArguments.length != argTypes.length ) + { + throw new IllegalArgumentException( + format( "wrong number of constructor arguments specified: %s instead of %s", + constructorArguments.length, argTypes.length ) ); + } + arraycopy( constructorArguments, 0, templateConstructorArguments, 0, constructorArguments.length ); + } + convertTo( argTypes, templateConstructorArguments ); + this.digester = digester; + } + + Object createProxy() + { + Object[] constructorArguments = new Object[templateConstructorArguments.length]; + arraycopy( templateConstructorArguments, 0, constructorArguments, 0, constructorArguments.length ); + digester.pushParams( constructorArguments ); + + DeferredConstructionCallback callback = new DeferredConstructionCallback(constructor, constructorArguments); + + Object result; + if ( factory == null ) + { + synchronized ( this ) + { + // check again for null now that we're in the synchronized block: + if ( factory == null ) + { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass( clazz ); + enhancer.setCallback( callback ); + enhancer.setClassLoader( digester.getClassLoader() ); + enhancer.setInterceptDuringConstruction(false); + if ( hasDefaultConstructor ) + { + result = enhancer.create(); + } + else + { + result = enhancer.create( constructor.getParameterTypes(), constructorArguments ); + } + factory = (Factory) result; + return result; + } + } + } + if ( hasDefaultConstructor ) + { + result = factory.newInstance( callback ); + } + else + { + result = factory.newInstance( constructor.getParameterTypes(), + constructorArguments, new Callback[] { callback } ); + } + return result; + } + + void finalize( Object proxy ) throws Exception + { + digester.popParams(); + ( ( DeferredConstructionCallback ) ( (Factory) proxy).getCallback( 0 ) ).establishDelegate(); + } + } + // ----------------------------------------------------------- Constructors /** @@ -173,35 +257,59 @@ public class ObjectCreateRule protected String className = null; /** - * The constructor arguments - order is preserved by the LinkedHashMap + * The constructor argument types. * * @since 3.2 */ - private Class[] constructorArgumentsTypes; + private Class[] constructorArgumentTypes; /** - * cglib Factory for lazily-loaded instances after the first. - * Only used in the presence of constructor args. + * The explictly specified default constructor arguments which may be overridden by CallParamRules. * * @since 3.2 */ - private Factory proxyFactory; + private Object[] defaultConstructorArguments; + + /** + * Helper object for managing proxies. + * + * @since 3.2 + */ + private ProxyManager proxyManager; // --------------------------------------------------------- Public Methods /** - * Allows users specify constructor arguments. + * Allows users to specify constructor argument types. * * @since 3.2 */ - public void setConstructorArguments( Class...constructorArgumentsTypes ) + public void setConstructorArgumentTypes( Class... constructorArgumentTypes ) { - if ( constructorArgumentsTypes == null ) + if ( constructorArgumentTypes == null ) { - throw new IllegalArgumentException( "Parameter 'constructorArgumentsTypes' must not be null" ); + throw new IllegalArgumentException( "Parameter 'constructorArgumentTypes' must not be null" ); } - this.constructorArgumentsTypes = constructorArgumentsTypes; + this.constructorArgumentTypes = constructorArgumentTypes; + } + + /** + * Allows users to specify default constructor arguments. If a default/no-arg constructor is not available + * for the target class, these arguments will be used to create the proxy object. For any argument + * not supplied by a {@link CallParamRule}, the corresponding item from this array will be used + * to construct the final object as well. + * + * @since 3.2 + */ + public void setDefaultConstructorArguments( Object... constructorArguments ) + { + if ( constructorArguments == null ) + { + throw new IllegalArgumentException( "Parameter 'constructorArguments' must not be null" ); + } + + this.defaultConstructorArguments = constructorArguments; } /** @@ -236,7 +344,7 @@ public class ObjectCreateRule clazz = getDigester().getClassLoader().loadClass( realClassName ); } Object instance; - if ( constructorArgumentsTypes == null || constructorArgumentsTypes.length == 0 ) + if ( constructorArgumentTypes == null || constructorArgumentTypes.length == 0 ) { if ( getDigester().getLogger().isDebugEnabled() ) { @@ -249,44 +357,28 @@ public class ObjectCreateRule } else { - Constructor constructor = getAccessibleConstructor( clazz, constructorArgumentsTypes ); - - if ( constructor == null ) + if ( proxyManager == null ) { - throw new SAXException( format( "[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types", - getDigester().getMatch(), - clazz.getName(), - Arrays.toString( constructorArgumentsTypes ) ) ); - } - - instance = createLazyProxy( constructor ); - } - getDigester().push( instance ); - } - - private Object createLazyProxy( Constructor constructor ) { - Object[] constructorArguments = new Object[constructorArgumentsTypes.length]; - fill( constructorArguments, null ); - getDigester().pushParams( constructorArguments ); - - DeferredConstructionCallback callback = new DeferredConstructionCallback(constructor, constructorArguments); + synchronized ( this ) + { + if ( proxyManager == null ) + { + Constructor constructor = getAccessibleConstructor( clazz, constructorArgumentTypes ); - 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.setInterfaces(new Class[] { DeferredConstructionProxy.class }); - enhancer.setCallback( callback ); - enhancer.setClassLoader( getDigester().getClassLoader() ); - Object result = enhancer.create(); - proxyFactory = (Factory) result; - return result; + if ( constructor == null ) + { + throw new SAXException( format( "[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types", + getDigester().getMatch(), + clazz.getName(), + Arrays.toString( constructorArgumentTypes ) ) ); + } + proxyManager = new ProxyManager(clazz, constructor, defaultConstructorArguments, getDigester()); + } } } + instance = proxyManager.createProxy(); } - return proxyFactory.newInstance( callback ); + getDigester().push( instance ); } /** @@ -298,10 +390,9 @@ public class ObjectCreateRule { Object top = getDigester().pop(); - if (top instanceof DeferredConstructionProxy) + if ( proxyManager != null ) { - getDigester().popParams(); - ((DeferredConstructionProxy) top).finish(); + proxyManager.finalize(top); } if ( getDigester().getLogger().isDebugEnabled() ) @@ -321,4 +412,22 @@ public class ObjectCreateRule return format( "ObjectCreateRule[className=%s, attributeName=%s]", className, attributeName ); } + private static void convertTo( Class[] types, Object[] array ) + { + if ( array.length != types.length ) + { + throw new IllegalArgumentException(); + } + // this piece of code is adapted from CallMethodRule + for ( int i = 0; i < array.length; i++ ) + { + // convert nulls and convert stringy parameters for non-stringy param types + if ( array[i] == null + || ( array[i] instanceof String && !String.class.isAssignableFrom( types[i] ) ) ) + { + array[i] = convert( (String) array[i], types[i] ); + } + } + } + } Modified: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java?rev=1210631&r1=1210630&r2=1210631&view=diff ============================================================================== --- commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java (original) +++ commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/binder/ObjectCreateBuilder.java Mon Dec 5 21:02:59 2011 @@ -22,6 +22,9 @@ package org.apache.commons.digester3.bin import static java.lang.String.format; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.apache.commons.digester3.ObjectCreateRule; @@ -33,6 +36,20 @@ import org.apache.commons.digester3.Obje public final class ObjectCreateBuilder extends AbstractBackToLinkedRuleBuilder { + private static final Map> PRIMITIVE_TYPES; + static + { + HashMap> primitiveTypes = new HashMap>(); + primitiveTypes.put("boolean", boolean.class); + primitiveTypes.put("byte", byte.class); + primitiveTypes.put("short", short.class); + primitiveTypes.put("int", int.class); + primitiveTypes.put("char", char.class); + primitiveTypes.put("long", long.class); + primitiveTypes.put("float", float.class); + primitiveTypes.put("double", double.class); + PRIMITIVE_TYPES = Collections.unmodifiableMap(primitiveTypes); + } private final ClassLoader classLoader; @@ -41,12 +58,19 @@ public final class ObjectCreateBuilder private String attributeName; /** - * The constructor arguments - order is preserved by the LinkedHashMap + * The constructor argument types * * @since 3.2 */ private Class[] constructorArgumentsType; + /** + * Default constructor arguments. + * + * @since 3.2 + */ + private Object[] defaultConstructorArguments; + ObjectCreateBuilder( String keyPattern, String namespaceURI, RulesBinder mainBinder, LinkedRuleBuilder mainBuilder, ClassLoader classLoader ) { @@ -121,24 +145,26 @@ public final class ObjectCreateBuilder if ( paramTypeNames == null ) { reportError( "createObject().usingConstructor( String[] )", "NULL parametersTypes not allowed" ); + return this; } - Class[] paramTypes = null; - if ( paramTypeNames != null ) + Class[] paramTypes = new Class[paramTypeNames.length]; + for ( int i = 0; i < paramTypeNames.length; i++ ) { - paramTypes = new Class[paramTypeNames.length]; - for ( int i = 0; i < paramTypeNames.length; i++ ) + if ( PRIMITIVE_TYPES.containsKey( paramTypeNames[i] ) ) + { + paramTypes[i] = PRIMITIVE_TYPES.get( paramTypeNames[i] ); + continue; + } + try { - try - { - paramTypes[i] = classLoader.loadClass( paramTypeNames[i] ); - } - catch ( ClassNotFoundException e ) - { - this.reportError( format( "createObject().usingConstructor( %s )", - Arrays.toString( paramTypeNames ) ), - format( "class '%s' cannot be load", paramTypeNames[i] ) ); - } + paramTypes[i] = classLoader.loadClass( paramTypeNames[i] ); + } + catch ( ClassNotFoundException e ) + { + this.reportError( format( "createObject().usingConstructor( %s )", + Arrays.toString( paramTypeNames ) ), + format( "class '%s' cannot be loaded", paramTypeNames[i] ) ); } } @@ -150,18 +176,33 @@ public final class ObjectCreateBuilder * @return * @since 3.2 */ - public ObjectCreateBuilder usingConstructor( Class...constructorArgumentsType ) + public ObjectCreateBuilder usingConstructor( Class... constructorArgumentTypes ) { - if ( constructorArgumentsType == null ) + if ( constructorArgumentTypes == null ) { - reportError( "createObject().usingConstructor( Class[] )", "NULL parametersTypes not allowed" ); + reportError( "createObject().usingConstructor( Class[] )", "NULL constructorArgumentTypes not allowed" ); + return this; } - this.constructorArgumentsType = constructorArgumentsType; + this.constructorArgumentsType = constructorArgumentTypes; return this; } + public ObjectCreateBuilder usingDefaultConstructorArguments( Object... defaultConstructorArguments) + { + if ( defaultConstructorArguments == null ) + { + reportError( "createObject().usingDefaultConstructorArguments( Object[] )", "NULL defaultConstructorArguments not allowed" ); + return this; + } + + this.defaultConstructorArguments = defaultConstructorArguments; + + return this; + + } + /** * {@inheritDoc} */ @@ -172,7 +213,11 @@ public final class ObjectCreateBuilder if ( constructorArgumentsType != null ) { - objectCreateRule.setConstructorArguments( constructorArgumentsType ); + objectCreateRule.setConstructorArgumentTypes( constructorArgumentsType ); + } + if ( defaultConstructorArguments != null ) + { + objectCreateRule.setDefaultConstructorArguments( defaultConstructorArguments ); } return objectCreateRule; Modified: commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/xmlrules/ObjectCreateRule.java URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/xmlrules/ObjectCreateRule.java?rev=1210631&r1=1210630&r2=1210631&view=diff ============================================================================== --- commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/xmlrules/ObjectCreateRule.java (original) +++ commons/proper/digester/trunk/src/main/java/org/apache/commons/digester3/xmlrules/ObjectCreateRule.java Mon Dec 5 21:02:59 2011 @@ -61,6 +61,18 @@ final class ObjectCreateRule } builder.usingConstructor( paramTypeNames ); } + String paramsStr = attributes.getValue( "params" ); + if ( paramsStr != null && paramsStr.length() > 0 ) + { + StringTokenizer tokens = new StringTokenizer( paramTypesStr, " \t\n\r," ); + Object[] params = new Object[tokens.countTokens()]; + int counter = 0; + while ( tokens.hasMoreTokens() ) + { + params[counter++] = tokens.nextToken(); + } + builder.usingDefaultConstructorArguments( params ); + } } } Modified: commons/proper/digester/trunk/src/main/resources/org/apache/commons/digester3/xmlrules/digester-rules.dtd URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/main/resources/org/apache/commons/digester3/xmlrules/digester-rules.dtd?rev=1210631&r1=1210630&r2=1210631&view=diff ============================================================================== --- commons/proper/digester/trunk/src/main/resources/org/apache/commons/digester3/xmlrules/digester-rules.dtd (original) +++ commons/proper/digester/trunk/src/main/resources/org/apache/commons/digester3/xmlrules/digester-rules.dtd Mon Dec 5 21:02:59 2011 @@ -113,13 +113,13 @@ usingElementBodyAsArgument CDATA #IMPLIED useExactMatch CDATA #IMPLIED> - @@ -162,7 +162,7 @@ @@ -170,7 +170,7 @@ pattern CDATA #IMPLIED type CDATA #IMPLIED> - 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=1210631&r1=1210630&r2=1210631&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 Mon Dec 5 21:02:59 2011 @@ -40,7 +40,7 @@ public final class Digester153TestCase throws Exception { ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); - createRule.setConstructorArguments( boolean.class, double.class ); + createRule.setConstructorArgumentTypes( boolean.class, double.class ); Digester digester = new Digester(); digester.addRule( "toplevel/bean", createRule ); @@ -64,7 +64,7 @@ public final class Digester153TestCase throws Exception { ObjectCreateRule createRule = new ObjectCreateRule( TestBean.class ); - createRule.setConstructorArguments( boolean.class, double.class ); + createRule.setConstructorArgumentTypes( boolean.class, double.class ); Digester digester = new Digester(); digester.addRule( "toplevel/bean", createRule ); Modified: commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/xmlrules/constructor-testrules.xml URL: http://svn.apache.org/viewvc/commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/xmlrules/constructor-testrules.xml?rev=1210631&r1=1210630&r2=1210631&view=diff ============================================================================== --- commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/xmlrules/constructor-testrules.xml (original) +++ commons/proper/digester/trunk/src/test/resources/org/apache/commons/digester3/xmlrules/constructor-testrules.xml Mon Dec 5 21:02:59 2011 @@ -7,9 +7,9 @@ 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. @@ -19,7 +19,7 @@ + paramtypes="java.lang.Boolean,java.lang.Double" params="true,0" />