View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.xdbm.search.cursor;
21  
22  
23  import java.io.IOException;
24  
25  import org.apache.directory.api.ldap.model.constants.Loggers;
26  import org.apache.directory.api.ldap.model.cursor.Cursor;
27  import org.apache.directory.api.ldap.model.cursor.CursorException;
28  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
29  import org.apache.directory.api.ldap.model.entry.Value;
30  import org.apache.directory.api.ldap.model.exception.LdapException;
31  import org.apache.directory.api.ldap.model.schema.AttributeType;
32  import org.apache.directory.server.core.api.partition.PartitionTxn;
33  import org.apache.directory.server.i18n.I18n;
34  import org.apache.directory.server.xdbm.AbstractIndexCursor;
35  import org.apache.directory.server.xdbm.Index;
36  import org.apache.directory.server.xdbm.IndexEntry;
37  import org.apache.directory.server.xdbm.IndexNotFoundException;
38  import org.apache.directory.server.xdbm.Store;
39  import org.apache.directory.server.xdbm.search.evaluator.EqualityEvaluator;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  
44  /**
45   * A Cursor over entry candidates matching an equality assertion filter.  This
46   * Cursor operates in two modes.  The first is when an index exists for the
47   * attribute the equality assertion is built on.  The second is when the user
48   * index for the assertion attribute does not exist.  Different Cursors are
49   * used in each of these cases where the other remains null.
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   */
53  public class EqualityCursor<V> extends AbstractIndexCursor<V>
54  {
55      /** A dedicated log for cursors */
56      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
57  
58      /** Speedup for logs */
59      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
60  
61      /** The message for unsupported operations */
62      private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_714 );
63  
64      /** An equality evaluator for candidates */
65      private final EqualityEvaluator<V> equalityEvaluator;
66  
67      /** Cursor over attribute entry matching filter: set when index present */
68      private final Cursor<IndexEntry<V, String>> userIdxCursor;
69  
70      /** NDN Cursor on all entries in  (set when no index on user attribute) */
71      private final Cursor<IndexEntry<String, String>> uuidIdxCursor;
72  
73  
74      /**
75       * Creates a new instance of an EqualityCursor
76       * 
77       * @param partitionTxn The transaction to use
78       * @param store The store
79       * @param equalityEvaluator The EqualityEvaluator
80       * @throws LdapException If the creation failed
81       * @throws IndexNotFoundException If the index was not found
82       */
83      @SuppressWarnings("unchecked")
84      public EqualityCursor( PartitionTxn partitionTxn, Store store, EqualityEvaluator<V> equalityEvaluator ) 
85          throws LdapException, IndexNotFoundException
86      {
87          if ( IS_DEBUG )
88          {
89              LOG_CURSOR.debug( "Creating EqualityCursor {}", this );
90          }
91  
92          this.equalityEvaluator = equalityEvaluator;
93          this.partitionTxn = partitionTxn;
94  
95          AttributeType attributeType = equalityEvaluator.getExpression().getAttributeType();
96          Value value = equalityEvaluator.getExpression().getValue();
97  
98          if ( store.hasIndexOn( attributeType ) )
99          {
100             Index<V, String> userIndex = ( Index<V, String> ) store.getIndex( attributeType );
101             String normalizedValue = attributeType.getEquality().getNormalizer().normalize( value.getString() );
102             userIdxCursor = userIndex.forwardCursor( partitionTxn, ( V ) normalizedValue );
103             uuidIdxCursor = null;
104         }
105         else
106         {
107             uuidIdxCursor = new AllEntriesCursor( partitionTxn, store );
108             userIdxCursor = null;
109         }
110     }
111 
112 
113     /**
114      * {@inheritDoc}
115      */
116     protected String getUnsupportedMessage()
117     {
118         return UNSUPPORTED_MSG;
119     }
120 
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public boolean available()
127     {
128         if ( userIdxCursor != null )
129         {
130             return userIdxCursor.available();
131         }
132 
133         return super.available();
134     }
135 
136 
137     /**
138      * {@inheritDoc}
139      */
140     @Override
141     public void before( IndexEntry<V, String> element ) throws LdapException, CursorException
142     {
143         checkNotClosed();
144 
145         if ( userIdxCursor != null )
146         {
147             userIdxCursor.before( element );
148         }
149         else
150         {
151             super.before( element );
152         }
153     }
154 
155 
156     /**
157      * {@inheritDoc}
158      */
159     @Override
160     public void after( IndexEntry<V, String> element ) throws LdapException, CursorException
161     {
162         checkNotClosed();
163 
164         if ( userIdxCursor != null )
165         {
166             userIdxCursor.after( element );
167         }
168         else
169         {
170             super.after( element );
171         }
172     }
173 
174 
175     /**
176      * {@inheritDoc}
177      */
178     public void beforeFirst() throws LdapException, CursorException
179     {
180         checkNotClosed();
181 
182         if ( userIdxCursor != null )
183         {
184             userIdxCursor.beforeFirst();
185         }
186         else
187         {
188             uuidIdxCursor.beforeFirst();
189         }
190 
191         setAvailable( false );
192     }
193 
194 
195     /**
196      * {@inheritDoc}
197      */
198     public void afterLast() throws LdapException, CursorException
199     {
200         checkNotClosed();
201 
202         if ( userIdxCursor != null )
203         {
204             userIdxCursor.afterLast();
205         }
206         else
207         {
208             uuidIdxCursor.afterLast();
209         }
210 
211         setAvailable( false );
212     }
213 
214 
215     /**
216      * {@inheritDoc}
217      */
218     public boolean first() throws LdapException, CursorException
219     {
220         beforeFirst();
221 
222         return next();
223     }
224 
225 
226     /**
227      * {@inheritDoc}
228      */
229     public boolean last() throws LdapException, CursorException
230     {
231         afterLast();
232 
233         return previous();
234     }
235 
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     public boolean previous() throws LdapException, CursorException
242     {
243         if ( userIdxCursor != null )
244         {
245             return userIdxCursor.previous();
246         }
247 
248         while ( uuidIdxCursor.previous() )
249         {
250             checkNotClosed();
251             IndexEntry<?, String> candidate = uuidIdxCursor.get();
252 
253             if ( equalityEvaluator.evaluate( partitionTxn, candidate ) )
254             {
255                 return setAvailable( true );
256             }
257         }
258 
259         return setAvailable( false );
260     }
261 
262 
263     /**
264      * {@inheritDoc}
265      */
266     @Override
267     public boolean next() throws LdapException, CursorException
268     {
269         if ( userIdxCursor != null )
270         {
271             return userIdxCursor.next();
272         }
273 
274         while ( uuidIdxCursor.next() )
275         {
276             checkNotClosed();
277             IndexEntry<?, String> candidate = uuidIdxCursor.get();
278 
279             if ( equalityEvaluator.evaluate( partitionTxn, candidate ) )
280             {
281                 return setAvailable( true );
282             }
283         }
284 
285         return setAvailable( false );
286     }
287 
288 
289     /**
290      * {@inheritDoc}
291      */
292     @SuppressWarnings("unchecked")
293     public IndexEntry<V, String> get() throws CursorException
294     {
295         checkNotClosed();
296 
297         if ( userIdxCursor != null )
298         {
299             return userIdxCursor.get();
300         }
301 
302         if ( available() )
303         {
304             return ( IndexEntry<V, String> ) uuidIdxCursor.get();
305         }
306 
307         throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
308     }
309 
310 
311     /**
312      * {@inheritDoc}
313      */
314     @Override
315     public void close() throws IOException
316     {
317         if ( IS_DEBUG )
318         {
319             LOG_CURSOR.debug( "Closing EqualityCursor {}", this );
320         }
321 
322         super.close();
323 
324         if ( userIdxCursor != null )
325         {
326             userIdxCursor.close();
327         }
328         else
329         {
330             uuidIdxCursor.close();
331         }
332     }
333 
334 
335     /**
336      * {@inheritDoc}
337      */
338     @Override
339     public void close( Exception cause ) throws IOException
340     {
341         if ( IS_DEBUG )
342         {
343             LOG_CURSOR.debug( "Closing EqualityCursor {}", this );
344         }
345 
346         super.close( cause );
347 
348         if ( userIdxCursor != null )
349         {
350             userIdxCursor.close( cause );
351         }
352         else
353         {
354             uuidIdxCursor.close( cause );
355         }
356     }
357 
358 
359     /**
360      * @see Object#toString()
361      */
362     @Override
363     public String toString( String tabs )
364     {
365         StringBuilder sb = new StringBuilder();
366 
367         sb.append( tabs ).append( "EqualityCursor (" );
368 
369         if ( available() )
370         {
371             sb.append( "available)" );
372         }
373         else
374         {
375             sb.append( "absent)" );
376         }
377 
378         sb.append( " :\n" );
379 
380         sb.append( tabs + "  >>" ).append( equalityEvaluator );
381 
382         if ( userIdxCursor != null )
383         {
384             sb.append( tabs + "  <user>\n" );
385             sb.append( userIdxCursor.toString( tabs + "    " ) );
386         }
387 
388         if ( uuidIdxCursor != null )
389         {
390             sb.append( tabs + "  <uuid>\n" );
391             sb.append( uuidIdxCursor.toString( tabs + "  " ) );
392         }
393 
394         return sb.toString();
395     }
396 
397 
398     /**
399      * @see Object#toString()
400      */
401     public String toString()
402     {
403         return toString( "" );
404     }
405 }