• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
21 package org.apache.xalan.transformer;
22 
23 import java.util.Hashtable;
24 import java.util.Vector;
25 
26 import javax.xml.transform.TransformerException;
27 
28 import org.apache.xalan.templates.ElemNumber;
29 import org.apache.xml.dtm.DTM;
30 import org.apache.xpath.NodeSetDTM;
31 import 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  */
40 public class CountersTable extends Hashtable
41 {
42     static final long serialVersionUID = 2159100770924179875L;
43 
44   /**
45    * Construct a CountersTable.
46    */
CountersTable()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    */
getCounters(ElemNumber numberElem)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    */
putElemNumber(ElemNumber numberElem)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    */
appendBtoFList(NodeSetDTM flist, NodeSetDTM blist)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    */
countNode(XPathContext support, ElemNumber numberElem, int node)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