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 */
020 package org.apache.directory.server.xdbm.search.impl;
021
022
023 import org.apache.directory.server.xdbm.Index;
024 import org.apache.directory.server.xdbm.IndexEntry;
025 import org.apache.directory.server.xdbm.Store;
026 import org.apache.directory.server.xdbm.AbstractIndexCursor;
027 import org.apache.directory.server.xdbm.IndexCursor;
028 import org.apache.directory.server.i18n.I18n;
029 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
030 import org.apache.directory.shared.ldap.entry.ServerEntry;
031 import org.apache.directory.shared.ldap.entry.Value;
032
033
034 /**
035 * A Cursor over entry candidates matching an approximate assertion filter.
036 * This Cursor really is a copy of EqualityCursor for now but later on
037 * approximate matching can be implemented and this can change. It operates
038 * in two modes. The first is when an index exists for the attribute the
039 * approximate assertion is built on. The second is when the user index for
040 * the assertion attribute does not exist. Different Cursors are used in each
041 * of these cases where the other remains null.
042 *
043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044 * @version $$Rev$$
045 */
046 public class ApproximateCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID>
047 {
048 private static final String UNSUPPORTED_MSG = "ApproximateCursors only support positioning by element when a user index exists on the asserted attribute.";
049
050 /** An approximate evaluator for candidates */
051 private final ApproximateEvaluator<V, ID> approximateEvaluator;
052
053 /** Cursor over attribute entry matching filter: set when index present */
054 private final IndexCursor<V, ServerEntry, ID> userIdxCursor;
055
056 /** NDN Cursor on all entries in (set when no index on user attribute) */
057 private final IndexCursor<String, ServerEntry, ID> ndnIdxCursor;
058
059 /** used only when ndnIdxCursor is used (no index on attribute) */
060 private boolean available = false;
061
062
063 @SuppressWarnings("unchecked")
064 public ApproximateCursor( Store<ServerEntry, ID> db, ApproximateEvaluator<V, ID> approximateEvaluator ) throws Exception
065 {
066 this.approximateEvaluator = approximateEvaluator;
067
068 String attribute = approximateEvaluator.getExpression().getAttribute();
069 Value<V> value = approximateEvaluator.getExpression().getValue();
070 if ( db.hasIndexOn( attribute ) )
071 {
072 Index<V, ServerEntry, ID> index = ( Index<V, ServerEntry, ID> ) db.getIndex( attribute );
073 userIdxCursor = index.forwardCursor( value.get() );
074 ndnIdxCursor = null;
075 }
076 else
077 {
078 ndnIdxCursor = db.getNdnIndex().forwardCursor();
079 userIdxCursor = null;
080 }
081 }
082
083
084 public boolean available()
085 {
086 if ( userIdxCursor != null )
087 {
088 return userIdxCursor.available();
089 }
090
091 return available;
092 }
093
094
095 public void beforeValue( ID id, V value ) throws Exception
096 {
097 checkNotClosed( "beforeValue()" );
098 if ( userIdxCursor != null )
099 {
100 userIdxCursor.beforeValue( id, value );
101 }
102 else
103 {
104 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
105 }
106 }
107
108
109 public void afterValue( ID 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, ID> 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, ID> 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, ID> 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, ID> 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, ID> 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, ID> ) ndnIdxCursor.get();
249 }
250
251 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
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 }