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.xdbm.search.impl;
021    
022    
023    import org.apache.directory.server.xdbm.Index;
024    import org.apache.directory.server.xdbm.IndexEntry;
025    import org.apache.directory.server.xdbm.Store;
026    import org.apache.directory.server.xdbm.AbstractIndexCursor;
027    import org.apache.directory.server.xdbm.IndexCursor;
028    import org.apache.directory.server.i18n.I18n;
029    import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
030    import org.apache.directory.shared.ldap.entry.ServerEntry;
031    import org.apache.directory.shared.ldap.entry.Value;
032    
033    
034    /**
035     * A Cursor over entry candidates matching an equality assertion filter.  This
036     * Cursor operates in two modes.  The first is when an index exists for the
037     * attribute the equality assertion is built on.  The second is when the user
038     * index for the assertion attribute does not exist.  Different Cursors are
039     * used in each of these cases where the other remains null.
040     *
041     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042     * @version $$Rev$$
043     */
044    public class EqualityCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID>
045    {
046        private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_714 );
047    
048        /** An equality evaluator for candidates */
049        @SuppressWarnings("unchecked")
050        private final EqualityEvaluator equalityEvaluator;
051    
052        /** Cursor over attribute entry matching filter: set when index present */
053        private final IndexCursor<V, ServerEntry, ID> userIdxCursor;
054    
055        /** NDN Cursor on all entries in  (set when no index on user attribute) */
056        private final IndexCursor<String, ServerEntry, ID> ndnIdxCursor;
057    
058        /** used only when ndnIdxCursor is used (no index on attribute) */
059        private boolean available = false;
060    
061    
062        @SuppressWarnings("unchecked")
063        public EqualityCursor( Store<ServerEntry, ID> db, EqualityEvaluator<V, ID> equalityEvaluator ) throws Exception
064        {
065            this.equalityEvaluator = equalityEvaluator;
066    
067            String attribute = equalityEvaluator.getExpression().getAttribute();
068            Value<V> value = equalityEvaluator.getExpression().getValue();
069            if ( db.hasIndexOn( attribute ) )
070            {
071                Index<V, ServerEntry, ID> userIndex = ( Index<V, ServerEntry, ID> ) db.getIndex( attribute );
072                userIdxCursor = userIndex.forwardCursor( value.get() );
073                ndnIdxCursor = null;
074            }
075            else
076            {
077                ndnIdxCursor = db.getNdnIndex().forwardCursor();
078                userIdxCursor = null;
079            }
080        }
081    
082    
083        public boolean available()
084        {
085            if ( userIdxCursor != null )
086            {
087                return userIdxCursor.available();
088            }
089    
090            return available;
091        }
092    
093    
094        public void beforeValue( ID id, V value ) throws Exception
095        {
096            checkNotClosed( "beforeValue()" );
097            if ( userIdxCursor != null )
098            {
099                userIdxCursor.beforeValue( id, value );
100            }
101            else
102            {
103                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
104            }
105        }
106    
107    
108        public void before( IndexEntry<V, ServerEntry, ID> element ) throws Exception
109        {
110            checkNotClosed( "before()" );
111            if ( userIdxCursor != null )
112            {
113                userIdxCursor.before( element );
114            }
115            else
116            {
117                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
118            }
119        }
120    
121    
122        public void afterValue( ID id, V key ) throws Exception
123        {
124            checkNotClosed( "afterValue()" );
125            if ( userIdxCursor != null )
126            {
127                userIdxCursor.afterValue( id, key );
128            }
129            else
130            {
131                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
132            }
133        }
134    
135    
136        public void after( IndexEntry<V, ServerEntry, ID> element ) throws Exception
137        {
138            checkNotClosed( "after()" );
139            if ( userIdxCursor != null )
140            {
141                userIdxCursor.after( element );
142            }
143            else
144            {
145                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
146            }
147        }
148    
149    
150        public void beforeFirst() throws Exception
151        {
152            checkNotClosed( "beforeFirst()" );
153            if ( userIdxCursor != null )
154            {
155                userIdxCursor.beforeFirst();
156            }
157            else
158            {
159                ndnIdxCursor.beforeFirst();
160                available = false;
161            }
162        }
163    
164    
165        public void afterLast() throws Exception
166        {
167            checkNotClosed( "afterLast()" );
168            if ( userIdxCursor != null )
169            {
170                userIdxCursor.afterLast();
171            }
172            else
173            {
174                ndnIdxCursor.afterLast();
175                available = false;
176            }
177        }
178    
179    
180        public boolean first() throws Exception
181        {
182            beforeFirst();
183            return next();
184        }
185    
186    
187        public boolean last() throws Exception
188        {
189            afterLast();
190            return previous();
191        }
192    
193    
194        @SuppressWarnings("unchecked")
195        public boolean previous() throws Exception
196        {
197            if ( userIdxCursor != null )
198            {
199                return userIdxCursor.previous();
200            }
201    
202            while ( ndnIdxCursor.previous() )
203            {
204                checkNotClosed( "previous()" );
205                IndexEntry<?, ServerEntry, ID> candidate = ndnIdxCursor.get();
206                if ( equalityEvaluator.evaluate( candidate ) )
207                {
208                    return available = true;
209                }
210            }
211    
212            return available = false;
213        }
214    
215    
216        @SuppressWarnings("unchecked")
217        public boolean next() throws Exception
218        {
219            if ( userIdxCursor != null )
220            {
221                return userIdxCursor.next();
222            }
223    
224            while ( ndnIdxCursor.next() )
225            {
226                checkNotClosed( "next()" );
227                IndexEntry<?, ServerEntry, ID> candidate = ndnIdxCursor.get();
228                if ( equalityEvaluator.evaluate( candidate ) )
229                {
230                    return available = true;
231                }
232            }
233    
234            return available = false;
235        }
236    
237    
238        @SuppressWarnings("unchecked")
239        public IndexEntry<V, ServerEntry, ID> get() throws Exception
240        {
241            checkNotClosed( "get()" );
242            if ( userIdxCursor != null )
243            {
244                return userIdxCursor.get();
245            }
246    
247            if ( available )
248            {
249                return ( IndexEntry<V, ServerEntry, ID> ) ndnIdxCursor.get();
250            }
251    
252            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
253        }
254    
255    
256        public boolean isElementReused()
257        {
258            if ( userIdxCursor != null )
259            {
260                return userIdxCursor.isElementReused();
261            }
262    
263            return ndnIdxCursor.isElementReused();
264        }
265    
266    
267        public void close() throws Exception
268        {
269            super.close();
270    
271            if ( userIdxCursor != null )
272            {
273                userIdxCursor.close();
274            }
275            else
276            {
277                ndnIdxCursor.close();
278            }
279        }
280    }