Help with dom4j.DocumentHelper

This is my first venture into Java hacking, so be gentle.

I’‘m trying to fix some bugs in the ldap-vcard code posted by Robert to JM-121. I’‘ve run into an issue using dom4j.DocumentHelper. Here’'s what I want the output to be:

<vCard xmlns=’‘vcard-temp’’>

555-5555

555-5551

When using DocumentHelper to create those nodes, I end up with this:

<vCard xmlns=’‘vcard-temp’’>

555-5551

You can see that DocumentHelper doesn’‘t create a new node, it just builds upon the one that’'s already in the document. What is the best way to get around this? Is there some sample code in other parts of the source tree that could help me out? Thanks

Cameron

Cameron,

Can you paste in the code in question? DOM4J can be a bit tricky the first time you use it.

-Matt

Sure. The code is from handler/IQvCardHandler.java:handleIQ():

if (recipient != null && recipient.getNode() != null) {
  User user = userManager.getUser(recipient.getNode());
  VCardManager vManager = VCardManager.getInstance();
  Collection<String> names = vManager.getVCardPropertyNames(user.getUsername());
  for (String name : names) {
    String[] stringParts = name.split(";");     for (int x=0; x < stringParts.length; x++) {
      // If we are at the last property name, save the text into the node
      if (x == stringParts.length - 1) {
        if (x == 0) {
          // If there are no subnames, just save the text
          // Example: "TITLE:Programmer"
          //   <TITLE>Programmer</TITLE>
          Element node = DocumentHelper.makeElement(vcard, stringParts[0]);
          node.setText(vManager.getVCardProperty(user.getUsername(), name));
        } else {
          // Example: "ORG;ORGNAME:Jive Software" becomes
          //   <ORG><ORGNAME>Jive Software</ORGNAME></ORG>
          String path = stringParts[0] + ''/'' + stringParts[x];
          Element node = DocumentHelper.makeElement(vcard, path);
          node.setText(vManager.getVCardProperty(user.getUsername(), name));
        }
      } else if (x > 0) {
        // If there are internal property names, simply create empty XML nodes
        // Example: "TEL;WORK;VOICE;NUMBER:555-5555" becomes
        //   <TEL><WORK/><VOICE/><NUMBER>555-5555</NUMBER></TEL>
        String path = stringParts[0] + ''/'' + stringParts[x];
        Element node = DocumentHelper.makeElement(vcard, path);
      }
    }
  }
}

Also note that I changed the field delimiter to a semicolon from a colon, which affects createName() and the ldap-vcard.xml as well.

Hi!

I think the problem is this line:

Element node = DocumentHelper.makeElement(vcard, path);

Quote from javadoc: makeElement = a helper method which navigates from the given Document or Element node to some Element using the path expression, creating any *necessary elements *along the way.

Because the “” Element already exists, the DocumentHelper does not create it, but adds the other Elements to the “” Element.

Why don’'t you create your XML File like this:

public Document createDocument() {

Document document = DocumentHelper.createDocument();

Element root = document.addElement( “root” );

Element author2 = root.addElement( “author” )

.addAttribute( “name”, “Toby” )

.addAttribute( “location”, “Germany” )

.addText( “Tobias Rademacher” );

Element author1 = root.addElement( “author” )

.addAttribute( “name”, “James” )

.addAttribute( “location”, “UK” )

.addText( “James Strachan” );

return document;

}

Source: DOM4J cookbook http://dom4j.org/cookbook.html

Greetz,

Jörg

Thanks, Jörg . The light just came on. I haven’'t tested this, but this would accomplish what I want, but read below…

for (int x=0; x < stringParts.length; x++) {
  // If we are at the last property name, save the text into the node
  if (x == stringParts.length - 1) {
    if (x == 0) {
      // If there are no subnames, just save the text
      // Example: "TITLE:Programmer"
      //   <TITLE>Programmer</TITLE>
      Element node = vcard.addElement(stringParts[0]);
      node.setText(vManager.getVCardProperty(user.getUsername(), name));
    } else {
      // Example: "ORG;ORGNAME:Jive Software" becomes
      //   <ORG><ORGNAME>Jive Software</ORGNAME></ORG>
      Element node = vcard.addElement(stringParts[0]).addElement(stringParts[x]);
      node.setText(vManager.getVCardProperty(user.getUsername(), name));
    }
  } else if (x > 0) {
    // If there are internal property names, simply create empty XML nodes
    // Example: "TEL;WORK;VOICE;NUMBER:555-5555" becomes
    //   <TEL><WORK/><VOICE/><NUMBER>555-5555</NUMBER></TEL>
    String path = stringParts[0] + ''/'' + stringParts[x];
    vcard.addElement(stringParts[0]).addElement(stringParts[x]);
  }
}

But this will break the other pieces of XML. I need a way to distinguish when new elements need to be created and when the don’‘t. Here’'s a test case from JEP-0054:

<vCard xmlns=''vcard-temp''>
  <N>
    <FAMILY>Saint-Andre<FAMILY>
    <GIVEN>Peter</GIVEN>
    <MIDDLE/>
  </N>
  <TEL><VOICE/><WORK/><NUMBER>303-308-3282</NUMBER></TEL>
  <TEL><FAX/><WORK/><NUMBER/></TEL>
</vCard>

You can see that there should only be one node, but there can be multiple nodes. I don’‘t see anywhere in the vCard DTD that says which elements can appear multiple times, so I guess we can just go by experience. Looks like there can be multiple TEL, ADR, and EMAIL elements. Correct me if I’'m wrong, but it appears that the only way to do this right is to handle TEL, ADR, and EMAIL elements differently from the other elements. Am I on the right track here?

Thanks

Well, I got it working like I want. There were some major logic problems with my last code snippet. I need to do some testing to make sure my changes will work with the DBProvider. Once I get the IQvCardHandler changes smoothed out, I’‘ll submit a patch. And once that’‘s done, I’'ll try to get JM-121 fixed and coax Matt into putting it into CVS.

PS - Jörg, I labeled your post as helpful thinking I would make it the correct answer after I did some tested, but I can’'t change it. Sorry.

Thanks for the points!

  • Jörg

How about 10 more since I screwed up the first time.