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 }