commons-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Simon Kitching <skitch...@apache.org>
Subject Re: [Digester] Method call order.
Date Fri, 03 Jun 2005 06:07:59 GMT
On Fri, 2005-06-03 at 13:17 +0800, Eugene.Ng@hbosa.com.au wrote: 
> The XML I'm trying to parse:
> <Common>
>       <attributes1 attr="common_attributes1"/>
>       <attributes2 attr2="common_attributes2"/>
>       <Type1>
>             <attributes attr="subclass1_attributes"/>
>       </Type1>
>       <Type2>
>             <attributes attr="subclass2_attributes"/>
>       </Type2>
> </Common>
> 
> In this scenario, I'm trying to generate an appropriate object depending on
> whether the tag <Type1> or <Type2> appears but both must have common
> attributes "common_attributes1" & "common_attributes2" set into them. Since
> this is my first time using Digester, I'm unaware of a way to write a
> factory at <Common> to peek deeper to get the appropriate Type class to
> load. 

You can't write a factory to do that. The object must be created when
the start tag is encountered, in order for it to be available to store
attribute info on when that is found. But digester is based on SAX, so
has *no* ability to "look ahead" in the stream of data.

Some variant of your CommonLoader is the way to go I expect - though as
you have found out it isn't easy...

> So I introduced a "CommonLoader" intermediate class and wrote the
> rules as follows...
> 
> <digester-rules>
>       <pattern value="Common">
>             <object-create-rule classname="CommonLoader"/>
>             <call-method-rule methodname="setCommonAttributes1"
> paramcount="1" paramtypes="java.lang.Boolean"/>
>             <call-param-rule pattern="attributes1" attrname="attr1"
> paramnumber="0"/>
>             <call-method-rule methodname="setCommonAttributes2"
> paramcount="1" />
>             <call-param-rule pattern="attributes2" attrname="attr2"
> paramnumber="0"/>
>             <pattern value="Type1">
>                   <object-create-rule classname="Type1"/>
>                   <set-next-rule methodname="setType1"/>
>                   <call-method-rule methodname="setType1Attribute"
> paramcount="1" paramtypes="java.lang.Boolean"/>
>                   <call-param-rule pattern="attributes" attrname="attr"
> paramnumber="0"/>
>             </pattern>
>             <pattern value="Type2">
>                   <object-create-rule classname="Type2"/>
>                   <set-next-rule methodname="setType2"/>
>                   <call-method-rule methodname="setType1Attribute"
> paramcount="1" paramtypes="java.lang.Boolean"/>
>                   <call-param-rule pattern="attributes" attrname="attr"
> paramnumber="0"/>
>             </pattern>
>       </pattern>
> </digester-rules>
> 
> CommonLoader will have the method setType1() and setType2() and these
> methods accepts the object created (either Type1 or Type2) and then sets
> the common attributes obtained from within the <Common> tag to these Type
> objects. I require that the method setType1() and setType2() be called last
> to ensure all the common attributes are loaded into CommonLoader before it
> proceeds to set them into the appropriate Type object. However the order of
> execution I got is as follows:
> 
> 1.    setCommonAttributes2.
> 2.    setType1.
> 3.    setCommonAttributes1.
> 
> Because setType1 as seen above is called before setCommonAttributes1,
> common_attribute1 never gets set into the object Type1.
> 
> I've got a few workarounds in mind that would work, but before I proceed, I
> need to understand as to why setCommonAttributes2() gets called based on
> the proper order while setCommonAttributes1() gets called last? Why is the
> order not as follows?
> 
> 1.    setType1
> 2.    setCommonAttributes2
> 3.    setCommonAttributes1
> 
> However if I were to remove "paramtypes=java.lang.Boolean" and pass it in
> as a String then the order will be as follows:
> 
> 1.    setCommonAttributes1
> 2.    setCommonAttributes2
> 3.    setType1
> 
> I hope my explanation is clear and the experts out there is able to help.

Adding multiple CallMethodRule rules with the same pattern is a bad
idea. It's a design flaw in Digester, and can result in some very
unexpected behaviour. The fundamental cause is that there is just one
"parameter stack" in digester, and CallParamRule objects always operate
on the "parameter-info" structure on the top of the stack.

In your case it's not necessary to have CallMethodRule rules with the
same pattern. I suggest adding the CallMethodRule rules with the same
pattern that triggers their sole parameter:

<pattern value="Common">
  <object-create-rule ...>

  <pattern value="attributes1">
    <call-method-rule name="setCommonAttributes1" paramcount="1" .../>
    <call-param-rule attrname="attr" paramnumber="0"/>
  </pattern>

  <pattern value="attributes2">
    <call-method-rule name="setCommonAttributes2" paramcount="1" .../>
    <call-param-rule attrname="attr" paramnumber="0"/>
  </pattern>

  <pattern value="Type1">
     ....
  </pattern>
</pattern>

Regards,

Simon



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org


Mime
View raw message