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: 640657 $
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    
064        public IndexAssertionEnumeration( NamingEnumeration<ForwardIndexEntry> underlying, IndexAssertion assertion )
065            throws NamingException
066        {
067            this.underlying = underlying;
068            candidates = null;
069            this.assertion = assertion;
070            checkDups = false;
071            prefetch();
072        }
073    
074    
075        public IndexAssertionEnumeration( NamingEnumeration<ForwardIndexEntry> underlying, IndexAssertion assertion,
076            boolean enableDupCheck ) throws NamingException
077        {
078            this.underlying = underlying;
079            candidates = new HashMap<Object,Object>();
080            this.assertion = assertion;
081            checkDups = enableDupCheck;
082            prefetch();
083        }
084    
085    
086        // ------------------------------------------------------------------------
087        // Enumeration Method Implementations
088        // ------------------------------------------------------------------------
089    
090        /**
091         * @see java.util.Enumeration#nextElement()
092         */
093        public IndexEntry nextElement()
094        {
095            try
096            {
097                return next();
098            }
099            catch ( NamingException e )
100            {
101                throw new NoSuchElementException();
102            }
103        }
104    
105    
106        /**
107         * @see java.util.Enumeration#hasMoreElements()
108         */
109        public boolean hasMoreElements()
110        {
111            return hasMore;
112        }
113    
114    
115        // ------------------------------------------------------------------------
116        // NamingEnumeration Method Implementations
117        // ------------------------------------------------------------------------
118    
119        /**
120         * @see javax.naming.NamingEnumeration#next()
121         */
122        public IndexEntry next() throws NamingException
123        {
124            candidate.copy( prefetched );
125            prefetch();
126            return candidate;
127        }
128    
129    
130        /**
131         * @see javax.naming.NamingEnumeration#hasMore()
132         */
133        public boolean hasMore()
134        {
135            return hasMore;
136        }
137    
138    
139        /**
140         * @see javax.naming.NamingEnumeration#close()
141         */
142        public void close() throws NamingException
143        {
144            hasMore = false;
145            underlying.close();
146        }
147    
148    
149        // ------------------------------------------------------------------------
150        // Private and Protected Methods
151        // ------------------------------------------------------------------------
152    
153        private void prefetch() throws NamingException
154        {
155            IndexEntry rec = null;
156    
157            /*
158             * Scan underlying Cursor until we arrive at the next valid candidate
159             * if the cursor is exhuasted we clean up after completing the loop
160             */
161            while ( underlying.hasMore() )
162            {
163                rec = underlying.next();
164    
165                // If value is valid then we set it as the next candidate to return
166                try
167                {
168                    if ( assertion.assertCandidate( rec ) )
169                    {
170                        if ( checkDups )
171                        {
172                            boolean dup = candidates.containsKey( rec.getId() );
173    
174                            if ( dup )
175                            {
176                                /*
177                                 * Dup checking is on and candidate is a duplicate that
178                                 * has already been seen so we need to skip it.
179                                 */
180                                continue;
181                            }
182                            else
183                            {
184                                /*
185                                 * Dup checking is on and the candidate is not in the
186                                 * dup LUT so we need to set it as the next to return
187                                 * and add it to the LUT in case we encounter it another
188                                 * time.
189                                 */
190                                prefetched.copy( rec );
191                                candidates.put( rec.getId(), rec.getId() );
192                                return;
193                            }
194                        }
195    
196                        prefetched.copy( rec );
197                        return;
198                    }
199                }
200                catch ( Exception e )
201                {
202                    NamingException ne = new NamingException();
203                    ne.setRootCause( e );
204                    throw ne;
205                }
206            }
207    
208            // At this pt the underlying Cursor has been exhausted so we close up
209            close();
210        }
211    }