You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@commons.apache.org by Ken Egervari <ke...@upfactor.com> on 2004/08/31 02:01:02 UTC

[Digester] Parent Reference Help

Hi,

 

Is there any way to reference a parent object when parsing an XML document
using the commons-digester component?  For example, let's assume that there
is an addObjectCreate() rule that creates a Page object for the <page> tag
and a SelectOne object for the <selectone> tag.  If I have the following XML
(shown below), is there any way the reference of my Page object can be
passed in the SelectOne object before the SelectOne's setters are called
using a typical addSetProperties() rule?  I'm having trouble obtaining a
reference since using addSetTop() is called after the setters are called.  

 

This is rather inconvenient since some setters (in SelectOne for instance)
will often need access to the parent object (the Page object in this
instance).  In other instances, I need to traverse up the graph of objects
to the root.  Since the parent is not defined, this is impossible.  

 

If I cannot access the parent as the setters are being called, I'm basically
forced to put this logic in the parent's addChild method instead where all
the attributes have been mapped to the object's properties already, but this
makes the object less convenient outside the digester.

 

<page index="2" formView="page.three.form">

      <selectone name="company.language" referenceData="languages"

            validatorId="singlereference"

            errorMessage="There are no languages in the database." />
</page>

 

I'm not flooding the message with source code because I don't think it's
required.  The specifics of the example are unimportant and I'm more
interested in a general solution.

 

Thanks for helping me out and best regards,

 

Ken Egervari

 

 

 


RE: [Digester] Parent Reference Help

Posted by Ken Egervari <ke...@upfactor.com>.
I believe the problem I'm referring to is that while it does set the parent,
the reference is not available until after the properties have been set in
full.  Thus, if I have a property called "setValidatorId" that needs to
lookup a parent object (let's say a Validator object in this case, but it
doesn't really matter) in the root of the object graph, the child object
won't be able to do this because he'll get a NullPointerException when
trying to do "parent.whatever()".  The only way that I can see around this
is to call the parent's method in the setParent() or addChild() method, thus
forcing the object to temporarily store "validatorId" until setParent() or
addChild() is actually called.  

For instance, in my example below, a SelectOne object would have to store
the validatorId temporarily so that it can do something like this when
setParent() is called in Page:

public void setParent( Page page ) {
	this.validator = page.getContext().getValidator( this.validatorId );
}

Does that help clarify my problem?  I'd like to avoid the use of validatorId
and get at this validator object immediately when setValidatorId() is called
rather than later.  Perhaps something like this:

Public void setValidatorId( String validatorId ) {
	this.validator = parent.getContext().getValidator( validatorId );
}

The code above avoids the use of temporary variable storage, but I can't
seem to get it to work.

Best regards and many thanks,
Ken Egervari

-----Original Message-----
From: Craig McClanahan [mailto:craigmcc@gmail.com] 
Sent: Monday, August 30, 2004 8:07 PM
To: Jakarta Commons Users List
Subject: Re: [Digester] Parent Reference Help

The simplest way to address this need is to use the object stack that
Digester creates for you.  Every time you execute an Object Create
Rule, for example, the new instance gets pushed on to the stack, so
that Digester rules for nested elements can get to it easily:

    Page page = (Page) digester.peek();

There's examples of this in the "Package Documentation" part of the
Digester javadocs:

http://jakarta.apache.org/commons/digester/apidocs/org/apache/commons/digest
er/package-summary.html#package_description

as well as in the source code to rules like SetParentRule and
SetNextRule, which rely on exactly this capability in order to perform
their functions.

Craig

On Mon, 30 Aug 2004 20:01:02 -0400, Ken Egervari <ke...@upfactor.com> wrote:
> Hi,
> 
> Is there any way to reference a parent object when parsing an XML document
> using the commons-digester component?  For example, let's assume that
there
> is an addObjectCreate() rule that creates a Page object for the <page> tag
> and a SelectOne object for the <selectone> tag.  If I have the following
XML
> (shown below), is there any way the reference of my Page object can be
> passed in the SelectOne object before the SelectOne's setters are called
> using a typical addSetProperties() rule?  I'm having trouble obtaining a
> reference since using addSetTop() is called after the setters are called.
> 
> This is rather inconvenient since some setters (in SelectOne for instance)
> will often need access to the parent object (the Page object in this
> instance).  In other instances, I need to traverse up the graph of objects
> to the root.  Since the parent is not defined, this is impossible.
> 
> If I cannot access the parent as the setters are being called, I'm
basically
> forced to put this logic in the parent's addChild method instead where all
> the attributes have been mapped to the object's properties already, but
this
> makes the object less convenient outside the digester.
> 
> <page index="2" formView="page.three.form">
> 
>       <selectone name="company.language" referenceData="languages"
> 
>             validatorId="singlereference"
> 
>             errorMessage="There are no languages in the database." />
> </page>
> 
> I'm not flooding the message with source code because I don't think it's
> required.  The specifics of the example are unimportant and I'm more
> interested in a general solution.
> 
> Thanks for helping me out and best regards,
> 
> 
> Ken Egervari
> 
>

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



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


Re: [Digester] Parent Reference Help

Posted by Craig McClanahan <cr...@gmail.com>.
The simplest way to address this need is to use the object stack that
Digester creates for you.  Every time you execute an Object Create
Rule, for example, the new instance gets pushed on to the stack, so
that Digester rules for nested elements can get to it easily:

    Page page = (Page) digester.peek();

There's examples of this in the "Package Documentation" part of the
Digester javadocs:

http://jakarta.apache.org/commons/digester/apidocs/org/apache/commons/digester/package-summary.html#package_description

as well as in the source code to rules like SetParentRule and
SetNextRule, which rely on exactly this capability in order to perform
their functions.

Craig

On Mon, 30 Aug 2004 20:01:02 -0400, Ken Egervari <ke...@upfactor.com> wrote:
> Hi,
> 
> Is there any way to reference a parent object when parsing an XML document
> using the commons-digester component?  For example, let's assume that there
> is an addObjectCreate() rule that creates a Page object for the <page> tag
> and a SelectOne object for the <selectone> tag.  If I have the following XML
> (shown below), is there any way the reference of my Page object can be
> passed in the SelectOne object before the SelectOne's setters are called
> using a typical addSetProperties() rule?  I'm having trouble obtaining a
> reference since using addSetTop() is called after the setters are called.
> 
> This is rather inconvenient since some setters (in SelectOne for instance)
> will often need access to the parent object (the Page object in this
> instance).  In other instances, I need to traverse up the graph of objects
> to the root.  Since the parent is not defined, this is impossible.
> 
> If I cannot access the parent as the setters are being called, I'm basically
> forced to put this logic in the parent's addChild method instead where all
> the attributes have been mapped to the object's properties already, but this
> makes the object less convenient outside the digester.
> 
> <page index="2" formView="page.three.form">
> 
>       <selectone name="company.language" referenceData="languages"
> 
>             validatorId="singlereference"
> 
>             errorMessage="There are no languages in the database." />
> </page>
> 
> I'm not flooding the message with source code because I don't think it's
> required.  The specifics of the example are unimportant and I'm more
> interested in a general solution.
> 
> Thanks for helping me out and best regards,
> 
> 
> Ken Egervari
> 
>

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


RE: [Digester] Parent Reference Help

Posted by Simon Kitching <si...@ecnetwork.co.nz>.
On Wed, 2004-09-01 at 01:52, Ken Egervari wrote:
> Actually, Simon, this is precisely along the track that I need to go.  Thank
> you so much for suggesting this as I'm still a bit new to the digester.  
> 
> However, I'm still having some problems with this approach, mainly that
> rules are being called prematurely and maybe that's why it was placed in
> end() in the first place.  
> 
> In my case, the setters are still not being called, but this time it is
> because SetNextRule or SetTopRule is calling setParent() or addChild()
> before the child's properties have been set.  For example, if addChild() is
> called, the child's properties might be needed at this point (in my case,
> they do) and thus it adds a child with all null references.  While they
> might be filled with values afterwards, they need values when addChild() is
> called.

Hi Ken,

If you are using:

  digester.addObjectCreate("page/selectone", SelectOne.class);
  digester.addSetProperties("page/selectone");
  digester.addSetNext("page/selectone");

then the setter methods on the SelectOne instance will be called before
the Page object's addChild method is called.

If you are using CallMethodRule rules to call methods on the SelectOne
object, then you have to add the rules to the digester in reverse order,
for reasons explained in a FAQ entry I have just created
(http://wiki.apache.org/jakarta-commons/Digester_2fFAQ).


> Usually people put default values for unspecified attributes or elements to
> avoid the null values but that isn't possible for required attributes like
> controller class name in my case since they cannot be assumed by default.
> Since the digester is inherently aware of XML, it should be aware of
> optional and required attributes and put the objects in a state that is
> consistent with the semantics of valid XML document.  Now, it doesn't need
> to be aware of the DTD, but it can at least try to populate values before
> these methods are invoked.  It's not something the user should have to worry
> about I don't think.  Fortunately, achieving this functionality is probably
> not that hard since your framework is already very flexible and it's
> probably a matter of defining higher-level sets of rules that people can use
> while the existing software is being used under the hood.

As described in the FAQ entry, setting object properties from xml
attributes (using the SetPropertiesRule) should "just work" (tm).

Setting object properties using CallMethodRule has slightly unintuitive
behaviour wrt SetNextRule, but it can be done. Removing the quirk is
unfortunately very difficult; it is pretty much due to the nature of
stacks, and the concept of stacks is at the very core of Digester. So we
generally just live with the slightly odd workaround required (see the
FAQ).

Thanks for your positive feedback, and I hope the FAQ entry solves your
problem. If it doesn't, please feel free to email again.

Regards,

Simon



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


RE: [Digester] Parent Reference Help

Posted by Ken Egervari <ke...@upfactor.com>.
Actually, Simon, this is precisely along the track that I need to go.  Thank
you so much for suggesting this as I'm still a bit new to the digester.  

However, I'm still having some problems with this approach, mainly that
rules are being called prematurely and maybe that's why it was placed in
end() in the first place.  

In my case, the setters are still not being called, but this time it is
because SetNextRule or SetTopRule is calling setParent() or addChild()
before the child's properties have been set.  For example, if addChild() is
called, the child's properties might be needed at this point (in my case,
they do) and thus it adds a child with all null references.  While they
might be filled with values afterwards, they need values when addChild() is
called.  Consider this code:

	public void addForm( Form form ) {
		if( form == null ) {
			throw new FormControllerClassNotDefinedException();
		}

		String controller = form.getControllerClassName();

		if( StringUtils.isNullOrEmpty( controller ) ) {
			throw new FormControllerClassNotDefinedException();
		}

		logger.info( "Adding Form [" + controller + "] with " +
			form.getControls().size() + " Controls" );

		form.setContext( this );
		form.implementSuperForm();

		forms.put( controller, form );
	}

This is an example of a typical addChild method.  As you can see, if
form.getControllerClassName() returns empty or null, the addForm method will
fail since it is used as a map key.  Thus, it's imperative that it is
defined before this method is called.  You'll also notice that
form.setContext() and form.implementSuperForm() should not be here - they
should have been in the Form object itself but these were required to get
around the limitation discussed earlier yesterday.

Now, not all is lost.  This pretty much means that I have to use a
combination of SetTopRule and SetNextRule to achieve the desired
functionality and that's what I'm going to play with for the next few hours.
I imagine that I'll get it working the way I want it.

However, I think this problem can be defined to be a more generic problem.
The Digester could enforce an integrity constraint that all objects created
by the Digester are always guaranteed to be initialized when addChild() or
setParent() rules are invoked, which is not a reasonable constraint to put
on the digester since null-valued objects are pretty much meaningless.  If
this is already possible, it should be more explicit.  

Usually people put default values for unspecified attributes or elements to
avoid the null values but that isn't possible for required attributes like
controller class name in my case since they cannot be assumed by default.
Since the digester is inherently aware of XML, it should be aware of
optional and required attributes and put the objects in a state that is
consistent with the semantics of valid XML document.  Now, it doesn't need
to be aware of the DTD, but it can at least try to populate values before
these methods are invoked.  It's not something the user should have to worry
about I don't think.  Fortunately, achieving this functionality is probably
not that hard since your framework is already very flexible and it's
probably a matter of defining higher-level sets of rules that people can use
while the existing software is being used under the hood.

Anyway, thanks for your help and keep up the great work.

Best Regards,
Ken Egervari



-----Original Message-----
From: Simon Kitching [mailto:simon@ecnetwork.co.nz] 
Sent: Monday, August 30, 2004 8:42 PM
To: Jakarta Commons Users List
Subject: Re: [Digester] Parent Reference Help

On Tue, 2004-08-31 at 12:01, Ken Egervari wrote:
> Hi,
> 
>  
> 
> Is there any way to reference a parent object when parsing an XML document
> using the commons-digester component?  For example, let's assume that
there
> is an addObjectCreate() rule that creates a Page object for the <page> tag
> and a SelectOne object for the <selectone> tag.  If I have the following
XML
> (shown below), is there any way the reference of my Page object can be
> passed in the SelectOne object before the SelectOne's setters are called
> using a typical addSetProperties() rule?  I'm having trouble obtaining a
> reference since using addSetTop() is called after the setters are called.

> 
>  
> 
> This is rather inconvenient since some setters (in SelectOne for instance)
> will often need access to the parent object (the Page object in this
> instance).  In other instances, I need to traverse up the graph of objects
> to the root.  Since the parent is not defined, this is impossible.  

Hmm.. I see that the SetTopRule is invoking the target method in the
end() method, ie when it sees the closing tag.

I don't see why it does this. If it were to invoke the target method in
begin(), then that would solve your issue, as:
  digester.addObjectCreate("page/selectone", SelectOne.class);
  digester.addSetTop("page/selectone", "setParentPage");
  digester.addSetProperties("page/selectone");
would then invoke the setParentPage before the property setter methods.

I don't currently see any reason *why* it waits until end() to invoke
the target method.

I suggest taking the source code for SetTopRule, copying it into your
namespace then modifying it so that the work is done in begin() rather
than end(), and seeing if this works for you. If it does, let us know as
I for one think this should be merged into the digester mainline code. 

You would of course use it as:
  NewSetTopRule rule = new NewSetTopRule("setParentPage");
  digester.addRule("page/selectone", rule);

NB: If a change is to be applied to digester CVS, I think it's actually
more likely to involve a modification which adds an extra constructor
param indicating whether invocation at begin or end is desired, and
default to the current behaviour, for backwards compatibility.


Regards,

Simon


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



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


Re: [Digester] Parent Reference Help

Posted by Simon Kitching <si...@ecnetwork.co.nz>.
On Tue, 2004-08-31 at 12:01, Ken Egervari wrote:
> Hi,
> 
>  
> 
> Is there any way to reference a parent object when parsing an XML document
> using the commons-digester component?  For example, let's assume that there
> is an addObjectCreate() rule that creates a Page object for the <page> tag
> and a SelectOne object for the <selectone> tag.  If I have the following XML
> (shown below), is there any way the reference of my Page object can be
> passed in the SelectOne object before the SelectOne's setters are called
> using a typical addSetProperties() rule?  I'm having trouble obtaining a
> reference since using addSetTop() is called after the setters are called.  
> 
>  
> 
> This is rather inconvenient since some setters (in SelectOne for instance)
> will often need access to the parent object (the Page object in this
> instance).  In other instances, I need to traverse up the graph of objects
> to the root.  Since the parent is not defined, this is impossible.  

Hmm.. I see that the SetTopRule is invoking the target method in the
end() method, ie when it sees the closing tag.

I don't see why it does this. If it were to invoke the target method in
begin(), then that would solve your issue, as:
  digester.addObjectCreate("page/selectone", SelectOne.class);
  digester.addSetTop("page/selectone", "setParentPage");
  digester.addSetProperties("page/selectone");
would then invoke the setParentPage before the property setter methods.

I don't currently see any reason *why* it waits until end() to invoke
the target method.

I suggest taking the source code for SetTopRule, copying it into your
namespace then modifying it so that the work is done in begin() rather
than end(), and seeing if this works for you. If it does, let us know as
I for one think this should be merged into the digester mainline code. 

You would of course use it as:
  NewSetTopRule rule = new NewSetTopRule("setParentPage");
  digester.addRule("page/selectone", rule);

NB: If a change is to be applied to digester CVS, I think it's actually
more likely to involve a modification which adds an extra constructor
param indicating whether invocation at begin or end is desired, and
default to the current behaviour, for backwards compatibility.


Regards,

Simon


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