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}