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}