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