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();
 | |
|   }
 | |
| 
 | |
| }
 |