Hello,

I am working with the java version of xerces and am having trouble
figuring out how to get adopted nodes to carry attached UserData objects
along with them from one document object to another.

In particular, I am trying to add Line Number information to Node objects
in a xerces document. There is a handy xerces sample application that
shows me how to do this, you can find the sample in
Xerces-J-src.2.9.1.zip:xerces-2_9_1\samples\dom\DOMAddLines.java.

This sample is great! It got me started in the right direction and works
perfectly as long as you are not trying to clone nodes or adopt them to
other documents with the expectation that your line numbers will stick.

The first problem is that the sample does not provide a UserDataHandler
when it calls setUserData(), so I changed my sample around to include my
own handler:

public class DOMLineNumberUserDataHandler implements UserDataHandler
{

  public void handle(short operation, String key, Object data, Node src,
Node dst)
  {
    System.out.println("\nDOMLineNumberUserDataHandler Called: operation="
+ operation + ", key=" +
                                key + ", data=" + data + ", src=" + src + ", 
dst=" + dst);

    if (operation == UserDataHandler.NODE_ADOPTED)
    {
      System.out.println("(Current Operation is NODE_ADOPTED)");
    }

    if (dst != null)
    {
      dst.setUserData(key, data, this);
    }
   }

}

That's my handler code, then the sample needed to be changed around, I
added a private UserDataHandler like so at the top of the DOMAddLines:

private UserDataHandler userDataHandler = new DOMLineNumberUserDataHandler();

Then I changed the two lines from this:

node.setUserData("startLine", String.valueOf(locator.getLineNumber()), null);

to this:

node.setUserData("startLine", String.valueOf(locator.getLineNumber()),
this.userDataHandler);

Those two lines are in the DOMAddLines "startDocument" and "startElement"
methods.

With my handler in place, cloning nodes works beautifully.

However, adopting nodes from one document to another does not work.

The scenario we're developing for is this: We have XML a, it has some
specialized tags that are place holders for other XML documents (b) we
pull in. When our code is searching through the DOM tree for XML-a and we
find one of these tags that needs to be replaced with XML-b's contents,
then we read in XML-b, adopt XML-b's nodes into the XML-a document, and do
the replace. The adoptNodes call does not copy UserData for nodes other
than the top level node we're adopting though. We determined that we must
call adoptNode on the XML-b's root element before sticking the nodes into
the XML-a DOM tree in order to avoid 'WRONG_DOCUMENT_ERR' errors.

Here is a brief test case that exemplifies the behaviour I am seeing:

public void testLineNumberStuff()
{
  try
  {
    String originalDocument =
"<some>\n<document>\n<with>\n<no>content</no></with></document></some>";
    DOMParser originalParser = new DOMAddLines();
    originalParser.parse(new InputSource(new
StringReader(originalDocument)));
    Element originalDocumentElement =
originalParser.getDocument().getDocumentElement();

    String adoptingDocument = "<some>\n<other>\ndocument</other></some>";
    DOMParser adoptingParser = new DOMAddLines();
    adoptingParser.parse(new InputSource(new
StringReader(adoptingDocument)));
    Element adoptingDocumentElement =
adoptingParser.getDocument().getDocumentElement();

    DOMAddLines someUnrelatedParserForPrinting = new DOMAddLines();

    System.out.println("ORIGINAL DOCUMENT BEFORE ADOPTION:");
    someUnrelatedParserForPrinting.print(originalDocumentElement);

    System.out.println("ADOPTING DOCUMENT BEFORE ADOPTION: ");
    someUnrelatedParserForPrinting.print(adoptingDocumentElement);

    
adoptingDocumentElement.getOwnerDocument().adoptNode(originalDocumentElement);

    someUnrelatedParserForPrinting = new DOMAddLines();

    System.out.println("ORIGINAL DOCUMENT AFTER ADOPTION:");
    someUnrelatedParserForPrinting.print(originalDocumentElement);

    System.out.println("ADOPTING DOCUMENT AFTER ADOPTION: ");
    someUnrelatedParserForPrinting.print(adoptingDocumentElement);
  }
  catch (Exception e)
  {
    System.out.println("Caught exception: " + e.getMessage());
    e.printStackTrace();
  }
}

The output of this test case is:

ORIGINAL DOCUMENT BEFORE ADOPTION:

content4:<no></no>3:<with></with>2:<document></document>1:<some></some>

ADOPTING DOCUMENT BEFORE ADOPTION:

document2:<other></other>1:<some></some>

DOMLineNumberUserDataHandler Called: operation=5, key=startLine, data=1,
src=[some: null], dst=null
(Current Operation is NODE_ADOPTED)

ORIGINAL DOCUMENT AFTER ADOPTION:

contentnull:<no></no>null:<with></with>null:<document></document>1:<some></some>

ADOPTING DOCUMENT AFTER ADOPTION:


document2:<other></other>1:<some></some>

As you can see, all of the line number UserData objects except one are
dropped during the adoptNode scenario. All of the numbers in the "ORIGINAL
DOCUMENT" are replaced with nulls except for the "1" that is the UserData
on the top-most node in our example DOM tree.

I have dug around a little in an eclipse debug view of the Document
objects and I have also dug around a little on google and have not found a
clear statement of this issue elsewhere, but I have seen discussion of the
UserData objects being stored in the parent Document object in a hash map.
It appears only the initial root node's UserData in the DOM tree for a
document is being "adopted" over when an adoptNode call is made with that
root node.

Am I doing this incorrectly? Is the correct way to get the UserData copied
over simply a manual traversal of the DOM tree I want to adopt and calling
adoptNode() one each of those nodes?

Thanks for your time,

Jason Baker


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to