001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Max Stepanov 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.platform.domsync.core; 021 022import java.text.MessageFormat; 023import java.util.Stack; 024import java.util.StringTokenizer; 025 026import org.w3c.dom.Attr; 027import org.w3c.dom.Element; 028import org.w3c.dom.NamedNodeMap; 029import org.w3c.dom.Node; 030import org.w3c.dom.NodeList; 031 032/** 033 * @author Max Stepanov 034 */ 035public class DOMUtil { 036 037 private DOMUtil() { 038 } 039 040 public static String computeNodeXPath(Node base, Node node) { 041 if (base.isSameNode(node)) { 042 return "/"; 043 } 044 short nodeType = node.getNodeType(); 045 String subpath; 046 if (nodeType == Node.ELEMENT_NODE || nodeType == Node.TEXT_NODE) { 047 String localName = node.getLocalName().toLowerCase(); 048 int pos = 0; 049 Node sibling = node.getPreviousSibling(); 050 while (sibling != null) { 051 if (sibling.getNodeType() == nodeType) { 052 if (localName.equals(sibling.getLocalName().toLowerCase())) { 053 ++pos; 054 } 055 } 056 sibling = sibling.getPreviousSibling(); 057 } 058 if (nodeType == Node.TEXT_NODE) { 059 localName = "text()"; 060 } 061 062 if (pos == 0) { 063 subpath = localName; 064 } else { 065 subpath = MessageFormat.format("{0}[{1}]", localName, new Integer(pos)); 066 } 067 } else { 068 System.err.println("Unsupported type " + nodeType); 069 subpath = "unknown"; 070 } 071 Node parent = node.getParentNode(); 072 if (base.isSameNode(parent)) { 073 return '/' + subpath; 074 } else { 075 return computeNodeXPath(base, parent) + '/' + subpath; 076 } 077 } 078 079 public static Node findNodeByXPath(Node base, String xpath) { 080 if ("/".equals(xpath)) { 081 return base; 082 } 083 Node node = base; 084 StringTokenizer tok = new StringTokenizer(xpath, "/"); 085 while (tok.hasMoreTokens()) { 086 String subpath = tok.nextToken(); 087 String localName; 088 int nodePos = 0; 089 int index = subpath.indexOf('['); 090 if (index > 0) { 091 localName = subpath.substring(0, index).toLowerCase(); 092 nodePos = Integer.parseInt(subpath.substring(index + 1, subpath.indexOf(']'))); 093 } else { 094 localName = subpath.toLowerCase(); 095 } 096 short nodeType = Node.ELEMENT_NODE; 097 if ("text()".equals(localName)) { 098 nodeType = Node.TEXT_NODE; 099 localName = ""; 100 } 101 node = node.getFirstChild(); 102 int pos = 0; 103 while (node != null) { 104 if (node.getNodeType() == nodeType && localName.equals(node.getLocalName().toLowerCase())) { 105 if (pos == nodePos) { 106 break; 107 } 108 ++pos; 109 } 110 node = node.getNextSibling(); 111 } 112 } 113 return node; 114 /* 115 * try { XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression 116 * expr = xpath.compile(path); return (Node) expr.evaluate(document, XPathConstants.NODE); } catch 117 * (XPathExpressionException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (DOMException 118 * e) { // TODO Auto-generated catch block e.printStackTrace(); } 119 */ 120 121 } 122 123 public static Node getNodeAtPosition(Node parent, int offset) { 124 if (offset < 0) { 125 return null; 126 } else if (offset == 0) { 127 return parent.getFirstChild(); 128 } 129 NodeList nodes = parent.getChildNodes(); 130 int count = nodes.getLength(); 131 if (offset < count) { 132 return nodes.item(offset); 133 } 134 return null; 135 } 136 137 public static int getNodePosition(Node node) { 138 int pos = -1; 139 while (node != null) { 140 ++pos; 141 node = node.getPreviousSibling(); 142 } 143 return pos; 144 } 145 146 public static String getElementOuterNoChildren(Element element) { 147 StringBuilder sb = new StringBuilder(); 148 String tagName = element.getTagName(); 149 sb.append('<').append(tagName); 150 NamedNodeMap attrs = element.getAttributes(); 151 int count = attrs.getLength(); 152 for (int i = 0; i < count; ++i) { 153 Attr attr = (Attr) attrs.item(i); 154 if (attr.getSpecified()) { 155 sb.append(' ').append(attr.getName()).append("=\"").append(attr.getValue()).append('"'); 156 } 157 } 158 if ("br".equals(tagName.toLowerCase())) { 159 sb.append("/>"); 160 } else { 161 sb.append("></").append(tagName).append('>'); 162 } 163 return sb.toString(); 164 } 165 166 public static String dumpTree(Node node) { 167 StringBuilder sb = new StringBuilder(); 168 Stack<Node> stack = new Stack<Node>(); 169 int level = 0; 170 while (node != null || !stack.isEmpty()) { 171 if (node == null) { 172 do { 173 node = stack.pop(); 174 --level; 175 } while (node == null && !stack.isEmpty()); 176 continue; 177 } 178 for (int i = 0; i < level; ++i) { 179 sb.append(' '); 180 } 181 sb.append(node.getNodeName()).append(" <").append(node.getNodeValue()).append(">\n"); 182 stack.push(node.getNextSibling()); 183 node = node.getFirstChild(); 184 ++level; 185 } 186 return sb.toString(); 187 } 188}