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