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}