1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2006 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *     http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 /*
  21  * $Id: UnionIterator.java 337874 2004-02-16 23:06:53Z minchau $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.dom;
  25 
  26 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  27 import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
  28 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  29 import com.sun.org.apache.xml.internal.dtm.ref.DTMAxisIteratorBase;
  30 
  31 /**
  32  * <p><code>MultiValuedNodeHeapIterator</code> takes a set of multi-valued
  33  * heap nodes and produces a merged NodeSet in document order with duplicates
  34  * removed.</p>
  35  * <p>Each multi-valued heap node (which might be a
  36  * {@link org.apache.xml.dtm.DTMAxisIterator}, but that's  not necessary)
  37  * generates DTM node handles in document order.  The class
  38  * maintains the multi-valued heap nodes in a heap, not surprisingly, sorted by
  39  * the next DTM node handle available form the heap node.</p>
  40  * <p>After a DTM node is pulled from the heap node that's at the top of the
  41  * heap, the heap node is advanced to the next DTM node handle it makes
  42  * available, and the heap nature of the heap is restored to ensure the next
  43  * DTM node handle pulled is next in document order overall.
  44  *
  45  * @author Jacek Ambroziak
  46  * @author Santiago Pericas-Geertsen
  47  */
  48 public abstract class MultiValuedNodeHeapIterator extends DTMAxisIteratorBase {
  49     /** wrapper for NodeIterators to support iterator
  50         comparison on the value of their next() method
  51     */
  52 
  53     /**
  54      * An abstract representation of a set of nodes that will be retrieved in
  55      * document order.
  56      */
  57     public abstract class HeapNode implements Cloneable {
  58         protected int _node, _markedNode;
  59         protected boolean _isStartSet = false;
  60 
  61         /**
  62          * Advance to the next node represented by this {@link HeapNode}
  63          *
  64          * @return the next DTM node.
  65          */
  66         public abstract int step();
  67 
  68 
  69         /**
  70          * Creates a deep copy of this {@link HeapNode}.  The clone is not
  71          * reset from the current position of the original.
  72          *
  73          * @return the cloned heap node
  74          */
  75         public HeapNode cloneHeapNode() {
  76             HeapNode clone;
  77 
  78             try {
  79                 clone = (HeapNode) super.clone();
  80             } catch (CloneNotSupportedException e) {
  81                 BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
  82                                           e.toString());
  83                 return null;
  84             }
  85 
  86             clone._node = _node;
  87             clone._markedNode = _node;
  88 
  89             return clone;
  90         }
  91 
  92         /**
  93          * Remembers the current node for the next call to {@link #gotoMark()}.
  94          */
  95         public void setMark() {
  96             _markedNode = _node;
  97         }
  98 
  99         /**
 100          * Restores the current node remembered by {@link #setMark()}.
 101          */
 102         public void gotoMark() {
 103             _node = _markedNode;
 104         }
 105 
 106         /**
 107          * Performs a comparison of the two heap nodes
 108          *
 109          * @param heapNode the heap node against which to compare
 110          * @return <code>true</code> if and only if the current node for this
 111          *         heap node is before the current node of the argument heap
 112          *         node in document order.
 113          */
 114         public abstract boolean isLessThan(HeapNode heapNode);
 115 
 116         /**
 117          * Sets context with respect to which this heap node is evaluated.
 118          *
 119          * @param node The new context node
 120          * @return a {@link HeapNode} which may or may not be the same as
 121          *         this <code>HeapNode</code>.
 122          */
 123         public abstract HeapNode setStartNode(int node);
 124 
 125         /**
 126          * Reset the heap node back to its beginning.
 127          *
 128          * @return a {@link HeapNode} which may or may not be the same as
 129          *         this <code>HeapNode</code>.
 130          */
 131         public abstract HeapNode reset();
 132     } // end of HeapNode
 133 
 134     private static final int InitSize = 8;
 135 
 136     private int        _heapSize = 0;
 137     private int        _size = InitSize;
 138     private HeapNode[] _heap = new HeapNode[InitSize];
 139     private int        _free = 0;
 140 
 141     // Last node returned by this MultiValuedNodeHeapIterator to the caller of
 142     // next; used to prune duplicates
 143     private int _returnedLast;
 144 
 145     // cached returned last for use in gotoMark
 146     private int _cachedReturnedLast = END;
 147 
 148     // cached heap size for use in gotoMark
 149     private int _cachedHeapSize;
 150 
 151 
 152     public DTMAxisIterator cloneIterator() {
 153         _isRestartable = false;
 154         final HeapNode[] heapCopy = new HeapNode[_heap.length];
 155         try {
 156             MultiValuedNodeHeapIterator clone =
 157                     (MultiValuedNodeHeapIterator)super.clone();
 158 
 159             for (int i = 0; i < _free; i++) {
 160                 heapCopy[i] = _heap[i].cloneHeapNode();
 161             }
 162             clone.setRestartable(false);
 163             clone._heap = heapCopy;
 164             return clone.reset();
 165         }
 166         catch (CloneNotSupportedException e) {
 167             BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
 168                                       e.toString());
 169             return null;
 170         }
 171     }
 172 
 173     protected void addHeapNode(HeapNode node) {
 174         if (_free == _size) {
 175             HeapNode[] newArray = new HeapNode[_size *= 2];
 176             System.arraycopy(_heap, 0, newArray, 0, _free);
 177             _heap = newArray;
 178         }
 179         _heapSize++;
 180         _heap[_free++] = node;
 181     }
 182 
 183     public int next() {
 184         while (_heapSize > 0) {
 185             final int smallest = _heap[0]._node;
 186             if (smallest == END) { // iterator _heap[0] is done
 187                 if (_heapSize > 1) {
 188                     // Swap first and last (iterator must be restartable)
 189                     final HeapNode temp = _heap[0];
 190                     _heap[0] = _heap[--_heapSize];
 191                     _heap[_heapSize] = temp;
 192                 }
 193                 else {
 194                     return END;
 195                 }
 196             }
 197             else if (smallest == _returnedLast) {       // duplicate
 198                 _heap[0].step(); // value consumed
 199             }
 200             else {
 201                 _heap[0].step(); // value consumed
 202                 heapify(0);
 203                 return returnNode(_returnedLast = smallest);
 204             }
 205             // fallthrough if not returned above
 206             heapify(0);
 207         }
 208         return END;
 209     }
 210 
 211     public DTMAxisIterator setStartNode(int node) {
 212         if (_isRestartable) {
 213             _startNode = node;
 214             for (int i = 0; i < _free; i++) {
 215                 if(!_heap[i]._isStartSet){
 216                    _heap[i].setStartNode(node);
 217                    _heap[i].step();     // to get the first node
 218                    _heap[i]._isStartSet = true;
 219                 }
 220             }
 221             // build heap
 222             for (int i = (_heapSize = _free)/2; i >= 0; i--) {
 223                 heapify(i);
 224             }
 225             _returnedLast = END;
 226             return resetPosition();
 227         }
 228         return this;
 229     }
 230 
 231     protected void init() {
 232         for (int i =0; i < _free; i++) {
 233             _heap[i] = null;
 234         }
 235 
 236         _heapSize = 0;
 237         _free = 0;
 238     }
 239 
 240     /* Build a heap in document order. put the smallest node on the top.
 241      * "smallest node" means the node before other nodes in document order
 242      */
 243     private void heapify(int i) {
 244         for (int r, l, smallest;;) {
 245             r = (i + 1) << 1; l = r - 1;
 246             smallest = l < _heapSize
 247                 && _heap[l].isLessThan(_heap[i]) ? l : i;
 248             if (r < _heapSize && _heap[r].isLessThan(_heap[smallest])) {
 249                 smallest = r;
 250             }
 251             if (smallest != i) {
 252                 final HeapNode temp = _heap[smallest];
 253                 _heap[smallest] = _heap[i];
 254                 _heap[i] = temp;
 255                 i = smallest;
 256             } else {
 257                 break;
 258             }
 259         }
 260     }
 261 
 262     public void setMark() {
 263         for (int i = 0; i < _free; i++) {
 264             _heap[i].setMark();
 265         }
 266         _cachedReturnedLast = _returnedLast;
 267         _cachedHeapSize = _heapSize;
 268     }
 269 
 270     public void gotoMark() {
 271         for (int i = 0; i < _free; i++) {
 272             _heap[i].gotoMark();
 273         }
 274         // rebuild heap after call last() function. fix for bug 20913
 275         for (int i = (_heapSize = _cachedHeapSize)/2; i >= 0; i--) {
 276             heapify(i);
 277         }
 278         _returnedLast = _cachedReturnedLast;
 279     }
 280 
 281     public DTMAxisIterator reset() {
 282         for (int i = 0; i < _free; i++) {
 283             _heap[i].reset();
 284             _heap[i].step();
 285         }
 286 
 287         // build heap
 288         for (int i = (_heapSize = _free)/2; i >= 0; i--) {
 289             heapify(i);
 290         }
 291 
 292         _returnedLast = END;
 293         return resetPosition();
 294     }
 295 
 296 }