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 package org.apache.directory.server.core.filtering;
020
021
022 import java.io.IOException;
023 import java.util.Collections;
024 import java.util.Iterator;
025 import java.util.List;
026
027 import org.apache.directory.server.core.entry.ClonedServerEntry;
028 import org.apache.directory.server.core.interceptor.context.SearchingOperationContext;
029 import org.apache.directory.shared.i18n.I18n;
030 import org.apache.directory.shared.ldap.cursor.ClosureMonitor;
031 import org.apache.directory.shared.ldap.cursor.Cursor;
032 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
033 import org.apache.directory.shared.ldap.cursor.ListCursor;
034 import org.slf4j.Logger;
035 import org.slf4j.LoggerFactory;
036
037
038 /**
039 * An implementation of a Cursor based on a {@link List} of {@link Cursor}s. Optionally, the
040 * Cursor may be limited to a specific range within the list.
041 *
042 * This class is modeled based on the implementation of {@link ListCursor}
043 *
044 * WARN this is only used internally
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 * @version $Rev$, $Date$
048 */
049 public class CursorList implements EntryFilteringCursor
050 {
051 /** The inner List */
052 private final List<EntryFilteringCursor> list;
053
054 /** The starting position for the cursor in the list. It can be > 0 */
055 private final int start;
056
057 /** The ending position for the cursor in the list. It can be < List.size() */
058 private final int end;
059
060 /** The current position in the list */
061 private int index = -1;
062
063 /** the operation context */
064 private SearchingOperationContext opContext;
065
066 /** flag to detect the closed cursor */
067 private boolean closed;
068
069 private static final Logger LOG = LoggerFactory.getLogger( CursorList.class );
070
071
072 /**
073 * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
074 * bounds.
075 *
076 * As with all Cursors, this ListCursor requires a successful return from
077 * advance operations (next() or previous()) to properly return values
078 * using the get() operation.
079 *
080 * @param start the lower bound index
081 * @param list the list this ListCursor operates on
082 * @param end the upper bound index
083 */
084 public CursorList( int start, List<EntryFilteringCursor> list, int end, SearchingOperationContext opContext )
085 {
086 if ( ( start < 0 ) || ( start > list.size() ) )
087 {
088 throw new IllegalArgumentException( I18n.err( I18n.ERR_02005, start ) );
089 }
090
091 if ( ( end < 0 ) || ( end > list.size() ) )
092 {
093 throw new IllegalArgumentException( I18n.err( I18n.ERR_02006, end ) );
094 }
095
096 // check list is not empty list since the empty list is the only situation
097 // where we allow for start to equal the end: in other cases it makes no sense
098 if ( ( list.size() > 0 ) && ( start >= end ) )
099 {
100 throw new IllegalArgumentException( I18n.err( I18n.ERR_02007, start, end ) );
101 }
102
103 if ( list != null )
104 {
105 this.list = list;
106 }
107 else
108 {
109 this.list = Collections.emptyList();
110 }
111
112 this.start = start;
113 this.end = end;
114 this.opContext = opContext;
115 }
116
117
118 /**
119 * Creates a new ListCursor without specific bounds: the bounds are
120 * acquired from the size of the list.
121 *
122 * @param list the backing for this ListCursor
123 */
124 public CursorList( List<EntryFilteringCursor> list, SearchingOperationContext opContext )
125 {
126 this( 0, list, list.size(), opContext );
127 }
128
129
130 /**
131 * {@inheritDoc}
132 */
133 public boolean available()
134 {
135 if ( index >= 0 && index < end )
136 {
137 return list.get( index ).available();
138 }
139
140 return false;
141 }
142
143
144 /**
145 * @throws IllegalStateException if the underlying list is not sorted
146 * and/or a comparator is not provided.
147 */
148 public void before( ClonedServerEntry element ) throws Exception
149 {
150 // checkNotClosed( "before()" );
151 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
152 }
153
154
155 /**
156 * {@inheritDoc}
157 */
158 public void after( ClonedServerEntry element ) throws Exception
159 {
160 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
161 }
162
163
164 /**
165 * {@inheritDoc}
166 */
167 public void beforeFirst() throws Exception
168 {
169 this.index = -1;
170 list.get( index ).beforeFirst();
171 }
172
173
174 /**
175 * {@inheritDoc}
176 */
177 public void afterLast() throws Exception
178 {
179 this.index = end;
180 list.get( index ).afterLast();
181 }
182
183
184 /**
185 * {@inheritDoc}
186 */
187 public boolean first() throws Exception
188 {
189 if ( list.size() > 0 )
190 {
191 index = start;
192 return list.get( index ).first();
193 }
194
195 return false;
196 }
197
198
199 /**
200 * {@inheritDoc}
201 */
202 public boolean last() throws Exception
203 {
204 if ( list.size() > 0 )
205 {
206 index = end - 1;
207 return list.get( index ).last();
208 }
209
210 return false;
211 }
212
213
214 /**
215 * {@inheritDoc}
216 */
217 public boolean isFirst() throws Exception
218 {
219 return ( list.size() > 0 ) && ( index == start ) && list.get( index ).first();
220 }
221
222
223 /**
224 * {@inheritDoc}
225 */
226 public boolean isLast() throws Exception
227 {
228 return ( list.size() > 0 ) && ( index == end - 1 ) && list.get( index ).last();
229 }
230
231
232 /**
233 * {@inheritDoc}
234 */
235 public boolean isAfterLast() throws Exception
236 {
237 return index == end;
238 }
239
240
241 /**
242 * {@inheritDoc}
243 */
244 public boolean isBeforeFirst() throws Exception
245 {
246 return index == -1;
247 }
248
249
250 /**
251 * {@inheritDoc}
252 */
253 public boolean previous() throws Exception
254 {
255 // if parked at -1 we cannot go backwards
256 if ( index == -1 )
257 {
258 return false;
259 }
260
261 // if the index moved back is still greater than or eq to start then OK
262 if ( index - 1 >= start )
263 {
264 if ( index == end )
265 {
266 index--;
267 }
268
269 if ( !list.get( index ).previous() )
270 {
271 index--;
272 if ( index != -1 )
273 {
274 return list.get( index ).previous();
275 }
276 else
277 {
278 return false;
279 }
280 }
281 else
282 {
283 return true;
284 }
285 }
286
287 // if the index currently less than or equal to start we need to park it at -1 and return false
288 if ( index <= start )
289 {
290 if ( !list.get( index ).previous() )
291 {
292 index = -1;
293 return false;
294 }
295 else
296 {
297 return true;
298 }
299 }
300
301 if ( list.size() <= 0 )
302 {
303 index = -1;
304 }
305
306 return false;
307 }
308
309
310 /**
311 * {@inheritDoc}
312 */
313 public boolean next() throws Exception
314 {
315 // if parked at -1 we advance to the start index and return true
316 if ( list.size() > 0 && index == -1 )
317 {
318 index = start;
319 return list.get( index ).next();
320 }
321
322 // if the index plus one is less than the end then increment and return true
323 if ( list.size() > 0 && index + 1 < end )
324 {
325 if ( !list.get( index ).next() )
326 {
327 index++;
328 if ( index < end )
329 {
330 return list.get( index ).next();
331 }
332 else
333 {
334 return false;
335 }
336 }
337 else
338 {
339 return true;
340 }
341 }
342
343 // if the index plus one is equal to the end then increment and return false
344 if ( list.size() > 0 && index + 1 == end )
345 {
346 if ( !list.get( index ).next() )
347 {
348 index++;
349 return false;
350 }
351 else
352 {
353 return true;
354 }
355 }
356
357 if ( list.size() <= 0 )
358 {
359 index = end;
360 }
361
362 return false;
363 }
364
365
366 /**
367 * {@inheritDoc}
368 */
369 public ClonedServerEntry get() throws Exception
370 {
371 if ( index < start || index >= end )
372 {
373 throw new IOException( I18n.err( I18n.ERR_02009 ) );
374 }
375
376 if( list.get( index ).available() )
377 {
378 return ( ClonedServerEntry ) list.get( index ).get();
379 }
380 throw new InvalidCursorPositionException();
381 }
382
383
384 /**
385 * {@inheritDoc}
386 */
387 public boolean isElementReused()
388 {
389 return true;
390 }
391
392
393 public boolean addEntryFilter( EntryFilter filter )
394 {
395 for( EntryFilteringCursor efc : list )
396 {
397 efc.addEntryFilter( filter );
398 }
399
400 // returning hard coded value, shouldn't be a problem
401 return true;
402 }
403
404
405 public List<EntryFilter> getEntryFilters()
406 {
407 throw new UnsupportedOperationException( "CursorList doesn't support this operation" );
408 }
409
410
411 public SearchingOperationContext getOperationContext()
412 {
413 return opContext;
414 }
415
416
417 public boolean isAbandoned()
418 {
419 return getOperationContext().isAbandoned();
420 }
421
422
423 public boolean removeEntryFilter( EntryFilter filter )
424 {
425 return false;
426 }
427
428
429 public void setAbandoned( boolean abandoned )
430 {
431 getOperationContext().setAbandoned( abandoned );
432
433 if ( abandoned )
434 {
435 LOG.info( "Cursor has been abandoned." );
436 }
437 }
438
439
440 public void close() throws Exception
441 {
442 close( null );
443 }
444
445
446 public void close( Exception reason ) throws Exception
447 {
448 closed = true;
449 for ( Cursor c : list )
450 {
451 try
452 {
453 if ( reason != null )
454 {
455 c.close();
456 }
457 else
458 {
459 c.close( reason );
460 }
461 }
462 catch ( Exception e )
463 {
464 LOG.warn( "Failed to close the cursor" );
465 }
466 }
467 }
468
469
470 public boolean isClosed() throws Exception
471 {
472 return closed;
473 }
474
475
476 public Iterator<ClonedServerEntry> iterator()
477 {
478 throw new UnsupportedOperationException();
479 }
480
481
482 public void setClosureMonitor( ClosureMonitor monitor )
483 {
484 for ( Cursor c : list )
485 {
486 c.setClosureMonitor( monitor );
487 }
488 }
489
490 }