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 }