1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: CountersTable.java 468645 2006-10-28 06:57:24Z minchau $
20 */
21package org.apache.xalan.transformer;
22
23import java.util.Hashtable;
24import java.util.Vector;
25
26import javax.xml.transform.TransformerException;
27
28import org.apache.xalan.templates.ElemNumber;
29import org.apache.xml.dtm.DTM;
30import org.apache.xpath.NodeSetDTM;
31import org.apache.xpath.XPathContext;
32
33/**
34 * This is a table of counters, keyed by ElemNumber objects, each
35 * of which has a list of Counter objects.  This really isn't a true
36 * table, it is more like a list of lists (there must be a technical
37 * term for that...).
38 * @xsl.usage internal
39 */
40public class CountersTable extends Hashtable
41{
42    static final long serialVersionUID = 2159100770924179875L;
43
44  /**
45   * Construct a CountersTable.
46   */
47  public CountersTable(){}
48
49  /**
50   * Get the list of counters that corresponds to
51   * the given ElemNumber object.
52   *
53   * @param numberElem the given xsl:number element.
54   *
55   * @return the list of counters that corresponds to
56   * the given ElemNumber object.
57   */
58  Vector getCounters(ElemNumber numberElem)
59  {
60
61    Vector counters = (Vector) this.get(numberElem);
62
63    return (null == counters) ? putElemNumber(numberElem) : counters;
64  }
65
66  /**
67   * Put a counter into the table and create an empty
68   * vector as it's value.
69   *
70   * @param numberElem the given xsl:number element.
71   *
72   * @return an empty vector to be used to store counts
73   * for this number element.
74   */
75  Vector putElemNumber(ElemNumber numberElem)
76  {
77
78    Vector counters = new Vector();
79
80    this.put(numberElem, counters);
81
82    return counters;
83  }
84
85  /**
86   * Place to collect new counters.
87   */
88  transient private NodeSetDTM m_newFound;
89
90  /**
91   * Add a list of counted nodes that were built in backwards document
92   * order, or a list of counted nodes that are in forwards document
93   * order.
94   *
95   * @param flist Vector of nodes built in forwards document order
96   * @param blist Vector of nodes built in backwards document order
97   */
98  void appendBtoFList(NodeSetDTM flist, NodeSetDTM blist)
99  {
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}
209