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