nddelima 2004/06/24 10:57:00 Modified: java/src/org/apache/xerces/dom TextImpl.java Log: Patch to fix problems with replaceWholeText. Revision Changes Path 1.24 +252 -64 xml-xerces/java/src/org/apache/xerces/dom/TextImpl.java Index: TextImpl.java =================================================================== RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/TextImpl.java,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- TextImpl.java 24 Feb 2004 23:23:17 -0000 1.23 +++ TextImpl.java 24 Jun 2004 17:57:00 -0000 1.24 @@ -41,6 +41,12 @@ extends CharacterDataImpl implements CharacterData, Text { + // + // Private Data members + // + //Used by replaceWholeText + private boolean fTextOnlyChildren = false; + // // Constants // @@ -183,77 +189,258 @@ } /** - * DOM Level 3 WD - Experimental. - */ - public Text replaceWholeText(String content) - throws DOMException{ + * Replaces the text of the current node and all logically-adjacent text + * nodes with the specified text. All logically-adjacent text nodes are + * removed including the current node unless it was the recipient of the + * replacement text. + * + * @param content + * The content of the replacing Text node. + * @return text - The Text node created with the specified content. + * @since DOM Level 3 + */ + public Text replaceWholeText(String content) throws DOMException { if (needsSyncData()) { synchronizeData(); } - - // make sure we can make the replacement - if (!canModify(nextSibling)) { - throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, - DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null)); - } - + //if the content is null Node parent = this.getParentNode(); if (content == null || content.length() == 0) { // remove current node - if (parent !=null) { // check if node in the tree + if (parent != null) { // check if node in the tree parent.removeChild(this); - return null; } + return null; } + + //replace the text node Text currentNode = null; - if (isReadOnly()){ + if (isReadOnly()) { Text newNode = this.ownerDocument().createTextNode(content); - if (parent !=null) { // check if node in the tree + if (parent != null) { // check if node in the tree parent.insertBefore(newNode, this); parent.removeChild(this); currentNode = newNode; } else { return newNode; } - } else { + } else { this.setData(content); currentNode = this; } - Node sibling = currentNode.getNextSibling(); - while ( sibling !=null) { - parent.removeChild(sibling); - sibling = currentNode.getNextSibling(); - } + //check logically-adjacent text nodes + Node prev = this.getPreviousSibling(); + while (prev != null) { + fTextOnlyChildren = false; + + // make sure we can make the replacement + if (!canModifyPrev(prev)) { + throw new DOMException( + DOMException.NO_MODIFICATION_ALLOWED_ERR, + DOMMessageFormatter.formatMessage( + DOMMessageFormatter.DOM_DOMAIN, + "NO_MODIFICATION_ALLOWED_ERR", null)); + } else { + //If the logically-adjacent previous node can be replaced + //remove it. A logically adjacent node can be replaced if + //it is a Text or CDATASection node or an EntityReference with + //Text and CDATA only children + if ((prev.getNodeType() == Node.TEXT_NODE) || (prev + .getNodeType() == Node.CDATA_SECTION_NODE)|| + (prev.getNodeType() == Node.ENTITY_REFERENCE_NODE && fTextOnlyChildren)) { + parent.removeChild(prev); + prev = this; + } + } + prev = prev.getPreviousSibling(); + } + + //check logically-adjacent text nodes + Node next = this.getNextSibling(); + while (next != null) { + fTextOnlyChildren = false; + + // make sure we can make the replacement + if (!canModifyNext(next)) { + throw new DOMException( + DOMException.NO_MODIFICATION_ALLOWED_ERR, + DOMMessageFormatter.formatMessage( + DOMMessageFormatter.DOM_DOMAIN, + "NO_MODIFICATION_ALLOWED_ERR", null)); + } else { + //If the logically-adjacent next node can be replaced + //remove it. A logically adjacent node can be replaced if + //it is a Text or CDATASection node or an EntityReference with + //Text and CDATA only children + if ((next.getNodeType() == Node.TEXT_NODE) || (next + .getNodeType() == Node.CDATA_SECTION_NODE) || + (next.getNodeType() == Node.ENTITY_REFERENCE_NODE && fTextOnlyChildren)) { + parent.removeChild(next); + next = this; + } + } + next = next.getNextSibling(); + } + return currentNode; } /** - * If any EntityReference to be removed has descendants - * that are not EntityReference, Text, or CDATASection - * nodes, the replaceWholeText method must fail before - * performing any modification of the document, raising a - * DOMException with the code NO_MODIFICATION_ALLOWED_ERR. + * If any EntityReference to be removed has descendants that are not + * EntityReference, Text, or CDATASection nodes, the replaceWholeText method + * must fail before performing any modification of the document, raising a + * DOMException with the code NO_MODIFICATION_ALLOWED_ERR. Traverse previous + * siblings of the node to be replaced. If a previous sibling is an + * EntityReference node, get it's last child. If the last child was a Text + * or CDATASection node and its previous siblings are neither a replaceable + * EntityReference or Text or CDATASection nodes, return false. IF the last + * child was neither Text nor CDATASection nor a replaceable EntityReference + * Node, then return true. If the last child was a Text or CDATASection node + * any its previous sibling was not or was an EntityReference that did not + * contain only Text or CDATASection nodes, return false. Check this recursively + * for EntityReference nodes. * * @param node - * @return true - can replace text - * false - can't replace exception must be raised + * @return true - can replace text false - can't replace exception must be + * raised */ - private boolean canModify(Node node){ - while (node != null) { - short type = node.getNodeType(); - if (type == Node.ENTITY_REFERENCE_NODE) { - if (!canModify(node.getFirstChild())){ - return false; + private boolean canModifyPrev(Node node) { + boolean textLastChild = false; + + short type = node.getNodeType(); + + if (type == Node.ENTITY_REFERENCE_NODE) { + //If the previous sibling was entityreference + //check if its content is replaceable + Node lastChild = node.getLastChild(); + + //if the entity reference has no children + //return false + if (lastChild == null) { + fTextOnlyChildren = false; + return false; + } + + //The replacement text of the entity reference should + //be either only text,cadatsections or replaceable entity + //reference nodes or the last child should be neither of these + while (lastChild != null) { + short lType = lastChild.getNodeType(); + + if (lType == Node.TEXT_NODE || lType == Node.CDATA_SECTION_NODE) { + textLastChild = true; + fTextOnlyChildren = true; + } else if (lType == Node.ENTITY_REFERENCE_NODE) { + if (!canModifyPrev(lastChild)) { + fTextOnlyChildren = false; + return false; + } else { + //If the EntityReference child contains + //only text, or non-text or ends with a + //non-text node. + textLastChild = true; + } + + } else { + if (textLastChild) { + fTextOnlyChildren = false; + return false; + } else { + return true; + } } + lastChild = lastChild.getPreviousSibling(); } - else if (type != Node.TEXT_NODE && - type != Node.CDATA_SECTION_NODE) { + } else if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) { + //If the previous sibling was text or cdatasection move to next + fTextOnlyChildren = true; + } else { + //If the previous sibling was anything but text or + //cdatasection or an entity reference, stop search and + //return true + return true; + } + return true; + } + + /** + * If any EntityReference to be removed has descendants that are not + * EntityReference, Text, or CDATASection nodes, the replaceWholeText method + * must fail before performing any modification of the document, raising a + * DOMException with the code NO_MODIFICATION_ALLOWED_ERR. Traverse previous + * siblings of the node to be replaced. If a previous sibling is an + * EntityReference node, get it's last child. If the first child was a Text + * or CDATASection node and its next siblings are neither a replaceable + * EntityReference or Text or CDATASection nodes, return false. IF the first + * child was neither Text nor CDATASection nor a replaceable EntityReference + * Node, then return true. If the first child was a Text or CDATASection node + * any its next sibling was not or was an EntityReference that did not + * contain only Text or CDATASection nodes, return false. Check this recursively + * for EntityReference nodes. + * + * @param node + * @return true - can replace text false - can't replace exception must be + * raised + */ + + private boolean canModifyNext(Node node) { + boolean textFirstChild = false; + + short type = node.getNodeType(); + + if (type == Node.ENTITY_REFERENCE_NODE) { + //If the previous sibling was entityreference + //check if its content is replaceable + Node firstChild = node.getFirstChild(); + + //if the entity reference has no children + //return false + if (firstChild == null) { + fTextOnlyChildren = false; return false; } - - node = node.getNextSibling(); + + //The replacement text of the entity reference should + //be either only text,cadatsections or replaceable entity + //reference nodes or the last child should be neither of these + while (firstChild != null) { + short lType = firstChild.getNodeType(); + + if (lType == Node.TEXT_NODE || lType == Node.CDATA_SECTION_NODE) { + fTextOnlyChildren = true; + textFirstChild = true; + } else if (lType == Node.ENTITY_REFERENCE_NODE) { + if (!canModifyNext(firstChild)) { + fTextOnlyChildren = false; + return false; + } else { + //If the EntityReference child contains + //only text, or non-text or ends with a + //non-text node. + textFirstChild = true; + } + } else { + //If the first child was replaceable text and next + //children are not, then return false + if (textFirstChild) { + fTextOnlyChildren = false; + return false; + } else { + return true; + } + } + firstChild = firstChild.getNextSibling(); + } + } else if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) { + //If the previous sibling was text or cdatasection move to next + fTextOnlyChildren = true; + } else { + //If the next sibling was anything but text or + //cdatasection or an entity reference, stop search and + //return true + return true; } return true; } @@ -275,51 +462,53 @@ // Text methods // - /** - * Break a text node into two sibling nodes. (Note that if the - * current node has no parent, they won't wind up as "siblings" -- - * they'll both be orphans.) - * - * @param offset The offset at which to split. If offset is at the - * end of the available data, the second node will be empty. - * - * @return A reference to the new node (containing data after the - * offset point). The original node will contain data up to that - * point. - * - * @throws DOMException(INDEX_SIZE_ERR) if offset is <0 or >length. - * - * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if node is read-only. + /** + * Break a text node into two sibling nodes. (Note that if the current node + * has no parent, they won't wind up as "siblings" -- they'll both be + * orphans.) + * + * @param offset + * The offset at which to split. If offset is at the end of the + * available data, the second node will be empty. + * + * @return A reference to the new node (containing data after the offset + * point). The original node will contain data up to that point. + * + * @throws DOMException(INDEX_SIZE_ERR) + * if offset is <0 or >length. + * + * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) + * if node is read-only. */ public Text splitText(int offset) throws DOMException { - if (isReadOnly()) { + if (isReadOnly()) { throw new DOMException( - DOMException.NO_MODIFICATION_ALLOWED_ERR, + DOMException.NO_MODIFICATION_ALLOWED_ERR, DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null)); } if (needsSyncData()) { synchronizeData(); } - if (offset < 0 || offset > data.length() ) { + if (offset < 0 || offset > data.length() ) { throw new DOMException(DOMException.INDEX_SIZE_ERR, DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INDEX_SIZE_ERR", null)); } - + // split text into two separate nodes - Text newText = + Text newText = getOwnerDocument().createTextNode(data.substring(offset)); - setNodeValue(data.substring(0, offset)); + setNodeValue(data.substring(0, offset)); // insert new text node Node parentNode = getParentNode(); - if (parentNode != null) { - parentNode.insertBefore(newText, nextSibling); + if (parentNode != null) { + parentNode.insertBefore(newText, nextSibling); } - return newText; + return newText; } // splitText(int):Text @@ -341,6 +530,5 @@ data = ""; return olddata; } - } // class TextImpl
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]