1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 2001, 2002,2004,2005 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * 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, Object value, short valueType, ShortList itemValueType) { 160 } 161 162 /** 163 * This method is called when the XPath handler matches the 164 * XPath expression. Subclasses can override this method to 165 * provide default handling upon a match. 166 */ 167 protected void matched(Object actualValue, short valueType, ShortList itemValueType, boolean isNil) { 168 if (DEBUG_METHODS3) { 169 System.out.println(toString()+"#matched(\""+actualValue+"\")"); 170 } 171 } // matched(String content, XSSimpleType val) 172 173 // 174 // ~XMLDocumentFragmentHandler methods 175 // 176 177 /** 178 * The start of the document fragment. 179 */ 180 public void startDocumentFragment(){ 181 if (DEBUG_METHODS) { 182 System.out.println(toString()+"#startDocumentFragment("+ 183 ")"); 184 } 185 186 // reset state 187 fMatchedString = null; 188 for(int i = 0; i < fLocationPaths.length; i++) { 189 fStepIndexes[i].clear(); 190 fCurrentStep[i] = 0; 191 fNoMatchDepth[i] = 0; 192 fMatched[i] = 0; 193 } 194 195 196 } // startDocumentFragment() 197 198 /** 199 * The start of an element. If the document specifies the start element 200 * by using an empty tag, then the startElement method will immediately 201 * be followed by the endElement method, with no intervening methods. 202 * 203 * @param element The name of the element. 204 * @param attributes The element attributes. 205 * 206 * @throws SAXException Thrown by handler to signal an error. 207 */ 208 public void startElement(QName element, XMLAttributes attributes){ 209 if (DEBUG_METHODS2) { 210 System.out.println(toString()+"#startElement("+ 211 "element={"+element+"},"+ 212 "attributes=..."+attributes+ 213 ")"); 214 } 215 216 for(int i = 0; i < fLocationPaths.length; i++) { 217 // push context 218 int startStep = fCurrentStep[i]; 219 fStepIndexes[i].push(startStep); 220 221 // try next xpath, if not matching 222 if ((fMatched[i] & MATCHED_DESCENDANT) == MATCHED || fNoMatchDepth[i] > 0) { 223 fNoMatchDepth[i]++; 224 continue; 225 } 226 if((fMatched[i] & MATCHED_DESCENDANT) == MATCHED_DESCENDANT) { 227 fMatched[i] = MATCHED_DESCENDANT_PREVIOUS; 228 } 229 230 if (DEBUG_STACK) { 231 System.out.println(toString()+": "+fStepIndexes[i]); 232 } 233 234 // consume self::node() steps 235 XPath.Step[] steps = fLocationPaths[i].steps; 236 while (fCurrentStep[i] < steps.length && 237 steps[fCurrentStep[i]].axis.type == XPath.Axis.SELF) { 238 if (DEBUG_MATCH) { 239 XPath.Step step = steps[fCurrentStep[i]]; 240 System.out.println(toString()+" [SELF] MATCHED!"); 241 } 242 fCurrentStep[i]++; 243 } 244 if (fCurrentStep[i] == steps.length) { 245 if (DEBUG_MATCH) { 246 System.out.println(toString()+" XPath MATCHED!"); 247 } 248 fMatched[i] = MATCHED; 249 continue; 250 } 251 252 // now if the current step is a descendant step, we let the next 253 // step do its thing; if it fails, we reset ourselves 254 // to look at this step for next time we're called. 255 // so first consume all descendants: 256 int descendantStep = fCurrentStep[i]; 257 while(fCurrentStep[i] < steps.length && steps[fCurrentStep[i]].axis.type == XPath.Axis.DESCENDANT) { 258 if (DEBUG_MATCH) { 259 XPath.Step step = steps[fCurrentStep[i]]; 260 System.out.println(toString()+" [DESCENDANT] MATCHED!"); 261 } 262 fCurrentStep[i]++; 263 } 264 boolean sawDescendant = fCurrentStep[i] > descendantStep; 265 if (fCurrentStep[i] == steps.length) { 266 if (DEBUG_MATCH) { 267 System.out.println(toString()+" XPath DIDN'T MATCH!"); 268 } 269 fNoMatchDepth[i]++; 270 if (DEBUG_MATCH) { 271 System.out.println(toString()+" [CHILD] after NO MATCH"); 272 } 273 continue; 274 } 275 276 // match child::... step, if haven't consumed any self::node() 277 if ((fCurrentStep[i] == startStep || fCurrentStep[i] > descendantStep) && 278 steps[fCurrentStep[i]].axis.type == XPath.Axis.CHILD) { 279 XPath.Step step = steps[fCurrentStep[i]]; 280 XPath.NodeTest nodeTest = step.nodeTest; 281 if (DEBUG_MATCH) { 282 System.out.println(toString()+" [CHILD] before"); 283 } 284 if (nodeTest.type == XPath.NodeTest.QNAME) { 285 if (!nodeTest.name.equals(element)) { 286 if(fCurrentStep[i] > descendantStep) { 287 fCurrentStep[i] = descendantStep; 288 continue; 289 } 290 fNoMatchDepth[i]++; 291 if (DEBUG_MATCH) { 292 System.out.println(toString()+" [CHILD] after NO MATCH"); 293 } 294 continue; 295 } 296 } 297 fCurrentStep[i]++; 298 if (DEBUG_MATCH) { 299 System.out.println(toString()+" [CHILD] after MATCHED!"); 300 } 301 } 302 if (fCurrentStep[i] == steps.length) { 303 if(sawDescendant) { 304 fCurrentStep[i] = descendantStep; 305 fMatched[i] = MATCHED_DESCENDANT; 306 } else { 307 fMatched[i] = MATCHED; 308 } 309 continue; 310 } 311 312 // match attribute::... step 313 if (fCurrentStep[i] < steps.length && 314 steps[fCurrentStep[i]].axis.type == XPath.Axis.ATTRIBUTE) { 315 if (DEBUG_MATCH) { 316 System.out.println(toString()+" [ATTRIBUTE] before"); 317 } 318 int attrCount = attributes.getLength(); 319 if (attrCount > 0) { 320 XPath.NodeTest nodeTest = steps[fCurrentStep[i]].nodeTest; 321 322 for (int aIndex = 0; aIndex < attrCount; aIndex++) { 323 attributes.getName(aIndex, fQName); 324 if (nodeTest.type != XPath.NodeTest.QNAME || 325 nodeTest.name.equals(fQName)) { 326 fCurrentStep[i]++; 327 if (fCurrentStep[i] == steps.length) { 328 fMatched[i] = MATCHED_ATTRIBUTE; 329 int j=0; 330 for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++); 331 if(j==i) { 332 AttributePSVI attrPSVI = (AttributePSVI)attributes.getAugmentations(aIndex).getItem(Constants.ATTRIBUTE_PSVI); 333 fMatchedString = attrPSVI.getActualNormalizedValue(); 334 matched(fMatchedString, attrPSVI.getActualNormalizedValueType(), attrPSVI.getItemValueTypes(), false); 335 } 336 } 337 break; 338 } 339 } 340 } 341 if ((fMatched[i] & MATCHED) != MATCHED) { 342 if(fCurrentStep[i] > descendantStep) { 343 fCurrentStep[i] = descendantStep; 344 continue; 345 } 346 fNoMatchDepth[i]++; 347 if (DEBUG_MATCH) { 348 System.out.println(toString()+" [ATTRIBUTE] after"); 349 } 350 continue; 351 } 352 if (DEBUG_MATCH) { 353 System.out.println(toString()+" [ATTRIBUTE] after MATCHED!"); 354 } 355 } 356 } 357 358 } 359 // startElement(QName,XMLAttrList,int) 360 361 /** 362 * @param element 363 * name of the element. 364 * @param type 365 * content type of this element. IOW, the XML schema type 366 * of the <tt>value</tt>. Note that this may not be the type declared 367 * in the element declaration, but it is "the actual type". For example, 368 * if the XML is <foo xsi:type="xs:string">aaa</foo>, this 369 * parameter will be "xs:string". 370 * @param nillable - nillable 371 * true if the element declaration is nillable. 372 * @param value - actual value 373 * the typed value of the content of this element. 374 */ 375 public void endElement(QName element, XSTypeDefinition type, boolean nillable, Object value, short valueType, ShortList itemValueType) { 376 if (DEBUG_METHODS2) { 377 System.out.println(toString()+"#endElement("+ 378 "element={"+element+"},"+ 379 ")"); 380 } 381 for(int i = 0; i<fLocationPaths.length; i++) { 382 // go back a step 383 fCurrentStep[i] = fStepIndexes[i].pop(); 384 385 // don't do anything, if not matching 386 if (fNoMatchDepth[i] > 0) { 387 fNoMatchDepth[i]--; 388 } 389 390 // signal match, if appropriate 391 else { 392 int j=0; 393 for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++); 394 if ((j<i) || (fMatched[j] == 0) || 395 ((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE)) { 396 continue; 397 } 398 // only certain kinds of matchers actually 399 // match element content. This permits 400 // them a way to override this to do nothing 401 // and hopefully save a few operations. 402 handleContent(type, nillable, value, valueType, itemValueType); 403 fMatched[i] = 0; 404 } 405 406 if (DEBUG_STACK) { 407 System.out.println(toString()+": "+fStepIndexes[i]); 408 } 409 } 410 411 } // endElement(QName) 412 413 // 414 // Object methods 415 // 416 417 /** Returns a string representation of this object. */ 418 public String toString() { 419 /*** 420 return fLocationPath.toString(); 421 /***/ 422 StringBuffer str = new StringBuffer(); 423 String s = super.toString(); 424 int index2 = s.lastIndexOf('.'); 425 if (index2 != -1) { 426 s = s.substring(index2 + 1); 427 } 428 str.append(s); 429 for(int i =0;i<fLocationPaths.length; i++) { 430 str.append('['); 431 XPath.Step[] steps = fLocationPaths[i].steps; 432 for (int j = 0; j < steps.length; j++) { 433 if (j == fCurrentStep[i]) { 434 str.append('^'); 435 } 436 str.append(steps[j].toString()); 437 if (j < steps.length - 1) { 438 str.append('/'); 439 } 440 } 441 if (fCurrentStep[i] == steps.length) { 442 str.append('^'); 443 } 444 str.append(']'); 445 str.append(','); 446 } 447 return str.toString(); 448 } // toString():String 449 450 // 451 // Private methods 452 // 453 454 /** Normalizes text. */ 455 private String normalize(String s) { 456 StringBuffer str = new StringBuffer(); 457 int length = s.length(); 458 for (int i = 0; i < length; i++) { 459 char c = s.charAt(i); 460 switch (c) { 461 case '\n': { 462 str.append("\\n"); 463 break; 464 } 465 default: { 466 str.append(c); 467 } 468 } 469 } 470 return str.toString(); 471 } // normalize(String):String 472 473 // 474 // MAIN 475 // 476 477 // NOTE: The main of this class is here for debugging purposes. 478 // However, javac (JDK 1.1.8) has an internal compiler 479 // error when compiling. Jikes has no problem, though. 480 // 481 // If you want to use this main, use Jikes to compile but 482 // *never* check in this code to CVS without commenting it 483 // out. -Ac 484 485 /** Main program. */ 486 /*** 487 public static void main(String[] argv) throws XNIException { 488 489 if (DEBUG_ANY) { 490 for (int i = 0; i < argv.length; i++) { 491 final String expr = argv[i]; 492 final XPath xpath = new XPath(expr, symbols, null); 493 final XPathMatcher matcher = new XPathMatcher(xpath, true); 494 com.sun.org.apache.xerces.internal.parsers.SAXParser parser = 495 new com.sun.org.apache.xerces.internal.parsers.SAXParser(symbols) { 496 public void startDocument() throws XNIException { 497 matcher.startDocumentFragment(symbols, null); 498 } 499 public void startElement(QName element, XMLAttrList attributes, int handle) throws XNIException { 500 matcher.startElement(element, attributes, handle); 501 } 502 public void characters(char[] ch, int offset, int length) throws XNIException { 503 matcher.characters(ch, offset, length); 504 } 505 public void endElement(QName element) throws XNIException { 506 matcher.endElement(element); 507 } 508 public void endDocument() throws XNIException { 509 matcher.endDocumentFragment(); 510 } 511 }; 512 System.out.println("#### argv["+i+"]: \""+expr+"\" -> \""+xpath.toString()+'"'); 513 final String uri = argv[++i]; 514 System.out.println("#### argv["+i+"]: "+uri); 515 parser.parse(uri); 516 } 517 } 518 519 } // main(String[]) 520 /***/ 521 522 } // class XPathMatcher