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
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  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,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.partition.impl.btree;
021    
022    
023    import org.apache.directory.server.xdbm.ForwardIndexEntry;
024    import org.apache.directory.server.xdbm.IndexEntry;
025    
026    import java.util.HashMap;
027    import java.util.Map;
028    import java.util.NoSuchElementException;
029    
030    import javax.naming.NamingEnumeration;
031    import javax.naming.NamingException;
032    
033    
034    /**
035     * A prefetching NamingEnumeration over an underlying NamingEnumeration which 
036     * determines if a element should be returned based on a Assertion.
037     * 
038     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039     * @version $Rev: 917231 $
040     */
041    public class IndexAssertionEnumeration implements NamingEnumeration<IndexEntry>
042    {
043        /** The prefetched candidate */
044        private final ForwardIndexEntry prefetched = new ForwardIndexEntry();
045        /** The returned candidate */
046        private final ForwardIndexEntry candidate = new ForwardIndexEntry();
047        /** The iteration cursor */
048        private final NamingEnumeration<ForwardIndexEntry> underlying;
049        /** LUT used to avoid returning duplicates */
050        private final Map<Object, Object> candidates;
051        /** */
052        private final IndexAssertion assertion;
053        /** */
054        private final boolean checkDups;
055        /** */
056        private boolean hasMore = true;
057    
058    
059        // ------------------------------------------------------------------------
060        // C O N S T R U C T O R S
061        // ------------------------------------------------------------------------
062    
063        public IndexAssertionEnumeration( NamingEnumeration<ForwardIndexEntry> underlying, IndexAssertion assertion )
064            throws NamingException
065        {
066            this.underlying = underlying;
067            candidates = null;
068            this.assertion = assertion;
069            checkDups = false;
070            prefetch();
071        }
072    
073    
074        public IndexAssertionEnumeration( NamingEnumeration<ForwardIndexEntry> underlying, IndexAssertion assertion,
075            boolean enableDupCheck ) throws NamingException
076        {
077            this.underlying = underlying;
078            candidates = new HashMap<Object, Object>();
079            this.assertion = assertion;
080            checkDups = enableDupCheck;
081            prefetch();
082        }
083    
084    
085        // ------------------------------------------------------------------------
086        // Enumeration Method Implementations
087        // ------------------------------------------------------------------------
088    
089        /**
090         * @see java.util.Enumeration#nextElement()
091         */
092        public IndexEntry nextElement()
093        {
094            try
095            {
096                return next();
097            }
098            catch ( NamingException e )
099            {
100                throw new NoSuchElementException();
101            }
102        }
103    
104    
105        /**
106         * @see java.util.Enumeration#hasMoreElements()
107         */
108        public boolean hasMoreElements()
109        {
110            return hasMore;
111        }
112    
113    
114        // ------------------------------------------------------------------------
115        // NamingEnumeration Method Implementations
116        // ------------------------------------------------------------------------
117    
118        /**
119         * @see javax.naming.NamingEnumeration#next()
120         */
121        public IndexEntry next() throws NamingException
122        {
123            candidate.copy( prefetched );
124            prefetch();
125            return candidate;
126        }
127    
128    
129        /**
130         * @see javax.naming.NamingEnumeration#hasMore()
131         */
132        public boolean hasMore()
133        {
134            return hasMore;
135        }
136    
137    
138        /**
139         * @see javax.naming.NamingEnumeration#close()
140         */
141        public void close() throws NamingException
142        {
143            hasMore = false;
144            underlying.close();
145        }
146    
147    
148        // ------------------------------------------------------------------------
149        // Private and Protected Methods
150        // ------------------------------------------------------------------------
151    
152        private void prefetch() throws NamingException
153        {
154            IndexEntry rec = null;
155    
156            /*
157             * Scan underlying Cursor until we arrive at the next valid candidate
158             * if the cursor is exhuasted we clean up after completing the loop
159             */
160            while ( underlying.hasMore() )
161            {
162                rec = underlying.next();
163    
164                // If value is valid then we set it as the next candidate to return
165                try
166                {
167                    if ( assertion.assertCandidate( rec ) )
168                    {
169                        if ( checkDups )
170                        {
171                            boolean dup = candidates.containsKey( rec.getId() );
172    
173                            if ( dup )
174                            {
175                                /*
176                                 * Dup checking is on and candidate is a duplicate that
177                                 * has already been seen so we need to skip it.
178                                 */
179                                continue;
180                            }
181                            else
182                            {
183                                /*
184                                 * Dup checking is on and the candidate is not in the
185                                 * dup LUT so we need to set it as the next to return
186                                 * and add it to the LUT in case we encounter it another
187                                 * time.
188                                 */
189                                prefetched.copy( rec );
190                                candidates.put( rec.getId(), rec.getId() );
191                                return;
192                            }
193                        }
194    
195                        prefetched.copy( rec );
196                        return;
197                    }
198                }
199                catch ( Exception e )
200                {
201                    NamingException ne = new NamingException();
202                    ne.setRootCause( e );
203                    throw ne;
204                }
205            }
206    
207            // At this pt the underlying Cursor has been exhausted so we close up
208            close();
209        }
210    }