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