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}