001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jxpath.ri.axes; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.commons.jxpath.JXPathException; 024import org.apache.commons.jxpath.ri.Compiler; 025import org.apache.commons.jxpath.ri.EvalContext; 026import org.apache.commons.jxpath.ri.InfoSetUtil; 027import org.apache.commons.jxpath.ri.QName; 028import org.apache.commons.jxpath.ri.compiler.Expression; 029import org.apache.commons.jxpath.ri.compiler.NameAttributeTest; 030import org.apache.commons.jxpath.ri.compiler.NodeNameTest; 031import org.apache.commons.jxpath.ri.compiler.NodeTest; 032import org.apache.commons.jxpath.ri.compiler.Step; 033import org.apache.commons.jxpath.ri.model.NodeIterator; 034import org.apache.commons.jxpath.ri.model.NodePointer; 035import org.apache.commons.jxpath.ri.model.beans.LangAttributePointer; 036import org.apache.commons.jxpath.ri.model.beans.NullElementPointer; 037import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer; 038import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer; 039import org.apache.commons.jxpath.ri.model.beans.PropertyPointer; 040 041/** 042 * An evaluation mechanism for simple XPaths, which 043 * is much faster than the usual process. It is only used for 044 * xpaths which have no context-dependent parts, consist entirely of 045 * <code>child::name</code> and <code>self::node()</code> steps with 046 * predicates that either integer or have the form <code>[@name = ...]</code>. 047 * 048 * @author Dmitri Plotnikov 049 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $ 050 */ 051public class SimplePathInterpreter { 052 053 // Because of the complexity caused by the variety of situations 054 // that need to be addressed by this class, we attempt to break up 055 // the class into individual methods addressing those situations 056 // individually. The names of the methods are supposed to 057 // give brief descriptions of those situations. 058 059 private static final QName QNAME_NAME = new QName(null, "name"); 060 private static final int PERFECT_MATCH = 1000; 061 062 // Uncomment this variable and the PATH = ... lines in 063 // the two following methods in order to be able to print the 064 // currently evaluated path for debugging of this class 065// private static String PATH; // Debugging 066 067 /** 068 * Interpret a simple path that starts with the given root and 069 * follows the given steps. All steps must have the axis "child::" 070 * and a name test. They can also optionally have predicates 071 * of type [@name=expression] or simply [expression] interpreted 072 * as an index. 073 * @param context evaluation context 074 * @param root root pointer 075 * @param steps path steps 076 * @return NodePointer 077 */ 078 public static NodePointer interpretSimpleLocationPath( 079 EvalContext context, NodePointer root, Step[] steps) { 080// PATH = createNullPointer(context, root, steps, 0).toString(); // Dbg 081 NodePointer pointer = doStep(context, root, steps, 0); 082// return valuePointer(pointer); 083 return pointer; 084 } 085 086 /** 087 * Interpret the steps of a simple expression path that 088 * starts with the given root, which is the result of evaluation 089 * of the root expression of the expression path, applies the 090 * given predicates to it and then follows the given steps. 091 * All steps must have the axis "child::" or "attribute::" 092 * and a name test. They can also optionally have predicates 093 * of type [@name=...] or simply [...] interpreted as an index. 094 * @param context evaluation context 095 * @param root root pointer 096 * @param predicates predicates corresponding to <code>steps</code> 097 * @param steps path steps 098 * @return NodePointer 099 */ 100 public static NodePointer interpretSimpleExpressionPath( 101 EvalContext context, NodePointer root, 102 Expression[] predicates, Step[] steps) { 103// PATH = createNullPointerForPredicates(context, root, 104// steps, -1, predicates, 0).toString(); // Debugging 105 NodePointer pointer = 106 doPredicate(context, root, steps, -1, predicates, 0); 107// return valuePointer(pointer); 108 return pointer; 109 } 110 111 /** 112 * Recursive evaluation of a path. The general plan is: 113 * Look at the current step, 114 * find nodes that match it, 115 * iterate over those nodes and 116 * for each of them call doStep again for subsequent steps. 117 * @param context evaluation context 118 * @param parent parent pointer 119 * @param steps path steps 120 * @param currentStep step number 121 * @return NodePointer 122 */ 123 private static NodePointer doStep( 124 EvalContext context, NodePointer parent, 125 Step[] steps, int currentStep) { 126 if (parent == null) { 127 return null; 128 } 129 130 if (currentStep == steps.length) { 131 // We have reached the end of the list of steps 132 return parent; 133 } 134 135 // Open all containers 136 parent = valuePointer(parent); 137 138 Step step = steps[currentStep]; 139 Expression[] predicates = step.getPredicates(); 140 141 // Divide and conquer: the process is broken out into 142 // four major use cases. 143 // 1. Current step has no predicates and 144 // the root is a property owner (e.g. bean or map) 145 // 2. Current step has predicates and 146 // the root is a property owner (e.g. bean or map) 147 // 3. Current step has no predicates and 148 // the root is an InfoSet standard node (e.g. DOM Node) 149 // 4. Current step has predicates and 150 // the root is an InfoSet standard node (e.g. DOM Node) 151 152 if (parent instanceof PropertyOwnerPointer) { 153 if (predicates == null || predicates.length == 0) { 154 return doStepNoPredicatesPropertyOwner( 155 context, 156 (PropertyOwnerPointer) parent, 157 steps, 158 currentStep); 159 } 160 return doStepPredicatesPropertyOwner( 161 context, 162 (PropertyOwnerPointer) parent, 163 steps, 164 currentStep); 165 } 166 if (predicates == null || predicates.length == 0) { 167 return doStepNoPredicatesStandard( 168 context, 169 parent, 170 steps, 171 currentStep); 172 } 173 return doStepPredicatesStandard( 174 context, 175 parent, 176 steps, 177 currentStep); 178 } 179 180 /** 181 * We have a step that starts with a property owner (bean, map, etc) and has 182 * no predicates. The name test of the step may map to a scalar property 183 * or to a collection. If it is a collection, we should apply the tail of 184 * the path to each element until we find a match. If we don't find 185 * a perfect match, we should return the "best quality" pointer, which 186 * has the longest chain of steps mapping to existing nodes and the shortes 187 * tail of Null* pointers. 188 * @param context evaluation context 189 * @param parentPointer property owner pointer 190 * @param steps path steps 191 * @param currentStep step number 192 * @return NodePointer 193 */ 194 private static NodePointer doStepNoPredicatesPropertyOwner( 195 EvalContext context, PropertyOwnerPointer parentPointer, 196 Step[] steps, int currentStep) { 197 Step step = steps[currentStep]; 198 NodePointer childPointer = 199 createChildPointerForStep(parentPointer, step); 200 201 if (childPointer == null) { 202 return null; 203 } 204 if (!childPointer.isActual()) { 205 // The property does not exist - create a null pointer. 206 return createNullPointer( 207 context, 208 parentPointer, 209 steps, 210 currentStep); 211 } 212 if (currentStep == steps.length - 1) { 213 // If this is the last step - we are done, we found it 214 return childPointer; 215 } 216 if (childPointer.isCollection()) { 217 // Iterate over all values and 218 // execute remaining steps for each node, 219 // looking for the best quality match 220 int bestQuality = 0; 221 childPointer = (NodePointer) childPointer.clone(); 222 NodePointer bestMatch = null; 223 int count = childPointer.getLength(); 224 for (int i = 0; i < count; i++) { 225 childPointer.setIndex(i); 226 NodePointer pointer = 227 doStep(context, childPointer, steps, currentStep + 1); 228 int quality = computeQuality(pointer); 229 if (quality == PERFECT_MATCH) { 230 return pointer; 231 } 232 else if (quality > bestQuality) { 233 bestQuality = quality; 234 bestMatch = (NodePointer) pointer.clone(); 235 } 236 } 237 if (bestMatch != null) { 238 return bestMatch; 239 } 240 // This step did not find anything - return a null pointer 241 return createNullPointer(context, childPointer, steps, currentStep); 242 } 243 // Evaluate subsequent steps 244 return doStep(context, childPointer, steps, currentStep + 1); 245 } 246 247 /** 248 * A path that starts with a standard InfoSet node (e.g. DOM Node) and 249 * has no predicates. Get a child iterator and apply the tail of 250 * the path to each element until we find a match. If we don't find 251 * a perfect match, we should return the "best quality" pointer, which 252 * has the longest chain of steps mapping to existing nodes and the shortes 253 * tail of Null* pointers. 254 * @param context evaluation context 255 * @param parentPointer parent pointer 256 * @param steps path steps 257 * @param currentStep step number 258 * @return NodePointer 259 */ 260 private static NodePointer doStepNoPredicatesStandard( 261 EvalContext context, NodePointer parentPointer, 262 Step[] steps, int currentStep) { 263 Step step = steps[currentStep]; 264 265 if (step.getAxis() == Compiler.AXIS_SELF) { 266 return doStep(context, parentPointer, steps, currentStep + 1); 267 } 268 269 int bestQuality = 0; 270 NodePointer bestMatch = null; 271 NodeIterator it = getNodeIterator(context, parentPointer, step); 272 if (it != null) { 273 for (int i = 1; it.setPosition(i); i++) { 274 NodePointer childPointer = it.getNodePointer(); 275 if (steps.length == currentStep + 1) { 276 // If this is the last step - we are done, we found it 277 return childPointer; 278 } 279 NodePointer pointer = doStep( 280 context, childPointer, steps, currentStep + 1); 281 int quality = computeQuality(pointer); 282 if (quality == PERFECT_MATCH) { 283 return pointer; 284 } 285 if (quality > bestQuality) { 286 bestQuality = quality; 287 bestMatch = (NodePointer) pointer.clone(); 288 } 289 } 290 } 291 return bestMatch != null ? bestMatch 292 : createNullPointer(context, parentPointer, steps, currentStep); 293 } 294 295 /** 296 * A path that starts with a property owner. The method evaluates 297 * the first predicate in a special way and then forwards to 298 * a general predicate processing method. 299 * @param context evaluation context 300 * @param parentPointer parent pointer 301 * @param steps path steps 302 * @param currentStep step number 303 * @return NodePointer 304 */ 305 private static NodePointer doStepPredicatesPropertyOwner( 306 EvalContext context, PropertyOwnerPointer parentPointer, 307 Step[] steps, int currentStep) { 308 Step step = steps[currentStep]; 309 Expression[] predicates = step.getPredicates(); 310 311 NodePointer childPointer = 312 createChildPointerForStep(parentPointer, step); 313 if (!childPointer.isActual()) { 314 // Property does not exist - return a null pointer 315 return createNullPointer( 316 context, 317 parentPointer, 318 steps, 319 currentStep); 320 } 321 322 // Evaluate predicates 323 return doPredicate( 324 context, 325 childPointer, 326 steps, 327 currentStep, 328 predicates, 329 0); 330 } 331 332 /** 333 * Create the child pointer for a given step. 334 * @param parentPointer parent pointer 335 * @param step associated step 336 * @return NodePointer 337 */ 338 private static NodePointer createChildPointerForStep( 339 PropertyOwnerPointer parentPointer, Step step) { 340 int axis = step.getAxis(); 341 if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) { 342 QName name = ((NodeNameTest) step.getNodeTest()).getNodeName(); 343 if (axis == Compiler.AXIS_ATTRIBUTE && isLangAttribute(name)) { 344 return new LangAttributePointer(parentPointer); 345 } 346 if (parentPointer.isValidProperty(name)) { 347 NodePointer childPointer = parentPointer.getPropertyPointer(); 348 ((PropertyPointer) childPointer).setPropertyName( 349 name.toString()); 350 childPointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE); 351 return childPointer; 352 } 353 //invalid property gets nothing, not even a NullPointer 354 return null; 355 } 356 return parentPointer; 357 } 358 359 /** 360 * A path that starts with a standard InfoSet node, e.g. a DOM Node. 361 * The method evaluates the first predicate in a special way and 362 * then forwards to a general predicate processing method. 363 * @param context evaluation context 364 * @param parent parent pointer 365 * @param steps path steps 366 * @param currentStep step number 367 * @return NodePointer 368 */ 369 private static NodePointer doStepPredicatesStandard( 370 EvalContext context, NodePointer parent, 371 Step[] steps, int currentStep) { 372 Step step = steps[currentStep]; 373 Expression[] predicates = step.getPredicates(); 374 375 int axis = step.getAxis(); 376 if (axis == Compiler.AXIS_SELF) { 377 return doPredicate( 378 context, 379 parent, 380 steps, 381 currentStep, 382 predicates, 383 0); 384 } 385 386 Expression predicate = predicates[0]; 387 388 // Optimize for a single predicate to avoid building a list 389 // and to allow the direct access to the index'th element 390 // in the case of a simple subscript predecate 391 // It is a very common use case, so it deserves individual 392 // attention 393 if (predicates.length == 1) { 394 NodeIterator it = getNodeIterator(context, parent, step); 395 NodePointer pointer = null; 396 if (it != null) { 397 if (predicate instanceof NameAttributeTest) { // [@name = key] 398 String key = keyFromPredicate(context, predicate); 399 for (int i = 1; it.setPosition(i); i++) { 400 NodePointer ptr = it.getNodePointer(); 401 if (isNameAttributeEqual(ptr, key)) { 402 pointer = ptr; 403 break; 404 } 405 } 406 } 407 else { 408 int index = indexFromPredicate(context, predicate); 409 if (it.setPosition(index + 1)) { 410 pointer = it.getNodePointer(); 411 } 412 } 413 } 414 if (pointer != null) { 415 return doStep(context, pointer, steps, currentStep + 1); 416 } 417 } 418 else { 419 NodeIterator it = getNodeIterator(context, parent, step); 420 if (it != null) { 421 List list = new ArrayList(); 422 for (int i = 1; it.setPosition(i); i++) { 423 list.add(it.getNodePointer()); 424 } 425 NodePointer pointer = 426 doPredicatesStandard( 427 context, 428 list, 429 steps, 430 currentStep, 431 predicates, 432 0); 433 if (pointer != null) { 434 return pointer; 435 } 436 } 437 } 438 return createNullPointer(context, parent, steps, currentStep); 439 } 440 441 /** 442 * Evaluates predicates and proceeds with the subsequent steps 443 * of the path. 444 * @param context evaluation context 445 * @param parent parent pointer 446 * @param steps path steps 447 * @param currentStep step number 448 * @param predicates predicate expressions 449 * @param currentPredicate int predicate number 450 * @return NodePointer 451 */ 452 private static NodePointer doPredicate( 453 EvalContext context, NodePointer parent, 454 Step[] steps, int currentStep, 455 Expression[] predicates, int currentPredicate) { 456 if (currentPredicate == predicates.length) { 457 return doStep(context, parent, steps, currentStep + 1); 458 } 459 460 Expression predicate = predicates[currentPredicate]; 461 if (predicate instanceof NameAttributeTest) { // [@name = key1] 462 return doPredicateName( 463 context, 464 parent, 465 steps, 466 currentStep, 467 predicates, 468 currentPredicate); 469 } 470 // else [index] 471 return doPredicateIndex( 472 context, 473 parent, 474 steps, 475 currentStep, 476 predicates, 477 currentPredicate); 478 } 479 480 /** 481 * Execute a NameAttributeTest predicate 482 * @param context evaluation context 483 * @param parent parent pointer 484 * @param steps path steps 485 * @param currentStep int step number 486 * @param predicates predicates 487 * @param currentPredicate int predicate number 488 * @return NodePointer 489 */ 490 private static NodePointer doPredicateName( 491 EvalContext context, NodePointer parent, 492 Step[] steps, int currentStep, 493 Expression[] predicates, int currentPredicate) { 494 Expression predicate = predicates[currentPredicate]; 495 String key = keyFromPredicate(context, predicate); 496 NodePointer child = valuePointer(parent); 497 if (child instanceof PropertyOwnerPointer) { 498 PropertyPointer pointer = 499 ((PropertyOwnerPointer) child).getPropertyPointer(); 500 pointer.setPropertyName(key); 501 if (pointer.isActual()) { 502 return doPredicate( 503 context, 504 pointer, 505 steps, 506 currentStep, 507 predicates, 508 currentPredicate + 1); 509 } 510 } 511 else if (child.isCollection()) { 512 // For each node in the collection, perform the following: 513 // if the node is a property owner, apply this predicate to it; 514 // if the node is a collection, apply this predicate to each elem.; 515 // if the node is not a prop owner or a collection, 516 // see if it has the attribute "name" with the right value, 517 // if so - proceed to the next predicate 518 NodePointer bestMatch = null; 519 int bestQuality = 0; 520 child = (NodePointer) child.clone(); 521 int count = child.getLength(); 522 for (int i = 0; i < count; i++) { 523 child.setIndex(i); 524 NodePointer valuePointer = valuePointer(child); 525 NodePointer pointer; 526 if ((valuePointer instanceof PropertyOwnerPointer) 527 || valuePointer.isCollection()) { 528 pointer = 529 doPredicateName( 530 context, 531 valuePointer, 532 steps, 533 currentStep, 534 predicates, 535 currentPredicate); 536 } 537 else if (isNameAttributeEqual(valuePointer, key)) { 538 pointer = 539 doPredicate( 540 context, 541 valuePointer, 542 steps, 543 currentStep, 544 predicates, 545 currentPredicate + 1); 546 } 547 else { 548 pointer = null; 549 } 550 if (pointer != null) { 551 int quality = computeQuality(pointer); 552 if (quality == PERFECT_MATCH) { 553 return pointer; 554 } 555 if (quality > bestQuality) { 556 bestMatch = (NodePointer) pointer.clone(); 557 bestQuality = quality; 558 } 559 } 560 } 561 if (bestMatch != null) { 562 return bestMatch; 563 } 564 } 565 else { 566 // If the node is a standard InfoSet node (e.g. DOM Node), 567 // employ doPredicates_standard, which will iterate through 568 // the node's children and apply all predicates 569 NodePointer found = 570 doPredicatesStandard( 571 context, 572 Collections.singletonList(child), 573 steps, 574 currentStep, 575 predicates, 576 currentPredicate); 577 if (found != null) { 578 return found; 579 } 580 } 581 // If nothing worked - return a null pointer 582 return createNullPointerForPredicates( 583 context, 584 child, 585 steps, 586 currentStep, 587 predicates, 588 currentPredicate); 589 } 590 591 /** 592 * Called exclusively for standard InfoSet nodes, e.g. DOM nodes 593 * to evaluate predicate sequences like [@name=...][@name=...][index]. 594 * @param context evaluation context 595 * @param parents List of parent pointers 596 * @param steps path steps 597 * @param currentStep step number 598 * @param predicates predicates 599 * @param currentPredicate int predicate number 600 * @return NodePointer 601 */ 602 private static NodePointer doPredicatesStandard( 603 EvalContext context, List parents, 604 Step[] steps, int currentStep, 605 Expression[] predicates, int currentPredicate) { 606 if (parents.size() == 0) { 607 return null; 608 } 609 610 // If all predicates have been processed, take the first 611 // element from the list of results and proceed to the 612 // remaining steps with that element. 613 if (currentPredicate == predicates.length) { 614 NodePointer pointer = (NodePointer) parents.get(0); 615 return doStep(context, pointer, steps, currentStep + 1); 616 } 617 618 Expression predicate = predicates[currentPredicate]; 619 if (predicate instanceof NameAttributeTest) { 620 String key = keyFromPredicate(context, predicate); 621 List newList = new ArrayList(); 622 for (int i = 0; i < parents.size(); i++) { 623 NodePointer pointer = (NodePointer) parents.get(i); 624 if (isNameAttributeEqual(pointer, key)) { 625 newList.add(pointer); 626 } 627 } 628 if (newList.size() == 0) { 629 return null; 630 } 631 return doPredicatesStandard( 632 context, 633 newList, 634 steps, 635 currentStep, 636 predicates, 637 currentPredicate + 1); 638 } 639 else { 640 // For a subscript, simply take the corresponding 641 // element from the list of results and 642 // proceed to the remaining predicates with that element 643 int index = indexFromPredicate(context, predicate); 644 if (index < 0 || index >= parents.size()) { 645 return null; 646 } 647 NodePointer ptr = (NodePointer) parents.get(index); 648 return doPredicate( 649 context, 650 ptr, 651 steps, 652 currentStep, 653 predicates, 654 currentPredicate + 1); 655 } 656 } 657 658 /** 659 * Evaluate a subscript predicate: see if the node is a collection and 660 * if the index is inside the collection. 661 * @param context evaluation context 662 * @param parent parent pointer 663 * @param steps path steps 664 * @param currentStep step number 665 * @param predicates predicates 666 * @param currentPredicate int predicate number 667 * @return NodePointer 668 */ 669 private static NodePointer doPredicateIndex( 670 EvalContext context, NodePointer parent, 671 Step[] steps, int currentStep, 672 Expression[] predicates, int currentPredicate) { 673 Expression predicate = predicates[currentPredicate]; 674 int index = indexFromPredicate(context, predicate); 675 NodePointer pointer = parent; 676 if (isCollectionElement(pointer, index)) { 677 pointer = (NodePointer) pointer.clone(); 678 pointer.setIndex(index); 679 return doPredicate( 680 context, 681 pointer, 682 steps, 683 currentStep, 684 predicates, 685 currentPredicate + 1); 686 } 687 return createNullPointerForPredicates( 688 context, 689 parent, 690 steps, 691 currentStep, 692 predicates, 693 currentPredicate); 694 } 695 696 /** 697 * Extract an integer from a subscript predicate. The returned index 698 * starts with 0, even though the subscript starts with 1. 699 * @param context evaluation context 700 * @param predicate to evaluate 701 * @return calculated index 702 */ 703 private static int indexFromPredicate( 704 EvalContext context, 705 Expression predicate) { 706 Object value = predicate.computeValue(context); 707 if (value instanceof EvalContext) { 708 value = ((EvalContext) value).getSingleNodePointer(); 709 } 710 if (value instanceof NodePointer) { 711 value = ((NodePointer) value).getValue(); 712 } 713 if (value == null) { 714 throw new JXPathException("Predicate value is null: " + predicate); 715 } 716 717 if (value instanceof Number) { 718 final double round = 0.5; 719 return (int) (InfoSetUtil.doubleValue(value) + round) - 1; 720 } 721 return InfoSetUtil.booleanValue(value) ? 0 : -1; 722 } 723 724 /** 725 * Extracts the string value of the expression from a predicate like 726 * [@name=expression]. 727 * @param context evaluation context 728 * @param predicate predicate to evaluate 729 * @return String key extracted 730 */ 731 private static String keyFromPredicate(EvalContext context, 732 Expression predicate) { 733 Expression expr = 734 ((NameAttributeTest) predicate).getNameTestExpression(); 735 return InfoSetUtil.stringValue(expr.computeValue(context)); 736 } 737 738 /** 739 * For a pointer that matches an actual node, returns 0. 740 * For a pointer that does not match an actual node, but whose 741 * parent pointer does returns -1, etc. 742 * @param pointer input pointer 743 * @return int match quality code 744 */ 745 private static int computeQuality(NodePointer pointer) { 746 int quality = PERFECT_MATCH; 747 while (pointer != null && !pointer.isActual()) { 748 quality--; 749 pointer = pointer.getImmediateParentPointer(); 750 } 751 return quality; 752 } 753 754 /** 755 * Returns true if the pointer has an attribute called "name" and 756 * its value is equal to the supplied string. 757 * @param pointer input pointer 758 * @param name name to check 759 * @return boolean 760 */ 761 private static boolean isNameAttributeEqual( 762 NodePointer pointer, 763 String name) { 764 NodeIterator it = pointer.attributeIterator(QNAME_NAME); 765 return it != null 766 && it.setPosition(1) 767 && name.equals(it.getNodePointer().getValue()); 768 } 769 770 /** 771 * Returns true if the pointer is a collection and the index is 772 * withing the bounds of the collection. 773 * @param pointer input pointer 774 * @param index to check 775 * @return boolean 776 */ 777 private static boolean isCollectionElement( 778 NodePointer pointer, 779 int index) { 780 return pointer.isActual() 781 && (index == 0 782 || (pointer.isCollection() 783 && index >= 0 784 && index < pointer.getLength())); 785 } 786 787 /** 788 * For an intermediate pointer (e.g. PropertyPointer, ContainerPointer) 789 * returns a pointer for the contained value. 790 * @param pointer input pointer 791 * @return NodePointer 792 */ 793 private static NodePointer valuePointer(NodePointer pointer) { 794 return pointer == null ? null : pointer.getValuePointer(); 795 } 796 797 /** 798 * Creates a "null pointer" that 799 * a) represents the requested path and 800 * b) can be used for creation of missing nodes in the path. 801 * @param context evaluation context 802 * @param parent parent pointer 803 * @param steps path steps 804 * @param currentStep step number 805 * @return NodePointer 806 */ 807 public static NodePointer createNullPointer( 808 EvalContext context, NodePointer parent, Step[] steps, 809 int currentStep) { 810 if (currentStep == steps.length) { 811 return parent; 812 } 813 814 parent = valuePointer(parent); 815 816 Step step = steps[currentStep]; 817 818 int axis = step.getAxis(); 819 if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) { 820 NullPropertyPointer pointer = new NullPropertyPointer(parent); 821 QName name = ((NodeNameTest) step.getNodeTest()).getNodeName(); 822 pointer.setPropertyName(name.toString()); 823 pointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE); 824 parent = pointer; 825 } 826 // else { it is self::node() } 827 828 Expression[] predicates = step.getPredicates(); 829 return createNullPointerForPredicates( 830 context, 831 parent, 832 steps, 833 currentStep, 834 predicates, 835 0); 836 } 837 838 /** 839 * Creates a "null pointer" that starts with predicates. 840 * @param context evaluation context 841 * @param parent parent pointer 842 * @param steps path steps 843 * @param currentStep step number 844 * @param predicates predicates 845 * @param currentPredicate int predicate number 846 * @return NodePointer 847 */ 848 private static NodePointer createNullPointerForPredicates( 849 EvalContext context, NodePointer parent, 850 Step[] steps, int currentStep, 851 Expression[] predicates, int currentPredicate) { 852 for (int i = currentPredicate; i < predicates.length; i++) { 853 Expression predicate = predicates[i]; 854 if (predicate instanceof NameAttributeTest) { 855 String key = keyFromPredicate(context, predicate); 856 parent = valuePointer(parent); 857 NullPropertyPointer pointer = new NullPropertyPointer(parent); 858 pointer.setNameAttributeValue(key); 859 parent = pointer; 860 } 861 else { 862 int index = indexFromPredicate(context, predicate); 863 if (parent instanceof NullPropertyPointer) { 864 parent.setIndex(index); 865 } 866 else { 867 parent = new NullElementPointer(parent, index); 868 } 869 } 870 } 871 // Proceed with the remaining steps 872 return createNullPointer( 873 context, parent, steps, currentStep + 1); 874 } 875 876 /** 877 * Get a NodeIterator. 878 * @param context evaluation context 879 * @param pointer owning pointer 880 * @param step triggering step 881 * @return NodeIterator 882 */ 883 private static NodeIterator getNodeIterator( 884 EvalContext context, 885 NodePointer pointer, 886 Step step) { 887 if (step.getAxis() == Compiler.AXIS_CHILD) { 888 NodeTest nodeTest = step.getNodeTest(); 889 QName qname = ((NodeNameTest) nodeTest).getNodeName(); 890 String prefix = qname.getPrefix(); 891 if (prefix != null) { 892 String namespaceURI = context.getJXPathContext() 893 .getNamespaceURI(prefix); 894 nodeTest = new NodeNameTest(qname, namespaceURI); 895 } 896 return pointer.childIterator(nodeTest, false, null); 897 } 898 // else Compiler.AXIS_ATTRIBUTE 899 if (!(step.getNodeTest() instanceof NodeNameTest)) { 900 throw new UnsupportedOperationException( 901 "Not supported node test for attributes: " 902 + step.getNodeTest()); 903 } 904 return pointer.attributeIterator( 905 ((NodeNameTest) step.getNodeTest()).getNodeName()); 906 } 907 908 /** 909 * Learn whether <code>name</code> is a lang attribute. 910 * @param name to compare 911 * @return boolean 912 */ 913 private static boolean isLangAttribute(QName name) { 914 return name.getPrefix() != null 915 && name.getPrefix().equals("xml") 916 && name.getName().equals("lang"); 917 } 918}