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.xdbm.Index;
24  import org.apache.directory.server.xdbm.IndexEntry;
25  import org.apache.directory.server.xdbm.Store;
26  import org.apache.directory.server.xdbm.AbstractIndexCursor;
27  import org.apache.directory.server.xdbm.IndexCursor;
28  import org.apache.directory.server.core.cursor.InvalidCursorPositionException;
29  import org.apache.directory.server.core.entry.ServerEntry;
30  import org.apache.directory.shared.ldap.entry.Value;
31  
32  
33  /**
34   * A Cursor over entry candidates matching an approximate assertion filter.
35   * This Cursor really is a copy of EqualityCursor for now but later on
36   * approximate matching can be implemented and this can change.  It operates
37   * in two modes.  The first is when an index exists for the attribute the
38   * approximate assertion is built on.  The second is when the user index for
39   * the assertion attribute does not exist.  Different Cursors are used in each
40   * of these cases where the other remains null.
41   *
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   * @version $$Rev$$
44   */
45  public class ApproximateCursor<V> extends AbstractIndexCursor<V, ServerEntry>
46  {
47      private static final String UNSUPPORTED_MSG =
48          "ApproximateCursors only support positioning by element when a user index exists on the asserted attribute.";
49  
50      /** An approximate evaluator for candidates */
51      private final ApproximateEvaluator approximateEvaluator;
52  
53      /** Cursor over attribute entry matching filter: set when index present */
54      private final IndexCursor<V,ServerEntry> userIdxCursor;
55  
56      /** NDN Cursor on all entries in  (set when no index on user attribute) */
57      private final IndexCursor<String,ServerEntry> ndnIdxCursor;
58  
59      /** used only when ndnIdxCursor is used (no index on attribute) */
60      private boolean available = false;
61  
62  
63      public ApproximateCursor( Store<ServerEntry> db, ApproximateEvaluator approximateEvaluator ) throws Exception
64      {
65          this.approximateEvaluator = approximateEvaluator;
66  
67          String attribute = approximateEvaluator.getExpression().getAttribute();
68          Value<V> value = approximateEvaluator.getExpression().getValue();
69          if ( db.hasUserIndexOn( attribute ) )
70          {
71              //noinspection unchecked
72              Index<V,ServerEntry> index = ( Index<V, ServerEntry> ) db.getUserIndex( attribute );
73              userIdxCursor = index.forwardCursor( value.get() );
74              ndnIdxCursor = null;
75          }
76          else
77          {
78              ndnIdxCursor = db.getNdnIndex().forwardCursor();
79              userIdxCursor = null;
80          }
81      }
82  
83  
84      public boolean available()
85      {
86          if ( userIdxCursor != null )
87          {
88              return userIdxCursor.available();
89          }
90  
91          return available;
92      }
93  
94  
95      public void beforeValue( Long id, V value ) throws Exception
96      {
97          checkNotClosed( "beforeValue()" );
98          if ( userIdxCursor != null )
99          {
100             userIdxCursor.beforeValue( id, value );
101         }
102         else
103         {
104             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
105         }
106     }
107 
108 
109     public void afterValue( Long id, V value ) throws Exception
110     {
111         checkNotClosed( "afterValue()" );
112         if ( userIdxCursor != null )
113         {
114             userIdxCursor.afterValue( id, value );
115         }
116         else
117         {
118             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
119         }
120     }
121 
122 
123     public void before( IndexEntry<V, ServerEntry> element ) throws Exception
124     {
125         checkNotClosed( "before()" );
126         if ( userIdxCursor != null )
127         {
128             userIdxCursor.before( element );
129         }
130         else
131         {
132             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
133         }
134     }
135 
136 
137     public void after( IndexEntry<V, ServerEntry> element ) throws Exception
138     {
139         checkNotClosed( "after()" );
140         if ( userIdxCursor != null )
141         {
142             userIdxCursor.after( element );
143         }
144         else
145         {
146             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
147         }
148     }
149 
150 
151     public void beforeFirst() throws Exception
152     {
153         checkNotClosed( "beforeFirst()" );
154         if ( userIdxCursor != null )
155         {
156             userIdxCursor.beforeFirst();
157         }
158         else
159         {
160             ndnIdxCursor.beforeFirst();
161             available = false;
162         }
163     }
164 
165 
166     public void afterLast() throws Exception
167     {
168         checkNotClosed( "afterLast()" );
169         if ( userIdxCursor != null )
170         {
171             userIdxCursor.afterLast();
172         }
173         else
174         {
175             ndnIdxCursor.afterLast();
176             available = false;
177         }
178     }
179 
180 
181     public boolean first() throws Exception
182     {
183         beforeFirst();
184         return next();
185     }
186 
187 
188     public boolean last() throws Exception
189     {
190         afterLast();
191         return previous();
192     }
193 
194 
195     public boolean previous() throws Exception
196     {
197         if ( userIdxCursor != null )
198         {
199             return userIdxCursor.previous();
200         }
201 
202         while( ndnIdxCursor.previous() )
203         {
204             checkNotClosed( "previous()" );
205             IndexEntry<?,ServerEntry> candidate = ndnIdxCursor.get();
206             if ( approximateEvaluator.evaluate( candidate ) )
207             {
208                  return available = true;
209             }
210         }
211 
212         return available = false;
213     }
214 
215 
216     public boolean next() throws Exception
217     {
218         if ( userIdxCursor != null )
219         {
220             return userIdxCursor.next();
221         }
222 
223         while( ndnIdxCursor.next() )
224         {
225             checkNotClosed( "next()" );
226             IndexEntry<?,ServerEntry> candidate = ndnIdxCursor.get();
227             if ( approximateEvaluator.evaluate( candidate ) )
228             {
229                  return available = true;
230             }
231         }
232 
233         return available = false;
234     }
235 
236 
237     @SuppressWarnings("unchecked")
238     public IndexEntry<V, ServerEntry> get() throws Exception
239     {
240         checkNotClosed( "get()" );
241         if ( userIdxCursor != null )
242         {
243             return userIdxCursor.get();
244         }
245 
246         if ( available )
247         {
248             return ( IndexEntry<V, ServerEntry> ) ndnIdxCursor.get();
249         }
250 
251         throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
252     }
253 
254 
255     public boolean isElementReused()
256     {
257         if ( userIdxCursor != null )
258         {
259             return userIdxCursor.isElementReused();
260         }
261 
262         return ndnIdxCursor.isElementReused();
263     }
264 
265 
266     public void close() throws Exception
267     {
268         super.close();
269 
270         if ( userIdxCursor != null )
271         {
272             userIdxCursor.close();
273         }
274         else
275         {
276             ndnIdxCursor.close();
277         }
278     }
279 }