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