| /* NodeNumberNode.java -- |
| Copyright (C) 2004 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.transform; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import javax.xml.transform.TransformerException; |
| import org.w3c.dom.Node; |
| import gnu.xml.xpath.Expr; |
| import gnu.xml.xpath.Pattern; |
| import gnu.xml.xpath.Selector; |
| import gnu.xml.xpath.UnionExpr; |
| |
| /** |
| * A template node representing the XSL <code>number</code> instruction |
| * with no <code>value</code> expression, i.e. the value is computed from |
| * the document position of the context node. |
| * |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
| */ |
| final class NodeNumberNode |
| extends AbstractNumberNode |
| { |
| |
| static final int SINGLE = 0; |
| static final int MULTIPLE = 1; |
| static final int ANY = 2; |
| |
| final int level; |
| final Pattern count; |
| final Pattern from; |
| |
| NodeNumberNode(int level, Pattern count, Pattern from, |
| TemplateNode format, String lang, |
| int letterValue, String groupingSeparator, int groupingSize) |
| { |
| super(format, lang, letterValue, groupingSeparator, groupingSize); |
| this.level = level; |
| this.count = count; |
| this.from = from; |
| } |
| |
| TemplateNode clone(Stylesheet stylesheet) |
| { |
| TemplateNode ret = new NodeNumberNode(level, |
| (count == null) ? null : |
| (Pattern) count.clone(stylesheet), |
| (from == null) ? from : |
| (Pattern) from.clone(stylesheet), |
| format, lang, letterValue, |
| groupingSeparator, groupingSize); |
| if (children != null) |
| { |
| ret.children = children.clone(stylesheet); |
| } |
| if (next != null) |
| { |
| ret.next = next.clone(stylesheet); |
| } |
| return ret; |
| } |
| |
| int[] compute(Stylesheet stylesheet, Node context, int pos, int len) |
| throws TransformerException |
| { |
| /*if (from != null) |
| { |
| Object ret = from.evaluate(context, pos, len); |
| if (ret instanceof Collection) |
| { |
| Collection ns = (Collection) ret; |
| if (ns.size() > 0) |
| { |
| List list = new ArrayList(ns); |
| Collections.sort(list, documentOrderComparator); |
| context = (Node) list.get(0); |
| } |
| else |
| { |
| return new int[0]; |
| } |
| } |
| else |
| { |
| return new int[0]; |
| } |
| }*/ |
| Node current = context; |
| switch (level) |
| { |
| case SINGLE: |
| if (from == null) |
| { |
| while (context != null && !countMatches(current, context)) |
| { |
| context = context.getParentNode(); |
| } |
| } |
| else |
| { |
| while (context != null && !countMatches(current, context) && |
| !fromMatches(context)) |
| { |
| context = context.getParentNode(); |
| } |
| } |
| return (context == null) ? new int[0] : |
| new int[] { (context == current) ? pos : getIndex(current, context) }; |
| case MULTIPLE: |
| List ancestors = new ArrayList(); |
| while (context != null) |
| { |
| if (countMatches(current, context)) |
| { |
| if (from == null || fromMatches(context)) |
| { |
| ancestors.add(context); |
| } |
| } |
| context = context.getParentNode(); |
| } |
| Collections.sort(ancestors, documentOrderComparator); |
| int[] ret = new int[ancestors.size()]; |
| for (int i = 0; i < ret.length; i++) |
| { |
| ret[i] = getIndex(current, (Node) ancestors.get(i)); |
| } |
| return ret; |
| case ANY: |
| Expr preceding = new Selector(Selector.PRECEDING, |
| Collections.EMPTY_LIST); |
| Expr ancestorOrSelf = new Selector(Selector.ANCESTOR_OR_SELF, |
| Collections.EMPTY_LIST); |
| Expr any = new UnionExpr(preceding, ancestorOrSelf); |
| Object eval = any.evaluate(context, pos, len); |
| if (eval instanceof Collection) |
| { |
| Collection ns = (Collection) eval; |
| List candidates = new ArrayList(); |
| for (Iterator i = ns.iterator(); i.hasNext(); ) |
| { |
| Node candidate = (Node) i.next(); |
| if (countMatches(current, candidate)) |
| { |
| candidates.add(candidate); |
| if (from != null && from.matches(candidate)) |
| { |
| break; |
| } |
| } |
| } |
| return new int[] { candidates.size() }; |
| } |
| return new int[0]; |
| default: |
| throw new TransformerException("invalid level"); |
| } |
| } |
| |
| boolean countMatches(Node current, Node node) |
| { |
| if (count == null) |
| { |
| int cnt = current.getNodeType(); |
| int nnt = node.getNodeType(); |
| if (cnt != nnt) |
| { |
| return false; |
| } |
| if (nnt == Node.ELEMENT_NODE || nnt == Node.ATTRIBUTE_NODE) |
| { |
| String curi = current.getNamespaceURI(); |
| String nuri = node.getNamespaceURI(); |
| if ((curi == null && nuri != null) || |
| (curi != null && !curi.equals(nuri))) |
| { |
| return false; |
| } |
| String cn = current.getLocalName(); |
| if (cn == null) |
| { |
| cn = current.getNodeName(); |
| } |
| String nn = node.getLocalName(); |
| if (nn == null) |
| { |
| nn = node.getNodeName(); |
| } |
| if (!cn.equals(nn)) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| else |
| { |
| return count.matches(node); |
| } |
| } |
| |
| boolean fromMatches(Node node) |
| { |
| for (Node ctx = node.getParentNode(); ctx != null; |
| ctx = ctx.getParentNode()) |
| { |
| if (from.matches(ctx)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| int getIndex(Node current, Node node) |
| { |
| int index = 0; |
| do |
| { |
| do |
| { |
| node = node.getPreviousSibling(); |
| } |
| while (node != null && !countMatches(current, node)); |
| index++; |
| } |
| while (node != null); |
| return index; |
| } |
| |
| } |