mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			504 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			504 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
/* Selector.java --
 | 
						|
   Copyright (C) 2004,2006 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package gnu.xml.xpath;
 | 
						|
 | 
						|
import gnu.java.lang.CPStringBuilder;
 | 
						|
 | 
						|
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 javax.xml.namespace.QName;
 | 
						|
import org.w3c.dom.Attr;
 | 
						|
import org.w3c.dom.NamedNodeMap;
 | 
						|
import org.w3c.dom.Node;
 | 
						|
 | 
						|
/**
 | 
						|
 * A single component of a location path.
 | 
						|
 *
 | 
						|
 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
 | 
						|
 */
 | 
						|
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;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Axis to select nodes in.
 | 
						|
   */
 | 
						|
  final int axis;
 | 
						|
 | 
						|
  /**
 | 
						|
   * List of tests to perform on candidates.
 | 
						|
   */
 | 
						|
  final Test[] tests;
 | 
						|
 | 
						|
  public Selector(int axis, List<? extends Test> tests)
 | 
						|
  {
 | 
						|
    this.axis = axis;
 | 
						|
    int len = tests.size();
 | 
						|
    this.tests = new Test[(len == 0) ? 1 : len];
 | 
						|
    if (len > 0)
 | 
						|
      tests.toArray(this.tests);
 | 
						|
    else
 | 
						|
      this.tests[0] = new NodeTypeTest((short) 0);
 | 
						|
    if (axis == NAMESPACE && this.tests[0] instanceof NameTest)
 | 
						|
      {
 | 
						|
        NameTest nt = (NameTest) this.tests[0];
 | 
						|
        this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the list of tests to perform on candidates.
 | 
						|
   */
 | 
						|
  public Test[] getTests()
 | 
						|
  {
 | 
						|
    return tests;
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean matches(Node context)
 | 
						|
  {
 | 
						|
    // If called directly, selector is the top level of the path
 | 
						|
    return matches(context,
 | 
						|
                   getContextPosition(context),
 | 
						|
                   getContextSize(context));
 | 
						|
  }
 | 
						|
 | 
						|
  boolean matches(Node context, int pos, int len)
 | 
						|
  {
 | 
						|
    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;
 | 
						|
      }
 | 
						|
    for (int j = 0; j < tests.length && 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())
 | 
						|
      {
 | 
						|
        if (tests[0].matches(ctx, 1, 1))
 | 
						|
          pos++;
 | 
						|
      }
 | 
						|
    return pos;
 | 
						|
  }
 | 
						|
 | 
						|
  private int getContextSize(Node ctx)
 | 
						|
  {
 | 
						|
    if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
 | 
						|
      {
 | 
						|
        Node owner = ((Attr) ctx).getOwnerElement();
 | 
						|
        return owner.getAttributes().getLength();
 | 
						|
      }
 | 
						|
    int count = 1;
 | 
						|
    Node sib = ctx.getPreviousSibling();
 | 
						|
    for (; sib != null; sib = sib.getPreviousSibling())
 | 
						|
      {
 | 
						|
        if (tests[0].matches(ctx, 1, 1))
 | 
						|
          count++;
 | 
						|
      }
 | 
						|
    sib = ctx.getNextSibling();
 | 
						|
    for (; sib != null; sib = sib.getNextSibling())
 | 
						|
      {
 | 
						|
        if (tests[0].matches(ctx, 1, 1))
 | 
						|
          count++;
 | 
						|
      }
 | 
						|
    return count;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  @Override
 | 
						|
  public Object evaluate(Node context, int pos, int len)
 | 
						|
  {
 | 
						|
    Set<Node> acc = new LinkedHashSet<Node>();
 | 
						|
    addCandidates(context, acc);
 | 
						|
    List<Node> candidates = new ArrayList<Node>(acc);
 | 
						|
    List<Node> ret = filterCandidates(candidates, false);
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  Collection<Node> evaluate(Node context, Collection<Node> ns)
 | 
						|
  {
 | 
						|
    Set<Node> acc = new LinkedHashSet<Node>();
 | 
						|
    for (Iterator<Node> i = ns.iterator(); i.hasNext(); )
 | 
						|
      addCandidates(i.next(), acc);
 | 
						|
    List<Node> candidates = new ArrayList<Node>(acc);
 | 
						|
    List<Node> ret = filterCandidates(candidates, true);
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Filter the given list of candidates according to the node tests.
 | 
						|
   */
 | 
						|
  List<Node> filterCandidates(List<Node> candidates, boolean cascade)
 | 
						|
  {
 | 
						|
    int len = candidates.size();
 | 
						|
    int tlen = tests.length;
 | 
						|
    if (tlen > 0 && len > 0)
 | 
						|
      {
 | 
						|
        // Present the result of each successful generation to the next test
 | 
						|
        for (int j = 0; j < tlen && len > 0; j++)
 | 
						|
          {
 | 
						|
            Test test = tests[j];
 | 
						|
            List<Node> successful = new ArrayList<Node>(len);
 | 
						|
            for (int i = 0; i < len; i++)
 | 
						|
              {
 | 
						|
                Node node = candidates.get(i);
 | 
						|
                if (cascade)
 | 
						|
                  {
 | 
						|
                    // Documents and DocumentFragments should be considered
 | 
						|
                    // if part of a location path where the axis involves
 | 
						|
                    // the SELF concept
 | 
						|
                    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<Node> candidates)
 | 
						|
  {
 | 
						|
    // Build list of 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<Node> 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<Node> 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<Node> acc, boolean recurse)
 | 
						|
  {
 | 
						|
    if (context != null && recurse)
 | 
						|
      addChildNodes(context, acc, true);
 | 
						|
    Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
 | 
						|
      context.getNextSibling();
 | 
						|
    while (cur != null)
 | 
						|
      {
 | 
						|
        acc.add(cur);
 | 
						|
        if (recurse)
 | 
						|
          addChildNodes(cur, acc, true);
 | 
						|
        cur = cur.getNextSibling();
 | 
						|
      }
 | 
						|
    if (recurse)
 | 
						|
      {
 | 
						|
        while (context != null)
 | 
						|
          {
 | 
						|
            context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
 | 
						|
              ((Attr) context).getOwnerElement() : context.getParentNode();
 | 
						|
            if (context != null)
 | 
						|
              {
 | 
						|
                cur = context.getNextSibling();
 | 
						|
                while (cur != null)
 | 
						|
                  {
 | 
						|
                    acc.add(cur);
 | 
						|
                    if (recurse)
 | 
						|
                      addChildNodes(cur, acc, true);
 | 
						|
                    cur = cur.getNextSibling();
 | 
						|
                  }
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  void addPrecedingNodes(Node context, Collection<Node> acc, boolean recurse)
 | 
						|
  {
 | 
						|
    Node cur = (context.getNodeType() == Node.ATTRIBUTE_NODE) ? null :
 | 
						|
      context.getPreviousSibling();
 | 
						|
    while (cur != null)
 | 
						|
      {
 | 
						|
        acc.add(cur);
 | 
						|
        if (recurse)
 | 
						|
          addChildNodes(cur, acc, true);
 | 
						|
        cur = cur.getPreviousSibling();
 | 
						|
      }
 | 
						|
    if (recurse)
 | 
						|
      {
 | 
						|
        cur = context;
 | 
						|
        cur = (cur.getNodeType() == Node.ATTRIBUTE_NODE) ?
 | 
						|
          ((Attr) cur).getOwnerElement() : cur.getParentNode();
 | 
						|
        if (cur != null)
 | 
						|
          addPrecedingNodes(cur, acc, recurse);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  void addAttributes(Node context, Collection<Node> 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<Node> 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<Test> tests2 = new ArrayList<Test>(len);
 | 
						|
    for (int i = 0; i < len; i++)
 | 
						|
      tests2.add(tests[i].clone(context));
 | 
						|
    return new Selector(axis, tests2);
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean references(QName var)
 | 
						|
  {
 | 
						|
    for (int i = 0; i < tests.length; i++)
 | 
						|
      {
 | 
						|
        if (tests[i].references(var))
 | 
						|
          return true;
 | 
						|
      }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  public String toString()
 | 
						|
  {
 | 
						|
    CPStringBuilder buf = new CPStringBuilder();
 | 
						|
    switch (axis)
 | 
						|
      {
 | 
						|
      case ANCESTOR:
 | 
						|
        buf.append("ancestor::");
 | 
						|
        break;
 | 
						|
      case ANCESTOR_OR_SELF:
 | 
						|
        buf.append("ancestor-or-self::");
 | 
						|
        break;
 | 
						|
      case ATTRIBUTE:
 | 
						|
        if (tests.length == 0 ||
 | 
						|
            (tests[0] instanceof NameTest))
 | 
						|
          buf.append('@');
 | 
						|
        else
 | 
						|
          buf.append("attribute::");
 | 
						|
        break;
 | 
						|
      case CHILD:
 | 
						|
        //buf.append("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("[error]");
 | 
						|
    else
 | 
						|
      {
 | 
						|
        for (int i = 0; i < tests.length; i++)
 | 
						|
          buf.append(tests[i]);
 | 
						|
      }
 | 
						|
    return buf.toString();
 | 
						|
  }
 | 
						|
 | 
						|
}
 |