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    }