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 */ 020package org.apache.directory.server.xdbm.impl.avl; 021 022 023import java.io.IOException; 024import java.net.URI; 025 026import org.apache.directory.api.ldap.model.cursor.Cursor; 027import org.apache.directory.api.ldap.model.cursor.CursorException; 028import org.apache.directory.api.ldap.model.cursor.EmptyCursor; 029import org.apache.directory.api.ldap.model.cursor.Tuple; 030import org.apache.directory.api.ldap.model.exception.LdapException; 031import org.apache.directory.api.ldap.model.exception.LdapOtherException; 032import org.apache.directory.api.ldap.model.schema.AttributeType; 033import org.apache.directory.api.ldap.model.schema.LdapComparator; 034import org.apache.directory.api.ldap.model.schema.MatchingRule; 035import org.apache.directory.api.ldap.model.schema.Normalizer; 036import org.apache.directory.api.ldap.model.schema.SchemaManager; 037import org.apache.directory.api.ldap.model.schema.comparators.UuidComparator; 038import org.apache.directory.server.core.api.partition.PartitionTxn; 039import org.apache.directory.server.core.partition.impl.btree.IndexCursorAdaptor; 040import org.apache.directory.server.i18n.I18n; 041import org.apache.directory.server.xdbm.AbstractIndex; 042import org.apache.directory.server.xdbm.EmptyIndexCursor; 043import org.apache.directory.server.xdbm.IndexEntry; 044 045 046/** 047 * An Index backed by an AVL Tree. 048 * 049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 050 */ 051public class AvlIndex<K> extends AbstractIndex<K, String> 052{ 053 protected Normalizer normalizer; 054 protected AvlTable<K, String> forward; 055 protected AvlTable<String, K> reverse; 056 057 058 public AvlIndex() 059 { 060 super( true ); 061 } 062 063 064 public AvlIndex( String attributeId ) 065 { 066 super( attributeId, true ); 067 } 068 069 070 public AvlIndex( String attributeId, boolean withReverse ) 071 { 072 super( attributeId, withReverse ); 073 } 074 075 076 public void init( SchemaManager schemaManager, AttributeType attributeType ) throws LdapException 077 { 078 this.attributeType = attributeType; 079 080 MatchingRule mr = attributeType.getEquality(); 081 082 if ( mr == null ) 083 { 084 mr = attributeType.getOrdering(); 085 } 086 087 if ( mr == null ) 088 { 089 mr = attributeType.getSubstring(); 090 } 091 092 normalizer = mr.getNormalizer(); 093 094 if ( normalizer == null ) 095 { 096 throw new LdapOtherException( I18n.err( I18n.ERR_212, attributeType ) ); 097 } 098 099 LdapComparator<K> comp = ( LdapComparator<K> ) mr.getLdapComparator(); 100 101 /* 102 * The forward key/value map stores attribute values to master table 103 * primary keys. A value for an attribute can occur several times in 104 * different entries so the forward map can have more than one value. 105 */ 106 forward = new AvlTable<>( attributeType.getName(), comp, UuidComparator.INSTANCE, true ); 107 108 /* 109 * Now the reverse map stores the primary key into the master table as 110 * the key and the values of attributes as the value. If an attribute 111 * is single valued according to its specification based on a schema 112 * then duplicate keys should not be allowed within the reverse table. 113 */ 114 if ( withReverse ) 115 { 116 if ( attributeType.isSingleValued() ) 117 { 118 reverse = new AvlTable<>( attributeType.getName(), UuidComparator.INSTANCE, comp, false ); 119 } 120 else 121 { 122 reverse = new AvlTable<>( attributeType.getName(), UuidComparator.INSTANCE, comp, true ); 123 } 124 } 125 } 126 127 128 public void add( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException 129 { 130 forward.put( partitionTxn, attrVal, id ); 131 132 if ( withReverse ) 133 { 134 reverse.put( partitionTxn, id, attrVal ); 135 } 136 } 137 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 public void close( PartitionTxn partitionTxn ) throws LdapException, IOException 144 { 145 if ( forward != null ) 146 { 147 forward.close( partitionTxn ); 148 } 149 150 if ( reverse != null ) 151 { 152 reverse.close( partitionTxn ); 153 } 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 public long count( PartitionTxn partitionTxn ) throws LdapException 161 { 162 return forward.count( partitionTxn ); 163 } 164 165 166 /** 167 * {@inheritDoc} 168 */ 169 public long count( PartitionTxn partitionTxn, K attrVal ) throws LdapException 170 { 171 return forward.count( partitionTxn, attrVal ); 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 public void drop( PartitionTxn partitionTxn, String id ) throws LdapException 179 { 180 if ( withReverse ) 181 { 182 if ( isDupsEnabled() ) 183 { 184 Cursor<Tuple<String, K>> cursor = reverse.cursor( partitionTxn, id ); 185 186 try 187 { 188 while ( cursor.next() ) 189 { 190 Tuple<String, K> tuple = cursor.get(); 191 forward.remove( partitionTxn, tuple.getValue(), id ); 192 } 193 194 cursor.close(); 195 } 196 catch ( CursorException | IOException e ) 197 { 198 throw new LdapOtherException( e.getMessage(), e ); 199 } 200 } 201 else 202 { 203 K key = reverse.get( partitionTxn, id ); 204 forward.remove( partitionTxn, key ); 205 } 206 207 reverse.remove( partitionTxn, id ); 208 } 209 } 210 211 212 /** 213 * {@inheritDoc} 214 */ 215 @Override 216 public void drop( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException 217 { 218 forward.remove( partitionTxn, attrVal, id ); 219 220 if ( withReverse ) 221 { 222 reverse.remove( partitionTxn, id, attrVal ); 223 } 224 } 225 226 227 /** 228 * {@inheritDoc} 229 */ 230 public boolean forward( PartitionTxn partitionTxn, K attrVal ) throws LdapException 231 { 232 return forward.has( partitionTxn, attrVal ); 233 } 234 235 236 /** 237 * {@inheritDoc} 238 */ 239 public boolean forward( PartitionTxn partitionTxn, K attrVal, String id ) throws LdapException 240 { 241 return forward.has( partitionTxn, attrVal, id ); 242 } 243 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override 249 public Cursor<IndexEntry<K, String>> forwardCursor( PartitionTxn partitionTxn ) throws LdapException 250 { 251 return new IndexCursorAdaptor( partitionTxn, forward.cursor(), true ); 252 } 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 @SuppressWarnings("unchecked") 259 public Cursor<IndexEntry<K, String>> forwardCursor( PartitionTxn partitionTxn, K key ) throws LdapException 260 { 261 return new IndexCursorAdaptor( partitionTxn, forward.cursor( partitionTxn, key ), true ); 262 } 263 264 265 /** 266 * {@inheritDoc} 267 */ 268 public String forwardLookup( PartitionTxn partitionTxn, K attrVal ) throws LdapException 269 { 270 return forward.get( partitionTxn, attrVal ); 271 } 272 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override 278 public Cursor<String> forwardValueCursor( PartitionTxn partitionTxn, K key ) throws LdapException 279 { 280 return forward.valueCursor( partitionTxn, key ); 281 } 282 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override 288 public long greaterThanCount( PartitionTxn partitionTxn, K attrVal ) throws LdapException 289 { 290 return forward.greaterThanCount( partitionTxn, attrVal ); 291 } 292 293 294 /** 295 * {@inheritDoc} 296 */ 297 @Override 298 public long lessThanCount( PartitionTxn partitionTxn, K attrVal ) throws LdapException 299 { 300 return forward.lessThanCount( partitionTxn, attrVal ); 301 } 302 303 304 /** 305 * {@inheritDoc} 306 */ 307 @Override 308 public boolean reverse( PartitionTxn partitionTxn, String id ) throws LdapException 309 { 310 if ( withReverse ) 311 { 312 return reverse.has( partitionTxn, id ); 313 } 314 else 315 { 316 return false; 317 } 318 } 319 320 321 /** 322 * {@inheritDoc} 323 */ 324 @Override 325 public boolean reverse( PartitionTxn partitionTxn, String id, K attrVal ) throws LdapException 326 { 327 if ( withReverse ) 328 { 329 return reverse.has( partitionTxn, id, attrVal ); 330 } 331 else 332 { 333 return false; 334 } 335 } 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 @Override 342 public Cursor<IndexEntry<K, String>> reverseCursor( PartitionTxn partitionTxn ) throws LdapException 343 { 344 if ( withReverse ) 345 { 346 return new IndexCursorAdaptor( partitionTxn, reverse.cursor(), false ); 347 } 348 else 349 { 350 return new EmptyIndexCursor<>( partitionTxn ); 351 } 352 } 353 354 355 /** 356 * {@inheritDoc} 357 */ 358 @Override 359 public Cursor<IndexEntry<K, String>> reverseCursor( PartitionTxn partitionTxn, String id ) throws LdapException 360 { 361 if ( withReverse ) 362 { 363 return new IndexCursorAdaptor( partitionTxn, reverse.cursor( partitionTxn, id ), false ); 364 } 365 else 366 { 367 return new EmptyIndexCursor<>( partitionTxn ); 368 } 369 } 370 371 372 /** 373 * {@inheritDoc} 374 */ 375 public K reverseLookup( PartitionTxn partitionTxn, String id ) throws LdapException 376 { 377 if ( withReverse ) 378 { 379 return reverse.get( partitionTxn, id ); 380 } 381 else 382 { 383 return null; 384 } 385 } 386 387 388 /** 389 * {@inheritDoc} 390 */ 391 @Override 392 public Cursor<K> reverseValueCursor( PartitionTxn partitionTxn, String id ) throws LdapException 393 { 394 if ( withReverse ) 395 { 396 return reverse.valueCursor( partitionTxn, id ); 397 } 398 else 399 { 400 return new EmptyCursor<>(); 401 } 402 } 403 404 405 /** 406 * throws UnsupportedOperationException cause it is a in-memory index 407 */ 408 public void setWkDirPath( URI wkDirPath ) 409 { 410 throw new UnsupportedOperationException( I18n.err( I18n.ERR_213 ) ); 411 } 412 413 414 /** 415 * this method always returns null for AvlIndex cause this is a in-memory index. 416 */ 417 public URI getWkDirPath() 418 { 419 return null; 420 } 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override 427 public boolean isDupsEnabled() 428 { 429 if ( withReverse ) 430 { 431 return reverse.isDupsEnabled(); 432 } 433 else 434 { 435 return false; 436 } 437 } 438}