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.core.api.event;
021
022
023import org.apache.directory.api.ldap.model.entry.Attribute;
024import org.apache.directory.api.ldap.model.entry.Entry;
025import org.apache.directory.api.ldap.model.entry.Value;
026import org.apache.directory.api.ldap.model.exception.LdapException;
027import org.apache.directory.api.ldap.model.exception.LdapInvalidSearchFilterException;
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.ExtensibleNode;
032import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
033import org.apache.directory.api.ldap.model.filter.LessEqNode;
034import org.apache.directory.api.ldap.model.filter.PresenceNode;
035import org.apache.directory.api.ldap.model.filter.ScopeNode;
036import org.apache.directory.api.ldap.model.filter.SimpleNode;
037import org.apache.directory.api.ldap.model.filter.SubstringNode;
038import org.apache.directory.api.ldap.model.name.Dn;
039import org.apache.directory.api.ldap.model.schema.AttributeType;
040import org.apache.directory.api.ldap.model.schema.MatchingRule;
041import org.apache.directory.api.util.exception.NotImplementedException;
042import org.apache.directory.server.i18n.I18n;
043
044
045/**
046 * Evaluates LeafNode assertions on candidates using a database.
047 * 
048 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
049 */
050public class LeafEvaluator implements Evaluator
051{
052    /** equality matching type constant */
053    private static final int EQUALITY_MATCH = 0;
054
055    /** ordering matching type constant */
056    private static final int ORDERING_MATCH = 1;
057
058    /** substring matching type constant */
059    private static final int SUBSTRING_MATCH = 3;
060
061    /** Substring node evaluator we depend on */
062    private SubstringEvaluator substringEvaluator;
063
064    /** ScopeNode evaluator we depend on */
065    private ScopeEvaluator scopeEvaluator;
066
067    /** Constants used for comparisons */
068    private static final boolean COMPARE_GREATER = true;
069    private static final boolean COMPARE_LESSER = false;
070
071
072    /**
073     * Creates a leaf expression node evaluator.
074     *
075     * @param substringEvaluator The evaluator to use
076     */
077    public LeafEvaluator( SubstringEvaluator substringEvaluator )
078    {
079        this.scopeEvaluator = new ScopeEvaluator();
080        this.substringEvaluator = substringEvaluator;
081    }
082
083
084    public ScopeEvaluator getScopeEvaluator()
085    {
086        return scopeEvaluator;
087    }
088
089
090    public SubstringEvaluator getSubstringEvaluator()
091    {
092        return substringEvaluator;
093    }
094
095
096    /**
097     * {@inheritDoc}
098     */
099    public boolean evaluate( ExprNode node, Dn dn, Entry entry ) throws LdapException
100    {
101        if ( node instanceof ScopeNode )
102        {
103            return scopeEvaluator.evaluate( node, dn, entry );
104        }
105
106        if ( node instanceof PresenceNode )
107        {
108            return evalPresence( ( ( PresenceNode ) node ).getAttributeType(), entry );
109        }
110        else if ( ( node instanceof EqualityNode ) || ( node instanceof ApproximateNode ) )
111        {
112            return evalEquality( ( EqualityNode<?> ) node, entry );
113        }
114        else if ( node instanceof GreaterEqNode )
115        {
116            return evalGreaterOrLesser( ( GreaterEqNode<?> ) node, entry, COMPARE_GREATER );
117        }
118        else if ( node instanceof LessEqNode )
119        {
120            return evalGreaterOrLesser( ( LessEqNode<?> ) node, entry, COMPARE_LESSER );
121        }
122        else if ( node instanceof SubstringNode )
123        {
124            return substringEvaluator.evaluate( node, dn, entry );
125        }
126        else if ( node instanceof ExtensibleNode )
127        {
128            throw new NotImplementedException();
129        }
130        else
131        {
132            throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_245, node ) );
133        }
134    }
135
136
137    /**
138     * Evaluates a simple greater than or less than attribute value assertion on
139     * a perspective candidate.
140     * 
141     * @param node the greater than or less than node to evaluate
142     * @param entry the perspective candidate
143     * @param isGreater true if it is a greater than or equal to comparison,
144     *      false if it is a less than or equal to comparison.
145     * @return the ava evaluation on the perspective candidate
146     * @throws LdapException if there is a database access failure
147     */
148    private boolean evalGreaterOrLesser( SimpleNode<?> node, Entry entry, boolean isGreaterOrLesser )
149        throws LdapException
150    {
151        // get the attribute associated with the node
152        Attribute attr = entry.get( node.getAttribute() );
153
154        // If we do not have the attribute just return false
155        if ( null == attr )
156        {
157            return false;
158        }
159
160        /*
161         * We need to iterate through all values and for each value we normalize
162         * and use the comparator to determine if a match exists.
163         */
164        Value filterValue = node.getValue();
165
166        /*
167         * Cheaper to not check isGreater in one loop - better to separate
168         * out into two loops which you choose to execute based on isGreater
169         */
170        if ( isGreaterOrLesser == COMPARE_GREATER )
171        {
172            for ( Value value : attr )
173            {
174                // Found a value that is greater than or equal to the ava value
175                if ( value.compareTo( filterValue ) >= 0 )
176                {
177                    return true;
178                }
179            }
180        }
181        else
182        {
183            for ( Value value : attr )
184            {
185                // Found a value that is less than or equal to the ava value
186                if ( value.compareTo( filterValue ) <= 0 )
187                {
188                    return true;
189                }
190            }
191        }
192
193        // no match so return false
194        return false;
195    }
196
197
198    /**
199     * Evaluates a simple presence attribute value assertion on a perspective
200     * candidate.
201     * 
202     * @param attrId the name of the attribute tested for presence 
203     * @param entry the perspective candidate
204     * @return the ava evaluation on the perspective candidate
205     */
206    private boolean evalPresence( AttributeType attributeType, Entry entry )
207    {
208        if ( entry == null )
209        {
210            return false;
211        }
212
213        return null != entry.get( attributeType );
214    }
215
216
217    /**
218     * Evaluates a simple equality attribute value assertion on a perspective
219     * candidate.
220     *
221     * @param node the equality node to evaluate
222     * @param entry the perspective candidate
223     * @return the ava evaluation on the perspective candidate
224     * @throws org.apache.directory.api.ldap.model.exception.LdapException if there is a database access failure
225     */
226    private boolean evalEquality( EqualityNode<?> node, Entry entry ) throws LdapException
227    {
228        // get the attribute associated with the node
229        Attribute attr = entry.get( node.getAttribute() );
230
231        // If we do not have the attribute just return false
232        if ( null == attr )
233        {
234            return false;
235        }
236
237        // check if Ava value exists in attribute
238        Value value = node.getValue();
239
240        // check if the normalized value is present
241        if ( attr.contains( value ) )
242        {
243            return true;
244        }
245
246        /*
247         * We need to now iterate through all values because we could not get
248         * a lookup to work.  For each value we normalize and use the comparator
249         * to determine if a match exists.
250         */
251        for ( Value val : attr )
252        {
253            if ( 0 == val.compareTo( value ) )
254            {
255                return true;
256            }
257        }
258
259        // no match so return false
260        return false;
261    }
262
263
264    /**
265     * Gets the matching rule for an attributeType.
266     *
267     * @param attributeType the attributeType
268     * @return the matching rule
269     * @throws LdapException if there is a failure
270     */
271    private MatchingRule getMatchingRule( AttributeType attributeType, int matchType ) throws LdapException
272    {
273        MatchingRule mrule = null;
274
275        switch ( matchType )
276        {
277            case ( EQUALITY_MATCH ):
278                mrule = attributeType.getEquality();
279                break;
280
281            case ( SUBSTRING_MATCH ):
282                mrule = attributeType.getSubstring();
283                break;
284
285            case ( ORDERING_MATCH ):
286                mrule = attributeType.getOrdering();
287                break;
288
289            default:
290                throw new LdapException( I18n.err( I18n.ERR_246, matchType ) );
291        }
292
293        if ( ( mrule == null ) && ( matchType != EQUALITY_MATCH ) )
294        {
295            mrule = attributeType.getEquality();
296        }
297
298        return mrule;
299    }
300}