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.search.impl;
021
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.directory.api.ldap.model.exception.LdapException;
027import org.apache.directory.api.ldap.model.filter.AndNode;
028import org.apache.directory.api.ldap.model.filter.ApproximateNode;
029import org.apache.directory.api.ldap.model.filter.EqualityNode;
030import org.apache.directory.api.ldap.model.filter.ExprNode;
031import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
032import org.apache.directory.api.ldap.model.filter.LessEqNode;
033import org.apache.directory.api.ldap.model.filter.NotNode;
034import org.apache.directory.api.ldap.model.filter.OrNode;
035import org.apache.directory.api.ldap.model.filter.PresenceNode;
036import org.apache.directory.api.ldap.model.filter.ScopeNode;
037import org.apache.directory.api.ldap.model.filter.SubstringNode;
038import org.apache.directory.api.ldap.model.message.SearchScope;
039import org.apache.directory.api.ldap.model.schema.SchemaManager;
040import org.apache.directory.api.util.exception.NotImplementedException;
041import org.apache.directory.server.core.api.partition.PartitionTxn;
042import org.apache.directory.server.i18n.I18n;
043import org.apache.directory.server.xdbm.Store;
044import org.apache.directory.server.xdbm.search.Evaluator;
045import org.apache.directory.server.xdbm.search.evaluator.AndEvaluator;
046import org.apache.directory.server.xdbm.search.evaluator.ApproximateEvaluator;
047import org.apache.directory.server.xdbm.search.evaluator.EmptyEvaluator;
048import org.apache.directory.server.xdbm.search.evaluator.EqualityEvaluator;
049import org.apache.directory.server.xdbm.search.evaluator.GreaterEqEvaluator;
050import org.apache.directory.server.xdbm.search.evaluator.LessEqEvaluator;
051import org.apache.directory.server.xdbm.search.evaluator.NotEvaluator;
052import org.apache.directory.server.xdbm.search.evaluator.OneLevelScopeEvaluator;
053import org.apache.directory.server.xdbm.search.evaluator.OrEvaluator;
054import org.apache.directory.server.xdbm.search.evaluator.PresenceEvaluator;
055import org.apache.directory.server.xdbm.search.evaluator.SubstringEvaluator;
056import org.apache.directory.server.xdbm.search.evaluator.SubtreeScopeEvaluator;
057
058
059/**
060 * Top level filter expression evaluator builder implemenation.
061 * 
062 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
063 */
064public class EvaluatorBuilder
065{
066    private final Store db;
067    private final SchemaManager schemaManager;
068
069    private static final EmptyEvaluator EMPTY_EVALUATOR = new EmptyEvaluator();
070
071    /**
072     * Creates a top level Evaluator where leaves are delegated to a leaf node
073     * evaluator which will be created.
074     *
075     * @param db the database this evaluator operates upon
076     * @param schemaManager the schema manager
077     */
078    public EvaluatorBuilder( Store db, SchemaManager schemaManager )
079    {
080        this.db = db;
081        this.schemaManager = schemaManager;
082    }
083
084
085    public <T> Evaluator<? extends ExprNode> build( PartitionTxn partitionTxn, ExprNode node ) throws LdapException
086    {
087        Object count = node.get( "count" );
088
089        if ( ( count != null ) && ( ( Long ) count == 0L ) )
090        {
091            return EMPTY_EVALUATOR;
092        }
093
094        switch ( node.getAssertionType() )
095        {
096        /* ---------- LEAF NODE HANDLING ---------- */
097
098            case APPROXIMATE:
099                return new ApproximateEvaluator<>( ( ApproximateNode<T> ) node, db, schemaManager );
100
101            case EQUALITY:
102                return new EqualityEvaluator<>( ( EqualityNode<T> ) node, db, schemaManager );
103
104            case GREATEREQ:
105                return new GreaterEqEvaluator<>( ( GreaterEqNode<T> ) node, db, schemaManager );
106
107            case LESSEQ:
108                return new LessEqEvaluator<>( ( LessEqNode<T> ) node, db, schemaManager );
109
110            case PRESENCE:
111                return new PresenceEvaluator( ( PresenceNode ) node, db, schemaManager );
112
113            case SCOPE:
114                if ( ( ( ScopeNode ) node ).getScope() == SearchScope.ONELEVEL )
115                {
116                    return new OneLevelScopeEvaluator<>( db, ( ScopeNode ) node );
117                }
118                else
119                {
120                    return new SubtreeScopeEvaluator( partitionTxn, db, ( ScopeNode ) node );
121                }
122
123            case SUBSTRING:
124                return new SubstringEvaluator( ( SubstringNode ) node, db, schemaManager );
125
126                /* ---------- LOGICAL OPERATORS ---------- */
127
128            case AND:
129                return buildAndEvaluator( partitionTxn, ( AndNode ) node );
130
131            case NOT:
132                return new NotEvaluator( ( NotNode ) node, build( partitionTxn, ( ( NotNode ) node ).getFirstChild() ) );
133
134            case OR:
135                return buildOrEvaluator( partitionTxn, ( OrNode ) node );
136
137                /* ----------  NOT IMPLEMENTED  ---------- */
138
139            case UNDEFINED:
140                return new EmptyEvaluator();
141                
142            case ASSERTION:
143            case EXTENSIBLE:
144                throw new NotImplementedException();
145
146            default:
147                throw new IllegalStateException( I18n.err( I18n.ERR_260, node.getAssertionType() ) );
148        }
149    }
150
151
152    private <T> Evaluator<? extends ExprNode> buildAndEvaluator( PartitionTxn partitionTxn, AndNode node ) throws LdapException
153    {
154        List<ExprNode> children = node.getChildren();
155        List<Evaluator<? extends ExprNode>> evaluators = buildList( partitionTxn, children );
156
157        int size = evaluators.size();
158
159        switch ( size )
160        {
161            case 0:
162                return EMPTY_EVALUATOR;
163
164            case 1:
165                return evaluators.get( 0 );
166
167            default:
168                return new AndEvaluator( node, evaluators );
169        }
170    }
171
172
173    private <T> Evaluator<? extends ExprNode> buildOrEvaluator( PartitionTxn partitionTxn, OrNode node ) throws LdapException
174    {
175        List<ExprNode> children = node.getChildren();
176        List<Evaluator<? extends ExprNode>> evaluators = buildList( partitionTxn, children );
177
178        int size = evaluators.size();
179
180        switch ( size )
181        {
182            case 0:
183                return EMPTY_EVALUATOR;
184
185            case 1:
186                return evaluators.get( 0 );
187
188            default:
189                return new OrEvaluator( node, evaluators );
190        }
191    }
192
193
194    private List<Evaluator<? extends ExprNode>> buildList( PartitionTxn partitionTxn, List<ExprNode> children ) throws LdapException
195    {
196        List<Evaluator<? extends ExprNode>> evaluators = new ArrayList<>(
197            children.size() );
198
199        for ( ExprNode child : children )
200        {
201            Evaluator<? extends ExprNode> evaluator = build( partitionTxn, child );
202
203            if ( evaluator != null )
204            {
205                evaluators.add( evaluator );
206            }
207        }
208
209        return evaluators;
210    }
211
212
213    /**
214     * @return the schemaManager
215     */
216    public SchemaManager getSchemaManager()
217    {
218        return schemaManager;
219    }
220}