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.impl;
21  
22  
23  import org.apache.directory.server.core.cursor.InvalidCursorPositionException;
24  import org.apache.directory.server.core.entry.ServerEntry;
25  import org.apache.directory.server.xdbm.IndexEntry;
26  import org.apache.directory.server.xdbm.Store;
27  import org.apache.directory.server.xdbm.AbstractIndexCursor;
28  import org.apache.directory.server.xdbm.IndexCursor;
29  
30  
31  /**
32   * A Cursor over entries satisfying scope constraints with alias dereferencing
33   * considerations.
34   *
35   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
36   * @version $Rev$
37   */
38  public class SubtreeScopeCursor extends AbstractIndexCursor<Long, ServerEntry>
39  {
40      private static final String UNSUPPORTED_MSG =
41          "Scope Cursors are not ordered and do not support positioning by element.";
42  
43      /** The Entry database/store */
44      private final Store<ServerEntry> db;
45  
46      /** A ScopeNode Evaluator */
47      private final SubtreeScopeEvaluator<ServerEntry> evaluator;
48  
49      /** A Cursor over the entries in the scope of the search base */
50      private final IndexCursor<Long,ServerEntry> scopeCursor;
51  
52      /** A Cursor over entries brought into scope by alias dereferencing */
53      private final IndexCursor<Long,ServerEntry> dereferencedCursor;
54  
55      /** Currently active Cursor: we switch between two cursors */
56      private IndexCursor<Long,ServerEntry> cursor;
57  
58      /** Whether or not this Cursor is positioned so an entry is available */
59      private boolean available = false;
60  
61      private Long contextEntryId;
62  
63      
64      /**
65       * Creates a Cursor over entries satisfying subtree level scope criteria.
66       *
67       * @param db the entry store
68       * @param evaluator an IndexEntry (candidate) evaluator
69       * @throws Exception on db access failures
70       */
71      public SubtreeScopeCursor( Store<ServerEntry> db, SubtreeScopeEvaluator<ServerEntry> evaluator ) throws Exception
72      {
73          this.db = db;
74          this.evaluator = evaluator;
75          
76          if ( evaluator.getBaseId() == getContextEntryId() )
77          {
78              scopeCursor = new AllEntriesCursor( db );
79          }
80          else
81          {
82              scopeCursor = db.getSubLevelIndex().forwardCursor( evaluator.getBaseId() );
83          }
84  
85          if ( evaluator.isDereferencing() )
86          {
87              dereferencedCursor = db.getSubAliasIndex().forwardCursor( evaluator.getBaseId() );
88          }
89          else
90          {
91              dereferencedCursor = null;
92          }
93      }
94  
95  
96      private Long getContextEntryId()
97      {
98          if ( contextEntryId == null )
99          {
100             try
101             {
102                 this.contextEntryId = db.getEntryId( db.getSuffix().getNormName() );
103             }
104             catch ( Exception e )
105             {
106                 // might not have been created
107                 // might not have been created
108             }
109         }
110         
111         if ( contextEntryId == null )
112         {
113             return 1L;
114         }
115         
116         return contextEntryId;
117     }
118 
119     
120     public boolean available()
121     {
122         return available;
123     }
124 
125 
126     public void beforeValue( Long id, Long value ) throws Exception
127     {
128         throw new UnsupportedOperationException( UNSUPPORTED_MSG );
129     }
130 
131 
132     public void before( IndexEntry<Long, ServerEntry> element ) throws Exception
133     {
134         throw new UnsupportedOperationException( UNSUPPORTED_MSG );
135     }
136 
137 
138     public void afterValue( Long id, Long value ) throws Exception
139     {
140         throw new UnsupportedOperationException( UNSUPPORTED_MSG );
141     }
142 
143 
144     public void after( IndexEntry<Long, ServerEntry> element ) throws Exception
145     {
146         throw new UnsupportedOperationException( UNSUPPORTED_MSG );
147     }
148 
149 
150     public void beforeFirst() throws Exception
151     {
152         checkNotClosed( "beforeFirst()" );
153         cursor = scopeCursor;
154         cursor.beforeFirst();
155         available = false;
156     }
157 
158 
159     public void afterLast() throws Exception
160     {
161         checkNotClosed( "afterLast()" );
162         if ( evaluator.isDereferencing() )
163         {
164             cursor = dereferencedCursor;
165         }
166         else
167         {
168             cursor = scopeCursor;
169         }
170 
171         cursor.afterLast();
172         available = false;
173     }
174 
175 
176     public boolean first() throws Exception
177     {
178         beforeFirst();
179         return next();
180     }
181 
182 
183     public boolean last() throws Exception
184     {
185         afterLast();
186         return previous();
187     }
188 
189 
190     public boolean previous() throws Exception
191     {
192         checkNotClosed( "previous()" );
193         // if the cursor has not been set - position it after last element
194         if ( cursor == null )
195         {
196             afterLast();
197         }
198 
199         // if we're using the scopeCursor (1st Cursor) then return result as is
200         if ( cursor == scopeCursor )
201         {
202             /*
203              * If dereferencing is enabled then we must ignore alias entries, not
204              * returning them as part of the results.
205              */
206             if ( evaluator.isDereferencing() )
207             {
208                 // advance until nothing is available or until we find a non-alias
209                 do
210                 {
211                     checkNotClosed( "previous()" );
212                     available = cursor.previous();
213                     if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
214                     {
215                         break;
216                     }
217                 }
218                 while ( available );
219             }
220             else
221             {
222                 available = cursor.previous();
223             }
224 
225             return available;
226         }
227 
228         /*
229          * Below here we are using the dereferencedCursor so if nothing is
230          * available after an advance backwards we need to switch to the
231          * scopeCursor and try a previous call after positioning past it's
232          * last element.
233          */
234         available = cursor.previous();
235         if ( ! available )
236         {
237             cursor = scopeCursor;
238             cursor.afterLast();
239 
240             // advance until nothing is available or until we find a non-alias
241             do
242             {
243                 checkNotClosed( "previous()" );
244                 available = cursor.previous();
245 
246                 if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
247                 {
248                     break;
249                 }
250             }
251             while ( available );
252 
253             return available;
254         }
255 
256         return true;
257     }
258 
259 
260     public boolean next() throws Exception
261     {
262         checkNotClosed( "next()" );
263         // if the cursor hasn't been set position it before the first element
264         if ( cursor == null )
265         {
266             beforeFirst();
267         }
268 
269         /*
270          * If dereferencing is enabled then we must ignore alias entries, not
271          * returning them as part of the results.
272          */
273         if ( evaluator.isDereferencing() )
274         {
275             // advance until nothing is available or until we find a non-alias
276             do
277             {
278                 checkNotClosed( "next()" );
279                 available = cursor.next();
280 
281                 if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null )
282                 {
283                     break;
284                 }
285             }
286             while ( available );
287         }
288         else
289         {
290             available = cursor.next();
291         }
292 
293         // if we're using dereferencedCursor (2nd) then we return the result
294         if ( cursor == dereferencedCursor )
295         {
296             return available;
297         }
298 
299         /*
300          * Below here we are using the scopeCursor so if nothing is
301          * available after an advance forward we need to switch to the
302          * dereferencedCursor and try a previous call after positioning past
303          * it's last element.
304          */
305         if ( ! available )
306         {
307             if ( dereferencedCursor != null )
308             {
309                 cursor = dereferencedCursor;
310                 cursor.beforeFirst();
311                 return available = cursor.next();
312             }
313 
314             return false;
315         }
316 
317         return true;
318     }
319 
320 
321     public IndexEntry<Long, ServerEntry> get() throws Exception
322     {
323         checkNotClosed( "get()" );
324         if ( available )
325         {
326             return cursor.get();
327         }
328 
329         throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
330     }
331 
332 
333     public boolean isElementReused()
334     {
335         return scopeCursor.isElementReused() ||
336             ( dereferencedCursor != null && dereferencedCursor.isElementReused() );
337     }
338 }