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