Return-Path: Delivered-To: apmail-jakarta-commons-dev-archive@www.apache.org Received: (qmail 51000 invoked from network); 7 Feb 2005 08:02:37 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 7 Feb 2005 08:02:37 -0000 Received: (qmail 38235 invoked by uid 500); 7 Feb 2005 08:02:35 -0000 Delivered-To: apmail-jakarta-commons-dev-archive@jakarta.apache.org Received: (qmail 38190 invoked by uid 500); 7 Feb 2005 08:02:34 -0000 Mailing-List: contact commons-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Jakarta Commons Developers List" Reply-To: "Jakarta Commons Developers List" Delivered-To: mailing list commons-dev@jakarta.apache.org Received: (qmail 38177 invoked by uid 500); 7 Feb 2005 08:02:34 -0000 Received: (qmail 38173 invoked by uid 99); 7 Feb 2005 08:02:34 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from minotaur.apache.org (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.28) with SMTP; Mon, 07 Feb 2005 00:02:33 -0800 Received: (qmail 50945 invoked by uid 65534); 7 Feb 2005 08:02:32 -0000 Message-ID: <20050207080232.50944.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Mailer: svnmailer-1.0.0-dev Date: Mon, 07 Feb 2005 08:02:32 -0000 Subject: svn commit: r151707 - jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesAction.java To: commons-cvs@jakarta.apache.org From: skitching@apache.org X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N Author: skitching Date: Mon Feb 7 00:02:30 2005 New Revision: 151707 URL: http://svn.apache.org/viewcvs?view=3Drev&rev=3D151707 Log: Major rework. Modified: jakarta/commons/proper/digester/branches/digester2/src/java/org/apache/= commons/digester2/actions/SetPropertiesAction.java (contents, props chang= ed) Modified: jakarta/commons/proper/digester/branches/digester2/src/java/org/a= pache/commons/digester2/actions/SetPropertiesAction.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/digester/branches= /digester2/src/java/org/apache/commons/digester2/actions/SetPropertiesActio= n=2Ejava?view=3Ddiff&r1=3D151706&r2=3D151707 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D --- 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. - *=20 * 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 - *=20 + * * http://www.apache.org/licenses/LICENSE-2.0 - *=20 + * * 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. - */=20 + */ =20 =20 package org.apache.commons.digester2.actions; =20 import org.xml.sax.Attributes; +import java.util.Map; import java.util.HashMap; =20 import org.apache.commons.beanutils.BeanUtils; @@ -28,77 +29,121 @@ import org.apache.commons.digester2.AbstractAction; import org.apache.commons.digester2.ParseException; =20 - - /** - *

Rule implementation that sets properties on the object at the top of= the + *

An Action that sets properties on the object at the top of the * stack, based on attributes with corresponding names.

* - *

This rule supports custom mapping of attribute names to property nam= es. - * The default mapping for particular attributes can be overridden by usin= g=20 + *

By default, any xml attribute xyz=3D'value' causes a call to propert= y-setter + * method setXyz(value) on the top object on the stack. If the target meth= od + * 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> + * + *

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[] propertyN= ames)}. * This allows attributes to be mapped to properties with different names. - * Certain attributes can also be marked to be ignored.

+ * Certain attributes can also be marked to be ignored, by specifying the + * target property to be null.

+ * + *

XML Attributes that are not in the default namespace are ignored.

*/ =20 public class SetPropertiesAction extends AbstractAction { =20 - // ----------------------------------------------------------- Constru= ctors + private HashMap customMap =3D null; + + // ----------------------------------------------------------- + // Constructors + // ----------------------------------------------------------- =20 /** * Base constructor. */ public SetPropertiesAction() { - // nothing to set up=20 + // nothing to set up } - =20 - /**=20 - *

Convenience constructor overrides the mapping for just one prope= rty.

+ + /** + *

Constructor which allows the default attribute->property mapping= to + * be overriden.

+ * + *

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.

+ * + *
Example
+ *

The following constructs a rule that maps the class + * attribute to the className property. The attribute + * ignore is not mapped, and will therefore be ignored ra= ther + * than be passed to a setIgnore method (the default behaviour). All o= ther + * attributes are mapped as usual using exact name matching. + *

+     *      HashMap map =3D new HashMap();
+     *      map.put("class", "className");
+     *      map.put("ignore", null);
+     *      SetPropertiesAction(map);
+     * 

+ * + *

See also {@link #addAlias} which allows the custom mapping to be + * modified after the SetPropertiesAction has been constructed.

+ * + * @param customMap is a map. The map is copied, so future changes to + * the map will not affect this object.=20 + */ + public SetPropertiesAction(Map customMap) { + this.customMap =3D new HashMap(customMap); + } + + /** + *

Convenience constructor which overrides the default attribute->p= roperty + * mapping for just one property.

* *

For details about how this works, see - * {@link #ActionSetProperties(String[] attributeNames, String[] prope= rtyNames)}.

+ * {@link #ActionSetProperties(Map customMappings)}.

* - * @param attributeName map this attribute=20 - * @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 =3D new String[1]; - attributeNames[0] =3D attributeName; - propertyNames =3D new String[1]; - propertyNames[0] =3D propertyName; + customMap =3D new HashMap(1); + customMap.put(attributeName, propertyName); } - =20 - /**=20 - *

Constructor allows attribute->property mapping to be overriden.<= /p> + + /** + *

Constructor which allows the default attribute->property mapping= to + * be overriden.

* - *

Two arrays are passed in.=20 - * 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.

+ *

Two arrays are passed in. One contains the attribute names and t= he + * other the property names. The attribute name / property name pairs = are + * matched by position In order words, the first string in the attribu= te + * name list maps to the first string in the property name list and so= on. + *

* *

If a property name is null or the attribute name has no matching - * property name, then this indicates that the attibute should be igno= red.

- *=20 + * property name (ie the property array is shorter than the attribute = array) + * then the attibute will be ignored.

+ * *
Example One
*

The following constructs a rule that maps the alt-city * attribute to the city property and the alt-state= - * to the state property.=20 + * to the state property. * All other attributes are mapped as usual using exact name matching. *

-     *      SetPropertiesRule(
-     *                new String[] {"alt-city", "alt-state"},=20
+     *      SetPropertiesAction(
+     *                new String[] {"alt-city", "alt-state"},
      *                new String[] {"city", "state"});
      * 
* *
Example Two
*

The following constructs a rule that maps the class - * attribute to the className property. - * The attribute ignore-me is not mapped. - * All other attributes are mapped as usual using exact name matching. + * attribute to the className property. The attribute + * ignore is not mapped, and will therefore be ignored ra= ther + * than be passed to a setIgnore method (the default behaviour). All o= ther + * attributes are mapped as usual using exact name matching. *

-     *      SetPropertiesRule(
-     *                new String[] {"class", "ignore-me"},=20
+     *      SetPropertiesAction(
+     *                new String[] {"class", "ignore"},
      *                new String[] {"className"});
      * 
* @@ -106,105 +151,117 @@ * @param propertyNames names of properties mapped to */ public SetPropertiesAction(String[] attributeNames, String[] propertyN= ames) { - // create local copies - this.attributeNames =3D new String[attributeNames.length]; - for (int i=3D0, size=3DattributeNames.length; iproperty mapping - */ - private String [] attributeNames; =20 - /**=20 - * Property names used to override natural attribute->property mapping - */ =20 - private String [] propertyNames; + // --------------------------------------------------------- + // Public Methods + // --------------------------------------------------------- =20 + /** + *

Add an additional attribute name to property name mapping. + * This is particularly useful for the xmlrules optional module.

+ * + *

See {@link #SetPropertiesAction(Map customMap)}. + */ + public void addAlias(String attributeName, String propertyName) { + if (customMap =3D=3D null) { + customMap =3D new HashMap(); + } + customMap.put(attributeName, propertyName); + } =20 - // --------------------------------------------------------- Public Me= thods + // --------------------------------------------------------- + // Action Methods + // --------------------------------------------------------- =20 /** * 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 attr= ibutes)=20 + Context context, + String namespace, String elementName, + Attributes attributes) throws ParseException { - =20 + Log log =3D context.getLogger(); =20 - // Build a set of attribute names and corresponding values + // Build a set of property names and corresponding values HashMap values =3D new HashMap(); - =20 - // set up variables for custom names mappings - int attNamesLength =3D 0; - if (attributeNames !=3D null) { - attNamesLength =3D attributeNames.length; - } - int propNamesLength =3D 0; - if (propertyNames !=3D null) { - propNamesLength =3D propertyNames.length; - } - =20 + + // for each xml attribute for (int i =3D 0; i < attributes.getLength(); i++) { - String name =3D attributes.getLocalName(i); - if ("".equals(name)) { - name =3D attributes.getQName(i); - } - String value =3D attributes.getValue(i); - =20 - // we'll now check for custom mappings - for (int n =3D 0; n 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 =3D attributes.getLocalName(i); + if ("".equals(attrName)) { + attrName =3D attributes.getQName(i); + } + String value =3D attributes.getValue(i); + + String propName; + + // We'll now check for custom mappings. Note that propName + // can be set to null by this... + if ((customMap !=3D null) && customMap.containsKey(attrNam= e)) { + propName =3D 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 =3D convertHyphenatedToCamelCase(attrName); } - }=20 =20 - if (log.isDebugEnabled()) { - log.debug("[SetProperties]{" + context.getMatchPath() + - "} Setting property '" + name + "' to '" + - value + "'"); + if (propName !=3D null) { + if (log.isDebugEnabled()) { + log.debug("[SetProperties]{" + context.getMatchPat= h() + + "} Setting property '" + propName + "' to = '" + + value + "'"); + } + =20 + values.put(propName, value); + } } - if (name !=3D null) { - values.put(name, value); - }=20 } =20 // Populate the corresponding properties of the top object - Object top =3D context.peek(); + Object target =3D context.peek(); if (log.isDebugEnabled()) { - if (top !=3D null) { + if (target !=3D null) { log.debug("[ActionSetProperties]{" + context.getMatchPath(= ) + - "} Set " + top.getClass().getName() + + "} Set " + target.getClass().getName() + " properties"); } else { log.debug("[ActionSetProperties]{" + context.getMatchPath(= ) + "} Set NULL properties"); } } - + =20 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 @@ } =20 =20 + // --------------------------------------------------------- + // Private Methods + // --------------------------------------------------------- + /** - *

Add an additional attribute name to property name mapping. - * This is intended to be used from the xml rules. + *

This method is intended to convert xml hypenated names + * into javabean names.

+ * + *

Traditionally, xml element and attribute names that are=20 + * 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.

+ * + *

If src contains no hyphens then a reference to the same object + * is returned.

+ * + *

If src ends in a hyphen, then it is stripped.

+ * + * @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=20 + int firstPos =3D src.indexOf("-"); + if (firstPos =3D=3D -1) { + return src; + } =20 - // 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 =3D=3D null) { - =20 - attributeNames =3D new String[1]; - attributeNames[0] =3D attributeName; - propertyNames =3D new String[1]; - propertyNames[0] =3D propertyName; =20 - =20 - } else { - int length =3D attributeNames.length; - String [] tempAttributes =3D new String[length + 1]; - for (int i=3D0; i