001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: CountersTable.java 468645 2006-10-28 06:57:24Z minchau $
020     */
021    package org.apache.xalan.transformer;
022    
023    import java.util.Hashtable;
024    import java.util.Vector;
025    
026    import javax.xml.transform.TransformerException;
027    
028    import org.apache.xalan.templates.ElemNumber;
029    import org.apache.xml.dtm.DTM;
030    import org.apache.xpath.NodeSetDTM;
031    import org.apache.xpath.XPathContext;
032    
033    /**
034     * This is a table of counters, keyed by ElemNumber objects, each
035     * of which has a list of Counter objects.  This really isn't a true
036     * table, it is more like a list of lists (there must be a technical
037     * term for that...).
038     * @xsl.usage internal
039     */
040    public class CountersTable extends Hashtable
041    {
042        static final long serialVersionUID = 2159100770924179875L;
043    
044      /**
045       * Construct a CountersTable.
046       */
047      public CountersTable(){}
048    
049      /**
050       * Get the list of counters that corresponds to
051       * the given ElemNumber object.
052       *
053       * @param numberElem the given xsl:number element.
054       *
055       * @return the list of counters that corresponds to
056       * the given ElemNumber object.
057       */
058      Vector getCounters(ElemNumber numberElem)
059      {
060    
061        Vector counters = (Vector) this.get(numberElem);
062    
063        return (null == counters) ? putElemNumber(numberElem) : counters;
064      }
065    
066      /**
067       * Put a counter into the table and create an empty
068       * vector as it's value.
069       *
070       * @param numberElem the given xsl:number element.
071       *
072       * @return an empty vector to be used to store counts
073       * for this number element.
074       */
075      Vector putElemNumber(ElemNumber numberElem)
076      {
077    
078        Vector counters = new Vector();
079    
080        this.put(numberElem, counters);
081    
082        return counters;
083      }
084    
085      /**
086       * Place to collect new counters.
087       */
088      transient private NodeSetDTM m_newFound;
089    
090      /**
091       * Add a list of counted nodes that were built in backwards document
092       * order, or a list of counted nodes that are in forwards document
093       * order.
094       *
095       * @param flist Vector of nodes built in forwards document order
096       * @param blist Vector of nodes built in backwards document order
097       */
098      void appendBtoFList(NodeSetDTM flist, NodeSetDTM blist)
099      {
100    
101        int n = blist.size();
102    
103        for (int i = (n - 1); i >= 0; i--)
104        {
105          flist.addElement(blist.item(i));
106        }
107      }
108    
109      // For diagnostics
110    
111      /** Number of counters created so far          */
112      transient int m_countersMade = 0;
113    
114      /**
115       * Count forward until the given node is found, or until
116       * we have looked to the given amount.
117       *
118       * @param support The XPath context to use  
119       * @param numberElem The given xsl:number element.
120       * @param node The node to count.
121       * 
122       * @return The node count, or 0 if not found.
123       *
124       * @throws TransformerException
125       */
126      public int countNode(XPathContext support, ElemNumber numberElem, int node)
127              throws TransformerException
128      {
129    
130        int count = 0;
131        Vector counters = getCounters(numberElem);
132        int nCounters = counters.size();
133    
134        // XPath countMatchPattern = numberElem.getCountMatchPattern(support, node);
135        // XPath fromMatchPattern = numberElem.m_fromMatchPattern;
136        int target = numberElem.getTargetNode(support, node);
137    
138        if (DTM.NULL != target)
139        {
140          for (int i = 0; i < nCounters; i++)
141          {
142            Counter counter = (Counter) counters.elementAt(i);
143    
144            count = counter.getPreviouslyCounted(support, target);
145    
146            if (count > 0)
147              return count;
148          }
149    
150          // In the loop below, we collect the nodes in backwards doc order, so 
151          // we don't have to do inserts, but then we store the nodes in forwards 
152          // document order, so we don't have to insert nodes into that list, 
153          // so that's what the appendBtoFList stuff is all about.  In cases 
154          // of forward counting by one, this will mean a single node copy from 
155          // the backwards list (m_newFound) to the forwards list (counter.m_countNodes).
156          count = 0;
157          if (m_newFound == null)
158            m_newFound = new NodeSetDTM(support.getDTMManager());
159    
160          for (; DTM.NULL != target;
161                  target = numberElem.getPreviousNode(support, target))
162          {
163    
164            // First time in, we should not have to check for previous counts, 
165            // since the original target node was already checked in the 
166            // block above.
167            if (0 != count)
168            {
169              for (int i = 0; i < nCounters; i++)
170              {
171                Counter counter = (Counter) counters.elementAt(i);
172                int cacheLen = counter.m_countNodes.size();
173    
174                if ((cacheLen > 0)
175                        && (counter.m_countNodes.elementAt(cacheLen
176                                                          - 1) == target))
177                {
178                  count += (cacheLen + counter.m_countNodesStartCount);
179    
180                  if (cacheLen > 0)
181                    appendBtoFList(counter.m_countNodes, m_newFound);
182    
183                  m_newFound.removeAllElements();
184    
185                  return count;
186                }
187              }
188            }
189    
190            m_newFound.addElement(target);
191    
192            count++;
193          }
194    
195          // If we got to this point, then we didn't find a counter, so make 
196          // one and add it to the list.
197          Counter counter = new Counter(numberElem, new NodeSetDTM(support.getDTMManager()));
198    
199          m_countersMade++;  // for diagnostics
200    
201          appendBtoFList(counter.m_countNodes, m_newFound);
202          m_newFound.removeAllElements();
203          counters.addElement(counter);
204        }
205    
206        return count;
207      }
208    }