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.i18n.I18n;
024    import org.apache.directory.server.xdbm.AbstractIndexCursor;
025    import org.apache.directory.server.xdbm.ForwardIndexEntry;
026    import org.apache.directory.server.xdbm.Index;
027    import org.apache.directory.server.xdbm.IndexCursor;
028    import org.apache.directory.server.xdbm.IndexEntry;
029    import org.apache.directory.server.xdbm.Store;
030    import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
031    import org.apache.directory.shared.ldap.entry.ServerEntry;
032    
033    
034    /**
035     * A Cursor over entry candidates matching a LessEq assertion filter.  This
036     * Cursor operates in two modes.  The first is when an index exists for the
037     * attribute the assertion is built on.  The second is when the user index for
038     * the assertion attribute does not exist.  Different Cursors are used in each
039     * 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 LessEqCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID>
045    {
046        private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_716 );
047    
048        /** An less eq evaluator for candidates */
049        private final LessEqEvaluator<V, ID> lessEqEvaluator;
050    
051        /** Cursor over attribute entry matching filter: set when index present */
052        private final IndexCursor<V, ServerEntry, ID> userIdxCursor;
053    
054        /** NDN Cursor on all entries in  (set when no index on user attribute) */
055        private final IndexCursor<V, ServerEntry, ID> ndnIdxCursor;
056    
057        /**
058         * Used to store indexEntry from ndnCandidate so it can be saved after
059         * call to evaluate() which changes the value so it's not referring to
060         * the NDN but to the value of the attribute instead.
061         */
062        IndexEntry<V, ServerEntry, ID> ndnCandidate;
063    
064        /** used in both modes */
065        private boolean available = false;
066    
067    
068        @SuppressWarnings("unchecked")
069        public LessEqCursor( Store<ServerEntry, ID> db, LessEqEvaluator<V, ID> lessEqEvaluator ) throws Exception
070        {
071            this.lessEqEvaluator = lessEqEvaluator;
072    
073            String attribute = lessEqEvaluator.getExpression().getAttribute();
074            if ( db.hasIndexOn( attribute ) )
075            {
076                userIdxCursor = ( ( Index<V, ServerEntry, ID> ) db.getIndex( attribute ) ).forwardCursor();
077                ndnIdxCursor = null;
078            }
079            else
080            {
081                ndnIdxCursor = ( IndexCursor<V, ServerEntry, ID> ) db.getNdnIndex().forwardCursor();
082                userIdxCursor = null;
083            }
084        }
085    
086    
087        public boolean available()
088        {
089            return available;
090        }
091    
092    
093        public void beforeValue( ID id, V value ) throws Exception
094        {
095            checkNotClosed( "beforeValue()" );
096            if ( userIdxCursor != null )
097            {
098                /*
099                 * First we need to check and make sure this element is within
100                 * bounds as mandated by the assertion node.  To do so we compare
101                 * it's value with the value of the expression node.  If the
102                 * element's value is greater than this upper bound then we
103                 * position the userIdxCursor after the last node.
104                 *
105                 * If the element's value is equal to this upper bound then we
106                 * position the userIdxCursor right before the last node.
107                 *
108                 * If the element's value is smaller, then we delegate to the
109                 * before() method of the userIdxCursor.
110                 */
111                //noinspection unchecked
112                int compareValue = lessEqEvaluator.getLdapComparator().compare( value,
113                    lessEqEvaluator.getExpression().getValue().get() );
114    
115                if ( compareValue > 0 )
116                {
117                    afterLast();
118                    return;
119                }
120                else if ( compareValue == 0 )
121                {
122                    last();
123                    previous();
124                    available = false;
125                    return;
126                }
127    
128                userIdxCursor.beforeValue( id, value );
129                available = false;
130            }
131            else
132            {
133                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
134            }
135        }
136    
137    
138        public void before( IndexEntry<V, ServerEntry, ID> element ) throws Exception
139        {
140            checkNotClosed( "before()" );
141            if ( userIdxCursor != null )
142            {
143                /*
144                 * First we need to check and make sure this element is within
145                 * bounds as mandated by the assertion node.  To do so we compare
146                 * it's value with the value of the expression node.  If the
147                 * element's value is greater than this upper bound then we
148                 * position the userIdxCursor after the last node.
149                 *
150                 * If the element's value is equal to this upper bound then we
151                 * position the userIdxCursor right before the last node.
152                 *
153                 * If the element's value is smaller, then we delegate to the
154                 * before() method of the userIdxCursor.
155                 */
156                int compareValue = lessEqEvaluator.getLdapComparator().compare( element.getValue(),
157                    lessEqEvaluator.getExpression().getValue().get() );
158    
159                if ( compareValue > 0 )
160                {
161                    afterLast();
162                    return;
163                }
164                else if ( compareValue == 0 )
165                {
166                    last();
167                    previous();
168                    available = false;
169                    return;
170                }
171    
172                userIdxCursor.before( element );
173                available = false;
174            }
175            else
176            {
177                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
178            }
179        }
180    
181    
182        public void afterValue( ID id, V value ) throws Exception
183        {
184            checkNotClosed( "afterValue()" );
185            if ( userIdxCursor != null )
186            {
187                int comparedValue = lessEqEvaluator.getLdapComparator().compare( value,
188                    lessEqEvaluator.getExpression().getValue().get() );
189    
190                /*
191                 * First we need to check and make sure this element is within
192                 * bounds as mandated by the assertion node.  To do so we compare
193                 * it's value with the value of the expression node.
194                 *
195                 * If the element's value is equal to or greater than this upper
196                 * bound then we position the userIdxCursor after the last node.
197                 *
198                 * If the element's value is smaller, then we delegate to the
199                 * after() method of the userIdxCursor.
200                 */
201                if ( comparedValue >= 0 )
202                {
203                    afterLast();
204                    return;
205                }
206    
207                // Element is in the valid range as specified by assertion
208                userIdxCursor.afterValue( id, value );
209                available = false;
210            }
211            else
212            {
213                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
214            }
215        }
216    
217    
218        public void after( IndexEntry<V, ServerEntry, ID> element ) throws Exception
219        {
220            checkNotClosed( "after()" );
221            if ( userIdxCursor != null )
222            {
223                int comparedValue = lessEqEvaluator.getLdapComparator().compare( element.getValue(),
224                    lessEqEvaluator.getExpression().getValue().get() );
225    
226                /*
227                 * First we need to check and make sure this element is within
228                 * bounds as mandated by the assertion node.  To do so we compare
229                 * it's value with the value of the expression node.
230                 *
231                 * If the element's value is equal to or greater than this upper
232                 * bound then we position the userIdxCursor after the last node.
233                 *
234                 * If the element's value is smaller, then we delegate to the
235                 * after() method of the userIdxCursor.
236                 */
237                if ( comparedValue >= 0 )
238                {
239                    afterLast();
240                    return;
241                }
242    
243                // Element is in the valid range as specified by assertion
244                userIdxCursor.after( element );
245                available = false;
246            }
247            else
248            {
249                throw new UnsupportedOperationException( UNSUPPORTED_MSG );
250            }
251        }
252    
253    
254        public void beforeFirst() throws Exception
255        {
256            checkNotClosed( "beforeFirst()" );
257            if ( userIdxCursor != null )
258            {
259                userIdxCursor.beforeFirst();
260            }
261            else
262            {
263                ndnIdxCursor.beforeFirst();
264                ndnCandidate = null;
265            }
266    
267            available = false;
268        }
269    
270    
271        public void afterLast() throws Exception
272        {
273            checkNotClosed( "afterLast()" );
274            if ( userIdxCursor != null )
275            {
276                IndexEntry<V, ServerEntry, ID> advanceTo = new ForwardIndexEntry<V, ServerEntry, ID>();
277                //noinspection unchecked
278                advanceTo.setValue( ( V ) lessEqEvaluator.getExpression().getValue().get() );
279                userIdxCursor.after( advanceTo );
280            }
281            else
282            {
283                ndnIdxCursor.afterLast();
284                ndnCandidate = null;
285            }
286    
287            available = false;
288        }
289    
290    
291        public boolean first() throws Exception
292        {
293            beforeFirst();
294            return next();
295        }
296    
297    
298        public boolean last() throws Exception
299        {
300            afterLast();
301            return previous();
302        }
303    
304    
305        public boolean previous() throws Exception
306        {
307            checkNotClosed( "previous()" );
308    
309            if ( userIdxCursor != null )
310            {
311                /*
312                 * No need to do the same check that is done in next() since
313                 * values are decreasing with calls to previous().  We will
314                 * always have lesser values.
315                 */
316                return available = userIdxCursor.previous();
317            }
318    
319            while ( ndnIdxCursor.previous() )
320            {
321                checkNotClosed( "previous()" );
322                ndnCandidate = ndnIdxCursor.get();
323                if ( lessEqEvaluator.evaluate( ndnCandidate ) )
324                {
325                    return available = true;
326                }
327                else
328                {
329                    ndnCandidate = null;
330                }
331            }
332    
333            return available = false;
334        }
335    
336    
337        public boolean next() throws Exception
338        {
339            checkNotClosed( "next()" );
340            if ( userIdxCursor != null )
341            {
342                /*
343                 * We have to check and make sure the next value complies by
344                 * being less than or eq to the expression node's value.  We need
345                 * to do this since values are increasing and we must limit to our
346                 * upper bound.
347                 */
348                while ( userIdxCursor.next() )
349                {
350                    checkNotClosed( "next()" );
351                    IndexEntry<?, ServerEntry, ID> candidate = userIdxCursor.get();
352                    if ( lessEqEvaluator.getLdapComparator().compare( candidate.getValue(),
353                        lessEqEvaluator.getExpression().getValue().get() ) <= 0 )
354                    {
355                        return available = true;
356                    }
357                }
358    
359                return available = false;
360            }
361    
362            while ( ndnIdxCursor.next() )
363            {
364                checkNotClosed( "next()" );
365                ndnCandidate = ndnIdxCursor.get();
366                if ( lessEqEvaluator.evaluate( ndnCandidate ) )
367                {
368                    return available = true;
369                }
370                else
371                {
372                    ndnCandidate = null;
373                }
374            }
375    
376            return available = false;
377        }
378    
379    
380        public IndexEntry<V, ServerEntry, ID> get() throws Exception
381        {
382            checkNotClosed( "get()" );
383            if ( userIdxCursor != null )
384            {
385                if ( available )
386                {
387                    return userIdxCursor.get();
388                }
389    
390                throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
391            }
392    
393            if ( available )
394            {
395                return ndnCandidate;
396            }
397    
398            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
399        }
400    
401    
402        public boolean isElementReused()
403        {
404            if ( userIdxCursor != null )
405            {
406                return userIdxCursor.isElementReused();
407            }
408    
409            return ndnIdxCursor.isElementReused();
410        }
411    
412    
413        public void close() throws Exception
414        {
415            super.close();
416    
417            if ( userIdxCursor != null )
418            {
419                userIdxCursor.close();
420            }
421            else
422            {
423                ndnIdxCursor.close();
424                ndnCandidate = null;
425            }
426        }
427    }