package gnu.xml.xpath;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.xml.XMLConstants;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
public final class Selector
extends Path
{
public static final int ANCESTOR = 0;
public static final int ANCESTOR_OR_SELF = 1;
public static final int ATTRIBUTE = 2;
public static final int CHILD = 3;
public static final int DESCENDANT = 4;
public static final int DESCENDANT_OR_SELF = 5;
public static final int FOLLOWING = 6;
public static final int FOLLOWING_SIBLING = 7;
public static final int NAMESPACE = 8;
public static final int PARENT = 9;
public static final int PRECEDING = 10;
public static final int PRECEDING_SIBLING = 11;
public static final int SELF = 12;
final int axis;
final Test[] tests;
public Selector(int axis, List tests)
{
this.axis = axis;
this.tests = new Test[tests.size()];
tests.toArray(this.tests);
if (axis == NAMESPACE &&
this.tests.length > 0 &&
this.tests[0] instanceof NameTest)
{
NameTest nt = (NameTest) this.tests[0];
this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
}
}
public Test[] getTests()
{
return tests;
}
public boolean matches(Node context)
{
short nodeType = context.getNodeType();
switch (axis)
{
case CHILD:
if (nodeType == Node.ATTRIBUTE_NODE)
{
return false;
}
break;
case ATTRIBUTE:
case NAMESPACE:
if (nodeType != Node.ATTRIBUTE_NODE)
{
return false;
}
break;
case DESCENDANT_OR_SELF:
return true;
default:
return false;
}
int tlen = tests.length;
if (tlen > 0)
{
int pos = getContextPosition(context);
int len = getContextSize(context);
for (int j = 0; j < tlen && len > 0; j++)
{
Test test = tests[j];
if (!test.matches(context, pos, len))
{
return false;
}
}
}
return true;
}
private int getContextPosition(Node ctx)
{
int pos = 1;
for (ctx = ctx.getPreviousSibling(); ctx != null;
ctx = ctx.getPreviousSibling())
{
pos++;
}
return pos;
}
private int getContextSize(Node ctx)
{
if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
{
Node parent = ((Attr) ctx).getOwnerElement();
return parent.getAttributes().getLength();
}
Node parent = ctx.getParentNode();
if (parent != null)
{
return parent.getChildNodes().getLength();
}
return 1;
}
public Object evaluate(Node context, int pos, int len)
{
Set acc = new LinkedHashSet();
addCandidates(context, acc);
List candidates = new ArrayList(acc);
List ret = filterCandidates(candidates, false);
return ret;
}
Collection evaluate(Node context, Collection ns)
{
Set acc = new LinkedHashSet();
for (Iterator i = ns.iterator(); i.hasNext(); )
{
addCandidates((Node) i.next(), acc);
}
List candidates = new ArrayList(acc);
List ret = filterCandidates(candidates, true);
return ret;
}
List filterCandidates(List candidates, boolean cascade)
{
int len = candidates.size();
int tlen = tests.length;
if (tlen > 0 && len > 0)
{
for (int j = 0; j < tlen && len > 0; j++)
{
Test test = tests[j];
List successful = new ArrayList(len);
for (int i = 0; i < len; i++)
{
Node node = (Node) candidates.get(i);
if (cascade)
{
short nodeType = node.getNodeType();
if ((nodeType == Node.DOCUMENT_NODE ||
nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
(axis == DESCENDANT_OR_SELF ||
axis == ANCESTOR_OR_SELF ||
axis == SELF) &&
(tests.length == 1 &&
tests[0] instanceof NodeTypeTest &&
((NodeTypeTest) tests[0]).type == (short) 0))
{
successful.add(node);
continue;
}
}
if (test.matches(node, i + 1, len))
{
successful.add(node);
}
}
candidates = successful;
len = candidates.size();
}
}
return candidates;
}
void addCandidates(Node context, Collection candidates)
{
switch (axis)
{
case CHILD:
addChildNodes(context, candidates, false);
break;
case DESCENDANT:
addChildNodes(context, candidates, true);
break;
case DESCENDANT_OR_SELF:
candidates.add (context);
addChildNodes(context, candidates, true);
break;
case PARENT:
addParentNode(context, candidates, false);
break;
case ANCESTOR:
addParentNode(context, candidates, true);
break;
case ANCESTOR_OR_SELF:
candidates.add(context);
addParentNode(context, candidates, true);
break;
case FOLLOWING_SIBLING:
addFollowingNodes(context, candidates, false);
break;
case PRECEDING_SIBLING:
addPrecedingNodes(context, candidates, false);
break;
case FOLLOWING:
addFollowingNodes(context, candidates, true);
break;
case PRECEDING:
addPrecedingNodes(context, candidates, true);
break;
case ATTRIBUTE:
addAttributes(context, candidates);
break;
case NAMESPACE:
addNamespaceAttributes(context, candidates);
break;
case SELF:
candidates.add(context);
break;
}
}
void addChildNodes(Node context, Collection acc, boolean recurse)
{
Node child = context.getFirstChild();
while (child != null)
{
acc.add(child);
if (recurse)
{
addChildNodes(child, acc, recurse);
}
child = child.getNextSibling();
}
}
void addParentNode(Node context, Collection acc, boolean recurse)
{
Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
((Attr) context).getOwnerElement() : context.getParentNode();
if (parent != null)
{
acc.add(parent);
if (recurse)
{
addParentNode(parent, acc, recurse);
}
}
}
void addFollowingNodes(Node context, Collection acc, boolean recurse)
{
Node cur = context.getNextSibling();
while (cur != null)
{
acc.add(cur);
if (recurse)
{
addChildNodes(cur, acc, true);
}
cur = cur.getNextSibling();
}
if (recurse)
{
context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
((Attr) context).getOwnerElement() : context.getParentNode();
if (context != null)
{
addFollowingNodes(context, acc, recurse);
}
}
}
void addPrecedingNodes(Node context, Collection acc, boolean recurse)
{
Node cur = context.getPreviousSibling();
while (cur != null)
{
acc.add(cur);
if (recurse)
{
addChildNodes(cur, acc, true);
}
cur = cur.getPreviousSibling();
}
if (recurse)
{
context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
((Attr) context).getOwnerElement() : context.getParentNode();
if (context != null)
{
addPrecedingNodes(context, acc, recurse);
}
}
}
void addAttributes(Node context, Collection acc)
{
NamedNodeMap attrs = context.getAttributes();
if (attrs != null)
{
int attrLen = attrs.getLength();
for (int i = 0; i < attrLen; i++)
{
Node attr = attrs.item(i);
if (!isNamespaceAttribute(attr))
{
acc.add(attr);
}
}
}
}
void addNamespaceAttributes(Node context, Collection acc)
{
NamedNodeMap attrs = context.getAttributes();
if (attrs != null)
{
int attrLen = attrs.getLength();
for (int i = 0; i < attrLen; i++)
{
Node attr = attrs.item(i);
if (isNamespaceAttribute(attr))
{
acc.add(attr);
}
}
}
}
final boolean isNamespaceAttribute(Node node)
{
String uri = node.getNamespaceURI();
return (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()));
}
public Expr clone(Object context)
{
int len = tests.length;
List tests2 = new ArrayList(len);
for (int i = 0; i < len; i++)
{
tests2.add(tests[i].clone(context));
}
return new Selector(axis, tests2);
}
public String toString()
{
StringBuffer buf = new StringBuffer();
switch (axis)
{
case ANCESTOR:
buf.append("ancestor::");
break;
case ANCESTOR_OR_SELF:
buf.append("ancestor-or-self::");
break;
case ATTRIBUTE:
buf.append("attribute::");
break;
case CHILD:
break;
case DESCENDANT:
buf.append("descendant::");
break;
case DESCENDANT_OR_SELF:
buf.append("descendant-or-self::");
break;
case FOLLOWING:
buf.append("following::");
break;
case FOLLOWING_SIBLING:
buf.append("following-sibling::");
break;
case NAMESPACE:
buf.append("namespace::");
break;
case PARENT:
if (tests.length == 0 ||
(tests[0] instanceof NodeTypeTest &&
((NodeTypeTest) tests[0]).type == 0))
{
return "..";
}
buf.append("parent::");
break;
case PRECEDING:
buf.append("preceding::");
break;
case PRECEDING_SIBLING:
buf.append("preceding-sibling::");
break;
case SELF:
if (tests.length == 0 ||
(tests[0] instanceof NodeTypeTest &&
((NodeTypeTest) tests[0]).type == 0))
{
return ".";
}
buf.append("self::");
break;
}
if (tests.length == 0)
{
buf.append('*');
}
else
{
for (int i = 0; i < tests.length; i++)
{
buf.append(tests[i]);
}
}
return buf.toString();
}
}