You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by ve...@apache.org on 2008/12/22 16:12:06 UTC

svn commit: r728709 - in /webservices/commons/trunk/modules/axiom/modules: axiom-api/src/main/java/org/apache/axiom/om/ axiom-api/src/main/java/org/apache/axiom/om/impl/builder/ axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ axiom-dom/src/main/j...

Author: veithen
Date: Mon Dec 22 07:12:06 2008
New Revision: 728709

URL: http://svn.apache.org/viewvc?rev=728709&view=rev
Log:
WSCOMMONS-423: Make sure that in DOOM, Text nodes never appear as children of Document (forbidden by DOM).

Added:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java   (with props)
    webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/
    webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java   (with props)
Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/builder/StAXOMBuilder.java
    webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ParentNode.java
    webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/factory/OMDOMFactory.java
    webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/DocumentImplTest.java

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java?rev=728709&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java Mon Dec 22 07:12:06 2008
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axiom.om;
+
+/**
+ * Thrown if an object model operation would lead to a hierarchy that is not allowed in the
+ * given object model implementation.
+ * <p>
+ * If this exception is encountered by a builder when creating an OM node from an event received
+ * by the parser and if the corresponding content can be ignored (i.e. is not semantically
+ * relevant), the builder should ignore the exception and skip the event. An example is whitespace
+ * appearing before or after the root element of a document. This would be represented as an
+ * {@link OMText} node below the {@link OMDocument}. If the OM implementation doesn't allow text
+ * nodes as children of a document (as for example in DOM), it should throw this exception so
+ * that the builder can discard the event. 
+ */
+public class OMHierarchyException extends OMException {
+    private static final long serialVersionUID = 8391435427221729190L;
+
+    public OMHierarchyException(String message) {
+        super(message);
+    }
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMHierarchyException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/builder/StAXOMBuilder.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/builder/StAXOMBuilder.java?rev=728709&r1=728708&r2=728709&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/builder/StAXOMBuilder.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/builder/StAXOMBuilder.java Mon Dec 22 07:12:06 2008
@@ -24,6 +24,7 @@
 import org.apache.axiom.om.OMElement;
 import org.apache.axiom.om.OMException;
 import org.apache.axiom.om.OMFactory;
+import org.apache.axiom.om.OMHierarchyException;
 import org.apache.axiom.om.OMNamespace;
 import org.apache.axiom.om.OMNode;
 import org.apache.axiom.om.OMText;
@@ -163,86 +164,96 @@
      */
     public int next() throws OMException {
         try {
-            if (done) {
-                throw new OMException();
-            }
-            int token = parserNext();
-            if (!cache) {
-                return token;
-            }
-           
-            // The current token should be the same as the 
-            // one just obtained.  This bit of code is used to 
-            // detect invalid parser state.
-            if (doTrace) {
-                int currentParserToken = parser.getEventType();
-                if (currentParserToken != token) {
-
-
-                    log.debug("WARNING: The current state of the parser is not equal to the " +
-                              "state just received from the parser. The current state in the paser is " +
-                              getStateString(currentParserToken) + " the state just received is " +
-                              getStateString(token));
-
-                    /*
-                      throw new OMException("The current token " + token + 
-                                     " does not match the current event " +
-                                     "reported by the parser token.  The parser did not update its state correctly.  " +
-                                     "The parser is " + parser);
-                     */
-                }
-            }
-            
-            // Now log the current state of the parser
-            if (doTrace) {
-                logParserState();
-            }
-           
-            switch (token) {
-                case XMLStreamConstants.START_ELEMENT:
-                    elementLevel++;
-                    lastNode = createNextOMElement();
-                    break;
-                case XMLStreamConstants.START_DOCUMENT:
-                    // Document has already being created.
-
-                    document.setXMLVersion(parser.getVersion());
-                    document.setCharsetEncoding(parser.getEncoding());
-                    document.setStandalone(parser.isStandalone() ? "yes" : "no");
-                    break;
-                case XMLStreamConstants.CHARACTERS:
-                    lastNode = createOMText(XMLStreamConstants.CHARACTERS);
-                    break;
-                case XMLStreamConstants.CDATA:
-                    lastNode = createOMText(XMLStreamConstants.CDATA);
-                    break;
-                case XMLStreamConstants.END_ELEMENT:
-                    endElement();
-                    elementLevel--;
-                    break;
-                case XMLStreamConstants.END_DOCUMENT:
-                    done = true;
-                    ((OMContainerEx) this.document).setComplete(true);
-                    break;
-                case XMLStreamConstants.SPACE:
-                    lastNode = createOMText(XMLStreamConstants.SPACE);
-                    break;
-                case XMLStreamConstants.COMMENT:
-                    lastNode = createComment();
-                    break;
-                case XMLStreamConstants.DTD:
-                    createDTD();
-                    break;
-                case XMLStreamConstants.PROCESSING_INSTRUCTION:
-                    lastNode = createPI();
-                    break;
-                case XMLStreamConstants.ENTITY_REFERENCE:
-                    lastNode = createOMText(XMLStreamConstants.ENTITY_REFERENCE);
-                    break;
-                default :
+            // We need a loop here because we may decide to skip an event
+            while (true) {
+                if (done) {
                     throw new OMException();
+                }
+                int token = parserNext();
+                if (!cache) {
+                    return token;
+                }
+               
+                // The current token should be the same as the 
+                // one just obtained.  This bit of code is used to 
+                // detect invalid parser state.
+                if (doTrace) {
+                    int currentParserToken = parser.getEventType();
+                    if (currentParserToken != token) {
+    
+    
+                        log.debug("WARNING: The current state of the parser is not equal to the " +
+                                  "state just received from the parser. The current state in the paser is " +
+                                  getStateString(currentParserToken) + " the state just received is " +
+                                  getStateString(token));
+    
+                        /*
+                          throw new OMException("The current token " + token + 
+                                         " does not match the current event " +
+                                         "reported by the parser token.  The parser did not update its state correctly.  " +
+                                         "The parser is " + parser);
+                         */
+                    }
+                }
+                
+                // Now log the current state of the parser
+                if (doTrace) {
+                    logParserState();
+                }
+               
+                switch (token) {
+                    case XMLStreamConstants.START_ELEMENT:
+                        elementLevel++;
+                        lastNode = createNextOMElement();
+                        break;
+                    case XMLStreamConstants.START_DOCUMENT:
+                        // Document has already being created.
+    
+                        document.setXMLVersion(parser.getVersion());
+                        document.setCharsetEncoding(parser.getEncoding());
+                        document.setStandalone(parser.isStandalone() ? "yes" : "no");
+                        break;
+                    case XMLStreamConstants.CHARACTERS:
+                        lastNode = createOMText(XMLStreamConstants.CHARACTERS);
+                        break;
+                    case XMLStreamConstants.CDATA:
+                        lastNode = createOMText(XMLStreamConstants.CDATA);
+                        break;
+                    case XMLStreamConstants.END_ELEMENT:
+                        endElement();
+                        elementLevel--;
+                        break;
+                    case XMLStreamConstants.END_DOCUMENT:
+                        done = true;
+                        ((OMContainerEx) this.document).setComplete(true);
+                        break;
+                    case XMLStreamConstants.SPACE:
+                        try {
+                            lastNode = createOMText(XMLStreamConstants.SPACE);
+                        } catch (OMHierarchyException ex) {
+                            // The OM implementation doesn't allow text nodes at the current
+                            // position in the tree. Since it is only whitespace, we can safely
+                            // skip this event.
+                            continue;
+                        }
+                        break;
+                    case XMLStreamConstants.COMMENT:
+                        lastNode = createComment();
+                        break;
+                    case XMLStreamConstants.DTD:
+                        createDTD();
+                        break;
+                    case XMLStreamConstants.PROCESSING_INSTRUCTION:
+                        lastNode = createPI();
+                        break;
+                    case XMLStreamConstants.ENTITY_REFERENCE:
+                        lastNode = createOMText(XMLStreamConstants.ENTITY_REFERENCE);
+                        break;
+                    default :
+                        throw new OMException();
+                }
+                return token;
             }
-            return token;
         } catch (OMException e) {
             throw e;
         } catch (Exception e) {

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ParentNode.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ParentNode.java?rev=728709&r1=728708&r2=728709&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ParentNode.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/ParentNode.java Mon Dec 22 07:12:06 2008
@@ -218,19 +218,28 @@
         }
 
         if (this instanceof Document) {
-            if (((DocumentImpl) this).documentElement != null
-                    && !(newDomChild instanceof CommentImpl)) {
-                // Throw exception since there cannot be two document elements
-                throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
-                                       DOMMessageFormatter.formatMessage(
-                                               DOMMessageFormatter.DOM_DOMAIN,
-                                               "HIERARCHY_REQUEST_ERR", null));
-            } else if (newDomChild instanceof ElementImpl) {
+            if (newDomChild instanceof ElementImpl) {
+                if (((DocumentImpl) this).documentElement != null) {
+                    // Throw exception since there cannot be two document elements
+                    throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
+                                           DOMMessageFormatter.formatMessage(
+                                                   DOMMessageFormatter.DOM_DOMAIN,
+                                                   "HIERARCHY_REQUEST_ERR", null));
+                }
                 if (newDomChild.parentNode == null) {
                     newDomChild.parentNode = this;
                 }
                 // set the document element
                 ((DocumentImpl) this).documentElement = (ElementImpl) newDomChild;
+            } else if (!(newDomChild instanceof CommentImpl
+                    || newDomChild instanceof DocumentFragmentImpl)) {
+                // TODO: we should also check for ProcessingInstruction and DocumentType,
+                //       but since we don't have implementations yet, we can leave it
+                //       like this for now
+                throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
+                        DOMMessageFormatter.formatMessage(
+                                DOMMessageFormatter.DOM_DOMAIN,
+                                "HIERARCHY_REQUEST_ERR", null));
             }
         }
         boolean compositeChild = newDomChild.nextSibling != null;

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/factory/OMDOMFactory.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/factory/OMDOMFactory.java?rev=728709&r1=728708&r2=728709&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/factory/OMDOMFactory.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-dom/src/main/java/org/apache/axiom/om/impl/dom/factory/OMDOMFactory.java Mon Dec 22 07:12:06 2008
@@ -30,6 +30,7 @@
 import org.apache.axiom.om.OMElement;
 import org.apache.axiom.om.OMException;
 import org.apache.axiom.om.OMFactory;
+import org.apache.axiom.om.OMHierarchyException;
 import org.apache.axiom.om.OMNamespace;
 import org.apache.axiom.om.OMNode;
 import org.apache.axiom.om.OMProcessingInstruction;
@@ -241,10 +242,12 @@
      * @see org.apache.axiom.om.OMFactory#createOMText( org.apache.axiom.om.OMElement,String)
      */
     public OMText createOMText(OMContainer parent, String text) {
-        // A text node can also be added as the child of a Document node (at least if
-        // it contains whitespace only). Therefore we can't assume that the parent is
-        // an element and we need to use getDocumentFromParent instead.
-        TextImpl txt = new TextImpl(getDocumentFromParent(parent), text, this);
+        if (parent instanceof DocumentImpl) {
+            throw new OMHierarchyException(
+                    "DOM doesn't support text nodes as children of a document");
+        }
+        TextImpl txt = new TextImpl((DocumentImpl)((ElementImpl)parent).getOwnerDocument(),
+                text, this);
         parent.addChild(txt);
         return txt;
     }

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/DocumentImplTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/DocumentImplTest.java?rev=728709&r1=728708&r2=728709&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/DocumentImplTest.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/DocumentImplTest.java Mon Dec 22 07:12:06 2008
@@ -23,6 +23,7 @@
 
 import junit.framework.TestCase;
 import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -101,4 +102,44 @@
             }
         });
     }
+
+    public void testAllowedChildren() throws Exception {
+        DOMTestUtil.execute(new DOMTestUtil.Test() {
+            public void execute(DocumentBuilderFactory dbf) throws Exception {
+                Document doc = dbf.newDocumentBuilder().newDocument();
+                
+                doc.appendChild(doc.createComment("some comment"));
+                
+                // Document Object Model (DOM) Level 3 Core Specification, section 1.1.1
+                // says that text nodes are not allowed as children of a document.
+                try {
+                    doc.appendChild(doc.createTextNode("    "));
+                    fail("Expected DOMException");
+                } catch (DOMException ex) {
+                    assertEquals(DOMException.HIERARCHY_REQUEST_ERR, ex.code);
+                }
+                
+                doc.appendChild(doc.createElement("root1"));
+                
+                // Multiple document elements are not allowed
+                try {
+                    doc.appendChild(doc.createElement("root2"));
+                    fail("Expected DOMException");
+                } catch (DOMException ex) {
+                    assertEquals(DOMException.HIERARCHY_REQUEST_ERR, ex.code);
+                }
+                
+                // A comment after the document element is allowed
+                doc.appendChild(doc.createComment("some comment"));
+                
+                // Again, text nodes are not allowed
+                try {
+                    doc.appendChild(doc.createTextNode("    "));
+                    fail("Expected DOMException");
+                } catch (DOMException ex) {
+                    assertEquals(DOMException.HIERARCHY_REQUEST_ERR, ex.code);
+                }
+            }
+        });
+    }
 }

Added: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java?rev=728709&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java Mon Dec 22 07:12:06 2008
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axiom.om.impl.dom.jaxp;
+
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.axiom.om.impl.dom.DOMTestUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.InputSource;
+
+public class DocumentBuilderTest extends TestCase {
+    /**
+     * Test that whitespace around the document element is discarded.
+     * Indeed, DOM doesn't allow text nodes as children of a document and we
+     * need to check that the builder silently discards the corresponding
+     * events received from the parser.
+     * 
+     * @throws Exception
+     */
+    public void testWhitespaceAroundDocumentElement() throws Exception {
+        DOMTestUtil.execute(new DOMTestUtil.Test() {
+            public void execute(DocumentBuilderFactory dbf) throws Exception {
+                Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader("<!-- --> <root/> ")));
+                Node child = doc.getFirstChild();
+                do {
+                    assertFalse(child instanceof Text);
+                    child = child.getNextSibling();
+                } while (child != null);
+            }
+        });
+    }
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/om/impl/dom/jaxp/DocumentBuilderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native