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: MultiValuedNodeHeapIterator.java 1225426 2011-12-29 04:13:08Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.dom;
023    
024    import org.apache.xalan.xsltc.DOM;
025    import org.apache.xalan.xsltc.runtime.BasisLibrary;
026    import org.apache.xml.dtm.DTMAxisIterator;
027    import org.apache.xml.dtm.ref.DTMAxisIteratorBase;
028    
029    /**
030     * <p><code>MultiValuedNodeHeapIterator</code> takes a set of multi-valued
031     * heap nodes and produces a merged NodeSet in document order with duplicates
032     * removed.</p>
033     * <p>Each multi-valued heap node (which might be a
034     * {@link org.apache.xml.dtm.DTMAxisIterator}, but that's  not necessary)
035     * generates DTM node handles in document order.  The class
036     * maintains the multi-valued heap nodes in a heap, not surprisingly, sorted by
037     * the next DTM node handle available form the heap node.</p>
038     * <p>After a DTM node is pulled from the heap node that's at the top of the
039     * heap, the heap node is advanced to the next DTM node handle it makes
040     * available, and the heap nature of the heap is restored to ensure the next
041     * DTM node handle pulled is next in document order overall.
042     *
043     * @author Jacek Ambroziak
044     * @author Santiago Pericas-Geertsen
045     */
046    public abstract class MultiValuedNodeHeapIterator extends DTMAxisIteratorBase {
047        /** wrapper for NodeIterators to support iterator
048            comparison on the value of their next() method
049        */
050    
051        /**
052         * An abstract representation of a set of nodes that will be retrieved in
053         * document order.
054         */
055        public abstract class HeapNode implements Cloneable {
056            protected int _node, _markedNode;
057            protected boolean _isStartSet = false;
058                    
059            /**
060             * Advance to the next node represented by this {@link HeapNode}
061             *
062             * @return the next DTM node.
063             */
064            public abstract int step();
065    
066    
067            /**
068             * Creates a deep copy of this {@link HeapNode}.  The clone is not
069             * reset from the current position of the original.
070             *
071             * @return the cloned heap node
072             */
073            public HeapNode cloneHeapNode() {
074                HeapNode clone;
075    
076                try {
077                    clone = (HeapNode) super.clone();
078                } catch (CloneNotSupportedException e) {
079                    BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
080                                              e.toString());
081                    return null;
082                }
083    
084                clone._node = _node;
085                clone._markedNode = _node;
086    
087                return clone;
088            }
089    
090            /**
091             * Remembers the current node for the next call to {@link #gotoMark()}.
092             */
093            public void setMark() {
094                _markedNode = _node;
095            }
096    
097            /**
098             * Restores the current node remembered by {@link #setMark()}.
099             */
100            public void gotoMark() {
101                _node = _markedNode;
102            }
103    
104            /**
105             * Performs a comparison of the two heap nodes
106             *
107             * @param heapNode the heap node against which to compare
108             * @return <code>true</code> if and only if the current node for this
109             *         heap node is before the current node of the argument heap
110             *         node in document order.
111             */
112            public abstract boolean isLessThan(HeapNode heapNode);
113    
114            /**
115             * Sets context with respect to which this heap node is evaluated.
116             *
117             * @param node The new context node
118             * @return a {@link HeapNode} which may or may not be the same as
119             *         this <code>HeapNode</code>.
120             */
121            public abstract HeapNode setStartNode(int node);
122    
123            /**
124             * Reset the heap node back to its beginning.
125             *
126             * @return a {@link HeapNode} which may or may not be the same as
127             *         this <code>HeapNode</code>.
128             */
129            public abstract HeapNode reset();
130        } // end of HeapNode
131    
132        private static final int InitSize = 8;
133      
134        private int        _heapSize = 0;
135        private int        _size = InitSize;
136        private HeapNode[] _heap = new HeapNode[InitSize];
137        private int        _free = 0;
138      
139        // Last node returned by this MultiValuedNodeHeapIterator to the caller of
140        // next; used to prune duplicates
141        private int _returnedLast;
142    
143        // cached returned last for use in gotoMark
144        private int _cachedReturnedLast = END;
145    
146        // cached heap size for use in gotoMark
147        private int _cachedHeapSize;
148    
149    
150        public DTMAxisIterator cloneIterator() {
151            _isRestartable = false;
152            final HeapNode[] heapCopy = new HeapNode[_heap.length];
153            try {
154                MultiValuedNodeHeapIterator clone =
155                        (MultiValuedNodeHeapIterator)super.clone();
156    
157                for (int i = 0; i < _free; i++) {
158                    heapCopy[i] = _heap[i].cloneHeapNode();
159                }
160                clone.setRestartable(false);
161                clone._heap = heapCopy;
162                return clone.reset();
163            } 
164            catch (CloneNotSupportedException e) {
165                BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
166                                          e.toString());
167                return null;
168            }
169        }
170        
171        protected void addHeapNode(HeapNode node) {
172            if (_free == _size) {
173                HeapNode[] newArray = new HeapNode[_size *= 2];
174                System.arraycopy(_heap, 0, newArray, 0, _free);
175                _heap = newArray;
176            }
177            _heapSize++;
178            _heap[_free++] = node;
179        }
180      
181        public int next() {
182            while (_heapSize > 0) {
183                final int smallest = _heap[0]._node;
184                if (smallest == END) { // iterator _heap[0] is done
185                    if (_heapSize > 1) {
186                        // Swap first and last (iterator must be restartable)
187                        final HeapNode temp = _heap[0];
188                        _heap[0] = _heap[--_heapSize];
189                        _heap[_heapSize] = temp;
190                    }
191                    else {
192                        return END;
193                    }
194                }
195                else if (smallest == _returnedLast) {       // duplicate
196                    _heap[0].step(); // value consumed
197                }
198                else {
199                    _heap[0].step(); // value consumed
200                    heapify(0);
201                    return returnNode(_returnedLast = smallest);
202                }
203                // fallthrough if not returned above
204                heapify(0);
205            }
206            return END;
207        }
208      
209        public DTMAxisIterator setStartNode(int node) {
210            if (_isRestartable) {
211                _startNode = node;
212                for (int i = 0; i < _free; i++) {
213                    if(!_heap[i]._isStartSet){
214                       _heap[i].setStartNode(node);
215                       _heap[i].step();     // to get the first node
216                       _heap[i]._isStartSet = true;
217                    }
218                }
219                // build heap
220                for (int i = (_heapSize = _free)/2; i >= 0; i--) {
221                    heapify(i);
222                }
223                _returnedLast = END;
224                return resetPosition();
225            }
226            return this;
227        }
228    
229        protected void init() {
230            for (int i =0; i < _free; i++) {
231                _heap[i] = null;
232            }
233    
234            _heapSize = 0;
235            _free = 0;
236        }
237    
238        /* Build a heap in document order. put the smallest node on the top. 
239         * "smallest node" means the node before other nodes in document order
240         */
241        private void heapify(int i) {
242            for (int r, l, smallest;;) {
243                r = (i + 1) << 1; l = r - 1;
244                smallest = l < _heapSize 
245                    && _heap[l].isLessThan(_heap[i]) ? l : i;
246                if (r < _heapSize && _heap[r].isLessThan(_heap[smallest])) {
247                    smallest = r;
248                }
249                if (smallest != i) {
250                    final HeapNode temp = _heap[smallest];
251                    _heap[smallest] = _heap[i];
252                    _heap[i] = temp;
253                    i = smallest;
254                } else {
255                    break;
256                }
257            }
258        }
259    
260        public void setMark() {
261            for (int i = 0; i < _free; i++) {
262                _heap[i].setMark();
263            }
264            _cachedReturnedLast = _returnedLast;    
265            _cachedHeapSize = _heapSize;
266        }
267    
268        public void gotoMark() {
269            for (int i = 0; i < _free; i++) {
270                _heap[i].gotoMark();
271            }
272            // rebuild heap after call last() function. fix for bug 20913
273            for (int i = (_heapSize = _cachedHeapSize)/2; i >= 0; i--) {
274                heapify(i);
275            }
276            _returnedLast = _cachedReturnedLast;    
277        }
278    
279        public DTMAxisIterator reset() {
280            for (int i = 0; i < _free; i++) {
281                _heap[i].reset();
282                _heap[i].step();
283            }
284    
285            // build heap
286            for (int i = (_heapSize = _free)/2; i >= 0; i--) {
287                heapify(i);
288            }
289    
290            _returnedLast = END;
291            return resetPosition();
292        }
293    
294    }