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  package org.apache.directory.server.core.api.filtering;
20  
21  
22  import java.util.Collections;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.directory.api.i18n.I18n;
27  import org.apache.directory.api.ldap.model.constants.Loggers;
28  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
29  import org.apache.directory.api.ldap.model.cursor.ClosureMonitor;
30  import org.apache.directory.api.ldap.model.cursor.Cursor;
31  import org.apache.directory.api.ldap.model.cursor.CursorException;
32  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
33  import org.apache.directory.api.ldap.model.entry.Entry;
34  import org.apache.directory.api.ldap.model.exception.LdapException;
35  import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  
40  /**
41   * An implementation of a Cursor based on a {@link List} of {@link Cursor}s.  Optionally, the
42   * Cursor may be limited to a specific range within the list.
43   * 
44   * This class is modeled based on the implementation of {@link org.apache.directory.api.ldap.model.cursor.ListCursor}
45   * 
46   * WARN this is only used internally !
47   * 
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public class CursorList extends AbstractCursor<Entry> implements EntryFilteringCursor
51  {
52      /** A dedicated log for cursors */
53      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
54  
55      /** Speedup for logs */
56      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
57  
58      /** The inner List */
59      private final List<EntryFilteringCursor> list;
60  
61      /** The starting position for the cursor in the list. It can be > 0 */
62      private final int start;
63  
64      /** The ending position for the cursor in the list. It can be < List.size() */
65      private final int end;
66  
67      /** The number of cursors in the list */
68      private final int listSize;
69  
70      /** The current position in the list */
71      private int index;
72  
73      /** The current cursor being used */
74      private EntryFilteringCursor currentCursor;
75  
76      /** the operation context */
77      private SearchOperationContext searchContext;
78  
79      /** flag to detect the closed cursor */
80      private boolean closed;
81  
82      /** The logger for this class */
83      private static final Logger LOG = LoggerFactory.getLogger( CursorList.class );
84  
85  
86      /**
87       * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
88       * bounds.
89       *
90       * As with all Cursors, this ListCursor requires a successful return from
91       * advance operations (next() or previous()) to properly return values
92       * using the get() operation.
93       *
94       * @param start the lower bound index
95       * @param list the list this ListCursor operates on
96       * @param end the upper bound index
97       * @param searchContext The SearchContext instance
98       */
99      public CursorList( int start, List<EntryFilteringCursor> list, int end, SearchOperationContext searchContext )
100     {
101         if ( IS_DEBUG )
102         {
103             LOG_CURSOR.debug( "Creating CursorList {}", this );
104         }
105 
106         if ( list != null )
107         {
108             this.list = list;
109         }
110         else
111         {
112             this.list = Collections.emptyList();
113         }
114 
115         listSize = this.list.size();
116 
117         if ( ( start < 0 ) || ( start > listSize ) )
118         {
119             throw new IllegalArgumentException( I18n.err( I18n.ERR_13105_START_INDEX_OUT_OF_RANGE, start ) );
120         }
121 
122         if ( ( end < 0 ) || ( end > listSize ) )
123         {
124             throw new IllegalArgumentException( I18n.err( I18n.ERR_13106_END_INDEX_OUT_OF_RANGE, end ) );
125         }
126 
127         // check list is not empty list since the empty list is the only situation
128         // where we allow for start to equal the end: in other cases it makes no sense
129         if ( ( listSize > 0 ) && ( start >= end ) )
130         {
131             throw new IllegalArgumentException( I18n.err( I18n.ERR_13107_START_INDEX_ABOVE_END_INDEX, start, end ) );
132         }
133 
134         this.start = start;
135         this.end = end;
136         this.searchContext = searchContext;
137         index = start;
138         currentCursor = this.list.get( index );
139     }
140 
141 
142     /**
143      * Creates a new ListCursor without specific bounds: the bounds are
144      * acquired from the size of the list.
145      *
146      * @param list the backing for this ListCursor
147      * @param searchContext The SearchContext instance
148      */
149     public CursorList( List<EntryFilteringCursor> list, SearchOperationContext searchContext )
150     {
151         this( 0, list, list.size(), searchContext );
152     }
153 
154 
155     /**
156      * {@inheritDoc}
157      */
158     public boolean available()
159     {
160         if ( ( index >= 0 ) && ( index < end ) )
161         {
162             return list.get( index ).available();
163         }
164 
165         return false;
166     }
167 
168 
169     /**
170      * {@inheritDoc}
171      */
172     public void before( Entry element ) throws LdapException, CursorException
173     {
174         throw new UnsupportedOperationException( I18n.err( I18n.ERR_13108_LIST_MAY_BE_SORTED ) );
175     }
176 
177 
178     /**
179      * {@inheritDoc}
180      */
181     public void after( Entry element ) throws LdapException, CursorException
182     {
183         throw new UnsupportedOperationException( I18n.err( I18n.ERR_13108_LIST_MAY_BE_SORTED ) );
184     }
185 
186 
187     /**
188      * {@inheritDoc}
189      */
190     public void beforeFirst() throws LdapException, CursorException
191     {
192         index = 0;
193         currentCursor = list.get( index );
194         currentCursor.beforeFirst();
195     }
196 
197 
198     /**
199      * {@inheritDoc}
200      */
201     public void afterLast() throws LdapException, CursorException
202     {
203         index = end - 1;
204         currentCursor = list.get( index );
205         currentCursor.afterLast();
206     }
207 
208 
209     /**
210      * {@inheritDoc}
211      */
212     public boolean first() throws LdapException, CursorException
213     {
214         if ( listSize > 0 )
215         {
216             index = start;
217 
218             return list.get( index ).first();
219         }
220 
221         return false;
222     }
223 
224 
225     /**
226      * {@inheritDoc}
227      */
228     public boolean last() throws LdapException, CursorException
229     {
230         if ( listSize > 0 )
231         {
232             index = end - 1;
233             currentCursor = list.get( index );
234 
235             return currentCursor.last();
236         }
237 
238         return false;
239     }
240 
241 
242     /**
243      * {@inheritDoc}
244      */
245     public boolean isFirst()
246     {
247         return ( listSize > 0 ) && ( index == start ) && list.get( index ).isFirst();
248     }
249 
250 
251     /**
252      * {@inheritDoc}
253      */
254     public boolean isLast()
255     {
256         return ( listSize > 0 ) && ( index == end - 1 ) && list.get( index ).isLast();
257     }
258 
259 
260     /**
261      * {@inheritDoc}
262      */
263     public boolean isAfterLast()
264     {
265         return ( index == end );
266     }
267 
268 
269     /**
270      * {@inheritDoc}
271      */
272     public boolean isBeforeFirst()
273     {
274         return index == -1;
275     }
276 
277 
278     /**
279      * {@inheritDoc}
280      */
281     public boolean previous() throws LdapException, CursorException
282     {
283         while ( index > -1 )
284         {
285             currentCursor = list.get( index );
286 
287             if ( currentCursor.previous() )
288             {
289                 return true;
290             }
291             else
292             {
293                 index--;
294             }
295         }
296 
297         return false;
298     }
299 
300 
301     /**
302      * {@inheritDoc}
303      */
304     public boolean next() throws LdapException, CursorException
305     {
306         if ( listSize > 0 )
307         {
308             if ( index == -1 )
309             {
310                 index = start;
311             }
312 
313             while ( index < end )
314             {
315                 currentCursor = list.get( index );
316 
317                 if ( currentCursor.next() )
318                 {
319                     return true;
320                 }
321                 else
322                 {
323                     index++;
324                 }
325             }
326         }
327 
328         return false;
329     }
330 
331 
332     /**
333      * {@inheritDoc}
334      */
335     public Entry get() throws CursorException
336     {
337         if ( ( index < start ) || ( index >= end ) )
338         {
339             throw new CursorException( I18n.err( I18n.ERR_13109_CURSOR_NOT_POSITIONED ) );
340         }
341 
342         if ( currentCursor.available() )
343         {
344             return currentCursor.get();
345         }
346 
347         throw new InvalidCursorPositionException();
348     }
349 
350 
351     /**
352      * {@inheritDoc}
353      */
354     public boolean addEntryFilter( EntryFilter filter )
355     {
356         for ( EntryFilteringCursor efc : list )
357         {
358             efc.addEntryFilter( filter );
359         }
360 
361         // returning hard coded value, shouldn't be a problem
362         return true;
363     }
364 
365 
366     /**
367      * {@inheritDoc}
368      */
369     public List<EntryFilter> getEntryFilters()
370     {
371         throw new UnsupportedOperationException( "CursorList doesn't support this operation" );
372     }
373 
374 
375     /**
376      * {@inheritDoc}
377      */
378     public SearchOperationContext getOperationContext()
379     {
380         return searchContext;
381     }
382 
383 
384     public boolean isAbandoned()
385     {
386         return searchContext.isAbandoned();
387     }
388 
389 
390     public void setAbandoned( boolean abandoned )
391     {
392         searchContext.setAbandoned( abandoned );
393 
394         if ( abandoned )
395         {
396             LOG.info( "Cursor has been abandoned." );
397         }
398     }
399 
400 
401     /**
402      * {@inheritDoc}
403      */
404     public void close()
405     {
406         if ( IS_DEBUG )
407         {
408             LOG_CURSOR.debug( "Closing CursorList {}", this );
409         }
410 
411         close( null );
412     }
413 
414 
415     /**
416      * {@inheritDoc}
417      */
418     public void close( Exception reason )
419     {
420         if ( IS_DEBUG )
421         {
422             LOG_CURSOR.debug( "Closing CursorList {}", this );
423         }
424 
425         closed = true;
426 
427         for ( EntryFilteringCursor cursor : list )
428         {
429             try
430             {
431                 if ( reason != null )
432                 {
433                     cursor.close( reason );
434                 }
435                 else
436                 {
437                     cursor.close();
438                 }
439             }
440             catch ( Exception e )
441             {
442                 LOG.warn( "Failed to close the cursor" );
443             }
444         }
445     }
446 
447 
448     /**
449      * {@inheritDoc}
450      */
451     public boolean isClosed()
452     {
453         return closed;
454     }
455 
456 
457     public Iterator<Entry> iterator()
458     {
459         throw new UnsupportedOperationException();
460     }
461 
462 
463     /**
464      * {@inheritDoc}
465      */
466     public void setClosureMonitor( ClosureMonitor monitor )
467     {
468         for ( EntryFilteringCursor c : list )
469         {
470             c.setClosureMonitor( monitor );
471         }
472     }
473 }