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