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 equality assertion filter. This 036 * Cursor operates in two modes. The first is when an index exists for the 037 * attribute the equality assertion is built on. The second is when the user 038 * index for the assertion attribute does not exist. Different Cursors are 039 * used in each of these cases where the other remains null. 040 * 041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 042 * @version $$Rev$$ 043 */ 044 public class EqualityCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID> 045 { 046 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_714 ); 047 048 /** An equality evaluator for candidates */ 049 @SuppressWarnings("unchecked") 050 private final EqualityEvaluator equalityEvaluator; 051 052 /** Cursor over attribute entry matching filter: set when index present */ 053 private final IndexCursor<V, ServerEntry, ID> userIdxCursor; 054 055 /** NDN Cursor on all entries in (set when no index on user attribute) */ 056 private final IndexCursor<String, ServerEntry, ID> ndnIdxCursor; 057 058 /** used only when ndnIdxCursor is used (no index on attribute) */ 059 private boolean available = false; 060 061 062 @SuppressWarnings("unchecked") 063 public EqualityCursor( Store<ServerEntry, ID> db, EqualityEvaluator<V, ID> equalityEvaluator ) throws Exception 064 { 065 this.equalityEvaluator = equalityEvaluator; 066 067 String attribute = equalityEvaluator.getExpression().getAttribute(); 068 Value<V> value = equalityEvaluator.getExpression().getValue(); 069 if ( db.hasIndexOn( attribute ) ) 070 { 071 Index<V, ServerEntry, ID> userIndex = ( Index<V, ServerEntry, ID> ) db.getIndex( attribute ); 072 userIdxCursor = userIndex.forwardCursor( value.get() ); 073 ndnIdxCursor = null; 074 } 075 else 076 { 077 ndnIdxCursor = db.getNdnIndex().forwardCursor(); 078 userIdxCursor = null; 079 } 080 } 081 082 083 public boolean available() 084 { 085 if ( userIdxCursor != null ) 086 { 087 return userIdxCursor.available(); 088 } 089 090 return available; 091 } 092 093 094 public void beforeValue( ID id, V value ) throws Exception 095 { 096 checkNotClosed( "beforeValue()" ); 097 if ( userIdxCursor != null ) 098 { 099 userIdxCursor.beforeValue( id, value ); 100 } 101 else 102 { 103 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 104 } 105 } 106 107 108 public void before( IndexEntry<V, ServerEntry, ID> element ) throws Exception 109 { 110 checkNotClosed( "before()" ); 111 if ( userIdxCursor != null ) 112 { 113 userIdxCursor.before( element ); 114 } 115 else 116 { 117 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 118 } 119 } 120 121 122 public void afterValue( ID id, V key ) throws Exception 123 { 124 checkNotClosed( "afterValue()" ); 125 if ( userIdxCursor != null ) 126 { 127 userIdxCursor.afterValue( id, key ); 128 } 129 else 130 { 131 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 132 } 133 } 134 135 136 public void after( IndexEntry<V, ServerEntry, ID> element ) throws Exception 137 { 138 checkNotClosed( "after()" ); 139 if ( userIdxCursor != null ) 140 { 141 userIdxCursor.after( element ); 142 } 143 else 144 { 145 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 146 } 147 } 148 149 150 public void beforeFirst() throws Exception 151 { 152 checkNotClosed( "beforeFirst()" ); 153 if ( userIdxCursor != null ) 154 { 155 userIdxCursor.beforeFirst(); 156 } 157 else 158 { 159 ndnIdxCursor.beforeFirst(); 160 available = false; 161 } 162 } 163 164 165 public void afterLast() throws Exception 166 { 167 checkNotClosed( "afterLast()" ); 168 if ( userIdxCursor != null ) 169 { 170 userIdxCursor.afterLast(); 171 } 172 else 173 { 174 ndnIdxCursor.afterLast(); 175 available = false; 176 } 177 } 178 179 180 public boolean first() throws Exception 181 { 182 beforeFirst(); 183 return next(); 184 } 185 186 187 public boolean last() throws Exception 188 { 189 afterLast(); 190 return previous(); 191 } 192 193 194 @SuppressWarnings("unchecked") 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 ( equalityEvaluator.evaluate( candidate ) ) 207 { 208 return available = true; 209 } 210 } 211 212 return available = false; 213 } 214 215 216 @SuppressWarnings("unchecked") 217 public boolean next() throws Exception 218 { 219 if ( userIdxCursor != null ) 220 { 221 return userIdxCursor.next(); 222 } 223 224 while ( ndnIdxCursor.next() ) 225 { 226 checkNotClosed( "next()" ); 227 IndexEntry<?, ServerEntry, ID> candidate = ndnIdxCursor.get(); 228 if ( equalityEvaluator.evaluate( candidate ) ) 229 { 230 return available = true; 231 } 232 } 233 234 return available = false; 235 } 236 237 238 @SuppressWarnings("unchecked") 239 public IndexEntry<V, ServerEntry, ID> get() throws Exception 240 { 241 checkNotClosed( "get()" ); 242 if ( userIdxCursor != null ) 243 { 244 return userIdxCursor.get(); 245 } 246 247 if ( available ) 248 { 249 return ( IndexEntry<V, ServerEntry, ID> ) ndnIdxCursor.get(); 250 } 251 252 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 253 } 254 255 256 public boolean isElementReused() 257 { 258 if ( userIdxCursor != null ) 259 { 260 return userIdxCursor.isElementReused(); 261 } 262 263 return ndnIdxCursor.isElementReused(); 264 } 265 266 267 public void close() throws Exception 268 { 269 super.close(); 270 271 if ( userIdxCursor != null ) 272 { 273 userIdxCursor.close(); 274 } 275 else 276 { 277 ndnIdxCursor.close(); 278 } 279 } 280 }