/* * The Apache Software License, Version 1.1 * * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Xalan" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation and was * originally based on software copyright (c) 1999, Lotus * Development Corporation., http://www.lotus.com. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.xalan.transformer; import java.util.Hashtable; import java.util.Vector; //import org.w3c.dom.Node; import org.apache.xml.dtm.DTM; import javax.xml.transform.TransformerException; import org.apache.xpath.XPathContext; import org.apache.xpath.XPath; import org.apache.xpath.NodeSetDTM; import org.apache.xalan.templates.ElemNumber; /** * <meta name="usage" content="internal"/> * This is a table of counters, keyed by ElemNumber objects, each * of which has a list of Counter objects. This really isn't a true * table, it is more like a list of lists (there must be a technical * term for that...). */ public class CountersTable extends Hashtable { /** * Construct a CountersTable. */ public CountersTable(){} /** * Get the list of counters that corresponds to * the given ElemNumber object. * * @param numberElem the given xsl:number element. * * @return the list of counters that corresponds to * the given ElemNumber object. */ Vector getCounters(ElemNumber numberElem) { Vector counters = (Vector) this.get(numberElem); return (null == counters) ? putElemNumber(numberElem) : counters; } /** * Put a counter into the table and create an empty * vector as it's value. * * @param numberElem the given xsl:number element. * * @return an empty vector to be used to store counts * for this number element. */ Vector putElemNumber(ElemNumber numberElem) { Vector counters = new Vector(); this.put(numberElem, counters); return counters; } /** * Place to collect new counters. */ transient private NodeSetDTM m_newFound = new NodeSetDTM(); /** * Add a list of counted nodes that were built in backwards document * order, or a list of counted nodes that are in forwards document * order. * * @param flist Vector of nodes built in forwards document order * @param blist Vector of nodes built in backwards document order */ void appendBtoFList(NodeSetDTM flist, NodeSetDTM blist) { int n = blist.size(); for (int i = (n - 1); i >= 0; i--) { flist.addElement(blist.item(i)); } } // For diagnostics /** Number of counters created so far */ transient int m_countersMade = 0; /** * Count forward until the given node is found, or until * we have looked to the given amount. * * @param support The XPath context to use * @param numberElem The given xsl:number element. * @param node The node to count. * * @return The node count, or 0 if not found. * * @throws TransformerException */ public int countNode(XPathContext support, ElemNumber numberElem, int node) throws TransformerException { int count = 0; Vector counters = getCounters(numberElem); int nCounters = counters.size(); // XPath countMatchPattern = numberElem.getCountMatchPattern(support, node); // XPath fromMatchPattern = numberElem.m_fromMatchPattern; int target = numberElem.getTargetNode(support, node); if (DTM.NULL != target) { for (int i = 0; i < nCounters; i++) { Counter counter = (Counter) counters.elementAt(i); count = counter.getPreviouslyCounted(support, target); if (count > 0) return count; } // In the loop below, we collect the nodes in backwards doc order, so // we don't have to do inserts, but then we store the nodes in forwards // document order, so we don't have to insert nodes into that list, // so that's what the appendBtoFList stuff is all about. In cases // of forward counting by one, this will mean a single node copy from // the backwards list (m_newFound) to the forwards list (counter.m_countNodes). count = 0; for (; DTM.NULL != target; target = numberElem.getPreviousNode(support, target)) { // First time in, we should not have to check for previous counts, // since the original target node was already checked in the // block above. if (0 != count) { for (int i = 0; i < nCounters; i++) { Counter counter = (Counter) counters.elementAt(i); int cacheLen = counter.m_countNodes.size(); if ((cacheLen > 0) && (counter.m_countNodes.elementAt(cacheLen - 1) == target)) { count += (cacheLen + counter.m_countNodesStartCount); if (cacheLen > 0) appendBtoFList(counter.m_countNodes, m_newFound); m_newFound.removeAllElements(); return count; } } } m_newFound.addElement(target); count++; } // If we got to this point, then we didn't find a counter, so make // one and add it to the list. Counter counter = new Counter(numberElem); m_countersMade++; // for diagnostics appendBtoFList(counter.m_countNodes, m_newFound); m_newFound.removeAllElements(); counters.addElement(counter); } return count; } }