Uploaded image for project: 'Xerces2-J'
  1. Xerces2-J
  2. XERCESJ-1328

CoreDocumentImpl.adoptNode() does not copy UserData for child nodes.

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 2.9.1
    • None
    • DOM (Level 3 Core)
    • None
    • n/a

    Description

      SUMMARY:

      When a Node object is adopted into a CoreDocumentImpl Document, the top-most Node has its UserData copied from it's owning document into the adopting document, but other child nodes in the node tree below the top-most node do not get their UserData copied over.

      SOLUTION:

      The likely solution is to have adoptNode() traverse the Node's child tree and copy hashtable UserData entries from the Node's owning document into the adopting document's UserData hashtable.

      REPRO STEPS:

      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.

      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.

      ADDITIONAL DETAILS

      For additional details see this thread on the xerces-J-users mailing list:

      Thread title "Help with adoptNode() and setUserData() data being dropped?"

      http://marc.info/?l=xerces-j-user&m=122116446204144&w=2

      Attachments

        Activity

          People

            Unassigned Unassigned
            onejasonforsale Jason Baker
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated: