Home | History | Annotate | Download | only in transformer
      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    */
     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    */
     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    */
     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    */
     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    */
    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