commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robert burrell donkin <robertdon...@mac.com>
Subject Re: Digester: patch for easy mapping of xml-attribute-name to bean-property-name
Date Tue, 16 Jul 2002 21:39:23 GMT
i've have committed a slightly different implementation of the same 
functionality contained in this patch. i did use some of the code and the 
implementation was certainly influenced and informed by this patch.

this issue was discussed on the list and the consensus at the time was 
that there was no good reason why this functionality should be added to 
digester. so i feel that i probably should give my reasons for committed 
something like it.

the issue was raised again later on the user list but in this case, the 
example was mapping a class attribute. class attributes are common in xml 
and digester should support them - but the class property is possessed by 
every object. it's usual practice to map these to a property with some 
other name but BeanInfo isn't really suitable for this. therefore, it 
should be possible to specify how the natural attribute->name mapping 
should be overridden in digester.

i don't think that this change will lead down a slippery slope into 
strategies and heuristics so i felt that i should commit it. (i hope 
people will agree with me.)

- robert

On Thursday, May 16, 2002, at 12:07 AM, Simon Kitching wrote:

> Hi All,
>  
> Last week there was some discussion started by Kris Schneider on the 
> subject of mapping xml attribute tags to object properties
> when the tag name was not the same as the "bean property name", eg class 
> MyClass has setSomeProperty
> but the developer wants the xml tag to look like<my-class some-
> property="x"> instead of <my-class someProperty="x">.
>  
> As mentioned by Craig, there are existing solutions to this problem:
> (a) defining a BeanInfo class,
> (b) using the CallMethodRule.
>  
> However, both of these solutions seem a bit "clumsy"; given the xml 
> convention of separating words-like-this and the java convention of 
> wordsLikeThis, it seems worth having a streamlined solution.
>  
> Kris proposed a new SetMappedPropertiesRule; this didn't seem to generate 
> any great opposition so here is a proposed patch.
>  
> The approach below is quite similar to Kris' rule butadds functionality 
> to SetPropertiesRule rather than adding a new rule.Another difference is 
> that Kris' original code defines an _exact_ set of attribute->property 
> mappings; if there is a HashMap of mappings,then only these are applied. 
> The approach I have implemented looks _first_ for an explicit mapping for 
> each xmlattribute but still processes attributes not in the mapping in 
> the existing manner.
>  
> To summarise the changes:
>  
> Code that adds rules to Digester directly can now do this:
>   HashMap map = new HashMap();
>   map.put("some-attribute", "someAttribute");
>   digester.addSetProperties(String pattern, HashMap attributeMap) // new 
> function
>  
> xmlrules can now do this:
>   <set-properties-rule>
>     <alias attr-name="some-attribute" prop-name="someAttribute"/>
>   </set-properties-rule>
>  
> In both cases, an xml tag <tagname some-attribute="xxx'/> causes the 
> setSomeAttribute
> method to be called on the target object.
>  
> There is (of course) no breakage of existing code using digester.
> I hope I have provided all this in the correct format; please let me know.
>  [JUnit tests are included for new functionality too].
>  
> Cheers,
>  
> Simon
>  
> new files (attached):
>   *src/test/org/apache/commons/digester/Test6.xml
>
> Index: src/java/org/apache/commons/digester/Digester.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/java/org/apache/commons/digester/Digester.java,v
> retrieving revision 1.52
> diff -c -r1.52 Digester.java
> *** src/java/org/apache/commons/digester/Digester.java 27 Apr 2002 23:39:
> 50 -0000 1.52
> --- src/java/org/apache/commons/digester/Digester.java 15 May 2002 22:20:
> 27 -0000
> ***************
> *** 1763,1768 ****
> --- 1763,1781 ----
>  
>       }
>  
> +     /**
> +      * Add a "set properties" rule for the specified parameters.
> +      *
> +      * @param pattern Element matching pattern
> +      * @param attrMap Set of (xml-attribute-name, bean-property-name) 
> pairs.
> +      */
> +     public void addSetProperties(String pattern, HashMap attrMap) {
> +
> +         addRule(pattern,
> +                 new SetPropertiesRule(attrMap));
> +
> +     }
> +
>  
>       /**
>        * Add a "set property" rule for the specified parameters.
> Index: src/java/org/apache/commons/digester/SetPropertiesRule.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/java/org/apache/commons/digester/SetPropertiesRule.java,
> v
> retrieving revision 1.8
> diff -c -r1.8 SetPropertiesRule.java
> *** src/java/org/apache/commons/digester/SetPropertiesRule.java 23 Mar 
> 2002 17:45:58 -0000 1.8
> --- src/java/org/apache/commons/digester/SetPropertiesRule.java 15 May 
> 2002 22:20:27 -0000
> ***************
> *** 108,113 ****
> --- 108,138 ----
>       }
>  
>  
> +     /**
> +      * Constructor.
> +      *
> +      * @param attributeMap A map of (xml-attribute-name, 
> bean-property-name)
> +      * pairs which can be used to define "aliases" for bean properties.
>  For
> +      * example, defining ("thing-name", "thingName") means an attribute 
> of
> +      * thing-name="xxx" in the xml invokes the setThingName("xxx") 
> method on
> +      * the top object on the stack.
> +      */
> +     public SetPropertiesRule(HashMap attributeMap) {
> +
> +         this.attributeMap = attributeMap;
> +
> +     }
> +
> +
> +     // ----------------------------------------------------- Instance 
> Variables
> +
> +
> +     /**
> +      * A map of (xml-attribute-name -> bean-property-name)
> +      */
> +     protected HashMap attributeMap = null;
> +
> +
>       // --------------------------------------------------------- Public 
> Methods
>  
>  
> ***************
> *** 119,125 ****
>        */
>       public void begin(Attributes attributes) throws Exception {
>  
> !         // Build a set of attribute names and corresponding values
>           HashMap values = new HashMap();
>           for (int i = 0; i < attributes.getLength(); i++) {
>               String name = attributes.getLocalName(i);
> --- 144,153 ----
>        */
>       public void begin(Attributes attributes) throws Exception {
>  
> !         // Build a set of attribute names and corresponding values,
> !         // translating xml-attribute-name to bean-property-name as
> !         // required by the attributeMap.
> !        
>           HashMap values = new HashMap();
>           for (int i = 0; i < attributes.getLength(); i++) {
>               String name = attributes.getLocalName(i);
> ***************
> *** 127,137 ****
> --- 155,174 ----
>                   name = attributes.getQName(i);
>               }
>               String value = attributes.getValue(i);
> +            
> +             if (attributeMap != null) {
> +                 String mappedName = (String) attributeMap.get(name);
> +                 if (mappedName != null) {
> +                     name = mappedName;
> +                 }
> +             }
> +            
>               if (digester.log.isDebugEnabled()) {
>                   digester.log.debug("[SetPropertiesRule]{" + 
> digester.match +
>                           "} Setting property '" + name + "'
to '" +
>                           value + "'");
>               }
> +
>               values.put(name, value);
>           }
>  
> ***************
> *** 141,146 ****
> --- 178,185 ----
>               digester.log.debug("[SetPropertiesRule]{" + digester.match

> +
>                       "} Set " + top.getClass().getName() + " properties"
> );
>           }
> +        
> +        
>           BeanUtils.populate(top, values);
>  
>  
> ***************
> *** 158,162 ****
> --- 197,214 ----
>  
>       }
>  
> +
> +     /**
> +     * add an etnry to the set of mappings from xml attribute names
> +     * to bean property names.
> +     */
> +     public void addAlias(String attrName, String propName) {
> +
> +       if (attributeMap == null)
> +           attributeMap = new HashMap();
> +
> +       attributeMap.put(attrName, propName);
> +
> +     }
>  
>   }
> Index: 
> src/java/org/apache/commons/digester/xmlrules/DigesterRuleParser.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/java/org/apache/commons/digester/xmlrules/DigesterRuleParser.
> java,v
> retrieving revision 1.3
> diff -c -r1.3 DigesterRuleParser.java
> *** src/java/org/apache/commons/digester/xmlrules/DigesterRuleParser.java 
> 9 Jan 2002 20:22:50 -0000 1.3
> --- src/java/org/apache/commons/digester/xmlrules/DigesterRuleParser.java 
> 15 May 2002 22:20:28 -0000
> ***************
> *** 236,241 ****
> --- 236,243 ----
>           digester.addFactoryCreate("*/set-properties-rule", new 
> SetPropertiesRuleFactory());
>           digester.addRule("*/set-properties-rule", new 
> PatternRule(digester, "pattern"));
>           digester.addSetNext("*/set-properties-rule", "add", 
> ruleClassName);
> +        
> +         digester.addRule("*/set-properties-rule/alias", new 
> SetPropertiesAliasRule(digester));
>  
>           digester.addFactoryCreate("*/set-property-rule", new 
> SetPropertyRuleFactory());
>           digester.addRule("*/set-property-rule", new 
> PatternRule(digester, "pattern"));
> ***************
> *** 250,255 ****
> --- 252,283 ----
>           digester.addSetNext("*/set-next-rule", "add", ruleClassName);
>       }
>  
> +
> +     /**
> +      * A rule for adding property aliases to the map of aliases 
> contained by
> +      * a preceding SetPropertiesRule rule.
> +      */
> +     private class SetPropertiesAliasRule extends Rule {
> +
> +         /**
> +          * @param digester the Digester used to parse the rules XML file
> +          */
> +         public SetPropertiesAliasRule(Digester digester) {
> +             super(digester);
> +         }
> +
> +         /**
> +          * add the alias to the SetPropertiesRule object created by the
> +          * enclosing <set-properties-rule> tag.
> +          */
> +         public void begin(Attributes attributes) {
> +             String attrName = attributes.getValue("attr-name");
> +             String propName = attributes.getValue("prop-name");
> +            
> +             SetPropertiesRule rule = (SetPropertiesRule) digester.peek(
> );
> +             rule.addAlias(attrName, propName);
> +         }
> +     }
>  
>       /**
>        * A rule for extracting the pattern matching strings from the 
> rules XML.
> Index: src/java/org/apache/commons/digester/xmlrules/digester-rules.dtd
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/java/org/apache/commons/digester/xmlrules/digester-rules.
> dtd,v
> retrieving revision 1.1
> diff -c -r1.1 digester-rules.dtd
> *** src/java/org/apache/commons/digester/xmlrules/digester-rules.dtd 4 
> Dec 2001 19:49:36 -0000 1.1
> --- src/java/org/apache/commons/digester/xmlrules/digester-rules.dtd 15 
> May 2002 22:20:28 -0000
> ***************
> *** 95,103 ****
>       attrname  CDATA #IMPLIED>
>  
>   <!-- SetPropertiesRule -->
> ! <!ELEMENT set-properties-rule EMPTY>
>   <!ATTLIST factory-create-rule
>       pattern   CDATA #IMPLIED>
>  
>   <!-- SetPropertyRule -->
>   <!ELEMENT set-property-rule EMPTY>
> --- 95,109 ----
>       attrname  CDATA #IMPLIED>
>  
>   <!-- SetPropertiesRule -->
> ! <!ELEMENT set-properties-rule (alias)*>
>   <!ATTLIST factory-create-rule
>       pattern   CDATA #IMPLIED>
> +
> + <!-- alias -->
> + <!ELEMENT alias EMPTY>
> + <!ATTLIST alias
> +     attr-name CDATA #REQUIRED
> +     prop-name CDATA #REQUIRED>
>  
>   <!-- SetPropertyRule -->
>   <!ELEMENT set-property-rule EMPTY>
> Index: src/test/org/apache/commons/digester/RuleTestCase.java
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/test/org/apache/commons/digester/RuleTestCase.java,v
> retrieving revision 1.12
> diff -c -r1.12 RuleTestCase.java
> *** src/test/org/apache/commons/digester/RuleTestCase.java 18 Apr 2002 21:
> 23:44 -0000 1.12
> --- src/test/org/apache/commons/digester/RuleTestCase.java 15 May 2002 22:
> 20:29 -0000
> ***************
> *** 328,333 ****
> --- 328,370 ----
>  
>  
>       /**
> +      * Same as testObjectCreate1(), except uses aliases for property 
> names,
> +      * ie the xml attribute names do not match the bean property name.
> +      */
> +     public void testObjectCreate6() {
> +
> +         java.util.HashMap aliases = new java.util.HashMap();
> +         aliases.put("first-name", "firstName");
> +         aliases.put("last-name", "lastName");
> +
> +         // Configure the digester as required
> +         digester.addObjectCreate("employee",
> +                 "org.apache.commons.digester.Employee");
> +         digester.addSetProperties("employee", aliases);
> +
> +         // Parse our test input.
> +         Object root = null;
> +         try {
> +             root = digester.parse(getInputStream("Test6.xml"));
> +         } catch (Throwable t) {
> +             fail("Digester threw IOException: " + t);
> +         }
> +         assertNotNull("Digester returned an object", root);
> +         assertTrue("Digester returned an Employee",
> +                 root instanceof Employee);
> +         Employee employee = (Employee) root;
> +         assertEquals("First name is correct",
> +                 "First Name",
> +                 employee.getFirstName());
> +         assertEquals("Last name is correct",
> +                 "Last Name",
> +                 employee.getLastName());
> +
> +
> +     }
> +
> +
> +     /**
>        * It should be possible to parse the same input twice, and get 
> trees
>        * of objects that are isomorphic but not be identical object 
> instances.
>        */
> Index: src/test/org/apache/commons/digester/xmlrules/test.xml
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/test/org/apache/commons/digester/xmlrules/test.xml,v
> retrieving revision 1.1
> diff -c -r1.1 test.xml
> *** src/test/org/apache/commons/digester/xmlrules/test.xml 4 Dec 2001 19:
> 48:22 -0000 1.1
> --- src/test/org/apache/commons/digester/xmlrules/test.xml 15 May 2002 22:
> 20:29 -0000
> ***************
> *** 1,6 ****
>   <!-- Input data to test the DigesterLoader -->
>   <root>
> !   <foo value="foo1">
>       <baz  value="baz1"/>
>       <bar>
>         <foo value="foo2"/>
> --- 1,6 ----
>   <!-- Input data to test the DigesterLoader -->
>   <root>
> !   <foo alias-for-value="foo1">
>       <baz  value="baz1"/>
>       <bar>
>         <foo value="foo2"/>
> ***************
> *** 9,15 ****
>     <baz value="baz2"/>
>     <foo value="foo3">
>       <bar>
> !    <baz value="baz3"/>
>         <foo value="foo4"/>
>       </bar>
>     </foo>
> --- 9,15 ----
>     <baz value="baz2"/>
>     <foo value="foo3">
>       <bar>
> !       <baz value="baz3"/>
>         <foo value="foo4"/>
>       </bar>
>     </foo>
> Index: src/test/org/apache/commons/digester/xmlrules/testrules.xml
> ===================================================================
> RCS file: /home/cvspublic/jakarta-
> commons/digester/src/test/org/apache/commons/digester/xmlrules/testrules.
> xml,v
> retrieving revision 1.1
> diff -c -r1.1 testrules.xml
> *** src/test/org/apache/commons/digester/xmlrules/testrules.xml 4 Dec 
> 2001 19:48:22 -0000 1.1
> --- src/test/org/apache/commons/digester/xmlrules/testrules.xml 15 May 
> 2002 22:20:29 -0000
> ***************
> *** 3,9 ****
>     <pattern value="root/foo">
>       <object-create-rule classname="org.apache.commons.digester.xmlrules.
> TestObject"/>
>       <set-next-rule methodname="add" paramtype="java.lang.Object"/>
> !    <set-properties-rule/>
>      <include path="org/apache/commons/digester/xmlrules/testrulesinclude.
> xml"/>
>       <include 
> class="org.apache.commons.digester.xmlrules.TestDigesterRulesSource"/>
>     </pattern>
> --- 3,11 ----
>     <pattern value="root/foo">
>       <object-create-rule classname="org.apache.commons.digester.xmlrules.
> TestObject"/>
>       <set-next-rule methodname="add" paramtype="java.lang.Object"/>
> !    <set-properties-rule>
> !             <alias attr-name="alias-for-value" prop-name="value"/>
> !           </set-properties-rule>
>      <include path="org/apache/commons/digester/xmlrules/testrulesinclude.
> xml"/>
>       <include 
> class="org.apache.commons.digester.xmlrules.TestDigesterRulesSource"/>
>     </pattern>
>  --
> To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.
> org>
> For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.
> org>


--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message