addXYZ - create a new object and add it to self.
setXYZ - look for matching object, if found modify it, otherwise addXYZ.
Have I understood that correctly?
Date: Mon, 18 Jun 2001 20:29:01 +0100_______________________________________________ dom4j-dev mailing list [EMAIL PROTECTED] http://lists.sourceforge.net/lists/listinfo/dom4j-dev
To: "James Strachan" <[EMAIL PROTECTED]>, "dom4j-dev" <[EMAIL PROTECTED]>
From: Thomas Nichols <[EMAIL PROTECTED]>
Subject: Re: [dom4j-dev] Why do add(...) methods return void?
How do James,
At 16:57 18/06/2001 +0100, James Strachan wrote:
Hey Thomas
From: "Thomas Nichols" <[EMAIL PROTECTED]>
> >Yes I think returning the Element when adding text or entities sounds a
good
> >idea.
> >Though the addElement() returns the new Element child rather than the
parent
> >so care should be taken with nestings.
>
> Ah - this is not what I'd been thinking of. So
> foo.addEntity ("bar"); // returns foo
> foo.addElement ("bar"); // returns ref to the new bar Element ?? I'd
> expected it to return foo.
Thats right. But there's a reason for that. It allows the addElement() to be
a factory method, returning the new element for further customisation
without having to explicitly use the DocumentFactory or to hard code any
implementation class names in your code.
That's a good enough argument for me.
It allows documents to be build up (albeit in a more verbose manner than the
one you suggest) as follows:-
DocumentFactory factory = DocumentFactory.getInstance();
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement( "root" );
Element first = root.addElement( "first" );
Element second = root.addElement( "second" ).setAttributeValue( "currency",
"GBP" );
Element amount = root.addElement( "amount" ).addText( "100" );
The above would create a Document with some elements without any
implementation class being mentioned in code. (So it avoids referring to
DefaultElement explicitly).
<light-bulb status="on />
Using the "org.dom4j.factory" system property we could have changed the
exact DocumentFactory implementation used in the above code and so affected
the exact Element implementations used. (For example we might have used some
special schema based DocumentFactory which knows that the <amount> element
is an Integer).
So I'd like to keep this simple syntax if we can as it hides the use of the
DocumentFactory and hides implementation details too. But I'd also like to
support the kind of 'Element Construction Set' like syntax you're using.
So for example, to make the XML document :-.
<complexType mixed="true">
<sequence>
<element ref="xyz" />
</sequence>
</complexType>
You could use:-
Document doc = DocumentHelper.createDocument();
Element complexType = doc.addElement( "complexType" ).setAttribute( "mixed",
"true" );
Element sequence = complexType.addElement( "sequence" );
Element element = sequence.addElement( "element" ).setAttribute( "ref",
"xyz" );
Thats fairly concise, plus it allows exact elements to be further customised
later if required using instance variables when doing loops or conditional
logic. It could be condensed a little as follows:-
Document doc = DocumentHelper.createDocument();
doc.addElement( "complexType" ).setAttribute( "mixed",
"true" ).addElement( "sequence" ).addElement( "element" ).setAttribute(
"ref", "xyz" );
Though I tend to favour the former as its easier to read. (Also it might be
easier to just parse a text file ;-)
This sounds good - though see my comments to James E. re limitations of parseText().
. BTW, why is it "setAttribute" rather than "addAttribute" ?
It may be quite confusing for users, but we could let the add() method
return a reference to the Element on which the node was added since its not
acting as a factory method as you pass in the node you wish to add.
Although I'd probably find this quite useful, it's not what I'd expect from add(). Hmm...
Contrast:-
// create a new element, add it and return a reference
Element child = parent.add( "child" );
with
// create a new element
Element child = factory.createElement( "child" );
// add the element
parent.add( child );.
In the latter, the program knows what the child reference is, so the
add(Element) method could return the self (parent).
So your example:-
Element complexType = new DefaultElement ("complexType") . add (new
DefaultAttribute ("mixed", "true"))
.add (new DefaultElement ("sequence")
.add ((new DefaultElement ("element") . add (new
DefaultAttribute ("ref", "xyz")))));
Could be written (using a factory rather than using concrete implementation
classes):-
DocumentFactory factory = DocumentFactory.getInstance();
Element complexType
= factory.createElement( "complexType" )
.setAttributeValue( "mixed", "true" )
.add(
factory.createElement( "sequence" )
.add(
factory.createElement( "element" )
.setAttributeValue( "ref", "xyz" )
)
);
This is quite similar to your example but using the factory.
One added complication if we did go this way is that we've tried to make
Document and Element polymorphic such that they both extend the Branch
interface. So that adding ProcessingInstruction, Comment and Element might
need to return Branch rather than Element which might require some casting.
Maybe the add(Element), add(ProcessingInstruction) and add(Comment) should
move from Branch into both Document and Element such that they can return
Document or Element as the return type and leaving only add(Node) which can
be called polymorphically from Branch (and would return an instance of
Branch).
This sounds a workable approach - but would it mean copy-and-paste of code currently in AbstractBranch.java into both AbstractElement.java and AbstractDocument.java? Your call, I think, as to whether the (possibly) improved accessibility is worth duplicating code.
Maybe some package-visibility methods in DocumentHelper that abstract out the commonality, the AbstractElement and AbstractDocument impls just call the helpers and do a bit of casting?
Sometimes, on these occasions, C++ takes on a faintly rosy hue... until you remember the memory leaks and core dumps.
So then we could treat a Document in a similar way as:-
Document doc
= factory.createDocument()
.addComment( "start of document" )
.setDocType( "complexType", "my public ID", null )
.add(
factory.createElement( "complexType" )
.setAttributeValue( "mixed", "true" )
.addComment( "inside the complexType element" );
.add(
factory.createElement( "sequence" )
.add(
factory.createElement( "element" )
.setAttributeValue( "ref", "xyz" )
)
)
)
.addComment( "end of document" );
This syntax does help with "mixed content" type situations (e.g. look at the
second paragraph)...
Yes, this is elegant...
Element html
= factory.createElement( "html" )
.add(
factory.createElement( "head" )
.add(
factory.createElement( "title" )
.addText( "this is the title" )
)
)
.add(
factory.createElement( "body" )
.add(
factory.createElement( "h1" )
.addText( "title #1" )
)
.add(
factory.createElement( "p" )
.addText( "this is paragraph #1" )
)
.add(
factory.createElement( "p" )
.addText( "this is paragraph #2 " )
.add(
factory.createElement( "b" )
.addText( "bold" )
)
.addText( " more text" )
)
);
Though it is easier when just writing this as text ;-)
Absolutely. I'm seldom just printing static text, though, it's usually intermingled static and dynamic content. (And for the above you should almost certainly be using XMLC anyway :-)
title #1
this is paragraph #1
this is paragraph #2 bold more text
Does this approach appeal? Any thoughts?
James
1. Agreed, Element.addElement() returns the child.
2. Declaring add() to return anything at all is not what I'd expect, by analogy to the Collections family, but that's no big deal.
3. If add() is to return the added Branch, I'd much prefer add (Element) to return Element, not Branch - cast ye away the works of Mammon.
4. I'm confused about the distinction between addXYZ and setXYZ methods - I don't expect setXYZ(...) to create a new object.
Yours aye,
Thomas.