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.core.event;
021    
022    
023    import java.util.Comparator;
024    
025    import org.apache.directory.server.i18n.I18n;
026    import org.apache.directory.shared.ldap.NotImplementedException;
027    import org.apache.directory.shared.ldap.entry.StringValue;
028    import org.apache.directory.shared.ldap.entry.EntryAttribute;
029    import org.apache.directory.shared.ldap.entry.ServerEntry;
030    import org.apache.directory.shared.ldap.entry.Value;
031    import org.apache.directory.shared.ldap.exception.LdapException;
032    import org.apache.directory.shared.ldap.exception.LdapInvalidSearchFilterException;
033    import org.apache.directory.shared.ldap.filter.ApproximateNode;
034    import org.apache.directory.shared.ldap.filter.EqualityNode;
035    import org.apache.directory.shared.ldap.filter.ExprNode;
036    import org.apache.directory.shared.ldap.filter.ExtensibleNode;
037    import org.apache.directory.shared.ldap.filter.GreaterEqNode;
038    import org.apache.directory.shared.ldap.filter.LessEqNode;
039    import org.apache.directory.shared.ldap.filter.PresenceNode;
040    import org.apache.directory.shared.ldap.filter.ScopeNode;
041    import org.apache.directory.shared.ldap.filter.SimpleNode;
042    import org.apache.directory.shared.ldap.filter.SubstringNode;
043    import org.apache.directory.shared.ldap.schema.AttributeType;
044    import org.apache.directory.shared.ldap.schema.LdapComparator;
045    import org.apache.directory.shared.ldap.schema.MatchingRule;
046    import org.apache.directory.shared.ldap.schema.Normalizer;
047    import org.apache.directory.shared.ldap.schema.SchemaManager;
048    
049    
050    /**
051     * Evaluates LeafNode assertions on candidates using a database.
052     * 
053     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054     * @version $Rev: 928945 $
055     */
056    public class LeafEvaluator implements Evaluator
057    {
058        /** equality matching type constant */
059        private static final int EQUALITY_MATCH = 0;
060        
061        /** ordering matching type constant */
062        private static final int ORDERING_MATCH = 1;
063        
064        /** substring matching type constant */
065        private static final int SUBSTRING_MATCH = 3;
066    
067        /** SchemaManager needed for normalizing and comparing values */
068        private SchemaManager schemaManager;
069        
070        /** Substring node evaluator we depend on */
071        private SubstringEvaluator substringEvaluator;
072        
073        /** ScopeNode evaluator we depend on */
074        private ScopeEvaluator scopeEvaluator;
075    
076        /** Constants used for comparisons */
077        private static final boolean COMPARE_GREATER = true;
078        private static final boolean COMPARE_LESSER = false;
079    
080    
081        /**
082         * Creates a leaf expression node evaluator.
083         *
084         * @param substringEvaluator
085         */
086        public LeafEvaluator( SchemaManager schemaManager,
087            SubstringEvaluator substringEvaluator )
088        {
089            this.schemaManager = schemaManager;
090            this.scopeEvaluator = new ScopeEvaluator();
091            this.substringEvaluator = substringEvaluator;
092        }
093    
094    
095        public ScopeEvaluator getScopeEvaluator()
096        {
097            return scopeEvaluator;
098        }
099    
100    
101        public SubstringEvaluator getSubstringEvaluator()
102        {
103            return substringEvaluator;
104        }
105    
106    
107        /**
108         * @see Evaluator#evaluate(ExprNode, String, ServerEntry)
109         */
110        public boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws LdapException
111        {
112            if ( node instanceof ScopeNode )
113            {
114                return scopeEvaluator.evaluate( node, dn, entry );
115            }
116    
117            if ( node instanceof PresenceNode )
118            {
119                String attrId = ( ( PresenceNode ) node ).getAttribute();
120                return evalPresence( attrId, entry );
121            }
122            else if ( ( node instanceof EqualityNode ) || ( node instanceof ApproximateNode ) )
123            {
124                return evalEquality( ( EqualityNode<?> ) node, entry );
125            }
126            else if ( node instanceof GreaterEqNode )
127            {
128                return evalGreaterOrLesser( ( GreaterEqNode<?> ) node, entry, COMPARE_GREATER );
129            }
130            else if ( node instanceof LessEqNode )
131            {
132                return evalGreaterOrLesser( ( LessEqNode<?> ) node, entry, COMPARE_LESSER );
133            }
134            else if ( node instanceof SubstringNode )
135            {
136                return substringEvaluator.evaluate( node, dn, entry );
137            }
138            else if ( node instanceof ExtensibleNode )
139            {
140                throw new NotImplementedException();
141            }
142            else
143            {
144                throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_245, node ) );
145            }
146        }
147    
148    
149        /**
150         * Evaluates a simple greater than or less than attribute value assertion on
151         * a perspective candidate.
152         * 
153         * @param node the greater than or less than node to evaluate
154         * @param entry the perspective candidate
155         * @param isGreater true if it is a greater than or equal to comparison,
156         *      false if it is a less than or equal to comparison.
157         * @return the ava evaluation on the perspective candidate
158         * @throws LdapException if there is a database access failure
159         */
160        @SuppressWarnings("unchecked")
161        private boolean evalGreaterOrLesser( SimpleNode<?> node, ServerEntry entry, boolean isGreaterOrLesser )
162            throws LdapException
163        {
164            String attrId = node.getAttribute();
165    
166            // get the attribute associated with the node
167            AttributeType type = schemaManager.lookupAttributeTypeRegistry( attrId );
168            EntryAttribute attr = entry.get( type );
169    
170            // If we do not have the attribute just return false
171            if ( null == attr )
172            {
173                return false;
174            }
175    
176            /*
177             * We need to iterate through all values and for each value we normalize
178             * and use the comparator to determine if a match exists.
179             */
180            Normalizer normalizer = getNormalizer( attrId );
181            Comparator comparator = getComparator( attrId );
182            Object filterValue = normalizer.normalize( node.getValue() );
183    
184            /*
185             * Cheaper to not check isGreater in one loop - better to separate
186             * out into two loops which you choose to execute based on isGreater
187             */
188            if ( isGreaterOrLesser == COMPARE_GREATER )
189            {
190                for ( Value<?> value : attr )
191                {
192                    Object normValue = normalizer.normalize( value );
193    
194                    // Found a value that is greater than or equal to the ava value
195                    if ( 0 >= comparator.compare( normValue, filterValue ) )
196                    {
197                        return true;
198                    }
199                }
200            }
201            else
202            {
203                for ( Value<?> value : attr )
204                {
205                    Object normValue = normalizer.normalize( value );
206    
207                    // Found a value that is less than or equal to the ava value
208                    if ( 0 <= comparator.compare( normValue, filterValue ) )
209                    {
210                        return true;
211                    }
212                }
213            }
214    
215            // no match so return false
216            return false;
217        }
218    
219    
220        /**
221         * Evaluates a simple presence attribute value assertion on a perspective
222         * candidate.
223         * 
224         * @param attrId the name of the attribute tested for presence 
225         * @param entry the perspective candidate
226         * @return the ava evaluation on the perspective candidate
227         */
228        private boolean evalPresence( String attrId, ServerEntry entry ) throws LdapException
229        {
230            if ( entry == null )
231            {
232                return false;
233            }
234    
235            return null != entry.get( attrId );
236        }
237    
238    
239        /**
240         * Evaluates a simple equality attribute value assertion on a perspective
241         * candidate.
242         *
243         * @param node the equality node to evaluate
244         * @param entry the perspective candidate
245         * @return the ava evaluation on the perspective candidate
246         * @throws LdapException if there is a database access failure
247         */
248        @SuppressWarnings("unchecked")
249        private boolean evalEquality( EqualityNode<?> node, ServerEntry entry ) throws LdapException
250        {
251            Normalizer normalizer = getNormalizer( node.getAttribute() );
252            Comparator comparator = getComparator( node.getAttribute() );
253    
254            // get the attribute associated with the node
255            EntryAttribute attr = entry.get( node.getAttribute() );
256    
257            // If we do not have the attribute just return false
258            if ( null == attr )
259            {
260                return false;
261            }
262    
263            // check if AVA value exists in attribute
264            AttributeType at = schemaManager.lookupAttributeTypeRegistry( node.getAttribute() );
265            Value<?> value = null;
266            
267            if ( at.getSyntax().isHumanReadable() )
268            {
269                if ( node.getValue().isBinary() )
270                {
271                    value = new StringValue( node.getValue().getString() );
272                }
273                else
274                {
275                    value = node.getValue();
276                }
277            }
278            else
279            {
280                value = node.getValue();
281            }
282            
283            if ( attr.contains( value ) )
284            {
285                return true;
286            }
287    
288            // get the normalized AVA filter value
289            Value<?> filterValue = normalizer.normalize( value );
290    
291            // check if the normalized value is present
292            if ( attr.contains( filterValue ) )
293            {
294                return true;
295            }
296    
297            /*
298             * We need to now iterate through all values because we could not get
299             * a lookup to work.  For each value we normalize and use the comparator
300             * to determine if a match exists.
301             */
302            for ( Value<?> val : attr )
303            {
304                Value<?> normValue = normalizer.normalize( val );
305    
306                if ( 0 == comparator.compare( normValue.get(), filterValue.get() ) )
307                {
308                    return true;
309                }
310            }
311    
312            // no match so return false
313            return false;
314        }
315    
316    
317        /**
318         * Gets the comparator for equality matching.
319         *
320         * @param attrId the attribute identifier
321         * @return the comparator for equality matching
322         * @throws LdapException if there is a failure
323         */
324        private LdapComparator<? super Object> getComparator( String attrId ) throws LdapException
325        {
326            MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
327            return mrule.getLdapComparator();
328        }
329    
330    
331        /**
332         * Gets the normalizer for equality matching.
333         *
334         * @param attrId the attribute identifier
335         * @return the normalizer for equality matching
336         * @throws LdapException if there is a failure
337         */
338        private Normalizer getNormalizer( String attrId ) throws LdapException
339        {
340            MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
341            return mrule.getNormalizer();
342        }
343    
344    
345        /**
346         * Gets the matching rule for an attributeType.
347         *
348         * @param attrId the attribute identifier
349         * @return the matching rule
350         * @throws LdapException if there is a failure
351         */
352        private MatchingRule getMatchingRule( String attrId, int matchType ) throws LdapException
353        {
354            MatchingRule mrule = null;
355            AttributeType type = schemaManager.lookupAttributeTypeRegistry( attrId );
356    
357            switch ( matchType )
358            {
359                case ( EQUALITY_MATCH ):
360                    mrule = type.getEquality();
361                    break;
362    
363                case ( SUBSTRING_MATCH ):
364                    mrule = type.getSubstring();
365                    break;
366    
367                case ( ORDERING_MATCH ):
368                    mrule = type.getOrdering();
369                    break;
370    
371                default:
372                    throw new LdapException( I18n.err( I18n.ERR_246, matchType ) );
373            }
374    
375            if ( ( mrule == null ) && ( matchType != EQUALITY_MATCH ) )
376            {
377                mrule = type.getEquality();
378            }
379    
380            return mrule;
381        }
382    }