package lux.xpath;
import lux.xml.QName;
import lux.xml.ValueType;
public class NodeTest {
private final ValueType type;
private final QName name;
/** @param type the type of test: one of DOCUMENT, NODE, ELEMENT,
* ATTRIBUTE, TEXT, PROCESSING_INSTRUCTION or COMMENT.
*
* @param name the name of the node that the test matches, or null
* if any node of the given type matches.
*
* <p> The local * name may be "*", indicating a match to any local
* name.
*
* <p> The prefix may be "*" to indicate any namespace matches (in
* which case the namespace uri is ignored and should be null).
*
* <p> The namespace uri is preserved for use in query generation,
* however it is the caller's responsibility to ensure that namespace
* prefix bindings are consistent within an expression tree, and with
* the surrounding environment.
* </p>
*
* <p> For node tests without names (node(), comment(), text()), the
* name is ignored and should be null. For node tests without namespaces
* (processing-instruction()), the namespace uri and prefix are both
* ignored and should be null.
*
* @throws IllegalArgumentException if the type is not a node type,
* @throws NullPointerException if the type is null
*/
public NodeTest (ValueType type, QName name) {
if (type.isAtomic) {
throw new IllegalArgumentException ("Attempt to construct a NodeTest with type " +
type + " which is not a type of node");
}
this.type = type;
this.name = name;
}
public NodeTest (ValueType type) {
this (type, null);
}
public ValueType getType () {
return type;
}
public QName getQName() {
return name;
}
public boolean isWild () {
return name == null || name.getLocalPart().equals("*"); // namespace wildcard?
}
@Override
public String toString () {
StringBuilder buf = new StringBuilder ();
toString (buf);
return buf.toString();
}
public void toString (StringBuilder buf) {
if (name == null) {
buf.append (type.name).append ("()");
return;
}
switch (type) {
case NODE: case COMMENT: case TEXT:
buf.append (type.name).append ("()");
break;
case ATTRIBUTE:
case ELEMENT:
if ("*".equals(name.getLocalPart())) {
if (name.getPrefix().isEmpty()) {
buf.append (type.name).append ("()");
} else {
buf.append(name.getPrefix()).append(":*");
}
} else {
if ("*".equals(name.getPrefix())) {
name.toString(buf);
} else {
buf.append (type.name).append ("(");
name.toString(buf);
buf.append(')');
}
}
break;
case DOCUMENT:
buf.append (type.name).append("(element(");
name.toString(buf);
buf.append("))");
break;
case PROCESSING_INSTRUCTION:
buf.append (type.name).append('(').append(name.getLocalPart()).append(')');
break;
default:
throw new IllegalArgumentException ("invalid node type " + type);
}
}
public boolean equivalent (Object other) {
return other != null && other instanceof NodeTest && type == ((NodeTest) other).type &&
name.equals(((NodeTest) other).name);
}
public boolean propGreaterEqual (NodeTest other) {
// TODO: wildcard namespace
return (type == other.type || type == ValueType.NODE) &&
(isWild() ||
(!other.isWild() && name.equals(((NodeTest) other).name)));
}
public int equivHash () {
return type.ordinal() * (name == null ? 91 : name.hashCode());
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */