001/*
002 * (C) Copyright 2006-2008 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 *     Alexandre Russel
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.platform.annotations.gwt.client.util;
021
022import java.util.ArrayList;
023import java.util.List;
024
025import org.nuxeo.ecm.platform.annotations.gwt.client.AnnotationConstant;
026
027import com.allen_sauer.gwt.log.client.Log;
028import com.google.gwt.dom.client.DivElement;
029import com.google.gwt.dom.client.Document;
030import com.google.gwt.dom.client.Element;
031import com.google.gwt.dom.client.Node;
032import com.google.gwt.dom.client.NodeList;
033import com.google.gwt.dom.client.SpanElement;
034
035/**
036 * @author <a href="mailto:arussel@nuxeo.com">Alexandre Russel</a>
037 */
038public class XPathUtil {
039    public String getXPath(Node node) {
040        Log.debug("XPathUtil] node: " + node.getNodeName() + " parent node: " + node.getParentNode().getNodeName());
041        Document document = node.getOwnerDocument();
042        Node current = node;
043        StringBuilder xpath = new StringBuilder();
044        while (!current.equals(document)) {
045            int counter = 1;
046            String name = current.getNodeName();
047            while (current.getPreviousSibling() != null) {
048                if (current.getPreviousSibling().getNodeName().equalsIgnoreCase(name)
049                        && !isIgnored(current.getPreviousSibling())) {
050                    counter++;
051                }
052                current = current.getPreviousSibling();
053            }
054            xpath.insert(0, "/" + name.toLowerCase() + "[" + counter + "]");
055            current = current.getParentNode();
056        }
057        Log.debug("XPathUtil] xpath: " + xpath);
058        return xpath.toString();
059    }
060
061    public String getSelectionXPointer(Range range) {
062        int start = range.getStartOffset();
063        Node clickedNode = range.getStartContainer();
064        Node parentNode = clickedNode.getParentNode();
065
066        if (clickedNode.getNodeType() == Node.TEXT_NODE) {
067            TextGrabberVisitor processor = new TextGrabberVisitor();
068            Visitor visitor = new Visitor(processor);
069            visitor.process(parentNode, clickedNode);
070            start += processor.getText().length();
071        }
072        Log.debug("getSelectionXPointer; start: " + start);
073        return "#xpointer(string-range(" + getXPath(parentNode) + ",\"\"," + start + ","
074                + getShortLength(range.getSelectedText()) + "))";
075    }
076
077    public int getShortLength(String selectedText) {
078        for (String removed : new String[] { "\n", "\r" }) {
079            selectedText = selectedText.replace(removed, "");
080        }
081        return selectedText.length();
082    }
083
084    public List<Node> getNode(String xpath, Document document) {
085        List<Node> nodes = new ArrayList<Node>();
086        if (xpath.startsWith("//")) {
087            xpath = xpath.substring(2);
088            NodeList<Element> n = document.getElementsByTagName(xpath);
089            for (int x = 0; x < n.getLength(); x++) {
090                nodes.add(n.getItem(x));
091            }
092            return nodes;
093        }
094        Log.debug("XPathUtil#getNode -- xpath: " + xpath);
095        String[] paths = xpath.split("/");
096        Node result = document;
097        for (String path : paths) {
098            if ("".equals(path)) {
099                continue;
100            }
101            NodeList<Node> nodeList = result.getChildNodes();
102            String name = path.substring(0, path.indexOf("["));
103            int index = Integer.parseInt(path.substring(path.indexOf("[") + 1, path.indexOf("]")));
104            int counter = 1;
105            for (int x = 0; x < nodeList.getLength(); x++) {
106                Node node = nodeList.getItem(x);
107                if (node.getNodeName().equalsIgnoreCase(name)) {
108                    if (isIgnored(node)) {// considered as text node
109                        continue;
110                    }
111                    if (counter == index) {
112                        result = node;
113                        break;
114                    }
115                    counter++;
116                }
117            }
118
119        }
120        nodes.add(result);
121        Log.debug("XPathUtil#getNode -- end function: ");
122        return nodes;
123    }
124
125    private boolean isIgnored(Node node) {
126        int nodeType = node.getNodeType();
127        if (nodeType != Node.ELEMENT_NODE && nodeType != Node.TEXT_NODE) {
128            return true; // ignore non element and non text node
129        }
130        if (node.getNodeName().equalsIgnoreCase("span")) {
131            SpanElement spanElement = SpanElement.as(node).cast();
132            String name = spanElement.getClassName();
133            if (name == null) {
134                return false;
135            }
136            return name.contains(AnnotationConstant.IGNORED_ELEMENT);
137        } else if (node.getNodeName().equalsIgnoreCase("div")) {
138            DivElement divElement = DivElement.as(node).cast();
139            return divElement.getClassName().equals(AnnotationConstant.IGNORED_ELEMENT);
140        }
141        return false;
142    }
143
144    public boolean isChildOfIgnored(Node node) {
145        boolean ignored = false;
146        while (node != null && !ignored) {
147            ignored = isIgnored(node);
148            node = node.getParentNode();
149        }
150        return ignored;
151    }
152
153    public static String toIdableName(String xpath) {
154        xpath = "X" + xpath;
155        xpath = xpath.replace("/", "-");
156        xpath = xpath.replace("[", "_");
157        return xpath.replace("]", ":");
158    }
159
160    public static String fromIdableName(String xpath) {
161        xpath = xpath.substring(1);
162        xpath = xpath.replace("-", "/");
163        xpath = xpath.replace("_", "[");
164        return xpath.replace(":", "]");
165    }
166}