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 }