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 javax.naming.directory.SearchControls;
024    
025    import org.apache.directory.server.xdbm.EmptyIndexCursor;
026    import org.apache.directory.server.xdbm.ForwardIndexEntry;
027    import org.apache.directory.server.xdbm.IndexCursor;
028    import org.apache.directory.server.xdbm.IndexEntry;
029    import org.apache.directory.server.xdbm.SingletonIndexCursor;
030    import org.apache.directory.server.xdbm.Store;
031    import org.apache.directory.server.xdbm.search.Evaluator;
032    import org.apache.directory.server.xdbm.search.Optimizer;
033    import org.apache.directory.server.xdbm.search.SearchEngine;
034    import org.apache.directory.shared.ldap.entry.ServerEntry;
035    import org.apache.directory.shared.ldap.filter.AndNode;
036    import org.apache.directory.shared.ldap.filter.BranchNode;
037    import org.apache.directory.shared.ldap.filter.ExprNode;
038    import org.apache.directory.shared.ldap.filter.ScopeNode;
039    import org.apache.directory.shared.ldap.filter.SearchScope;
040    import org.apache.directory.shared.ldap.message.AliasDerefMode;
041    import org.apache.directory.shared.ldap.name.DN;
042    
043    
044    /**
045     * Given a search filter and a scope the search engine identifies valid
046     * candidate entries returning their ids.
047     * 
048     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
049     * @version $Rev: 927146 $
050     */
051    public class DefaultSearchEngine<ID> implements SearchEngine<ServerEntry, ID>
052    {
053        /** the Optimizer used by this DefaultSearchEngine */
054        private final Optimizer optimizer;
055        /** the Database this DefaultSearchEngine operates on */
056        private final Store<ServerEntry, ID> db;
057        /** creates Cursors over entries satisfying filter expressions */
058        private final CursorBuilder<ID> cursorBuilder;
059        /** creates evaluators which check to see if candidates satisfy a filter expression */
060        private final EvaluatorBuilder<ID> evaluatorBuilder;
061    
062    
063        // ------------------------------------------------------------------------
064        // C O N S T R U C T O R S
065        // ------------------------------------------------------------------------
066    
067        /**
068         * Creates a DefaultSearchEngine for searching a Database without setting
069         * up the database.
070         * @param db the btree based partition
071         * @param cursorBuilder an expression cursor builder
072         * @param evaluatorBuilder an expression evaluator builder
073         * @param optimizer an optimizer to use during search
074         */
075        public DefaultSearchEngine( Store<ServerEntry, ID> db, CursorBuilder<ID> cursorBuilder,
076            EvaluatorBuilder<ID> evaluatorBuilder, Optimizer optimizer )
077        {
078            this.db = db;
079            this.optimizer = optimizer;
080            this.cursorBuilder = cursorBuilder;
081            this.evaluatorBuilder = evaluatorBuilder;
082        }
083    
084    
085        /**
086         * Gets the optimizer for this DefaultSearchEngine.
087         *
088         * @return the optimizer
089         */
090        public Optimizer getOptimizer()
091        {
092            return optimizer;
093        }
094    
095    
096        /**
097         * @see SearchEngine#cursor(DN, AliasDerefMode, ExprNode, SearchControls)
098         */
099        public IndexCursor<ID, ServerEntry, ID> cursor( DN base, AliasDerefMode aliasDerefMode, ExprNode filter,
100            SearchControls searchCtls ) throws Exception
101        {
102            DN effectiveBase;
103            ID baseId = db.getEntryId( base.getNormName() );
104    
105            // Check that we have an entry, otherwise we can immediately get out
106            if ( baseId == null )
107            {
108                // The entry is not found : ciao !
109                return new EmptyIndexCursor<ID, ServerEntry, ID>();
110            }
111    
112            String aliasedBase = db.getAliasIndex().reverseLookup( baseId );
113    
114            // --------------------------------------------------------------------
115            // Determine the effective base with aliases
116            // --------------------------------------------------------------------
117    
118            /*
119             * If the base is not an alias or if alias dereferencing does not
120             * occur on finding the base then we set the effective base to the
121             * given base.
122             */
123            if ( ( null == aliasedBase ) || !aliasDerefMode.isDerefFindingBase() )
124            {
125                effectiveBase = base;
126            }
127    
128            /*
129             * If the base is an alias and alias dereferencing does occur on
130             * finding the base then we set the effective base to the alias target
131             * got from the alias index.
132             */
133            else
134            {
135                effectiveBase = new DN( aliasedBase );
136            }
137    
138            // --------------------------------------------------------------------
139            // Specifically Handle Object Level Scope
140            // --------------------------------------------------------------------
141    
142            if ( searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE )
143            {
144                ID effectiveBaseId = baseId;
145                if ( effectiveBase != base )
146                {
147                    effectiveBaseId = db.getEntryId( effectiveBase.getNormName() );
148                }
149    
150                IndexEntry<ID, ServerEntry, ID> indexEntry = new ForwardIndexEntry<ID, ServerEntry, ID>();
151                indexEntry.setId( effectiveBaseId );
152                optimizer.annotate( filter );
153                Evaluator<? extends ExprNode, ServerEntry, ID> evaluator = evaluatorBuilder.build( filter );
154    
155                if ( evaluator.evaluate( indexEntry ) )
156                {
157                    return new SingletonIndexCursor<ID, ServerEntry, ID>( indexEntry );
158                }
159                else
160                {
161                    return new EmptyIndexCursor<ID, ServerEntry, ID>();
162                }
163            }
164    
165            // Add the scope node using the effective base to the filter
166            BranchNode root = new AndNode();
167            ExprNode node = new ScopeNode( aliasDerefMode, effectiveBase.getNormName(), SearchScope.getSearchScope( searchCtls
168                .getSearchScope() ) );
169            root.getChildren().add( node );
170            root.getChildren().add( filter );
171    
172            // Annotate the node with the optimizer and return search enumeration.
173            optimizer.annotate( root );
174            return ( IndexCursor<ID, ServerEntry, ID> ) cursorBuilder.build( root );
175        }
176    
177    
178        /**
179         * @see SearchEngine#evaluator(ExprNode)
180         */
181        public Evaluator<? extends ExprNode, ServerEntry, ID> evaluator( ExprNode filter ) throws Exception
182        {
183            return evaluatorBuilder.build( filter );
184        }
185    }