1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.impl.xs.identity; 22 23 import com.sun.org.apache.xerces.internal.impl.Constants; 24 import com.sun.org.apache.xerces.internal.impl.xpath.XPath; 25 import com.sun.org.apache.xerces.internal.util.IntStack; 26 import com.sun.org.apache.xerces.internal.xni.QName; 27 import com.sun.org.apache.xerces.internal.xni.XMLAttributes; 28 import com.sun.org.apache.xerces.internal.xs.AttributePSVI; 29 import com.sun.org.apache.xerces.internal.xs.ShortList; 30 import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition; 31 import org.xml.sax.SAXException; 32 33 34 /** 35 * XPath matcher. 36 * 37 * @xerces.internal 38 * 39 * @author Andy Clark, IBM 40 * 41 */ 42 public class XPathMatcher { 43 44 // 45 // Constants 46 // 47 48 // debugging 49 50 /** Compile to true to debug everything. */ 51 protected static final boolean DEBUG_ALL = false; 52 53 /** Compile to true to debug method callbacks. */ 54 protected static final boolean DEBUG_METHODS = false || DEBUG_ALL; 55 56 /** Compile to true to debug important method callbacks. */ 57 protected static final boolean DEBUG_METHODS2 = false || DEBUG_METHODS || DEBUG_ALL; 58 59 /** Compile to true to debug the <em>really</em> important methods. */ 60 protected static final boolean DEBUG_METHODS3 = false || DEBUG_METHODS || DEBUG_ALL; 61 62 /** Compile to true to debug match. */ 63 protected static final boolean DEBUG_MATCH = false || DEBUG_ALL; 64 65 /** Compile to true to debug step index stack. */ 66 protected static final boolean DEBUG_STACK = false || DEBUG_ALL; 67 68 /** Don't touch this value unless you add more debug constants. */ 69 protected static final boolean DEBUG_ANY = DEBUG_METHODS || 70 DEBUG_METHODS2 || 71 DEBUG_METHODS3 || 72 DEBUG_MATCH || 73 DEBUG_STACK; 74 75 // constants describing whether a match was made, 76 // and if so how. 77 // matched any way 78 protected static final int MATCHED = 1; 79 // matched on the attribute axis 80 protected static final int MATCHED_ATTRIBUTE = 3; 81 // matched on the descendant-or-self axixs 82 protected static final int MATCHED_DESCENDANT = 5; 83 // matched some previous (ancestor) node on the descendant-or-self-axis, but not this node 84 protected static final int MATCHED_DESCENDANT_PREVIOUS = 13; 85 86 // 87 // Data 88 // 89 90 /** XPath location path. */ 91 private XPath.LocationPath[] fLocationPaths; 92 93 /** True if XPath has been matched. */ 94 private int[] fMatched; 95 96 /** The matching string. */ 97 protected Object fMatchedString; 98 99 /** Integer stack of step indexes. */ 100 private IntStack[] fStepIndexes; 101 102 /** Current step. */ 103 private int[] fCurrentStep; 104 105 /** 106 * No match depth. The value of this field will be zero while 107 * matching is successful for the given xpath expression. 108 */ 109 private int [] fNoMatchDepth; 110 111 final QName fQName = new QName(); 112 113 114 // 115 // Constructors 116 // 117 118 /** 119 * Constructs an XPath matcher that implements a document fragment 120 * handler. 121 * 122 * @param xpath The xpath. 123 */ 124 public XPathMatcher(XPath xpath) { 125 fLocationPaths = xpath.getLocationPaths(); 126 fStepIndexes = new IntStack[fLocationPaths.length]; 127 for(int i=0; i<fStepIndexes.length; i++) fStepIndexes[i] = new IntStack(); 128 fCurrentStep = new int[fLocationPaths.length]; 129 fNoMatchDepth = new int[fLocationPaths.length]; 130 fMatched = new int[fLocationPaths.length]; 131 } // <init>(XPath) 132 133 // 134 // Public methods 135 // 136 137 /** 138 * Returns value of first member of fMatched that 139 * is nonzero. 140 */ 141 public boolean isMatched() { 142 // xpath has been matched if any one of the members of the union have matched. 143 for (int i=0; i < fLocationPaths.length; i++) 144 if (((fMatched[i] & MATCHED) == MATCHED) 145 && ((fMatched[i] & MATCHED_DESCENDANT_PREVIOUS) != MATCHED_DESCENDANT_PREVIOUS) 146 && ((fNoMatchDepth[i] == 0) 147 || ((fMatched[i] & MATCHED_DESCENDANT) == MATCHED_DESCENDANT))) 148 return true; 149 150 return false; 151 } // isMatched():int 152 153 // 154 // Protected methods 155 // 156 157 // a place-holder method; to be overridden by subclasses 158 // that care about matching element content. 159 protected void handleContent(XSTypeDefinition type, boolean nillable, 160 Object value, short valueType, ShortList itemValueType) { 161 } 162 163 /** 164 * This method is called when the XPath handler matches the 165 * XPath expression. Subclasses can override this method to 166 * provide default handling upon a match. 167 */ 168 protected void matched(Object actualValue, short valueType, 169 ShortList itemValueType, boolean isNil) { 170 if (DEBUG_METHODS3) { 171 System.out.println(toString()+"#matched(\""+actualValue+"\")"); 172 } 173 } // matched(String content, XSSimpleType val) 174 175 // 176 // ~XMLDocumentFragmentHandler methods 177 // 178 179 /** 180 * The start of the document fragment. 181 */ 182 public void startDocumentFragment(){ 183 if (DEBUG_METHODS) { 184 System.out.println(toString()+"#startDocumentFragment("+ 185 ")"); 186 } 187 188 // reset state 189 fMatchedString = null; 190 for(int i = 0; i < fLocationPaths.length; i++) { 191 fStepIndexes[i].clear(); 192 fCurrentStep[i] = 0; 193 fNoMatchDepth[i] = 0; 194 fMatched[i] = 0; 195 } 196 197 198 } // startDocumentFragment() 199 200 /** 201 * The start of an element. If the document specifies the start element 202 * by using an empty tag, then the startElement method will immediately 203 * be followed by the endElement method, with no intervening methods. 204 * 205 * @param element The name of the element. 206 * @param attributes The element attributes. 207 * 208 * @throws SAXException Thrown by handler to signal an error. 209 */ 210 public void startElement(QName element, XMLAttributes attributes){ 211 if (DEBUG_METHODS2) { 212 System.out.println(toString()+"#startElement("+ 213 "element={"+element+"},"+ 214 "attributes=..."+attributes+ 215 ")"); 216 } 217 218 for(int i = 0; i < fLocationPaths.length; i++) { 219 // push context 220 int startStep = fCurrentStep[i]; 221 fStepIndexes[i].push(startStep); 222 223 // try next xpath, if not matching 224 if ((fMatched[i] & MATCHED_DESCENDANT) == MATCHED || fNoMatchDepth[i] > 0) { 225 fNoMatchDepth[i]++; 226 continue; 227 } 228 if((fMatched[i] & MATCHED_DESCENDANT) == MATCHED_DESCENDANT) { 229 fMatched[i] = MATCHED_DESCENDANT_PREVIOUS; 230 } 231 232 if (DEBUG_STACK) { 233 System.out.println(toString()+": "+fStepIndexes[i]); 234 } 235 236 // consume self::node() steps 237 XPath.Step[] steps = fLocationPaths[i].steps; 238 while (fCurrentStep[i] < steps.length && 239 steps[fCurrentStep[i]].axis.type == XPath.Axis.SELF) { 240 if (DEBUG_MATCH) { 241 XPath.Step step = steps[fCurrentStep[i]]; 242 System.out.println(toString()+" [SELF] MATCHED!"); 243 } 244 fCurrentStep[i]++; 245 } 246 if (fCurrentStep[i] == steps.length) { 247 if (DEBUG_MATCH) { 248 System.out.println(toString()+" XPath MATCHED!"); 249 } 250 fMatched[i] = MATCHED; 251 continue; 252 } 253 254 // now if the current step is a descendant step, we let the next 255 // step do its thing; if it fails, we reset ourselves 256 // to look at this step for next time we're called. 257 // so first consume all descendants: 258 int descendantStep = fCurrentStep[i]; 259 while(fCurrentStep[i] < steps.length && 260 steps[fCurrentStep[i]].axis.type == XPath.Axis.DESCENDANT) { 261 if (DEBUG_MATCH) { 262 XPath.Step step = steps[fCurrentStep[i]]; 263 System.out.println(toString()+" [DESCENDANT] MATCHED!"); 264 } 265 fCurrentStep[i]++; 266 } 267 boolean sawDescendant = fCurrentStep[i] > descendantStep; 268 if (fCurrentStep[i] == steps.length) { 269 if (DEBUG_MATCH) { 270 System.out.println(toString()+" XPath DIDN'T MATCH!"); 271 } 272 fNoMatchDepth[i]++; 273 if (DEBUG_MATCH) { 274 System.out.println(toString()+" [CHILD] after NO MATCH"); 275 } 276 continue; 277 } 278 279 // match child::... step, if haven't consumed any self::node() 280 if ((fCurrentStep[i] == startStep || fCurrentStep[i] > descendantStep) && 281 steps[fCurrentStep[i]].axis.type == XPath.Axis.CHILD) { 282 XPath.Step step = steps[fCurrentStep[i]]; 283 XPath.NodeTest nodeTest = step.nodeTest; 284 if (DEBUG_MATCH) { 285 System.out.println(toString()+" [CHILD] before"); 286 } 287 if (nodeTest.type == XPath.NodeTest.QNAME) { 288 if (!nodeTest.name.equals(element)) { 289 if(fCurrentStep[i] > descendantStep) { 290 fCurrentStep[i] = descendantStep; 291 continue; 292 } 293 fNoMatchDepth[i]++; 294 if (DEBUG_MATCH) { 295 System.out.println(toString()+" [CHILD] after NO MATCH"); 296 } 297 continue; 298 } 299 } 300 fCurrentStep[i]++; 301 if (DEBUG_MATCH) { 302 System.out.println(toString()+" [CHILD] after MATCHED!"); 303 } 304 } 305 if (fCurrentStep[i] == steps.length) { 306 if(sawDescendant) { 307 fCurrentStep[i] = descendantStep; 308 fMatched[i] = MATCHED_DESCENDANT; 309 } else { 310 fMatched[i] = MATCHED; 311 } 312 continue; 313 } 314 315 // match attribute::... step 316 if (fCurrentStep[i] < steps.length && 317 steps[fCurrentStep[i]].axis.type == XPath.Axis.ATTRIBUTE) { 318 if (DEBUG_MATCH) { 319 System.out.println(toString()+" [ATTRIBUTE] before"); 320 } 321 int attrCount = attributes.getLength(); 322 if (attrCount > 0) { 323 XPath.NodeTest nodeTest = steps[fCurrentStep[i]].nodeTest; 324 325 for (int aIndex = 0; aIndex < attrCount; aIndex++) { 326 attributes.getName(aIndex, fQName); 327 if (nodeTest.type != XPath.NodeTest.QNAME || 328 nodeTest.name.equals(fQName)) { 329 fCurrentStep[i]++; 330 if (fCurrentStep[i] == steps.length) { 331 fMatched[i] = MATCHED_ATTRIBUTE; 332 int j=0; 333 for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++); 334 if(j==i) { 335 AttributePSVI attrPSVI = (AttributePSVI)attributes. 336 getAugmentations(aIndex).getItem(Constants.ATTRIBUTE_PSVI); 337 fMatchedString = attrPSVI.getSchemaValue().getActualValue(); 338 matched(fMatchedString, attrPSVI.getSchemaValue().getActualValueType(), 339 attrPSVI.getSchemaValue().getListValueTypes(), false); 340 } 341 } 342 break; 343 } 344 } 345 } 346 if ((fMatched[i] & MATCHED) != MATCHED) { 347 if(fCurrentStep[i] > descendantStep) { 348 fCurrentStep[i] = descendantStep; 349 continue; 350 } 351 fNoMatchDepth[i]++; 352 if (DEBUG_MATCH) { 353 System.out.println(toString()+" [ATTRIBUTE] after"); 354 } 355 continue; 356 } 357 if (DEBUG_MATCH) { 358 System.out.println(toString()+" [ATTRIBUTE] after MATCHED!"); 359 } 360 } 361 } 362 363 } 364 // startElement(QName,XMLAttrList,int) 365 366 /** 367 * @param element 368 * name of the element. 369 * @param type 370 * content type of this element. IOW, the XML schema type 371 * of the <tt>value</tt>. Note that this may not be the type declared 372 * in the element declaration, but it is "the actual type". For example, 373 * if the XML is <foo xsi:type="xs:string">aaa</foo>, this 374 * parameter will be "xs:string". 375 * @param nillable - nillable 376 * true if the element declaration is nillable. 377 * @param value - actual value 378 * the typed value of the content of this element. 379 */ 380 public void endElement(QName element, XSTypeDefinition type, boolean nillable, 381 Object value, short valueType, ShortList itemValueType) { 382 if (DEBUG_METHODS2) { 383 System.out.println(toString()+"#endElement("+ 384 "element={"+element+"},"+ 385 ")"); 386 } 387 for(int i = 0; i<fLocationPaths.length; i++) { 388 // go back a step 389 fCurrentStep[i] = fStepIndexes[i].pop(); 390 391 // don't do anything, if not matching 392 if (fNoMatchDepth[i] > 0) { 393 fNoMatchDepth[i]--; 394 } 395 396 // signal match, if appropriate 397 else { 398 int j=0; 399 for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++); 400 if ((j<i) || (fMatched[j] == 0) || 401 ((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE)) { 402 continue; 403 } 404 // only certain kinds of matchers actually 405 // match element content. This permits 406 // them a way to override this to do nothing 407 // and hopefully save a few operations. 408 handleContent(type, nillable, value, valueType, itemValueType); 409 fMatched[i] = 0; 410 } 411 412 if (DEBUG_STACK) { 413 System.out.println(toString()+": "+fStepIndexes[i]); 414 } 415 } 416 417 } // endElement(QName) 418 419 // 420 // Object methods 421 // 422 423 /** Returns a string representation of this object. */ 424 public String toString() { 425 /*** 426 return fLocationPath.toString(); 427 /***/ 428 StringBuffer str = new StringBuffer(); 429 String s = super.toString(); 430 int index2 = s.lastIndexOf('.'); 431 if (index2 != -1) { 432 s = s.substring(index2 + 1); 433 } 434 str.append(s); 435 for(int i =0;i<fLocationPaths.length; i++) { 436 str.append('['); 437 XPath.Step[] steps = fLocationPaths[i].steps; 438 for (int j = 0; j < steps.length; j++) { 439 if (j == fCurrentStep[i]) { 440 str.append('^'); 441 } 442 str.append(steps[j].toString()); 443 if (j < steps.length - 1) { 444 str.append('/'); 445 } 446 } 447 if (fCurrentStep[i] == steps.length) { 448 str.append('^'); 449 } 450 str.append(']'); 451 str.append(','); 452 } 453 return str.toString(); 454 } // toString():String 455 456 // 457 // Private methods 458 // 459 460 /** Normalizes text. */ 461 private String normalize(String s) { 462 StringBuffer str = new StringBuffer(); 463 int length = s.length(); 464 for (int i = 0; i < length; i++) { 465 char c = s.charAt(i); 466 switch (c) { 467 case '\n': { 468 str.append("\\n"); 469 break; 470 } 471 default: { 472 str.append(c); 473 } 474 } 475 } 476 return str.toString(); 477 } // normalize(String):String 478 479 // 480 // MAIN 481 // 482 483 // NOTE: The main of this class is here for debugging purposes. 484 // However, javac (JDK 1.1.8) has an internal compiler 485 // error when compiling. Jikes has no problem, though. 486 // 487 // If you want to use this main, use Jikes to compile but 488 // *never* check in this code to CVS without commenting it 489 // out. -Ac 490 491 /** Main program. */ 492 /*** 493 public static void main(String[] argv) throws XNIException { 494 495 if (DEBUG_ANY) { 496 for (int i = 0; i < argv.length; i++) { 497 final String expr = argv[i]; 498 final XPath xpath = new XPath(expr, symbols, null); 499 final XPathMatcher matcher = new XPathMatcher(xpath, true); 500 com.sun.org.apache.xerces.internal.parsers.SAXParser parser = 501 new com.sun.org.apache.xerces.internal.parsers.SAXParser(symbols) { 502 public void startDocument() throws XNIException { 503 matcher.startDocumentFragment(symbols, null); 504 } 505 public void startElement(QName element, XMLAttrList attributes, int handle) throws XNIException { 506 matcher.startElement(element, attributes, handle); 507 } 508 public void characters(char[] ch, int offset, int length) throws XNIException { 509 matcher.characters(ch, offset, length); 510 } 511 public void endElement(QName element) throws XNIException { 512 matcher.endElement(element); 513 } 514 public void endDocument() throws XNIException { 515 matcher.endDocumentFragment(); 516 } 517 }; 518 System.out.println("#### argv["+i+"]: \""+expr+"\" -> \""+xpath.toString()+'"'); 519 final String uri = argv[++i]; 520 System.out.println("#### argv["+i+"]: "+uri); 521 parser.parse(uri); 522 } 523 } 524 525 } // main(String[]) 526 /***/ 527 528 } // class XPathMatcher