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 }