blob: b0f3fd8afd79e073195d805ebc17dd07c1d091da [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. 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:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.dom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* @author mengbo
*/
public class DOMUtil {
private static Logger _logger = PDPlugin.getLogger(DOMUtil.class);
/**
* Get a list of ancester nodes starting from the Document till the node.
*
* @param node
* @return
*/
private static List getAncesters(Node node) {
List list = new ArrayList();
while (node != null) {
list.add(node);
if (node instanceof Document) {
break;
}
node = node.getParentNode();
}
if (node == null) {
// if part ==null, means we didn't find a DocumentEditPart,
// something must be wrong.
return null;
}
// reverse to make it starting from the docuemnteditpart node.
Collections.reverse(list);
list.add(null); // add an null terminator.
return list;
}
/**
* find the smallest common ancester of two edit part.
*
* @param node1
* @param node2
* @return
*/
public static Node findCommonAncester(Node node1, Node node2) {
List list1 = getAncesters(node1);
if (list1 == null) {
return null;
}
List list2 = getAncesters(node2);
if (list2 == null) {
return null;
}
if (list1.get(0) != list2.get(0)) {
return null;
}
Node common = (Node) list1.get(0);
for (int i = 1;; i++) {
Node p1 = (Node) list1.get(i);
Node p2 = (Node) list2.get(i);
if (p1 == null || p2 == null) {
return common;
}
if (p1 != p2) {
return common;
}
common = p1;
}
}
/**
* this method is almost same as <code>cloneNodeDeep()</code>. The
* difference is that this method will try to ignore all kinds of error.
*
* In SSE, if the document model enforce some kinds of validation, then the
* clone may fail. During some cases, we want to ignore the validation
* errors.
*
* @param destDoc
* @param sourceNode
* @return
*/
public static Node cloneNodeDeepIgnoreError(Document destDoc,
Node sourceNode) {
switch (sourceNode.getNodeType()) {
case Node.ELEMENT_NODE:
Element sourceEle = (Element) sourceNode;
Element resultEle = destDoc.createElement(sourceEle.getTagName());
NamedNodeMap attrs = sourceEle.getAttributes();
for (int i = 0, size = attrs.getLength(); i < size; i++) {
Attr a = (Attr) attrs.item(i);
try {
resultEle.setAttribute(a.getName(), a.getValue());
} catch (Exception ex) {
// ignore
_logger.info("Exception", ex);
}
}
NodeList children = sourceEle.getChildNodes();
for (int i = 0, size = children.getLength(); i < size; i++) {
Node n = children.item(i);
Node d = cloneNodeDeepIgnoreError(destDoc, n);
if (d != null) {
try {
resultEle.appendChild(d);
} catch (Exception ex) {
// ignore
_logger.info("Exception", ex);
}
}
}
return resultEle;
case Node.TEXT_NODE:
Text txt = destDoc.createTextNode(sourceNode.getNodeValue());
if (txt instanceof IDOMText && sourceNode instanceof IDOMText) {
try {
((IDOMText) txt).setSource(((IDOMText) sourceNode)
.getSource());
} catch (Exception ex) {
// ignore
}
}
return txt;
case Node.CDATA_SECTION_NODE:
return destDoc.createCDATASection(sourceNode.getNodeValue());
default:
return null; // not support.
}
}
public static Node cloneNodeDeep(Document destDoc, Node sourceNode) {
switch (sourceNode.getNodeType()) {
case Node.ELEMENT_NODE:
Element sourceEle = (Element) sourceNode;
Element resultEle = destDoc.createElement(sourceEle.getTagName());
NamedNodeMap attrs = sourceEle.getAttributes();
for (int i = 0, size = attrs.getLength(); i < size; i++) {
Attr a = (Attr) attrs.item(i);
resultEle.setAttribute(a.getName(), a.getValue());
}
NodeList children = sourceEle.getChildNodes();
for (int i = 0, size = children.getLength(); i < size; i++) {
Node n = children.item(i);
Node d = cloneNodeDeep(destDoc, n);
if (d != null) {
resultEle.appendChild(d);
}
}
return resultEle;
case Node.TEXT_NODE:
Text txt = destDoc.createTextNode(sourceNode.getNodeValue());
if (txt instanceof IDOMText && sourceNode instanceof IDOMText) {
try {
((IDOMText) txt).setSource(((IDOMText) sourceNode)
.getSource());
} catch (Exception ex) {
// ignore
_logger.info("Exception", ex);
}
}
return txt;
case Node.CDATA_SECTION_NODE:
return destDoc.createCDATASection(sourceNode.getNodeValue());
default:
return null; // not support.
}
}
/**
* check whether the ancester relationship exist for the two nodes.
*
* @param ancester
* @param child
* @return
*/
public static boolean isAncester(Node ancester, Node child) {
while (child != null) {
if (child == ancester) {
return true;
}
child = child.getParentNode();
}
return false;
}
/**
* insert the node at specified position.
*
* @param insertPosition
* @param insert
* @return null if fail, otherwise return the inserted node.
*/
public static Node insertNode(IDOMPosition domPosition, Node node) {
IDOMPosition position = DOMPositionHelper.splitText(domPosition);
if (position == null || position.getContainerNode() == null) {
return null;
}
if (position.getNextSiblingNode() == null) {
position.getContainerNode().appendChild(node);
} else {
position.getContainerNode().insertBefore(node,
position.getNextSiblingNode());
}
return node;
}
}