| /******************************************************************************* |
| * Copyright (c) 2001, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Jens Lukowski/Innoopract - initial renaming/restructuring |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.xml.core.internal.document; |
| |
| |
| |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.Vector; |
| |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; |
| import org.eclipse.wst.xml.core.internal.commentelement.impl.CommentElementConfiguration; |
| import org.eclipse.wst.xml.core.internal.commentelement.impl.CommentElementRegistry; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Text; |
| |
| |
| /** |
| * XMLModelParser |
| */ |
| public class XMLModelParser { |
| private XMLModelContext context = null; |
| private DOMModelImpl model = null; |
| |
| /** |
| */ |
| protected XMLModelParser(DOMModelImpl model) { |
| super(); |
| |
| if (model != null) { |
| this.model = model; |
| } |
| } |
| |
| /** |
| */ |
| protected boolean canBeImplicitTag(Element element) { |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.canBeImplicitTag(element); |
| } |
| return false; |
| } |
| |
| /** |
| */ |
| protected boolean canBeImplicitTag(Element element, Node child) { |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.canBeImplicitTag(element, child); |
| } |
| return false; |
| } |
| |
| /** |
| */ |
| protected boolean canContain(Element element, Node child) { |
| if (element == null || child == null) |
| return false; |
| ElementImpl impl = (ElementImpl) element; |
| if (impl.isEndTag()) |
| return false; // invalid (floating) end tag |
| if (!impl.isContainer()) |
| return false; |
| if (child.getNodeType() != Node.TEXT_NODE) { |
| if (impl.isJSPContainer() || impl.isCDATAContainer()) { |
| // accepts only Text child |
| return false; |
| } |
| } |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.canContain(element, child); |
| } |
| return true; |
| } |
| |
| /** |
| */ |
| private void changeAttrEqual(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { |
| // just notify the change instead of setting data |
| ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; |
| pi.notifyValueChanged(); |
| } |
| return; |
| } |
| // actually, do nothing |
| } |
| |
| /** |
| * changeAttrName method |
| * |
| */ |
| private void changeAttrName(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { |
| // just notify the change instead of setting data |
| ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; |
| pi.notifyValueChanged(); |
| } |
| return; |
| } |
| |
| ElementImpl element = (ElementImpl) node; |
| NamedNodeMap attributes = element.getAttributes(); |
| if (attributes == null) |
| return; |
| int length = attributes.getLength(); |
| for (int i = 0; i < length; i++) { |
| AttrImpl attr = (AttrImpl) attributes.item(i); |
| if (attr == null) |
| continue; |
| if (attr.getNameRegion() != region) |
| continue; |
| |
| String name = flatNode.getText(region); |
| attr.setName(name); |
| break; |
| } |
| } |
| |
| /** |
| * changeAttrValue method |
| * |
| */ |
| private void changeAttrValue(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { |
| // just notify the change instead of setting data |
| ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; |
| pi.notifyValueChanged(); |
| } |
| return; |
| } |
| |
| ElementImpl element = (ElementImpl) node; |
| NamedNodeMap attributes = element.getAttributes(); |
| if (attributes == null) |
| return; |
| int length = attributes.getLength(); |
| for (int i = 0; i < length; i++) { |
| AttrImpl attr = (AttrImpl) attributes.item(i); |
| if (attr == null) |
| continue; |
| if (attr.getValueRegion() != region) |
| continue; |
| // just notify the change instead of setting value |
| attr.notifyValueChanged(); |
| break; |
| } |
| } |
| |
| /** |
| * changeData method |
| * |
| */ |
| private void changeData(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; |
| switch (node.getNodeType()) { |
| case Node.TEXT_NODE : { |
| TextImpl text = (TextImpl) node; |
| if (text.isSharingStructuredDocumentRegion(flatNode)) { |
| // has consecutive text sharing IStructuredDocumentRegion |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| this.context.setNextNode(node); |
| cleanupText(); |
| break; |
| } |
| case Node.CDATA_SECTION_NODE : |
| case Node.PROCESSING_INSTRUCTION_NODE : |
| break; |
| case Node.COMMENT_NODE : |
| case Node.ELEMENT_NODE : |
| // comment tag |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| default : |
| return; |
| } |
| |
| // just notify the change instead of setting data |
| NodeImpl impl = (NodeImpl) node; |
| impl.notifyValueChanged(); |
| } |
| |
| /** |
| */ |
| private void changeEndTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; // error |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; // error |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; // error |
| |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| |
| // check if change is only for close tag |
| if (newRegions != null) { |
| Iterator e = newRegions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_CLOSE) |
| continue; |
| |
| // other region has changed |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| } |
| if (oldRegions != null) { |
| Iterator e = oldRegions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_CLOSE) |
| continue; |
| |
| // other region has changed |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| } |
| |
| // change for close tag has no impact |
| // do nothing |
| } |
| |
| /** |
| * changeRegion method |
| * |
| */ |
| void changeRegion(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| if (flatNode == null || region == null) |
| return; |
| if (this.model.getDocument() == null) |
| return; |
| this.context = new XMLModelContext(this.model.getDocument()); |
| |
| // optimize typical cases |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_CONTENT || regionType == DOMRegionContext.XML_COMMENT_TEXT || regionType == DOMRegionContext.XML_CDATA_TEXT || regionType == DOMRegionContext.BLOCK_TEXT || isNestedContent(regionType)) { |
| changeData(flatNode, region); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| changeAttrName(flatNode, region); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| changeAttrValue(flatNode, region); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| changeAttrEqual(flatNode, region); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) { |
| changeTagName(flatNode, region); |
| } |
| else { |
| changeStructuredDocumentRegion(flatNode); |
| } |
| } |
| |
| |
| |
| /** |
| */ |
| private void changeStartTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; // error |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; // error |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; // error |
| |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| ElementImpl element = (ElementImpl) node; |
| |
| // check if changes are only for attributes and close tag |
| boolean tagNameUnchanged = false; |
| if (newRegions != null) { |
| Iterator e = newRegions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) |
| continue; |
| if (regionType == DOMRegionContext.XML_TAG_CLOSE) { |
| // change from empty tag may have impact on structure |
| if (!element.isEmptyTag()) |
| continue; |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) { |
| String oldTagName = element.getTagName(); |
| String newTagName = flatNode.getText(region); |
| if (oldTagName != null && newTagName != null && oldTagName.equals(newTagName)) { |
| // the tag name is unchanged |
| tagNameUnchanged = true; |
| continue; |
| } |
| } |
| |
| // other region has changed |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| } |
| if (oldRegions != null) { |
| Iterator e = oldRegions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) |
| continue; |
| if (regionType == DOMRegionContext.XML_TAG_CLOSE) { |
| // change from empty tag may have impact on structure |
| if (!element.isEmptyTag()) |
| continue; |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) { |
| // if new tag name is unchanged, it's OK |
| if (tagNameUnchanged) |
| continue; |
| } |
| |
| // other region has changed |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| } |
| |
| // update attributes |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; // error |
| NamedNodeMap attributes = element.getAttributes(); |
| if (attributes == null) |
| return; // error |
| |
| // first remove attributes |
| int regionIndex = 0; |
| int attrIndex = 0; |
| AttrImpl attr = null; |
| while (attrIndex < attributes.getLength()) { |
| attr = (AttrImpl) attributes.item(attrIndex); |
| if (attr == null) { // error |
| attrIndex++; |
| continue; |
| } |
| ITextRegion nameRegion = attr.getNameRegion(); |
| if (nameRegion == null) { // error |
| element.removeAttributeNode(attr); |
| continue; |
| } |
| boolean found = false; |
| for (int i = regionIndex; i < regions.size(); i++) { |
| ITextRegion region = regions.get(i); |
| if (region == nameRegion) { |
| regionIndex = i + 1; // next region |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| attrIndex++; |
| } |
| else { |
| element.removeAttributeNode(attr); |
| } |
| } |
| |
| // insert or update attributes |
| attrIndex = 0; // reset to first |
| AttrImpl newAttr = null; |
| ITextRegion oldValueRegion = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| if (newAttr != null) { |
| // insert deferred new attribute |
| element.insertAttributeNode(newAttr, attrIndex++); |
| newAttr = null; |
| } |
| else if (attr != null && oldValueRegion != null) { |
| // notify existing attribute value removal |
| attr.notifyValueChanged(); |
| } |
| |
| oldValueRegion = null; |
| attr = (AttrImpl) attributes.item(attrIndex); |
| if (attr != null && attr.getNameRegion() == region) { |
| // existing attribute |
| attrIndex++; |
| // clear other regions |
| oldValueRegion = attr.getValueRegion(); |
| attr.setEqualRegion(null); |
| attr.setValueRegion(null); |
| } |
| else { |
| String name = flatNode.getText(region); |
| attr = (AttrImpl) this.model.getDocument().createAttribute(name); |
| if (attr != null) |
| attr.setNameRegion(region); |
| // defer insertion of new attribute |
| newAttr = attr; |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| if (attr != null) { |
| attr.setEqualRegion(region); |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| if (attr != null) { |
| attr.setValueRegion(region); |
| if (attr != newAttr && oldValueRegion != region) { |
| // notify existing attribute value changed |
| attr.notifyValueChanged(); |
| } |
| oldValueRegion = null; |
| attr = null; |
| } |
| } |
| } |
| |
| if (newAttr != null) { |
| // insert deferred new attribute |
| element.appendAttributeNode(newAttr); |
| } |
| else if (attr != null && oldValueRegion != null) { |
| // notify existing attribute value removal |
| attr.notifyValueChanged(); |
| } |
| } |
| |
| /** |
| * changeStructuredDocumentRegion method |
| * |
| */ |
| private void changeStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { |
| if (flatNode == null) |
| return; |
| if (this.model.getDocument() == null) |
| return; |
| |
| setupContext(flatNode); |
| |
| removeStructuredDocumentRegion(flatNode); |
| // make sure the parent is set to deepest level |
| // when end tag has been removed |
| this.context.setLast(); |
| insertStructuredDocumentRegion(flatNode); |
| |
| cleanupText(); |
| cleanupEndTag(); |
| } |
| |
| /** |
| */ |
| private void changeTagName(IStructuredDocumentRegion flatNode, ITextRegion region) { |
| int offset = flatNode.getStart(); |
| if (offset < 0) |
| return; // error |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; // error |
| Node node = root.getNodeAt(offset); |
| if (node == null) |
| return; // error |
| |
| if (node.getNodeType() != Node.ELEMENT_NODE) { |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| |
| ElementImpl element = (ElementImpl) node; |
| String newTagName = flatNode.getText(region); |
| if (newTagName == null || !element.matchTagName(newTagName)) { |
| // the tag name is changed |
| changeStructuredDocumentRegion(flatNode); |
| return; |
| } |
| |
| // the tag name is unchanged |
| // this happens when typing spaces after the tag name |
| // do nothing, but... |
| // if it's not a change in the end tag of an element with the start |
| // tag, |
| // and case has been changed, set to element and notify |
| if (!element.hasStartTag() || StructuredDocumentRegionUtil.getFirstRegionType(flatNode) != DOMRegionContext.XML_END_TAG_OPEN) { |
| String tagName = element.getTagName(); |
| if (tagName == null || !tagName.equals(newTagName)) { |
| element.setTagName(newTagName); |
| element.notifyValueChanged(); |
| } |
| } |
| } |
| |
| /** |
| * cleanupContext method |
| */ |
| private void cleanupEndTag() { |
| Node parent = this.context.getParentNode(); |
| Node next = this.context.getNextNode(); |
| while (parent != null) { |
| while (next != null) { |
| if (next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl element = (ElementImpl) next; |
| if (element.isEndTag()) { |
| // floating end tag |
| String tagName = element.getTagName(); |
| String rootName = getFindRootName(tagName); |
| ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); |
| if (start != null) { |
| insertEndTag(start); |
| // move the end tag from 'element' to 'start' |
| start.addEndTag(element); |
| removeNode(element); |
| parent = this.context.getParentNode(); |
| next = this.context.getNextNode(); |
| continue; |
| } |
| } |
| } |
| |
| Node first = next.getFirstChild(); |
| if (first != null) { |
| parent = next; |
| next = first; |
| this.context.setNextNode(next); |
| } |
| else { |
| next = next.getNextSibling(); |
| this.context.setNextNode(next); |
| } |
| } |
| |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl element = (ElementImpl) parent; |
| if (!element.hasEndTag() && element.hasStartTag() && element.getNextSibling() == null) { |
| String tagName = element.getTagName(); |
| ElementImpl end = (ElementImpl) this.context.findEndTag(tagName); |
| if (end != null) { |
| // move the end tag from 'end' to 'element' |
| element.addEndTag(end); |
| removeEndTag(end); |
| this.context.setParentNode(parent); // reset context |
| continue; |
| } |
| } |
| } |
| |
| next = parent.getNextSibling(); |
| parent = parent.getParentNode(); |
| if (next != null) { |
| this.context.setNextNode(next); |
| } |
| else { |
| this.context.setParentNode(parent); |
| } |
| } |
| } |
| |
| /** |
| */ |
| private void cleanupText() { |
| Node parent = this.context.getParentNode(); |
| if (parent == null) |
| return; // error |
| Node next = this.context.getNextNode(); |
| Node prev = (next == null ? parent.getLastChild() : next.getPreviousSibling()); |
| |
| TextImpl nextText = null; |
| TextImpl prevText = null; |
| if (next != null && next.getNodeType() == Node.TEXT_NODE) { |
| nextText = (TextImpl) next; |
| } |
| if (prev != null && prev.getNodeType() == Node.TEXT_NODE) { |
| prevText = (TextImpl) prev; |
| } |
| if (nextText == null && prevText == null) |
| return; |
| if (nextText != null && prevText != null) { |
| // consecutive Text nodes created by setupContext(), |
| // concat them |
| IStructuredDocumentRegion flatNode = nextText.getStructuredDocumentRegion(); |
| if (flatNode != null) |
| prevText.appendStructuredDocumentRegion(flatNode); |
| Node newNext = next.getNextSibling(); |
| parent.removeChild(next); |
| next = null; |
| this.context.setNextNode(newNext); |
| } |
| |
| TextImpl childText = (prevText != null ? prevText : nextText); |
| if (childText.getNextSibling() == null && childText.getPreviousSibling() == null) { |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl parentElement = (ElementImpl) parent; |
| if (!parentElement.hasStartTag() && !parentElement.hasEndTag()) { |
| if (childText.isWhitespace() || childText.isInvalid()) { |
| // implicit parent is not required |
| Node newParent = parent.getParentNode(); |
| if (newParent != null) { |
| Node newNext = parent.getNextSibling(); |
| newParent.removeChild(parent); |
| parent.removeChild(childText); |
| newParent.insertBefore(childText, newNext); |
| if (childText == next) { |
| this.context.setNextNode(childText); |
| } |
| else if (newNext != null) { |
| this.context.setNextNode(newNext); |
| } |
| else { |
| this.context.setParentNode(newParent); |
| } |
| // try again |
| cleanupText(); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * This routine create an Element from comment data for comment style |
| * elements, such as SSI and METADATA |
| */ |
| protected Element createCommentElement(String data, boolean isJSPTag) { |
| String trimmedData = data.trim(); |
| CommentElementConfiguration[] configs = CommentElementRegistry.getInstance().getConfigurations(); |
| for (int iConfig = 0; iConfig < configs.length; iConfig++) { |
| CommentElementConfiguration config = configs[iConfig]; |
| if ((isJSPTag && !config.acceptJSPComment()) || (!isJSPTag && !config.acceptXMLComment())) { |
| continue; |
| } |
| String[] prefixes = config.getPrefix(); |
| for (int iPrefix = 0; iPrefix < prefixes.length; iPrefix++) { |
| if (trimmedData.startsWith(prefixes[iPrefix])) { |
| return config.createElement(this.model.getDocument(), data, isJSPTag); |
| } |
| } |
| } |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.createCommentElement(this.model.getDocument(), data, isJSPTag); |
| } |
| return null; |
| } |
| |
| /** |
| * This routine create an implicit Element for given parent and child, |
| * such as HTML, BODY, HEAD, and TBODY for HTML document. |
| */ |
| protected Element createImplicitElement(Node parent, Node child) { |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.createImplicitElement(this.model.getDocument(), parent, child); |
| } |
| return null; |
| } |
| |
| /** |
| */ |
| private void demoteNodes(Node root, Node newParent, Node oldParent, Node next) { |
| if (newParent.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| ElementImpl newElement = (ElementImpl) newParent; |
| |
| // find next |
| while (next == null) { |
| if (oldParent.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| ElementImpl oldElement = (ElementImpl) oldParent; |
| if (oldElement.hasEndTag()) |
| return; |
| oldParent = oldElement.getParentNode(); |
| if (oldParent == null) |
| return; // error |
| next = oldElement.getNextSibling(); |
| } |
| |
| while (next != null) { |
| boolean done = false; |
| if (next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl nextElement = (ElementImpl) next; |
| if (!nextElement.hasStartTag()) { |
| Node nextChild = nextElement.getFirstChild(); |
| if (nextChild != null) { |
| // demote children |
| next = nextChild; |
| oldParent = nextElement; |
| continue; |
| } |
| |
| if (nextElement.hasEndTag()) { |
| if (nextElement.matchEndTag(newElement)) { |
| // stop at the matched invalid end tag |
| next = nextElement.getNextSibling(); |
| oldParent.removeChild(nextElement); |
| newElement.addEndTag(nextElement); |
| |
| if (newElement == root) |
| return; |
| Node p = newElement.getParentNode(); |
| // check if reached to top |
| if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| newElement = (ElementImpl) p; |
| done = true; |
| } |
| } |
| else { |
| // remove implicit element |
| next = nextElement.getNextSibling(); |
| oldParent.removeChild(nextElement); |
| done = true; |
| } |
| } |
| } |
| |
| if (!done) { |
| if (!canContain(newElement, next)) { |
| if (newElement == root) |
| return; |
| Node p = newElement.getParentNode(); |
| // check if reached to top |
| if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| newElement = (ElementImpl) p; |
| continue; |
| } |
| |
| Node child = next; |
| next = next.getNextSibling(); |
| oldParent.removeChild(child); |
| insertNode(newElement, child, null); |
| Node childParent = child.getParentNode(); |
| if (childParent != newElement) { |
| newElement = (ElementImpl) childParent; |
| } |
| } |
| |
| // find next parent and sibling |
| while (next == null) { |
| if (oldParent.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| ElementImpl oldElement = (ElementImpl) oldParent; |
| |
| // dug parent must not have children at this point |
| if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) { |
| oldParent = oldElement.getParentNode(); |
| if (oldParent == null) |
| return; // error |
| next = oldElement; |
| break; |
| } |
| |
| if (oldElement.hasEndTag()) |
| return; |
| oldParent = oldElement.getParentNode(); |
| if (oldParent == null) |
| return; // error |
| next = oldElement.getNextSibling(); |
| } |
| } |
| } |
| |
| private ModelParserAdapter getParserAdapter() { |
| return (ModelParserAdapter) this.model.getDocument().getAdapterFor(ModelParserAdapter.class); |
| } |
| |
| /** |
| */ |
| protected String getFindRootName(String tagName) { |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.getFindRootName(tagName); |
| } |
| return null; |
| } |
| |
| /** |
| */ |
| protected final IDOMModel getModel() { |
| return this.model; |
| } |
| |
| /** |
| * insertCDATASection method |
| * |
| */ |
| private void insertCDATASection(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| CDATASectionImpl cdata = null; |
| try { |
| cdata = (CDATASectionImpl) this.model.getDocument().createCDATASection(null); |
| } |
| catch (DOMException ex) { |
| } |
| if (cdata == null) { // CDATA section might not be supported |
| insertInvalidDecl(flatNode); // regard as invalid decl |
| return; |
| } |
| |
| cdata.setStructuredDocumentRegion(flatNode); |
| insertNode(cdata); |
| } |
| |
| /** |
| * insertComment method |
| * |
| */ |
| private void insertComment(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String data = null; |
| boolean isJSPTag = false; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (isNestedCommentOpen(regionType)) { |
| isJSPTag = true; |
| } |
| else if (regionType == DOMRegionContext.XML_COMMENT_TEXT || isNestedCommentText(regionType)) { |
| if (data == null) { |
| data = flatNode.getText(region); |
| } |
| } |
| } |
| |
| if (data != null) { |
| ElementImpl element = (ElementImpl) createCommentElement(data, isJSPTag); |
| if (element != null) { |
| if (!isEndTag(element)) { |
| element.setStartStructuredDocumentRegion(flatNode); |
| insertStartTag(element); |
| return; |
| } |
| |
| // end tag |
| element.setEndStructuredDocumentRegion(flatNode); |
| |
| String tagName = element.getTagName(); |
| String rootName = getFindRootName(tagName); |
| ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); |
| if (start != null) { // start tag found |
| insertEndTag(start); |
| start.addEndTag(element); |
| return; |
| } |
| |
| // invalid end tag |
| insertNode(element); |
| return; |
| } |
| } |
| |
| CommentImpl comment = (CommentImpl) this.model.getDocument().createComment(null); |
| if (comment == null) |
| return; |
| if (isJSPTag) |
| comment.setJSPTag(true); |
| comment.setStructuredDocumentRegion(flatNode); |
| insertNode(comment); |
| } |
| |
| /** |
| * insertDecl method |
| * |
| */ |
| private void insertDecl(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| boolean isDocType = false; |
| String name = null; |
| String publicId = null; |
| String systemId = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_DOCTYPE_DECLARATION) { |
| isDocType = true; |
| } |
| else if (regionType == DOMRegionContext.XML_DOCTYPE_NAME) { |
| if (name == null) |
| name = flatNode.getText(region); |
| } |
| else if (regionType == DOMRegionContext.XML_DOCTYPE_EXTERNAL_ID_PUBREF) { |
| if (publicId == null) |
| publicId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region); |
| } |
| else if (regionType == DOMRegionContext.XML_DOCTYPE_EXTERNAL_ID_SYSREF) { |
| if (systemId == null) |
| systemId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region); |
| } |
| } |
| |
| // invalid declaration |
| if (!isDocType) { |
| insertInvalidDecl(flatNode); |
| return; |
| } |
| |
| DocumentTypeImpl docType = (DocumentTypeImpl) this.model.getDocument().createDoctype(name); |
| if (docType == null) |
| return; |
| if (publicId != null) |
| docType.setPublicId(publicId); |
| if (systemId != null) |
| docType.setSystemId(systemId); |
| docType.setStructuredDocumentRegion(flatNode); |
| insertNode(docType); |
| } |
| |
| /** |
| * insertEndTag method can be used by subclasses, but not overrided. |
| * |
| * @param element |
| * org.w3c.dom.Element |
| */ |
| protected void insertEndTag(Element element) { |
| if (element == null) |
| return; |
| |
| Node newParent = element.getParentNode(); |
| if (newParent == null) |
| return; // error |
| |
| if (!((ElementImpl) element).isContainer()) { |
| // just update context |
| Node elementNext = element.getNextSibling(); |
| if (elementNext != null) |
| this.context.setNextNode(elementNext); |
| else |
| this.context.setParentNode(newParent); |
| return; |
| } |
| |
| // promote children |
| Node newNext = element.getNextSibling(); |
| Node oldParent = this.context.getParentNode(); |
| if (oldParent == null) |
| return; // error |
| Node oldNext = this.context.getNextNode(); |
| promoteNodes(element, newParent, newNext, oldParent, oldNext); |
| |
| // update context |
| // re-check the next sibling |
| newNext = element.getNextSibling(); |
| if (newNext != null) |
| this.context.setNextNode(newNext); |
| else |
| this.context.setParentNode(newParent); |
| } |
| |
| /** |
| * insertEndTag method |
| * |
| */ |
| private void insertEndTag(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String tagName = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) { |
| if (tagName == null) |
| tagName = flatNode.getText(region); |
| } |
| } |
| |
| if (tagName == null) { // invalid end tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| |
| String rootName = getFindRootName(tagName); |
| ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); |
| if (start != null) { // start tag found |
| insertEndTag(start); |
| start.setEndStructuredDocumentRegion(flatNode); |
| return; |
| } |
| |
| // invalid end tag |
| ElementImpl end = null; |
| try { |
| end = (ElementImpl) this.model.getDocument().createElement(tagName); |
| } |
| catch (DOMException ex) { |
| } |
| if (end == null) { // invalid end tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| end.setEndStructuredDocumentRegion(flatNode); |
| insertNode(end); |
| } |
| |
| /** |
| * insertEntityRef method |
| * |
| */ |
| private void insertEntityRef(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String name = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_ENTITY_REFERENCE || regionType == DOMRegionContext.XML_CHAR_REFERENCE) { |
| if (name == null) |
| name = StructuredDocumentRegionUtil.getEntityRefName(flatNode, region); |
| } |
| } |
| |
| if (name == null) { // invalid entity |
| insertText(flatNode); |
| return; |
| } |
| |
| // ISSUE: avoid this cast |
| String value = ((DocumentImpl)this.model.getDocument()).getCharValue(name); |
| if (value != null) { // character entity |
| TextImpl text = (TextImpl) this.context.findPreviousText(); |
| if (text != null) { // existing text found |
| // do not append data |
| text.appendStructuredDocumentRegion(flatNode); |
| // notify the change |
| text.notifyValueChanged(); |
| return; |
| } |
| |
| // new text |
| text = (TextImpl) this.model.getDocument().createTextNode(null); |
| if (text == null) |
| return; |
| text.setStructuredDocumentRegion(flatNode); |
| insertNode(text); |
| return; |
| } |
| |
| // general entity reference |
| EntityReferenceImpl ref = null; |
| try { |
| ref = (EntityReferenceImpl) this.model.getDocument().createEntityReference(name); |
| } |
| catch (DOMException ex) { |
| } |
| if (ref == null) { // entity reference might not be supported |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| |
| ref.setStructuredDocumentRegion(flatNode); |
| insertNode(ref); |
| } |
| |
| /** |
| * insertInvalidDecl method |
| * |
| */ |
| private void insertInvalidDecl(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| ElementImpl element = null; |
| try { |
| element = (ElementImpl) this.model.getDocument().createElement("!");//$NON-NLS-1$ |
| } |
| catch (DOMException ex) { |
| } |
| if (element == null) { // invalid tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| element.setEmptyTag(true); |
| element.setStartStructuredDocumentRegion(flatNode); |
| insertNode(element); |
| } |
| |
| /** |
| * insertJSPTag method |
| * |
| */ |
| private void insertNestedTag(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String tagName = null; |
| AttrImpl attr = null; |
| Vector attrNodes = null; |
| boolean isCloseTag = false; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (isNestedTagOpen(regionType) || isNestedTagName(regionType)) { |
| tagName = computeNestedTag(regionType, tagName, flatNode, region); |
| } |
| else if (isNestedTagClose(regionType)) { |
| isCloseTag = true; |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| String name = flatNode.getText(region); |
| attr = (AttrImpl) this.model.getDocument().createAttribute(name); |
| if (attr != null) { |
| attr.setNameRegion(region); |
| if (attrNodes == null) |
| attrNodes = new Vector(); |
| attrNodes.addElement(attr); |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| if (attr != null) { |
| attr.setEqualRegion(region); |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| if (attr != null) { |
| attr.setValueRegion(region); |
| attr = null; |
| } |
| } |
| } |
| |
| if (tagName == null) { |
| if (isCloseTag) { |
| // close JSP tag |
| Node parent = this.context.getParentNode(); |
| if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl start = (ElementImpl) parent; |
| if (start.isJSPContainer()) { |
| insertEndTag(start); |
| start.setEndStructuredDocumentRegion(flatNode); |
| return; |
| } |
| } |
| } |
| // invalid JSP tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| |
| ElementImpl element = null; |
| try { |
| element = (ElementImpl) this.model.getDocument().createElement(tagName); |
| } |
| catch (DOMException ex) { |
| } |
| if (element == null) { // invalid tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| if (attrNodes != null) { |
| Enumeration ae = attrNodes.elements(); |
| while (ae.hasMoreElements()) { |
| Attr a = (Attr) ae.nextElement(); |
| if (a == null) |
| continue; |
| element.appendAttributeNode(a); |
| } |
| } |
| element.setJSPTag(true); |
| element.setStartStructuredDocumentRegion(flatNode); |
| insertStartTag(element); |
| } |
| |
| protected boolean isNestedTagClose(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| protected boolean isNestedTagOpen(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| protected String computeNestedTag(String regionType, String tagName, IStructuredDocumentRegion structuredDocumentRegion, ITextRegion region) { |
| return tagName; |
| } |
| |
| /** |
| * insertNode method |
| * |
| * @param child |
| * org.w3c.dom.Node |
| */ |
| private void insertNode(Node node) { |
| if (node == null) |
| return; |
| if (this.context == null) |
| return; |
| |
| Node parent = this.context.getParentNode(); |
| if (parent == null) |
| return; |
| Node next = this.context.getNextNode(); |
| while (parent.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl element = (ElementImpl) parent; |
| if (canContain(element, node)) { |
| if (!element.hasStartTag() && next == element.getFirstChild()) { |
| // first child of implicit tag |
| // deletege to the parent |
| parent = element.getParentNode(); |
| if (parent == null) |
| return; |
| next = element; |
| this.context.setNextNode(next); |
| continue; |
| } |
| break; |
| } |
| parent = element.getParentNode(); |
| if (parent == null) |
| return; |
| |
| // promote siblings |
| Node newNext = element.getNextSibling(); |
| Node child = next; |
| while (child != null) { |
| Node nextChild = child.getNextSibling(); |
| element.removeChild(child); |
| parent.insertBefore(child, newNext); |
| child = nextChild; |
| } |
| |
| // leave the old end tag where it is |
| if (element.hasEndTag()) { |
| Element end = element.removeEndTag(); |
| if (end != null) { |
| parent.insertBefore(end, newNext); |
| if (next == null) |
| next = end; |
| } |
| } |
| if (!element.hasStartTag()) { |
| // implicit element |
| if (!element.hasChildNodes()) { |
| parent.removeChild(element); |
| } |
| } |
| |
| // update context |
| if (next == null) |
| next = newNext; |
| if (next != null) |
| this.context.setNextNode(next); |
| else |
| this.context.setParentNode(parent); |
| } |
| |
| insertNode(parent, node, next); |
| next = node.getNextSibling(); |
| if (next != null) |
| this.context.setNextNode(next); |
| else |
| this.context.setParentNode(node.getParentNode()); |
| } |
| |
| /** |
| */ |
| private void insertNode(Node parent, Node node, Node next) { |
| while (next != null && next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl nextElement = (ElementImpl) next; |
| if (nextElement.hasStartTag()) |
| break; |
| if (!canBeImplicitTag(nextElement, node)) |
| break; |
| parent = nextElement; |
| next = nextElement.getFirstChild(); |
| } |
| Element implicitElement = createImplicitElement(parent, node); |
| if (implicitElement != null) |
| node = implicitElement; |
| parent.insertBefore(node, next); |
| } |
| |
| /** |
| * insertPI method |
| * |
| */ |
| private void insertPI(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String target = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_PI_OPEN || regionType == DOMRegionContext.XML_PI_CLOSE) |
| continue; |
| if (target == null) |
| target = flatNode.getText(region); |
| } |
| |
| ProcessingInstructionImpl pi = (ProcessingInstructionImpl) this.model.getDocument().createProcessingInstruction(target, null); |
| if (pi == null) |
| return; |
| pi.setStructuredDocumentRegion(flatNode); |
| insertNode(pi); |
| } |
| |
| /** |
| * insertStartTag method can be used by subclasses, but not overridden. |
| * |
| * @param element |
| * org.w3c.dom.Element |
| */ |
| protected void insertStartTag(Element element) { |
| if (element == null) |
| return; |
| if (this.context == null) |
| return; |
| |
| insertNode(element); |
| |
| ElementImpl newElement = (ElementImpl) element; |
| if (newElement.isEmptyTag() || !newElement.isContainer()) |
| return; |
| |
| // demote siblings |
| Node parent = this.context.getParentNode(); |
| if (parent == null) |
| return; // error |
| Node next = this.context.getNextNode(); |
| demoteNodes(element, element, parent, next); |
| |
| // update context |
| Node firstChild = element.getFirstChild(); |
| if (firstChild != null) |
| this.context.setNextNode(firstChild); |
| else |
| this.context.setParentNode(element); |
| } |
| |
| /** |
| * insertStartTag method |
| * |
| */ |
| private void insertStartTag(IStructuredDocumentRegion flatNode) { |
| ITextRegionList regions = flatNode.getRegions(); |
| if (regions == null) |
| return; |
| |
| String tagName = null; |
| boolean isEmptyTag = false; |
| AttrImpl attr = null; |
| Vector attrNodes = null; |
| Iterator e = regions.iterator(); |
| while (e.hasNext()) { |
| ITextRegion region = (ITextRegion) e.next(); |
| String regionType = region.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTagName(regionType)) { |
| if (tagName == null) |
| tagName = flatNode.getText(region); |
| } |
| else if (regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE) { |
| isEmptyTag = true; |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| String name = flatNode.getText(region); |
| attr = (AttrImpl) this.model.getDocument().createAttribute(name); |
| if (attr != null) { |
| attr.setNameRegion(region); |
| if (attrNodes == null) |
| attrNodes = new Vector(); |
| attrNodes.addElement(attr); |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| if (attr != null) { |
| attr.setEqualRegion(region); |
| } |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| if (attr != null) { |
| attr.setValueRegion(region); |
| attr = null; |
| } |
| } |
| } |
| |
| if (tagName == null) { // invalid start tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| |
| ElementImpl element = null; |
| try { |
| element = (ElementImpl) this.model.getDocument().createElement(tagName); |
| } |
| catch (DOMException ex) { |
| // typically invalid name |
| } |
| if (element == null) { // invalid tag |
| insertText(flatNode); // regard as invalid text |
| return; |
| } |
| if (attrNodes != null) { |
| Enumeration ae = attrNodes.elements(); |
| while (ae.hasMoreElements()) { |
| Attr a = (Attr) ae.nextElement(); |
| if (a == null) |
| continue; |
| element.appendAttributeNode(a); |
| } |
| } |
| if (isEmptyTag) |
| element.setEmptyTag(true); |
| element.setStartStructuredDocumentRegion(flatNode); |
| insertStartTag(element); |
| } |
| |
| /** |
| * insertStructuredDocumentRegion method |
| * |
| */ |
| protected void insertStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { |
| String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode); |
| if (regionType == DOMRegionContext.XML_TAG_OPEN) { |
| insertStartTag(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) { |
| insertEndTag(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_COMMENT_OPEN || isNestedCommentOpen(regionType)) { |
| insertComment(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_ENTITY_REFERENCE || regionType == DOMRegionContext.XML_CHAR_REFERENCE) { |
| insertEntityRef(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_DECLARATION_OPEN) { |
| insertDecl(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_PI_OPEN) { |
| insertPI(flatNode); |
| } |
| else if (regionType == DOMRegionContext.XML_CDATA_OPEN) { |
| insertCDATASection(flatNode); |
| } |
| else if (isNestedTag(regionType)) { |
| insertNestedTag(flatNode); |
| } |
| else { |
| insertText(flatNode); |
| } |
| } |
| |
| protected boolean isNestedTag(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| protected boolean isNestedCommentText(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| |
| protected boolean isNestedCommentOpen(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| protected boolean isNestedTagName(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| protected boolean isNestedContent(String regionType) { |
| boolean result = false; |
| return result; |
| } |
| |
| /** |
| * insertText method Can be called from subclasses, not to be overrided or |
| * re-implemented. |
| * |
| */ |
| protected void insertText(IStructuredDocumentRegion flatNode) { |
| TextImpl text = (TextImpl) this.context.findPreviousText(); |
| if (text != null) { // existing text found |
| text.appendStructuredDocumentRegion(flatNode); |
| // notify the change |
| text.notifyValueChanged(); |
| return; |
| } |
| |
| // new text |
| text = (TextImpl) this.model.getDocument().createTextNode(null); |
| if (text == null) |
| return; |
| text.setStructuredDocumentRegion(flatNode); |
| insertNode(text); |
| } |
| |
| /** |
| */ |
| protected boolean isEndTag(IDOMElement element) { |
| ModelParserAdapter adapter = getParserAdapter(); |
| if (adapter != null) { |
| return adapter.isEndTag(element); |
| } |
| return element.isEndTag(); |
| } |
| |
| /** |
| */ |
| private void promoteNodes(Node root, Node newParent, Node newNext, Node oldParent, Node next) { |
| ElementImpl newElement = null; |
| if (newParent.getNodeType() == Node.ELEMENT_NODE) { |
| newElement = (ElementImpl) newParent; |
| } |
| |
| Node rootParent = root.getParentNode(); |
| while (oldParent != rootParent) { |
| while (next != null) { |
| boolean done = false; |
| boolean endTag = false; |
| if (next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl nextElement = (ElementImpl) next; |
| if (!nextElement.hasStartTag()) { |
| Node nextChild = nextElement.getFirstChild(); |
| if (nextChild != null) { |
| // promote children |
| next = nextChild; |
| oldParent = nextElement; |
| continue; |
| } |
| |
| if (nextElement.hasEndTag()) { |
| if (nextElement.matchEndTag(newElement)) { |
| endTag = true; |
| } |
| } |
| else { |
| // remove implicit element |
| next = nextElement.getNextSibling(); |
| oldParent.removeChild(nextElement); |
| done = true; |
| } |
| } |
| } |
| |
| if (!done) { |
| if (!endTag && newElement != null && !canContain(newElement, next)) { |
| newParent = newElement.getParentNode(); |
| if (newParent == null) |
| return; // error |
| Node elementNext = newElement.getNextSibling(); |
| // promote siblings |
| promoteNodes(newElement, newParent, elementNext, newElement, newNext); |
| newNext = newElement.getNextSibling(); |
| if (newParent.getNodeType() == Node.ELEMENT_NODE) { |
| newElement = (ElementImpl) newParent; |
| } |
| else { |
| newElement = null; |
| } |
| continue; |
| } |
| |
| Node child = next; |
| next = next.getNextSibling(); |
| oldParent.removeChild(child); |
| insertNode(newParent, child, newNext); |
| Node childParent = child.getParentNode(); |
| if (childParent != newParent) { |
| newParent = childParent; |
| newElement = (ElementImpl) newParent; |
| newNext = child.getNextSibling(); |
| } |
| } |
| } |
| |
| if (oldParent.getNodeType() != Node.ELEMENT_NODE) |
| return; |
| ElementImpl oldElement = (ElementImpl) oldParent; |
| oldParent = oldElement.getParentNode(); |
| if (oldParent == null) |
| return; // error |
| next = oldElement.getNextSibling(); |
| |
| if (oldElement.hasEndTag()) { |
| Element end = null; |
| if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) { |
| oldParent.removeChild(oldElement); |
| end = oldElement; |
| } |
| else { |
| end = oldElement.removeEndTag(); |
| } |
| if (end != null) { |
| insertNode(newParent, end, newNext); |
| Node endParent = end.getParentNode(); |
| if (endParent != newParent) { |
| newParent = endParent; |
| newElement = (ElementImpl) newParent; |
| newNext = end.getNextSibling(); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * removeEndTag method |
| * |
| * @param element |
| * org.w3c.dom.Element |
| */ |
| private void removeEndTag(Element element) { |
| if (element == null) |
| return; |
| if (this.context == null) |
| return; |
| |
| Node parent = element.getParentNode(); |
| if (parent == null) |
| return; // error |
| |
| if (!((ElementImpl) element).isContainer()) { |
| // just update context |
| Node elementNext = element.getNextSibling(); |
| if (elementNext != null) |
| this.context.setNextNode(elementNext); |
| else |
| this.context.setParentNode(parent); |
| return; |
| } |
| |
| // demote siblings |
| Node next = element.getNextSibling(); |
| ElementImpl newElement = (ElementImpl) element; |
| // find new parent |
| for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) { |
| if (last.getNodeType() != Node.ELEMENT_NODE) |
| break; |
| ElementImpl lastElement = (ElementImpl) last; |
| if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) |
| break; |
| newElement = lastElement; |
| } |
| Node lastChild = newElement.getLastChild(); |
| demoteNodes(element, newElement, parent, next); |
| |
| // update context |
| Node newNext = null; |
| if (lastChild != null) |
| newNext = lastChild.getNextSibling(); |
| else |
| newNext = newElement.getFirstChild(); |
| if (newNext != null) |
| this.context.setNextNode(newNext); |
| else |
| this.context.setParentNode(newElement); |
| } |
| |
| /** |
| * Remove the specified node if it is no longer required implicit tag with |
| * remaining child nodes promoted. |
| */ |
| private Element removeImplicitElement(Node parent) { |
| if (parent == null) |
| return null; |
| if (parent.getNodeType() != Node.ELEMENT_NODE) |
| return null; |
| ElementImpl element = (ElementImpl) parent; |
| if (!element.isImplicitTag()) |
| return null; |
| if (canBeImplicitTag(element)) |
| return null; |
| |
| Node elementParent = element.getParentNode(); |
| if (elementParent == null) |
| return null; // error |
| Node firstChild = element.getFirstChild(); |
| Node child = firstChild; |
| Node elementNext = element.getNextSibling(); |
| while (child != null) { |
| Node nextChild = child.getNextSibling(); |
| element.removeChild(child); |
| elementParent.insertBefore(child, elementNext); |
| child = nextChild; |
| } |
| |
| // reset context |
| if (this.context.getParentNode() == element) { |
| Node oldNext = this.context.getNextNode(); |
| if (oldNext != null) { |
| this.context.setNextNode(oldNext); |
| } |
| else { |
| if (elementNext != null) { |
| this.context.setNextNode(elementNext); |
| } |
| else { |
| this.context.setParentNode(elementParent); |
| } |
| } |
| } |
| else if (this.context.getNextNode() == element) { |
| if (firstChild != null) { |
| this.context.setNextNode(firstChild); |
| } |
| else { |
| if (elementNext != null) { |
| this.context.setNextNode(elementNext); |
| } |
| else { |
| this.context.setParentNode(elementParent); |
| } |
| } |
| } |
| |
| removeNode(element); |
| return element; |
| } |
| |
| /** |
| * removeNode method |
| * |
| * @param node |
| * org.w3c.dom.Node |
| */ |
| private void removeNode(Node node) { |
| if (node == null) |
| return; |
| if (this.context == null) |
| return; |
| |
| Node parent = node.getParentNode(); |
| if (parent == null) |
| return; |
| Node next = node.getNextSibling(); |
| Node prev = node.getPreviousSibling(); |
| |
| // update context |
| Node oldParent = this.context.getParentNode(); |
| if (node == oldParent) { |
| if (next != null) |
| this.context.setNextNode(next); |
| else |
| this.context.setParentNode(parent); |
| } |
| else { |
| Node oldNext = this.context.getNextNode(); |
| if (node == oldNext) { |
| this.context.setNextNode(next); |
| } |
| } |
| |
| parent.removeChild(node); |
| |
| if (removeImplicitElement(parent) != null) |
| return; |
| |
| // demote sibling |
| if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl newElement = (ElementImpl) prev; |
| if (!newElement.hasEndTag() && !newElement.isEmptyTag() && newElement.isContainer()) { |
| // find new parent |
| for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) { |
| if (last.getNodeType() != Node.ELEMENT_NODE) |
| break; |
| ElementImpl lastElement = (ElementImpl) last; |
| if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) |
| break; |
| newElement = lastElement; |
| } |
| Node lastChild = newElement.getLastChild(); |
| demoteNodes(prev, newElement, parent, next); |
| |
| // update context |
| Node newNext = null; |
| if (lastChild != null) |
| newNext = lastChild.getNextSibling(); |
| else |
| newNext = newElement.getFirstChild(); |
| if (newNext != null) |
| this.context.setNextNode(newNext); |
| else |
| this.context.setParentNode(newElement); |
| } |
| } |
| } |
| |
| /** |
| * removeStartTag method |
| * |
| * @param element |
| * org.w3c.dom.Element |
| */ |
| private void removeStartTag(Element element) { |
| if (element == null) |
| return; |
| if (this.context == null) |
| return; |
| |
| // for implicit tag |
| ElementImpl oldElement = (ElementImpl) element; |
| if (canBeImplicitTag(oldElement)) { |
| Node newParent = null; |
| Node prev = oldElement.getPreviousSibling(); |
| if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl prevElement = (ElementImpl) prev; |
| if (!prevElement.hasEndTag()) { |
| if (prevElement.hasStartTag() || prevElement.matchTagName(oldElement.getTagName())) { |
| newParent = prevElement; |
| } |
| } |
| } |
| if (newParent == null) { |
| // this element should stay as implicit tag |
| // just remove all attributes |
| oldElement.removeStartTag(); |
| |
| // update context |
| Node child = oldElement.getFirstChild(); |
| if (child != null) { |
| this.context.setNextNode(child); |
| } |
| else if (oldElement.hasEndTag()) { |
| this.context.setParentNode(oldElement); |
| } |
| return; |
| } |
| } |
| // for comment tag |
| if (oldElement.isCommentTag()) |
| oldElement.removeStartTag(); |
| |
| // promote children |
| Node elementParent = element.getParentNode(); |
| Node parent = elementParent; |
| if (parent == null) |
| return; |
| Node first = element.getFirstChild(); |
| Node firstElement = null; // for the case first is removed as end |
| // tag |
| if (first != null) { |
| // find new parent for children |
| ElementImpl newElement = null; |
| for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) { |
| if (last.getNodeType() != Node.ELEMENT_NODE) |
| break; |
| ElementImpl lastElement = (ElementImpl) last; |
| if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) |
| break; |
| newElement = lastElement; |
| } |
| Node next = first; |
| if (newElement != null) { |
| while (next != null) { |
| if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl nextElement = (ElementImpl) next; |
| if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) { |
| // stop at the matched invalid end tag |
| Node elementChild = nextElement.getFirstChild(); |
| while (elementChild != null) { |
| Node nextChild = elementChild.getNextSibling(); |
| nextElement.removeChild(elementChild); |
| newElement.appendChild(elementChild); |
| elementChild = nextChild; |
| } |
| |
| next = nextElement.getNextSibling(); |
| element.removeChild(nextElement); |
| newElement.addEndTag(nextElement); |
| if (nextElement == first) |
| firstElement = newElement; |
| |
| Node newParent = newElement.getParentNode(); |
| if (newParent == parent) |
| break; |
| if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) |
| break; // error |
| newElement = (ElementImpl) newParent; |
| continue; |
| } |
| } |
| if (!canContain(newElement, next)) { |
| Node newParent = newElement.getParentNode(); |
| if (newParent == parent) |
| break; |
| if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) |
| break; // error |
| newElement = (ElementImpl) newParent; |
| continue; |
| } |
| Node child = next; |
| next = next.getNextSibling(); |
| element.removeChild(child); |
| newElement.appendChild(child); |
| } |
| newElement = null; |
| } |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| newElement = (ElementImpl) parent; |
| } |
| while (next != null) { |
| if (newElement == null || canContain(newElement, next)) { |
| Node child = next; |
| next = next.getNextSibling(); |
| element.removeChild(child); |
| parent.insertBefore(child, element); |
| continue; |
| } |
| |
| parent = newElement.getParentNode(); |
| if (parent == null) |
| return; |
| |
| // promote siblings |
| Node newNext = newElement.getNextSibling(); |
| Node child = element; |
| while (child != null) { |
| Node nextChild = child.getNextSibling(); |
| newElement.removeChild(child); |
| parent.insertBefore(child, newNext); |
| child = nextChild; |
| } |
| |
| // leave the old end tag where it is |
| if (newElement.hasEndTag()) { |
| Element end = newElement.removeEndTag(); |
| if (end != null) { |
| parent.insertBefore(end, newNext); |
| } |
| } |
| if (!newElement.hasStartTag()) { |
| // implicit element |
| if (!newElement.hasChildNodes()) { |
| parent.removeChild(newElement); |
| } |
| } |
| |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| newElement = (ElementImpl) parent; |
| } |
| else { |
| newElement = null; |
| } |
| } |
| } |
| |
| Node newNext = element; |
| Node startElement = null; // for the case element is removed as end |
| // tag |
| if (oldElement.hasEndTag()) { |
| // find new parent for invalid end tag and siblings |
| ElementImpl newElement = null; |
| for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) { |
| if (last.getNodeType() != Node.ELEMENT_NODE) |
| break; |
| ElementImpl lastElement = (ElementImpl) last; |
| if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) |
| break; |
| newElement = lastElement; |
| } |
| if (newElement != null) { |
| // demote invalid end tag and sibling |
| Node next = element; |
| while (next != null) { |
| if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) { |
| ElementImpl nextElement = (ElementImpl) next; |
| if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) { |
| // stop at the matched invalid end tag |
| Node elementChild = nextElement.getFirstChild(); |
| while (elementChild != null) { |
| Node nextChild = elementChild.getNextSibling(); |
| nextElement.removeChild(elementChild); |
| newElement.appendChild(elementChild); |
| elementChild = nextChild; |
| } |
| |
| next = nextElement.getNextSibling(); |
| parent.removeChild(nextElement); |
| newElement.addEndTag(nextElement); |
| if (nextElement == newNext) |
| startElement = newElement; |
| |
| Node newParent = newElement.getParentNode(); |
| if (newParent == parent) |
| break; |
| if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) |
| break; // error |
| newElement = (ElementImpl) newParent; |
| continue; |
| } |
| } |
| if (!canContain(newElement, next)) { |
| Node newParent = newElement.getParentNode(); |
| if (newParent == parent) |
| break; |
| if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) |
| break; // error |
| newElement = (ElementImpl) newParent; |
| continue; |
| } |
| Node child = next; |
| next = next.getNextSibling(); |
| parent.removeChild(child); |
| if (child == oldElement) { |
| if (!oldElement.isCommentTag()) { |
| // clone (re-create) end tag |
| Element end = oldElement.removeEndTag(); |
| if (end != null) { |
| child = end; |
| newNext = end; |
| } |
| } |
| } |
| newElement.appendChild(child); |
| } |
| } |
| else { |
| if (!oldElement.isCommentTag()) { |
| // clone (re-create) end tag |
| Element end = oldElement.removeEndTag(); |
| if (end != null) { |
| parent.insertBefore(end, oldElement); |
| parent.removeChild(oldElement); |
| newNext = end; |
| } |
| } |
| } |
| } |
| else { |
| newNext = oldElement.getNextSibling(); |
| parent.removeChild(oldElement); |
| } |
| |
| // update context |
| Node oldParent = this.context.getParentNode(); |
| Node oldNext = this.context.getNextNode(); |
| if (element == oldParent) { |
| if (oldNext != null) { |
| this.context.setNextNode(oldNext); // reset for new parent |
| } |
| else if (newNext != null) { |
| this.context.setNextNode(newNext); |
| } |
| else { |
| this.context.setParentNode(parent); |
| } |
| } |
| else if (element == oldNext) { |
| if (firstElement != null) { |
| this.context.setParentNode(firstElement); |
| } |
| else if (first != null) { |
| this.context.setNextNode(first); |
| } |
| else if (startElement != null) { |
| this.context.setParentNode(startElement); |
| } |
| else { |
| this.context.setNextNode(newNext); |
| } |
| } |
| |
| removeImplicitElement(elementParent); |
| } |
| |
| /** |
| * removeStructuredDocumentRegion method |
| * |
| */ |
| private void removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { |
| NodeImpl next = (NodeImpl) this.context.getNextNode(); |
| if (next != null) { |
| short nodeType = next.getNodeType(); |
| if (nodeType != Node.ELEMENT_NODE) { |
| IStructuredDocumentRegion flatNode = next.getStructuredDocumentRegion(); |
| if (flatNode == oldStructuredDocumentRegion) { |
| removeNode(next); |
| return; |
| } |
| if (nodeType != Node.TEXT_NODE) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| if (flatNode == null) { |
| // this is the case for empty Text |
| // remove and continue |
| removeNode(next); |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| TextImpl text = (TextImpl) next; |
| boolean isShared = text.isSharingStructuredDocumentRegion(oldStructuredDocumentRegion); |
| if (isShared) { |
| // make sure there is next Text node sharing this |
| TextImpl nextText = (TextImpl) this.context.findNextText(); |
| if (nextText == null || !nextText.hasStructuredDocumentRegion(oldStructuredDocumentRegion)) { |
| isShared = false; |
| } |
| } |
| oldStructuredDocumentRegion = text.removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| if (oldStructuredDocumentRegion == null) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| if (text.getStructuredDocumentRegion() == null) { |
| // this is the case partial IStructuredDocumentRegion is |
| // removed |
| removeNode(text); |
| } |
| else { |
| // notify the change |
| text.notifyValueChanged(); |
| } |
| // if shared, continue to remove IStructuredDocumentRegion |
| // from them |
| if (isShared) |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| |
| ElementImpl element = (ElementImpl) next; |
| if (element.hasStartTag()) { |
| IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion(); |
| if (flatNode != oldStructuredDocumentRegion) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| if (element.hasEndTag() || element.hasChildNodes()) { |
| element.setStartStructuredDocumentRegion(null); |
| removeStartTag(element); |
| } |
| else { |
| removeNode(element); |
| } |
| } |
| else { |
| Node child = element.getFirstChild(); |
| if (child != null) { |
| this.context.setNextNode(child); |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| |
| if (!element.hasEndTag()) { |
| // implicit element |
| removeNode(element); |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| |
| IStructuredDocumentRegion flatNode = element.getEndStructuredDocumentRegion(); |
| if (flatNode != oldStructuredDocumentRegion) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| removeNode(element); |
| } |
| return; |
| } |
| |
| Node parent = this.context.getParentNode(); |
| if (parent == null || parent.getNodeType() != Node.ELEMENT_NODE) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| |
| ElementImpl end = (ElementImpl) parent; |
| if (end.hasEndTag()) { |
| IStructuredDocumentRegion flatNode = end.getEndStructuredDocumentRegion(); |
| if (flatNode != oldStructuredDocumentRegion) { |
| throw new StructuredDocumentRegionManagementException(); |
| } |
| if (!end.hasStartTag() && !end.hasChildNodes()) { |
| this.context.setNextNode(end); |
| removeNode(end); |
| } |
| else { |
| end.setEndStructuredDocumentRegion(null); |
| removeEndTag(end); |
| } |
| return; |
| } |
| |
| next = (NodeImpl) end.getNextSibling(); |
| if (next != null) { |
| this.context.setNextNode(next); |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| |
| parent = (NodeImpl) end.getParentNode(); |
| if (parent != null) { |
| this.context.setParentNode(parent); |
| removeStructuredDocumentRegion(oldStructuredDocumentRegion); |
| return; |
| } |
| } |
| |
| /** |
| * replaceRegions method |
| * |
| * @param newRegions |
| * java.util.Vector |
| * @param oldRegions |
| * java.util.Vector |
| */ |
| void replaceRegions(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { |
| if (flatNode == null) |
| return; |
| if (this.model.getDocument() == null) |
| return; |
| this.context = new XMLModelContext(this.model.getDocument()); |
| |
| // optimize typical cases |
| String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode); |
| if (regionType == DOMRegionContext.XML_TAG_OPEN) { |
| changeStartTag(flatNode, newRegions, oldRegions); |
| } |
| else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) { |
| changeEndTag(flatNode, newRegions, oldRegions); |
| } |
| else { |
| changeStructuredDocumentRegion(flatNode); |
| } |
| } |
| |
| /** |
| * replaceStructuredDocumentRegions method |
| * |
| */ |
| void replaceStructuredDocumentRegions(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) { |
| if (this.model.getDocument() == null) |
| return; |
| this.context = new XMLModelContext(this.model.getDocument()); |
| |
| int newCount = (newStructuredDocumentRegions != null ? newStructuredDocumentRegions.getLength() : 0); |
| int oldCount = (oldStructuredDocumentRegions != null ? oldStructuredDocumentRegions.getLength() : 0); |
| |
| if (oldCount > 0) { |
| setupContext(oldStructuredDocumentRegions.item(0)); |
| // Node startParent = this.context.getParentNode(); |
| |
| Enumeration e = oldStructuredDocumentRegions.elements(); |
| while (e.hasMoreElements()) { |
| IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); |
| if (flatNode == null) |
| continue; |
| removeStructuredDocumentRegion(flatNode); |
| } |
| } |
| else { |
| if (newCount == 0) |
| return; |
| setupContext(newStructuredDocumentRegions.item(0)); |
| } |
| // make sure the parent is set to deepest level |
| // when end tag has been removed |
| this.context.setLast(); |
| |
| if (newCount > 0) { |
| Enumeration e = newStructuredDocumentRegions.elements(); |
| while (e.hasMoreElements()) { |
| IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); |
| if (flatNode == null) |
| continue; |
| insertStructuredDocumentRegion(flatNode); |
| } |
| } |
| |
| cleanupText(); |
| cleanupEndTag(); |
| } |
| |
| /** |
| * setupContext method |
| * |
| */ |
| private void setupContext(IStructuredDocumentRegion startStructuredDocumentRegion) { |
| int offset = startStructuredDocumentRegion.getStart(); |
| if (offset < 0) |
| return; |
| NodeImpl root = (NodeImpl) this.context.getRootNode(); |
| if (root == null) |
| return; |
| |
| if (offset == 0) { |
| // at the beggining of document |
| Node child = root.getFirstChild(); |
| if (child != null) |
| this.context.setNextNode(child); |
| else |
| this.context.setParentNode(root); |
| return; |
| } |
| |
| NodeImpl node = (NodeImpl) root.getNodeAt(offset); |
| if (node == null) { |
| // might be at the end of document |
| this.context.setParentNode(root); |
| this.context.setLast(); |
| return; |
| } |
| |
| if (offset == node.getStartOffset()) { |
| this.context.setNextNode(node); |
| return; |
| } |
| |
| if (node.getNodeType() == Node.TEXT_NODE) { |
| TextImpl text = (TextImpl) node; |
| Text nextText = text.splitText(startStructuredDocumentRegion); |
| // notify the change |
| text.notifyValueChanged(); |
| if (nextText == null) |
| return; // error |
| this.context.setNextNode(nextText); |
| return; |
| } |
| |
| for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (offset >= ((NodeImpl) child).getEndOffset()) |
| continue; |
| this.context.setNextNode(child); |
| return; |
| } |
| this.context.setParentNode(node); |
| this.context.setLast(); |
| } |
| |
| protected XMLModelContext getContext() { |
| return context; |
| } |
| |
| } |