You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sk...@apache.org on 2005/02/07 09:02:32 UTC
svn commit: r151707 -
jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java
Author: skitching
Date: Mon Feb 7 00:02:30 2005
New Revision: 151707
URL: http://svn.apache.org/viewcvs?view=rev&rev=151707
Log:
Major rework.
Modified:
jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java (contents, props changed)
Modified: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java?view=diff&r1=151706&r2=151707
==============================================================================
--- jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java (original)
+++ jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java Mon Feb 7 00:02:30 2005
@@ -1,24 +1,25 @@
-/* $Id: $
+/* $Id$
+ *
+ * Copyright 2001-2005 The Apache Software Foundation.
*
- * Copyright 2001-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 org.apache.commons.digester2.actions;
import org.xml.sax.Attributes;
+import java.util.Map;
import java.util.HashMap;
import org.apache.commons.beanutils.BeanUtils;
@@ -28,77 +29,121 @@
import org.apache.commons.digester2.AbstractAction;
import org.apache.commons.digester2.ParseException;
-
-
/**
- * <p>Rule implementation that sets properties on the object at the top of the
+ * <p>An Action that sets properties on the object at the top of the
* stack, based on attributes with corresponding names.</p>
*
- * <p>This rule supports custom mapping of attribute names to property names.
- * The default mapping for particular attributes can be overridden by using
+ * <p>By default, any xml attribute xyz='value' causes a call to property-setter
+ * method setXyz(value) on the top object on the stack. If the target method
+ * does not take a String parameter, then the BeanUtils library attempts
+ * to convert the String value of the xml attribute to the required type.</p>
+ *
+ * <p>Custom mapping of attribute names to property names can also be done.
+ * The default mapping for particular attributes can be overridden by using
* {@link #ActionSetProperties(String[] attributeNames, String[] propertyNames)}.
* This allows attributes to be mapped to properties with different names.
- * Certain attributes can also be marked to be ignored.</p>
+ * Certain attributes can also be marked to be ignored, by specifying the
+ * target property to be null.</p>
+ *
+ * <p>XML Attributes that are not in the default namespace are ignored.</p>
*/
public class SetPropertiesAction extends AbstractAction {
- // ----------------------------------------------------------- Constructors
+ private HashMap customMap = null;
+
+ // -----------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------
/**
* Base constructor.
*/
public SetPropertiesAction() {
- // nothing to set up
+ // nothing to set up
}
-
- /**
- * <p>Convenience constructor overrides the mapping for just one property.</p>
+
+ /**
+ * <p>Constructor which allows the default attribute->property mapping to
+ * be overriden.</p>
+ *
+ * <p>The keys of the map are xml attribute names, and the associated values
+ * are the java property name to map that attribute to. If the value
+ * associated with an attribute name is null then the attibute will be
+ * ignored.</p>
+ *
+ * <h5>Example</h5>
+ * <p> The following constructs a rule that maps the <code>class</code>
+ * attribute to the <code>className</code> property. The attribute
+ * <code>ignore</code> is not mapped, and will therefore be ignored rather
+ * than be passed to a setIgnore method (the default behaviour). All other
+ * attributes are mapped as usual using exact name matching.
+ * <code><pre>
+ * HashMap map = new HashMap();
+ * map.put("class", "className");
+ * map.put("ignore", null);
+ * SetPropertiesAction(map);
+ * </pre></code></p>
+ *
+ * <p>See also {@link #addAlias} which allows the custom mapping to be
+ * modified after the SetPropertiesAction has been constructed.</p>
+ *
+ * @param customMap is a map. The map is copied, so future changes to
+ * the map will not affect this object.
+ */
+ public SetPropertiesAction(Map customMap) {
+ this.customMap = new HashMap(customMap);
+ }
+
+ /**
+ * <p>Convenience constructor which overrides the default attribute->property
+ * mapping for just one property.</p>
*
* <p>For details about how this works, see
- * {@link #ActionSetProperties(String[] attributeNames, String[] propertyNames)}.</p>
+ * {@link #ActionSetProperties(Map customMappings)}.</p>
*
- * @param attributeName map this attribute
- * @param propertyName to a property with this name
+ * @param attributeName map this attribute. Must not be null.
+ * @param propertyName to a property with this name. May be null.
*/
public SetPropertiesAction(String attributeName, String propertyName) {
- attributeNames = new String[1];
- attributeNames[0] = attributeName;
- propertyNames = new String[1];
- propertyNames[0] = propertyName;
+ customMap = new HashMap(1);
+ customMap.put(attributeName, propertyName);
}
-
- /**
- * <p>Constructor allows attribute->property mapping to be overriden.</p>
+
+ /**
+ * <p>Constructor which allows the default attribute->property mapping to
+ * be overriden.</p>
*
- * <p>Two arrays are passed in.
- * One contains the attribute names and the other the property names.
- * The attribute name / property name pairs are match by position
- * In order words, the first string in the attribute name list matches
- * to the first string in the property name list and so on.</p>
+ * <p>Two arrays are passed in. One contains the attribute names and the
+ * other the property names. The attribute name / property name pairs are
+ * matched by position In order words, the first string in the attribute
+ * name list maps to the first string in the property name list and so on.
+ * </p>
*
* <p>If a property name is null or the attribute name has no matching
- * property name, then this indicates that the attibute should be ignored.</p>
- *
+ * property name (ie the property array is shorter than the attribute array)
+ * then the attibute will be ignored.</p>
+ *
* <h5>Example One</h5>
* <p> The following constructs a rule that maps the <code>alt-city</code>
* attribute to the <code>city</code> property and the <code>alt-state</code>
- * to the <code>state</code> property.
+ * to the <code>state</code> property.
* All other attributes are mapped as usual using exact name matching.
* <code><pre>
- * SetPropertiesRule(
- * new String[] {"alt-city", "alt-state"},
+ * SetPropertiesAction(
+ * new String[] {"alt-city", "alt-state"},
* new String[] {"city", "state"});
* </pre></code>
*
* <h5>Example Two</h5>
* <p> The following constructs a rule that maps the <code>class</code>
- * attribute to the <code>className</code> property.
- * The attribute <code>ignore-me</code> is not mapped.
- * All other attributes are mapped as usual using exact name matching.
+ * attribute to the <code>className</code> property. The attribute
+ * <code>ignore</code> is not mapped, and will therefore be ignored rather
+ * than be passed to a setIgnore method (the default behaviour). All other
+ * attributes are mapped as usual using exact name matching.
* <code><pre>
- * SetPropertiesRule(
- * new String[] {"class", "ignore-me"},
+ * SetPropertiesAction(
+ * new String[] {"class", "ignore"},
* new String[] {"className"});
* </pre></code>
*
@@ -106,105 +151,117 @@
* @param propertyNames names of properties mapped to
*/
public SetPropertiesAction(String[] attributeNames, String[] propertyNames) {
- // create local copies
- this.attributeNames = new String[attributeNames.length];
- for (int i=0, size=attributeNames.length; i<size; i++) {
- this.attributeNames[i] = attributeNames[i];
+ int nAttributes = attributeNames.length;
+ int nProperties = propertyNames.length;
+
+ customMap = new HashMap(nAttributes);
+
+ for(int i=0; i<nAttributes; ++i) {
+ if (i < nProperties) {
+ customMap.put(attributeNames[i], propertyNames[i]);
+ } else {
+ customMap.put(attributeNames[i], null);
+ }
}
-
- this.propertyNames = new String[propertyNames.length];
- for (int i=0, size=propertyNames.length; i<size; i++) {
- this.propertyNames[i] = propertyNames[i];
- }
}
-
- // ----------------------------------------------------- Instance Variables
-
- /**
- * Attribute names used to override natural attribute->property mapping
- */
- private String [] attributeNames;
- /**
- * Property names used to override natural attribute->property mapping
- */
- private String [] propertyNames;
+ // ---------------------------------------------------------
+ // Public Methods
+ // ---------------------------------------------------------
+ /**
+ * <p>Add an additional attribute name to property name mapping.
+ * This is particularly useful for the xmlrules optional module.</p>
+ *
+ * <p>See {@link #SetPropertiesAction(Map customMap)}.
+ */
+ public void addAlias(String attributeName, String propertyName) {
+ if (customMap == null) {
+ customMap = new HashMap();
+ }
+ customMap.put(attributeName, propertyName);
+ }
- // --------------------------------------------------------- Public Methods
+ // ---------------------------------------------------------
+ // Action Methods
+ // ---------------------------------------------------------
/**
* Process the beginning of this element.
*
- * @param attributes The attribute list of this element
+ * @param context The object on which all parsing state is stored.
+ * @param namespace The xml namespace the current element is in.
+ * @param name The local name of the current element.
+ * @param attributes The attribute list of the current element
*/
public void begin(
- Context context, String namespace, String elementName, Attributes attributes)
+ Context context,
+ String namespace, String elementName,
+ Attributes attributes)
throws ParseException {
-
+
Log log = context.getLogger();
- // Build a set of attribute names and corresponding values
+ // Build a set of property names and corresponding values
HashMap values = new HashMap();
-
- // set up variables for custom names mappings
- int attNamesLength = 0;
- if (attributeNames != null) {
- attNamesLength = attributeNames.length;
- }
- int propNamesLength = 0;
- if (propertyNames != null) {
- propNamesLength = propertyNames.length;
- }
-
+
+ // for each xml attribute
for (int i = 0; i < attributes.getLength(); i++) {
- String name = attributes.getLocalName(i);
- if ("".equals(name)) {
- name = attributes.getQName(i);
- }
- String value = attributes.getValue(i);
-
- // we'll now check for custom mappings
- for (int n = 0; n<attNamesLength; n++) {
- if (name.equals(attributeNames[n])) {
- if (n < propNamesLength) {
- // set this to value from list
- name = propertyNames[n];
-
- } else {
- // set name to null
- // we'll check for this later
- name = null;
- }
- break;
+ if (attributes.getURI(i).length() > 0) {
+ // currently we ignore any attributes with namespaces
+ if (log.isDebugEnabled()) {
+ log.debug(
+ "[SetProperties]{" + context.getMatchPath() +
+ "} Ignoring namespaced xml attribute "
+ + attributes.getLocalName(i));
+ }
+ } else {
+ String attrName = attributes.getLocalName(i);
+ if ("".equals(attrName)) {
+ attrName = attributes.getQName(i);
+ }
+ String value = attributes.getValue(i);
+
+ String propName;
+
+ // We'll now check for custom mappings. Note that propName
+ // can be set to null by this...
+ if ((customMap != null) && customMap.containsKey(attrName)) {
+ propName = customMap.get(attrName).toString();
+ } else {
+ // if attrName contains a hyphen, it will be converted
+ // to camelCase, otherwise we just try to use the
+ // unmodified attrName as the propName.
+ propName = convertHyphenatedToCamelCase(attrName);
}
- }
- if (log.isDebugEnabled()) {
- log.debug("[SetProperties]{" + context.getMatchPath() +
- "} Setting property '" + name + "' to '" +
- value + "'");
+ if (propName != null) {
+ if (log.isDebugEnabled()) {
+ log.debug("[SetProperties]{" + context.getMatchPath() +
+ "} Setting property '" + propName + "' to '" +
+ value + "'");
+ }
+
+ values.put(propName, value);
+ }
}
- if (name != null) {
- values.put(name, value);
- }
}
// Populate the corresponding properties of the top object
- Object top = context.peek();
+ Object target = context.peek();
if (log.isDebugEnabled()) {
- if (top != null) {
+ if (target != null) {
log.debug("[ActionSetProperties]{" + context.getMatchPath() +
- "} Set " + top.getClass().getName() +
+ "} Set " + target.getClass().getName() +
" properties");
} else {
log.debug("[ActionSetProperties]{" + context.getMatchPath() +
"} Set NULL properties");
}
}
-
+
try {
- BeanUtils.populate(top, values);
+ BeanUtils.populate(target, values);
} catch(IllegalAccessException ex) {
throw new ParseException(ex);
} catch(java.lang.reflect.InvocationTargetException ex) {
@@ -213,50 +270,69 @@
}
+ // ---------------------------------------------------------
+ // Private Methods
+ // ---------------------------------------------------------
+
/**
- * <p>Add an additional attribute name to property name mapping.
- * This is intended to be used from the xml rules.
+ * <p>This method is intended to convert xml hypenated names
+ * into javabean names.</p>
+ *
+ * <p>Traditionally, xml element and attribute names that are
+ * formed from multiple words use hyphens to separate the words,
+ * for example a-long-attribute-name. However the Java tradition
+ * is to use camel-case, eg aLongAttributeName. This method
+ * converts from the xml to the java convention.</p>
+ *
+ * <p>If src contains no hyphens then a reference to the same object
+ * is returned.</p>
+ *
+ * <p>If src ends in a hyphen, then it is stripped.</p>
+ *
+ * @param src is the string to be converted. Must not be null.
*/
- public void addAlias(String attributeName, String propertyName) {
+ private String convertHyphenatedToCamelCase(String src) {
+ // check whether there is a hyphen in this string or not
+ int firstPos = src.indexOf("-");
+ if (firstPos == -1) {
+ return src;
+ }
- // this is a bit tricky.
- // we'll need to resize the array.
- // probably should be synchronized but digester's not thread safe anyway
- if (attributeNames == null) {
-
- attributeNames = new String[1];
- attributeNames[0] = attributeName;
- propertyNames = new String[1];
- propertyNames[0] = propertyName;
-
- } else {
- int length = attributeNames.length;
- String [] tempAttributes = new String[length + 1];
- for (int i=0; i<length; i++) {
- tempAttributes[i] = attributeNames[i];
- }
- tempAttributes[length] = attributeName;
-
- String [] tempProperties = new String[length + 1];
- for (int i=0; i<length && i< propertyNames.length; i++) {
- tempProperties[i] = propertyNames[i];
+ int srcLen = src.length();
+ StringBuffer buf = new StringBuffer(srcLen);
+
+ // bulk append up until the first hyphen, as we already know
+ // where it is.
+ buf.append(src.substring(0, firstPos));
+
+ // now it is easiest to simply step through char-by-char until
+ // the end of the string.
+ boolean cap = true;
+ for(int i=firstPos+1; i<srcLen; ++i) {
+ char c = src.charAt(i);
+ if (c == '-') {
+ cap = true;
+ } else if (cap) {
+ buf.append(Character.toUpperCase(c));
+ cap = false;
+ } else {
+ buf.append(c);
}
- tempProperties[length] = propertyName;
-
- propertyNames = tempProperties;
- attributeNames = tempAttributes;
- }
+ }
+
+ return buf.toString();
}
-
+
+ // ---------------------------------------------------------
+ // Other Methods
+ // ---------------------------------------------------------
/**
* Render a printable version of this Rule.
*/
public String toString() {
-
StringBuffer sb = new StringBuffer("SetPropertiesRule[");
sb.append("]");
return (sb.toString());
-
}
}
Propchange: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java
------------------------------------------------------------------------------
svn:keywords = Id
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org