1 19 20 package org.netbeans.modules.xml.xdm.visitor; 21 22 import java.util.ArrayList ; 23 import java.util.Collections ; 24 import java.util.HashMap ; 25 import java.util.List ; 26 import java.util.Map ; 27 import java.util.StringTokenizer ; 28 import javax.xml.namespace.NamespaceContext ; 29 import javax.xml.xpath.XPath ; 30 import javax.xml.xpath.XPathConstants ; 31 import javax.xml.xpath.XPathExpressionException ; 32 import javax.xml.xpath.XPathFactory ; 33 import org.netbeans.modules.xml.xdm.nodes.Attribute; 34 import org.netbeans.modules.xml.xdm.nodes.Document; 35 import org.netbeans.modules.xml.xdm.nodes.Element; 36 import org.netbeans.modules.xml.xdm.nodes.Node; 37 import org.w3c.dom.NodeList ; 38 39 44 public class XPathFinder extends ChildVisitor { 45 46 51 public static String getXpath(Document root, Node target) { 52 if (!(target instanceof Element) && !(target instanceof Attribute)) { 53 throw new IllegalArgumentException ("Can only get XPath expression for Element or Attribute"); 54 } 55 List <Node> result = new PathFromRootVisitor().findPath(root, target); 56 StringBuffer sb = new StringBuffer (128); 57 Element parent = null; 58 for (int j=result.size()-1; j >= 0; j--) { 59 Node n = result.get(j); 60 boolean isElement = n instanceof Element; 61 boolean isAttribute = n instanceof Attribute; 62 if (! isElement && ! isAttribute) { 63 continue; 64 } 65 sb.append(SEP); 66 int index = isElement ? xpathIndexOf(parent, (Element) n) : -1; 67 String prefix = n.getPrefix(); 68 String namespace = n.lookupNamespaceURI(prefix); 69 if (isAttribute) { 70 sb.append(AT); 71 } 72 if (prefix != null) { 73 sb.append(prefix); 74 sb.append(':'); 75 } 76 sb.append(n.getLocalName()); 77 if (isElement && index > 0) { 78 sb.append(BRACKET0); 79 sb.append(String.valueOf(index)); 80 sb.append(BRACKET1); 81 } 82 if (isAttribute) { 83 break; 84 } 85 parent = (Element) n; 86 } 87 return sb.toString(); 88 } 89 90 96 public Node findNode(Document root, String xpath) { 97 List <Node> nodes = findNodes(root, xpath); 98 return nodes.size() > 0 ? nodes.get(0) : null; 99 } 100 101 106 public List <Node> findNodes(Document root, String xpathExpression) { 107 if (root == null || root.getDocumentElement() == null) { 108 return Collections.emptyList(); 109 } 110 111 init(root, xpathExpression); 112 if (! isReadyForEvaluation()) { 113 return new ArrayList <Node>(); 114 } 115 116 XPath xpath = XPathFactory.newInstance().newXPath(); 117 xpath.setNamespaceContext(getNamespaceContext()); 118 NodeList result = null; 119 try { 120 result = (NodeList ) xpath.evaluate(getFixedUpXpath(), root, XPathConstants.NODESET); 121 } catch (XPathExpressionException e) { 122 throw new RuntimeException (e.getCause().getLocalizedMessage()); 123 } 124 assert(result != null); 125 List <Node> ret = new ArrayList <Node>(); 126 for (int i=0; i<result.getLength(); i++) { 127 ret.add((Node) result.item(i)); 128 } 129 return ret; 130 } 131 132 private static int xpathIndexOf(Node parentNode, Element child) { 134 assert(child != null); 135 if (!(parentNode instanceof Element || parentNode instanceof Document)) { 136 return -1; 137 } 138 139 NodeList children = parentNode.getChildNodes(); 140 int index = 0; 141 String namespace = child.getNamespaceURI(); 142 String name = child.getLocalName(); 143 for (int i = 0; i<children.getLength(); i++) { 144 if (! (children.item(i) instanceof Element)) { 145 continue; 146 } 147 Element n = (Element) children.item(i); 148 if (namespace != null && ! namespace.equals(n.getNamespaceURI()) || 149 namespace == null && n.getNamespaceURI() != null) 150 { 151 continue; 152 } 153 154 if (name.equals(n.getLocalName())) { 155 index++; 156 } 157 158 if (n.getId() == child.getId()) { 159 return index; 160 } 161 } 162 163 return -1; 164 } 165 166 private void init(Document root, String xpath) { 167 currentParent = root; 168 initTokens(xpath); 169 ((Element)root.getDocumentElement()).accept(this); 170 } 171 172 private void initTokens(String xpath) { 173 tokens = new ArrayList <XPathSegment>(); 174 StringTokenizer tokenizer = new StringTokenizer (xpath, SEP); 175 while (tokenizer.hasMoreTokens()) { 176 String token = tokenizer.nextToken(); 177 tokens.add(new XPathSegment(token)); 178 } 179 } 180 181 private static class XPathSegment { 182 private final String token; 183 private String prefix; 184 private String localPart; 185 int index = -1; 186 private String remaining; 187 private boolean isAttribute; 188 private boolean hasConditions; 189 190 XPathSegment(String token) { 191 this.token = token; 192 parse(); 193 } 194 195 private void parse() { 196 StringTokenizer tokenizer = new StringTokenizer (token, ":["); 197 List <String > parts = new ArrayList <String >(); 198 while (tokenizer.hasMoreTokens()) { 199 String t = tokenizer.nextToken(); 200 parts.add(t); 201 } 202 if (parts.size() == 1) { 203 prefix = ""; 204 localPart = parts.get(0); 205 remaining = ""; 206 } else if (parts.size() == 3) { 207 prefix = parts.get(0); 208 localPart = parts.get(1); 209 remaining = parts.get(2); 210 } else if (parts.size() == 2) { 211 String part0 = parts.get(0); 212 if (token.charAt(part0.length()) == ':') { 213 prefix = part0; 214 localPart = parts.get(1); 215 remaining = ""; 216 } else { 217 localPart = parts.get(0); 218 remaining = parts.get(1); 219 prefix = ""; 220 } 221 } 222 if (prefix != null && prefix.startsWith(AT)) { 223 isAttribute = true; 224 prefix = prefix.substring(1); 225 } else if (localPart.startsWith(AT)) { 226 isAttribute = true; 227 localPart = localPart.substring(1); 228 } 229 if (! remaining.equals("")) { 230 String indexString = remaining.substring(0, remaining.length()-1); 231 try { 232 index = Integer.parseInt(indexString); 233 } catch (NumberFormatException ex) { 234 hasConditions = true; 235 } 236 remaining = BRACKET0 + remaining; 237 } 238 } 239 240 public boolean checkTypeAndName(Node e, Node parent) { 241 if (e instanceof Element) { 242 if (isAttribute()) return false; 243 if (hasPlainIndex() && getIndex() != xpathIndexOf(parent, (Element) e)) { 244 return false; 245 } 246 } else if (e instanceof Attribute) { 247 if (! isAttribute()) return false; 248 } else { 249 return false; 250 } 251 if (! e.getLocalName().equals(getLocalPart())){ 252 return false; 253 } 254 return true; 255 } 256 257 public void addToXpath(StringBuffer xpath, String prefix) { 258 xpath.append(SEP); 259 if (isAttribute()) { 260 xpath.append(AT); 261 } 262 xpath.append(prefix); 263 xpath.append(COLON); 264 xpath.append(localPart); 265 if (hasConditions() && !prefix.startsWith(XPNS)) { 266 String prefixing = AT + prefix + COLON; 267 xpath.append(remaining.replaceAll(AT, prefixing)); 269 } else { 270 xpath.append(remaining); 271 } 272 } 273 274 public void addToXpath(StringBuffer xpath) { 275 xpath.append(SEP); 276 xpath.append(token); 277 } 278 279 public String getPrefix() { return prefix; } 280 public String getLocalPart() { return localPart; } 281 public boolean hasPlainIndex() { return index > -1; } 282 public boolean hasConditions() { return hasConditions; } 283 public int getIndex() { return index; } 284 public String getRemaining() { return remaining; } 285 public String getToken() { return token; } 286 public boolean isAttribute() { return isAttribute; } 287 public String toString() { return token; } 288 } 289 290 protected void visitNode(Node e) { 291 if (tokens.size() == 0) { 292 done = true; 293 return; 294 } 295 296 XPathSegment segment = tokens.get(0); 297 if (! segment.checkTypeAndName(e, currentParent)) { 298 return; } 300 301 String currentNamespace = e.getNamespaceURI(); 302 if (currentNamespace != null) { 303 String prefix = segment.getPrefix(); 304 if (prefix.length() > 0) { 305 String namespace = e.lookupNamespaceURI(prefix); 306 if (namespace == null) { 307 namespace = namespaces.get(prefix); 308 } if (namespace != null && ! currentNamespace.equals(namespace)) { 309 return; } 311 segment.addToXpath(fixedUpXpath); 312 } else { 313 prefix = prefixes.get(currentNamespace); 315 if (prefix == null) { 316 prefix = XPNS + String.valueOf(countNamespaceSuffix++); 317 } 318 segment.addToXpath(fixedUpXpath, prefix); 319 } 320 prefixes.put(currentNamespace, prefix); 322 namespaces.put(prefix, currentNamespace); 323 } else { 324 segment.addToXpath(fixedUpXpath); 325 } 326 327 tokens.remove(0); 328 currentParent = e; 329 super.visitNode(e); 330 } 331 332 public boolean isReadyForEvaluation() { 333 return done; 334 } 335 public NamespaceContext getNamespaceContext() { 336 return new HashNamespaceResolver(namespaces, prefixes); 337 } 338 private String getFixedUpXpath() { 339 return fixedUpXpath.toString(); 340 } 341 342 private boolean done = false; 343 private Node currentParent = null; 344 private Map <String , String > namespaces = new HashMap <String ,String >(); 345 private Map <String , String > prefixes = new HashMap <String ,String >(); 346 private List <XPathSegment> tokens; 347 private StringBuffer fixedUpXpath = new StringBuffer (); 348 349 public static final String SEP = "/"; public static final String AT = "@"; public static final String COLON = ":"; public static final String BRACKET0 = "["; public static final String BRACKET1 = "]"; public static final String XPNS = "xpns"; private int countNamespaceSuffix = 0; 356 } 357 | Popular Tags |