You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2003/04/11 23:27:42 UTC
cvs commit: jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io BeanRuleSet.java
rdonkin 2003/04/11 14:27:42
Modified: betwixt/src/java/org/apache/commons/betwixt/digester
XMLIntrospectorHelper.java
betwixt/src/java/org/apache/commons/betwixt/io
BeanRuleSet.java
Added: betwixt/src/java/org/apache/commons/betwixt/expression
MapEntryAdder.java
Log:
Added bean reader for maps. This allows the standard ooutput of a composite property which is a map to be read back.
Revision Changes Path
1.20 +111 -43 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java
Index: XMLIntrospectorHelper.java
===================================================================
RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- XMLIntrospectorHelper.java 8 Apr 2003 13:41:40 -0000 1.19
+++ XMLIntrospectorHelper.java 11 Apr 2003 21:27:41 -0000 1.20
@@ -74,6 +74,7 @@
import org.apache.commons.betwixt.expression.IteratorExpression;
import org.apache.commons.betwixt.expression.MethodExpression;
import org.apache.commons.betwixt.expression.MethodUpdater;
+import org.apache.commons.betwixt.expression.MapEntryAdder;
import org.apache.commons.betwixt.strategy.PluralStemmer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -193,6 +194,9 @@
//loopDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
if ( Map.class.isAssignableFrom( type ) ) {
loopDescriptor.setQualifiedName( "entry" );
+ // add elements for reading
+ loopDescriptor.addElementDescriptor( new ElementDescriptor( "key" ) );
+ loopDescriptor.addElementDescriptor( new ElementDescriptor( "value" ) );
}
ElementDescriptor elementDescriptor = new ElementDescriptor();
@@ -441,57 +445,121 @@
// XXX: should we filter out non-void returning methods?
// some beans will return something as a helper
Class[] types = method.getParameterTypes();
- if ( types != null && types.length == 1 ) {
- String propertyName = Introspector.decapitalize( name.substring(3) );
+ if ( types != null) {
if ( log.isTraceEnabled() ) {
- log.trace( name + "->" + propertyName );
- }
-
- // now lets try find the ElementDescriptor which displays
- // a property which starts with propertyName
- // and if so, we'll set a new Updater on it if there
- // is not one already
- ElementDescriptor descriptor =
- findGetCollectionDescriptor(
- introspector,
- rootDescriptor,
- propertyName );
-
- if ( log.isDebugEnabled() ) {
- log.debug( "!! " + propertyName + " -> " + descriptor );
- log.debug( "!! " + name + " -> "
- + (descriptor!=null?descriptor.getPropertyName():"") );
+ log.trace("Searching for match for " + method);
}
- if ( descriptor != null ) {
- descriptor.setUpdater( new MethodUpdater( method ) );
- descriptor.setSingularPropertyType( types[0] );
-
+ if ( ( types.length == 1 ) || types.length == 2 ) {
+ String propertyName = Introspector.decapitalize( name.substring(3) );
+ if ( log.isTraceEnabled() ) {
+ log.trace( name + "->" + propertyName );
+ }
+
+ // now lets try find the ElementDescriptor which displays
+ // a property which starts with propertyName
+ // and if so, we'll set a new Updater on it if there
+ // is not one already
+ ElementDescriptor descriptor =
+ findGetCollectionDescriptor(
+ introspector,
+ rootDescriptor,
+ propertyName );
+
if ( log.isDebugEnabled() ) {
- log.debug( "!! " + method);
- log.debug( "!! " + types[0]);
+ log.debug( "!! " + propertyName + " -> " + descriptor );
+ log.debug( "!! " + name + " -> "
+ + (descriptor!=null?descriptor.getPropertyName():"") );
}
-
- // is there a child element with no localName
- ElementDescriptor[] children = descriptor.getElementDescriptors();
- if ( children != null && children.length > 0 ) {
- ElementDescriptor child = children[0];
- String localName = child.getLocalName();
- if ( localName == null || localName.length() == 0 ) {
- child.setLocalName(
- introspector.getElementNameMapper()
- .mapTypeToElementName( propertyName ) );
+ if ( descriptor != null ) {
+ boolean isMapDescriptor
+ = Map.class.isAssignableFrom( descriptor.getPropertyType() );
+ if ( !isMapDescriptor && types.length == 1 ) {
+ // this may match a standard collection or iteration
+ log.trace("Matching collection or iteration");
+
+ descriptor.setUpdater( new MethodUpdater( method ) );
+ descriptor.setSingularPropertyType( types[0] );
+
+ if ( log.isDebugEnabled() ) {
+ log.debug( "!! " + method);
+ log.debug( "!! " + types[0]);
+ }
+
+ // is there a child element with no localName
+ ElementDescriptor[] children
+ = descriptor.getElementDescriptors();
+ if ( children != null && children.length > 0 ) {
+ ElementDescriptor child = children[0];
+ String localName = child.getLocalName();
+ if ( localName == null || localName.length() == 0 ) {
+ child.setLocalName(
+ introspector.getElementNameMapper()
+ .mapTypeToElementName( propertyName ) );
+ }
+ }
+
+ } else if ( isMapDescriptor && types.length == 2 ) {
+ // this may match a map
+ log.trace("Matching map");
+ ElementDescriptor[] children
+ = descriptor.getElementDescriptors();
+ // see if the descriptor's been set up properly
+ if ( children.length == 0 ) {
+
+ log.info(
+ "'entry' descriptor is missing for map. "
+ + "Updaters cannot be set");
+
+ } else {
+ // loop through grandchildren
+ // adding updaters for key and value
+ ElementDescriptor[] grandchildren
+ = children[0].getElementDescriptors();
+ MapEntryAdder adder = new MapEntryAdder(method);
+ for (
+ int n=0,
+ noOfGrandChildren = grandchildren.length;
+ n < noOfGrandChildren;
+ n++ ) {
+ if ( "key".equals(
+ grandchildren[n].getLocalName() ) ) {
+
+ grandchildren[n].setUpdater(
+ adder.getKeyUpdater() );
+ grandchildren[n].setSingularPropertyType(
+ types[0] );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Key descriptor: " + grandchildren[n]);
+ }
+
+ } else if (
+ "value".equals(
+ grandchildren[n].getLocalName() ) ) {
+
+ grandchildren[n].setUpdater(
+ adder.getValueUpdater() );
+ grandchildren[n].setSingularPropertyType(
+ types[1] );
+ if ( log.isTraceEnabled() ) {
+ log.trace(
+ "Value descriptor: " + grandchildren[n]);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if ( log.isDebugEnabled() ) {
+ log.debug(
+ "Could not find an ElementDescriptor with property name: "
+ + propertyName + " to attach the add method: " + method
+ );
}
- }
- } else {
- if ( log.isDebugEnabled() ) {
- log.debug(
- "Could not find an ElementDescriptor with property name: "
- + propertyName + " to attach the add method: " + method
- );
}
}
- }
+ }
}
}
}
1.1 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/expression/MapEntryAdder.java
Index: MapEntryAdder.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/expression/MapEntryAdder.java,v 1.1 2003/04/11 21:27:42 rdonkin Exp $
* $Revision: 1.1 $
* $Date: 2003/04/11 21:27:42 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 The Apache Software Foundation. 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. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* $Id: MapEntryAdder.java,v 1.1 2003/04/11 21:27:42 rdonkin Exp $
*/
package org.apache.commons.betwixt.expression;
import java.lang.reflect.Method;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** <p><code>MapEntryAdder</code> is used to add entries to a map.</p>
*
* <p>
* <code>MapEntryAdder</code> supplies two updaters:
* <ul>
* <li>{@link #getKeyUpdater()} which allows the entry key to be updated</li>
* <li>{@link #getValueUpdater()} which allows the entry value to be updated</li>
* </ul>
* When both of these updaters have been called, the entry adder method is called.
* Once this has happened then the values can be updated again.
* Note that only the <code>Context</code> passed by the last update will be used.
* </p>
*
* @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
* @version $Revision: 1.1 $
*/
public class MapEntryAdder {
// Class Attributes
//-------------------------------------------------------------------------
/** Log used by this class */
private static Log log = LogFactory.getLog( MapEntryAdder.class );
// Class Methods
//-------------------------------------------------------------------------
/**
* Sets the logger used by this class.
*
* @param newLog log to this
*/
public static void setLog(Log newLog) {
log = newLog;
}
// Attributes
//-------------------------------------------------------------------------
/** The method to be called to add a new map entry */
private Method adderMethod;
/** Has the entry key been updated? */
private boolean keyUpdated = false;
/** The entry key */
private Object key;
/** Has the entry value been updated? */
private boolean valueUpdated = false;
/** The entry value */
private Object value;
// Constructors
//-------------------------------------------------------------------------
/**
* Construct a <code>MapEntryAdder</code> which adds entries to given method.
*
* @param method the <code>Method</code> called to add a key-value entry
* @throws IllegalArgumentException if the given method does not take two parameters
*/
public MapEntryAdder(Method method) {
Class[] types = method.getParameterTypes();
if ( types == null || types.length != 2) {
throw new IllegalArgumentException(
"Method used to add entries to maps must have two parameter.");
}
this.adderMethod = method;
}
// Properties
//-------------------------------------------------------------------------
/**
* Gets the entry key <code>Updater</code>.
* This is used to update the entry key value to the read value.
* If {@link #getValueUpdater} has been called previously,
* then this trigger the updating of the adder method.
*
* @return the <code>Updater</code> which should be used to populate the entry key
*/
public Updater getKeyUpdater() {
return new Updater() {
public void update( Context context, Object keyValue ) {
// might as well make sure that his can only be set once
if ( !keyUpdated ) {
keyUpdated = true;
key = keyValue;
if ( log.isTraceEnabled() ) {
log.trace( "Setting entry key to " + key );
log.trace( "Current entry value is " + value );
}
if ( valueUpdated ) {
callAdderMethod( context );
}
}
}
};
}
/**
* Gets the entry value <code>Updater</code>.
* This is used to update the entry key value to the read value.
* If {@link #getKeyUpdater} has been called previously,
* then this trigger the updating of the adder method.
*
* @return the <code>Updater</code> which should be used to populate the entry value
*/
public Updater getValueUpdater() {
return new Updater() {
public void update( Context context, Object valueValue ) {
// might as well make sure that his can only be set once
if ( !valueUpdated ) {
valueUpdated = true;
value = valueValue;
if ( log.isTraceEnabled() ) {
log.trace( "Setting entry value to " + value);
log.trace( "Current entry key is " + key );
}
if ( keyUpdated ) {
callAdderMethod( context );
}
}
}
};
}
// Implementation methods
//-------------------------------------------------------------------------
/**
* Call the adder method on the bean associated with the <code>Context</code>
* with the key, value entry values stored previously.
*
* @param context the Context against whose bean the adder method will be invoked
*/
private void callAdderMethod(Context context) {
log.trace("Calling adder method");
// this allows the same instance to be used multiple times.
keyUpdated = false;
valueUpdated = false;
//
// XXX This is (basically) cut and pasted from the MethodUpdater code
// I haven't abstracted this code just yet since I think that adding
// handling for non-beans will mean adding quite a lot more structure
// and only once this is added will the proper position for this method
// become clear.
//
Class[] types = adderMethod.getParameterTypes();
// key is first parameter
Class keyType = types[0];
// value is the second
Class valueType = types[1];
Object bean = context.getBean();
if ( bean != null ) {
if ( key instanceof String ) {
// try to convert into primitive types
key = ConvertUtils.convert( (String) key, keyType );
}
if ( value instanceof String ) {
// try to convert into primitive types
value = ConvertUtils.convert( (String) value, keyType );
}
Object[] arguments = { key, value };
try {
if ( log.isTraceEnabled() ) {
log.trace(
"Calling adder method: " + adderMethod.getName() + " on bean: " + bean
+ " with key: " + key + " and value: " + value
);
}
adderMethod.invoke( bean, arguments );
} catch (Exception e) {
log.warn(
"Cannot evaluate adder method: " + adderMethod.getName() + " on bean: " + bean
+ " of type: " + bean.getClass().getName() + " with value: " + value
+ " of type: " + valueType + " and key: " + key
+ " of type: " + keyType
);
log.debug(e);
}
}
}
}
1.5 +15 -8 jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanRuleSet.java
Index: BeanRuleSet.java
===================================================================
RCS file: /home/cvs/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/io/BeanRuleSet.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- BeanRuleSet.java 20 Mar 2003 19:29:00 -0000 1.4
+++ BeanRuleSet.java 11 Apr 2003 21:27:42 -0000 1.5
@@ -284,7 +284,10 @@
continue;
}
if ( childDescriptor.getUpdater() != null ) {
- if (log.isTraceEnabled()) {
+ if (
+ log.isTraceEnabled()
+ && childDescriptor.getUpdater() instanceof MethodUpdater) {
+
log.trace("Element has updater "
+ ((MethodUpdater) childDescriptor.getUpdater()).getMethod().getName());
}
@@ -319,7 +322,9 @@
}
}
} else {
- log.trace("Element does not have updater");
+ if ( log.isTraceEnabled() ) {
+ log.trace("Element does not have updater: " + childDescriptor);
+ }
}
ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
@@ -328,6 +333,8 @@
log.trace("Adding grand children @path:" + path);
}
addChildRules( path + '/', childDescriptor, context );
+ } else if ( log.isTraceEnabled() ) {
+ log.trace( "No children for " + childDescriptor);
}
}
}
@@ -345,7 +352,7 @@
*/
ElementDescriptor getElementDescriptor( ElementDescriptor propertyDescriptor ) {
Class beanClass = propertyDescriptor.getSingularPropertyType();
- if ( beanClass != null ) {
+ if ( beanClass != null && !Map.class.isAssignableFrom( beanClass ) ) {
try {
XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
return xmlInfo.getElementDescriptor();
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org