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