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: DupFilterIterator.java 468651 2006-10-28 07:04:25Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.dom;
023    
024    import org.apache.xalan.xsltc.runtime.BasisLibrary;
025    import org.apache.xalan.xsltc.util.IntegerArray;
026    import org.apache.xml.dtm.DTMAxisIterator;
027    import org.apache.xml.dtm.ref.DTMAxisIteratorBase;
028    import org.apache.xml.dtm.ref.DTMDefaultBase;
029    
030    /**
031     * Removes duplicates and sorts a source iterator. The nodes from the 
032     * source are collected in an array upon calling setStartNode(). This
033     * array is later sorted and duplicates are ignored in next().
034     * @author G. Todd Miller 
035     */
036    public final class DupFilterIterator extends DTMAxisIteratorBase {
037    
038        /**
039         * Reference to source iterator.
040         */
041        private DTMAxisIterator _source;
042    
043        /**
044         * Array to cache all nodes from source.
045         */
046        private IntegerArray _nodes = new IntegerArray();
047    
048        /**
049         * Index in _nodes array to current node.
050         */
051        private int _current = 0;
052    
053        /**
054         * Cardinality of _nodes array.
055         */
056        private int _nodesSize = 0; 
057    
058        /**
059         * Last value returned by next().
060         */
061        private int _lastNext = END;
062    
063        /**
064         * Temporary variable to store _lastNext.
065         */
066        private int _markedLastNext = END;
067    
068        public DupFilterIterator(DTMAxisIterator source) {
069            _source = source;
070    // System.out.println("DFI source = " + source + " this = " + this);
071    
072            // Cache contents of id() or key() index right away. Necessary for
073            // union expressions containing multiple calls to the same index, and
074            // correct as well since start-node is irrelevant for id()/key() exrp.
075            if (source instanceof KeyIndex) {
076                setStartNode(DTMDefaultBase.ROOTNODE);
077            }
078        }
079        
080        /**
081         * Set the start node for this iterator
082         * @param node The start node
083         * @return A reference to this node iterator
084         */
085        public DTMAxisIterator setStartNode(int node) {
086            if (_isRestartable) {
087                // KeyIndex iterators are always relative to the root node, so there
088                // is never any point in re-reading the iterator (and we SHOULD NOT).
089                boolean sourceIsKeyIndex = _source instanceof KeyIndex;
090    
091                if (sourceIsKeyIndex
092                        && _startNode == DTMDefaultBase.ROOTNODE) {
093                    return this;
094                }
095    
096                if (node != _startNode) {
097                    _source.setStartNode(_startNode = node);
098    
099                    _nodes.clear();
100                    while ((node = _source.next()) != END) {
101                        _nodes.add(node);
102                    }
103    
104                    // Nodes produced by KeyIndex are known to be in document order.
105                    // Take advantage of it.
106                    if (!sourceIsKeyIndex) {
107                        _nodes.sort();
108                    }
109    
110                    _nodesSize = _nodes.cardinality();
111                    _current = 0;
112                    _lastNext = END;
113                    resetPosition();
114                }
115            }
116            return this;
117        }
118    
119        public int next() {
120            while (_current < _nodesSize) {
121                final int next = _nodes.at(_current++);
122                if (next != _lastNext) {
123                    return returnNode(_lastNext = next);
124                }
125            }
126            return END;
127        }
128    
129        public DTMAxisIterator cloneIterator() {
130            try {
131                final DupFilterIterator clone =
132                    (DupFilterIterator) super.clone();
133                clone._nodes = (IntegerArray) _nodes.clone();
134                clone._source = _source.cloneIterator();
135                clone._isRestartable = false;
136                return clone.reset();
137            }
138            catch (CloneNotSupportedException e) {
139                BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
140                                          e.toString());
141                return null;
142            }
143        }
144    
145        public void setRestartable(boolean isRestartable) {
146            _isRestartable = isRestartable;
147            _source.setRestartable(isRestartable);
148        }
149       
150        public void setMark() {
151            _markedNode = _current;
152            _markedLastNext = _lastNext;    // Bugzilla 25924
153        }
154    
155        public void gotoMark() {
156            _current = _markedNode;
157            _lastNext = _markedLastNext;    // Bugzilla 25924
158        }
159    
160        public DTMAxisIterator reset() {
161            _current = 0;
162            _lastNext = END;
163            return resetPosition();
164        }
165    }