/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jxpath.ri.axes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.EvalContext;
import org.apache.commons.jxpath.ri.InfoSetUtil;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.ri.compiler.Expression;
import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
import org.apache.commons.jxpath.ri.compiler.NodeTest;
import org.apache.commons.jxpath.ri.compiler.Step;
import org.apache.commons.jxpath.ri.model.NodeIterator;
import org.apache.commons.jxpath.ri.model.NodePointer;
import org.apache.commons.jxpath.ri.model.beans.LangAttributePointer;
import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
/**
* An evaluation mechanism for simple XPaths, which
* is much faster than the usual process. It is only used for
* xpaths which have no context-dependent parts, consist entirely of
* <code>child::name</code> and <code>self::node()</code> steps with
* predicates that either integer or have the form <code>[@name = ...]</code>.
*
* @author Dmitri Plotnikov
* @version $Revision: 652845 $ $Date: 2008-05-02 12:46:46 -0500 (Fri, 02 May 2008) $
*/
public class SimplePathInterpreter {
// Because of the complexity caused by the variety of situations
// that need to be addressed by this class, we attempt to break up
// the class into individual methods addressing those situations
// individually. The names of the methods are supposed to
// give brief descriptions of those situations.
private static final QName QNAME_NAME = new QName(null, "name");
private static final int PERFECT_MATCH = 1000;
// Uncomment this variable and the PATH = ... lines in
// the two following methods in order to be able to print the
// currently evaluated path for debugging of this class
// private static String PATH; // Debugging
/**
* Interpret a simple path that starts with the given root and
* follows the given steps. All steps must have the axis "child::"
* and a name test. They can also optionally have predicates
* of type [@name=expression] or simply [expression] interpreted
* as an index.
* @param context evaluation context
* @param root root pointer
* @param steps path steps
* @return NodePointer
*/
public static NodePointer interpretSimpleLocationPath(
EvalContext context, NodePointer root, Step[] steps) {
// PATH = createNullPointer(context, root, steps, 0).toString(); // Dbg
NodePointer pointer = doStep(context, root, steps, 0);
// return valuePointer(pointer);
return pointer;
}
/**
* Interpret the steps of a simple expression path that
* starts with the given root, which is the result of evaluation
* of the root expression of the expression path, applies the
* given predicates to it and then follows the given steps.
* All steps must have the axis "child::" or "attribute::"
* and a name test. They can also optionally have predicates
* of type [@name=...] or simply [...] interpreted as an index.
* @param context evaluation context
* @param root root pointer
* @param predicates predicates corresponding to <code>steps</code>
* @param steps path steps
* @return NodePointer
*/
public static NodePointer interpretSimpleExpressionPath(
EvalContext context, NodePointer root,
Expression[] predicates, Step[] steps) {
// PATH = createNullPointerForPredicates(context, root,
// steps, -1, predicates, 0).toString(); // Debugging
NodePointer pointer =
doPredicate(context, root, steps, -1, predicates, 0);
// return valuePointer(pointer);
return pointer;
}
/**
* Recursive evaluation of a path. The general plan is:
* Look at the current step,
* find nodes that match it,
* iterate over those nodes and
* for each of them call doStep again for subsequent steps.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
private static NodePointer doStep(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep) {
if (parent == null) {
return null;
}
if (currentStep == steps.length) {
// We have reached the end of the list of steps
return parent;
}
// Open all containers
parent = valuePointer(parent);
Step step = steps[currentStep];
Expression[] predicates = step.getPredicates();
// Divide and conquer: the process is broken out into
// four major use cases.
// 1. Current step has no predicates and
// the root is a property owner (e.g. bean or map)
// 2. Current step has predicates and
// the root is a property owner (e.g. bean or map)
// 3. Current step has no predicates and
// the root is an InfoSet standard node (e.g. DOM Node)
// 4. Current step has predicates and
// the root is an InfoSet standard node (e.g. DOM Node)
if (parent instanceof PropertyOwnerPointer) {
if (predicates == null || predicates.length == 0) {
return doStepNoPredicatesPropertyOwner(
context,
(PropertyOwnerPointer) parent,
steps,
currentStep);
}
return doStepPredicatesPropertyOwner(
context,
(PropertyOwnerPointer) parent,
steps,
currentStep);
}
if (predicates == null || predicates.length == 0) {
return doStepNoPredicatesStandard(
context,
parent,
steps,
currentStep);
}
return doStepPredicatesStandard(
context,
parent,
steps,
currentStep);
}
/**
* We have a step that starts with a property owner (bean, map, etc) and has
* no predicates. The name test of the step may map to a scalar property
* or to a collection. If it is a collection, we should apply the tail of
* the path to each element until we find a match. If we don't find
* a perfect match, we should return the "best quality" pointer, which
* has the longest chain of steps mapping to existing nodes and the shortes
* tail of Null* pointers.
* @param context evaluation context
* @param parentPointer property owner pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
private static NodePointer doStepNoPredicatesPropertyOwner(
EvalContext context, PropertyOwnerPointer parentPointer,
Step[] steps, int currentStep) {
Step step = steps[currentStep];
NodePointer childPointer =
createChildPointerForStep(parentPointer, step);
if (childPointer == null) {
return null;
}
if (!childPointer.isActual()) {
// The property does not exist - create a null pointer.
return createNullPointer(
context,
parentPointer,
steps,
currentStep);
}
if (currentStep == steps.length - 1) {
// If this is the last step - we are done, we found it
return childPointer;
}
if (childPointer.isCollection()) {
// Iterate over all values and
// execute remaining steps for each node,
// looking for the best quality match
int bestQuality = 0;
childPointer = (NodePointer) childPointer.clone();
NodePointer bestMatch = null;
int count = childPointer.getLength();
for (int i = 0; i < count; i++) {
childPointer.setIndex(i);
NodePointer pointer =
doStep(context, childPointer, steps, currentStep + 1);
int quality = computeQuality(pointer);
if (quality == PERFECT_MATCH) {
return pointer;
}
else if (quality > bestQuality) {
bestQuality = quality;
bestMatch = (NodePointer) pointer.clone();
}
}
if (bestMatch != null) {
return bestMatch;
}
// This step did not find anything - return a null pointer
return createNullPointer(context, childPointer, steps, currentStep);
}
// Evaluate subsequent steps
return doStep(context, childPointer, steps, currentStep + 1);
}
/**
* A path that starts with a standard InfoSet node (e.g. DOM Node) and
* has no predicates. Get a child iterator and apply the tail of
* the path to each element until we find a match. If we don't find
* a perfect match, we should return the "best quality" pointer, which
* has the longest chain of steps mapping to existing nodes and the shortes
* tail of Null* pointers.
* @param context evaluation context
* @param parentPointer parent pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
private static NodePointer doStepNoPredicatesStandard(
EvalContext context, NodePointer parentPointer,
Step[] steps, int currentStep) {
Step step = steps[currentStep];
if (step.getAxis() == Compiler.AXIS_SELF) {
return doStep(context, parentPointer, steps, currentStep + 1);
}
int bestQuality = 0;
NodePointer bestMatch = null;
NodeIterator it = getNodeIterator(context, parentPointer, step);
if (it != null) {
for (int i = 1; it.setPosition(i); i++) {
NodePointer childPointer = it.getNodePointer();
if (steps.length == currentStep + 1) {
// If this is the last step - we are done, we found it
return childPointer;
}
NodePointer pointer = doStep(
context, childPointer, steps, currentStep + 1);
int quality = computeQuality(pointer);
if (quality == PERFECT_MATCH) {
return pointer;
}
if (quality > bestQuality) {
bestQuality = quality;
bestMatch = (NodePointer) pointer.clone();
}
}
}
return bestMatch != null ? bestMatch
: createNullPointer(context, parentPointer, steps, currentStep);
}
/**
* A path that starts with a property owner. The method evaluates
* the first predicate in a special way and then forwards to
* a general predicate processing method.
* @param context evaluation context
* @param parentPointer parent pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
private static NodePointer doStepPredicatesPropertyOwner(
EvalContext context, PropertyOwnerPointer parentPointer,
Step[] steps, int currentStep) {
Step step = steps[currentStep];
Expression[] predicates = step.getPredicates();
NodePointer childPointer =
createChildPointerForStep(parentPointer, step);
if (!childPointer.isActual()) {
// Property does not exist - return a null pointer
return createNullPointer(
context,
parentPointer,
steps,
currentStep);
}
// Evaluate predicates
return doPredicate(
context,
childPointer,
steps,
currentStep,
predicates,
0);
}
/**
* Create the child pointer for a given step.
* @param parentPointer parent pointer
* @param step associated step
* @return NodePointer
*/
private static NodePointer createChildPointerForStep(
PropertyOwnerPointer parentPointer, Step step) {
int axis = step.getAxis();
if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
if (axis == Compiler.AXIS_ATTRIBUTE && isLangAttribute(name)) {
return new LangAttributePointer(parentPointer);
}
if (parentPointer.isValidProperty(name)) {
NodePointer childPointer = parentPointer.getPropertyPointer();
((PropertyPointer) childPointer).setPropertyName(
name.toString());
childPointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
return childPointer;
}
//invalid property gets nothing, not even a NullPointer
return null;
}
return parentPointer;
}
/**
* A path that starts with a standard InfoSet node, e.g. a DOM Node.
* The method evaluates the first predicate in a special way and
* then forwards to a general predicate processing method.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
private static NodePointer doStepPredicatesStandard(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep) {
Step step = steps[currentStep];
Expression[] predicates = step.getPredicates();
int axis = step.getAxis();
if (axis == Compiler.AXIS_SELF) {
return doPredicate(
context,
parent,
steps,
currentStep,
predicates,
0);
}
Expression predicate = predicates[0];
// Optimize for a single predicate to avoid building a list
// and to allow the direct access to the index'th element
// in the case of a simple subscript predecate
// It is a very common use case, so it deserves individual
// attention
if (predicates.length == 1) {
NodeIterator it = getNodeIterator(context, parent, step);
NodePointer pointer = null;
if (it != null) {
if (predicate instanceof NameAttributeTest) { // [@name = key]
String key = keyFromPredicate(context, predicate);
for (int i = 1; it.setPosition(i); i++) {
NodePointer ptr = it.getNodePointer();
if (isNameAttributeEqual(ptr, key)) {
pointer = ptr;
break;
}
}
}
else {
int index = indexFromPredicate(context, predicate);
if (it.setPosition(index + 1)) {
pointer = it.getNodePointer();
}
}
}
if (pointer != null) {
return doStep(context, pointer, steps, currentStep + 1);
}
}
else {
NodeIterator it = getNodeIterator(context, parent, step);
if (it != null) {
List list = new ArrayList();
for (int i = 1; it.setPosition(i); i++) {
list.add(it.getNodePointer());
}
NodePointer pointer =
doPredicatesStandard(
context,
list,
steps,
currentStep,
predicates,
0);
if (pointer != null) {
return pointer;
}
}
}
return createNullPointer(context, parent, steps, currentStep);
}
/**
* Evaluates predicates and proceeds with the subsequent steps
* of the path.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @param predicates predicate expressions
* @param currentPredicate int predicate number
* @return NodePointer
*/
private static NodePointer doPredicate(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep,
Expression[] predicates, int currentPredicate) {
if (currentPredicate == predicates.length) {
return doStep(context, parent, steps, currentStep + 1);
}
Expression predicate = predicates[currentPredicate];
if (predicate instanceof NameAttributeTest) { // [@name = key1]
return doPredicateName(
context,
parent,
steps,
currentStep,
predicates,
currentPredicate);
}
// else [index]
return doPredicateIndex(
context,
parent,
steps,
currentStep,
predicates,
currentPredicate);
}
/**
* Execute a NameAttributeTest predicate
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep int step number
* @param predicates predicates
* @param currentPredicate int predicate number
* @return NodePointer
*/
private static NodePointer doPredicateName(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep,
Expression[] predicates, int currentPredicate) {
Expression predicate = predicates[currentPredicate];
String key = keyFromPredicate(context, predicate);
NodePointer child = valuePointer(parent);
if (child instanceof PropertyOwnerPointer) {
PropertyPointer pointer =
((PropertyOwnerPointer) child).getPropertyPointer();
pointer.setPropertyName(key);
if (pointer.isActual()) {
return doPredicate(
context,
pointer,
steps,
currentStep,
predicates,
currentPredicate + 1);
}
}
else if (child.isCollection()) {
// For each node in the collection, perform the following:
// if the node is a property owner, apply this predicate to it;
// if the node is a collection, apply this predicate to each elem.;
// if the node is not a prop owner or a collection,
// see if it has the attribute "name" with the right value,
// if so - proceed to the next predicate
NodePointer bestMatch = null;
int bestQuality = 0;
child = (NodePointer) child.clone();
int count = child.getLength();
for (int i = 0; i < count; i++) {
child.setIndex(i);
NodePointer valuePointer = valuePointer(child);
NodePointer pointer;
if ((valuePointer instanceof PropertyOwnerPointer)
|| valuePointer.isCollection()) {
pointer =
doPredicateName(
context,
valuePointer,
steps,
currentStep,
predicates,
currentPredicate);
}
else if (isNameAttributeEqual(valuePointer, key)) {
pointer =
doPredicate(
context,
valuePointer,
steps,
currentStep,
predicates,
currentPredicate + 1);
}
else {
pointer = null;
}
if (pointer != null) {
int quality = computeQuality(pointer);
if (quality == PERFECT_MATCH) {
return pointer;
}
if (quality > bestQuality) {
bestMatch = (NodePointer) pointer.clone();
bestQuality = quality;
}
}
}
if (bestMatch != null) {
return bestMatch;
}
}
else {
// If the node is a standard InfoSet node (e.g. DOM Node),
// employ doPredicates_standard, which will iterate through
// the node's children and apply all predicates
NodePointer found =
doPredicatesStandard(
context,
Collections.singletonList(child),
steps,
currentStep,
predicates,
currentPredicate);
if (found != null) {
return found;
}
}
// If nothing worked - return a null pointer
return createNullPointerForPredicates(
context,
child,
steps,
currentStep,
predicates,
currentPredicate);
}
/**
* Called exclusively for standard InfoSet nodes, e.g. DOM nodes
* to evaluate predicate sequences like [@name=...][@name=...][index].
* @param context evaluation context
* @param parents List of parent pointers
* @param steps path steps
* @param currentStep step number
* @param predicates predicates
* @param currentPredicate int predicate number
* @return NodePointer
*/
private static NodePointer doPredicatesStandard(
EvalContext context, List parents,
Step[] steps, int currentStep,
Expression[] predicates, int currentPredicate) {
if (parents.size() == 0) {
return null;
}
// If all predicates have been processed, take the first
// element from the list of results and proceed to the
// remaining steps with that element.
if (currentPredicate == predicates.length) {
NodePointer pointer = (NodePointer) parents.get(0);
return doStep(context, pointer, steps, currentStep + 1);
}
Expression predicate = predicates[currentPredicate];
if (predicate instanceof NameAttributeTest) {
String key = keyFromPredicate(context, predicate);
List newList = new ArrayList();
for (int i = 0; i < parents.size(); i++) {
NodePointer pointer = (NodePointer) parents.get(i);
if (isNameAttributeEqual(pointer, key)) {
newList.add(pointer);
}
}
if (newList.size() == 0) {
return null;
}
return doPredicatesStandard(
context,
newList,
steps,
currentStep,
predicates,
currentPredicate + 1);
}
else {
// For a subscript, simply take the corresponding
// element from the list of results and
// proceed to the remaining predicates with that element
int index = indexFromPredicate(context, predicate);
if (index < 0 || index >= parents.size()) {
return null;
}
NodePointer ptr = (NodePointer) parents.get(index);
return doPredicate(
context,
ptr,
steps,
currentStep,
predicates,
currentPredicate + 1);
}
}
/**
* Evaluate a subscript predicate: see if the node is a collection and
* if the index is inside the collection.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @param predicates predicates
* @param currentPredicate int predicate number
* @return NodePointer
*/
private static NodePointer doPredicateIndex(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep,
Expression[] predicates, int currentPredicate) {
Expression predicate = predicates[currentPredicate];
int index = indexFromPredicate(context, predicate);
NodePointer pointer = parent;
if (isCollectionElement(pointer, index)) {
pointer = (NodePointer) pointer.clone();
pointer.setIndex(index);
return doPredicate(
context,
pointer,
steps,
currentStep,
predicates,
currentPredicate + 1);
}
return createNullPointerForPredicates(
context,
parent,
steps,
currentStep,
predicates,
currentPredicate);
}
/**
* Extract an integer from a subscript predicate. The returned index
* starts with 0, even though the subscript starts with 1.
* @param context evaluation context
* @param predicate to evaluate
* @return calculated index
*/
private static int indexFromPredicate(
EvalContext context,
Expression predicate) {
Object value = predicate.computeValue(context);
if (value instanceof EvalContext) {
value = ((EvalContext) value).getSingleNodePointer();
}
if (value instanceof NodePointer) {
value = ((NodePointer) value).getValue();
}
if (value == null) {
throw new JXPathException("Predicate value is null: " + predicate);
}
if (value instanceof Number) {
final double round = 0.5;
return (int) (InfoSetUtil.doubleValue(value) + round) - 1;
}
return InfoSetUtil.booleanValue(value) ? 0 : -1;
}
/**
* Extracts the string value of the expression from a predicate like
* [@name=expression].
* @param context evaluation context
* @param predicate predicate to evaluate
* @return String key extracted
*/
private static String keyFromPredicate(EvalContext context,
Expression predicate) {
Expression expr =
((NameAttributeTest) predicate).getNameTestExpression();
return InfoSetUtil.stringValue(expr.computeValue(context));
}
/**
* For a pointer that matches an actual node, returns 0.
* For a pointer that does not match an actual node, but whose
* parent pointer does returns -1, etc.
* @param pointer input pointer
* @return int match quality code
*/
private static int computeQuality(NodePointer pointer) {
int quality = PERFECT_MATCH;
while (pointer != null && !pointer.isActual()) {
quality--;
pointer = pointer.getImmediateParentPointer();
}
return quality;
}
/**
* Returns true if the pointer has an attribute called "name" and
* its value is equal to the supplied string.
* @param pointer input pointer
* @param name name to check
* @return boolean
*/
private static boolean isNameAttributeEqual(
NodePointer pointer,
String name) {
NodeIterator it = pointer.attributeIterator(QNAME_NAME);
return it != null
&& it.setPosition(1)
&& name.equals(it.getNodePointer().getValue());
}
/**
* Returns true if the pointer is a collection and the index is
* withing the bounds of the collection.
* @param pointer input pointer
* @param index to check
* @return boolean
*/
private static boolean isCollectionElement(
NodePointer pointer,
int index) {
return pointer.isActual()
&& (index == 0
|| (pointer.isCollection()
&& index >= 0
&& index < pointer.getLength()));
}
/**
* For an intermediate pointer (e.g. PropertyPointer, ContainerPointer)
* returns a pointer for the contained value.
* @param pointer input pointer
* @return NodePointer
*/
private static NodePointer valuePointer(NodePointer pointer) {
return pointer == null ? null : pointer.getValuePointer();
}
/**
* Creates a "null pointer" that
* a) represents the requested path and
* b) can be used for creation of missing nodes in the path.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @return NodePointer
*/
public static NodePointer createNullPointer(
EvalContext context, NodePointer parent, Step[] steps,
int currentStep) {
if (currentStep == steps.length) {
return parent;
}
parent = valuePointer(parent);
Step step = steps[currentStep];
int axis = step.getAxis();
if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
NullPropertyPointer pointer = new NullPropertyPointer(parent);
QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
pointer.setPropertyName(name.toString());
pointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
parent = pointer;
}
// else { it is self::node() }
Expression[] predicates = step.getPredicates();
return createNullPointerForPredicates(
context,
parent,
steps,
currentStep,
predicates,
0);
}
/**
* Creates a "null pointer" that starts with predicates.
* @param context evaluation context
* @param parent parent pointer
* @param steps path steps
* @param currentStep step number
* @param predicates predicates
* @param currentPredicate int predicate number
* @return NodePointer
*/
private static NodePointer createNullPointerForPredicates(
EvalContext context, NodePointer parent,
Step[] steps, int currentStep,
Expression[] predicates, int currentPredicate) {
for (int i = currentPredicate; i < predicates.length; i++) {
Expression predicate = predicates[i];
if (predicate instanceof NameAttributeTest) {
String key = keyFromPredicate(context, predicate);
parent = valuePointer(parent);
NullPropertyPointer pointer = new NullPropertyPointer(parent);
pointer.setNameAttributeValue(key);
parent = pointer;
}
else {
int index = indexFromPredicate(context, predicate);
if (parent instanceof NullPropertyPointer) {
parent.setIndex(index);
}
else {
parent = new NullElementPointer(parent, index);
}
}
}
// Proceed with the remaining steps
return createNullPointer(
context, parent, steps, currentStep + 1);
}
/**
* Get a NodeIterator.
* @param context evaluation context
* @param pointer owning pointer
* @param step triggering step
* @return NodeIterator
*/
private static NodeIterator getNodeIterator(
EvalContext context,
NodePointer pointer,
Step step) {
if (step.getAxis() == Compiler.AXIS_CHILD) {
NodeTest nodeTest = step.getNodeTest();
QName qname = ((NodeNameTest) nodeTest).getNodeName();
String prefix = qname.getPrefix();
if (prefix != null) {
String namespaceURI = context.getJXPathContext()
.getNamespaceURI(prefix);
nodeTest = new NodeNameTest(qname, namespaceURI);
}
return pointer.childIterator(nodeTest, false, null);
}
// else Compiler.AXIS_ATTRIBUTE
if (!(step.getNodeTest() instanceof NodeNameTest)) {
throw new UnsupportedOperationException(
"Not supported node test for attributes: "
+ step.getNodeTest());
}
return pointer.attributeIterator(
((NodeNameTest) step.getNodeTest()).getNodeName());
}
/**
* Learn whether <code>name</code> is a lang attribute.
* @param name to compare
* @return boolean
*/
private static boolean isLangAttribute(QName name) {
return name.getPrefix() != null
&& name.getPrefix().equals("xml")
&& name.getName().equals("lang");
}
}
|