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