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