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 java.util.Comparator;
024    import java.util.Iterator;
025    
026    import org.apache.directory.server.xdbm.Index;
027    import org.apache.directory.server.xdbm.IndexEntry;
028    import org.apache.directory.server.xdbm.Store;
029    import org.apache.directory.server.xdbm.search.Evaluator;
030    import org.apache.directory.shared.ldap.entry.EntryAttribute;
031    import org.apache.directory.shared.ldap.entry.ServerEntry;
032    import org.apache.directory.shared.ldap.entry.Value;
033    import org.apache.directory.shared.ldap.filter.EqualityNode;
034    import org.apache.directory.shared.ldap.schema.AttributeType;
035    import org.apache.directory.shared.ldap.schema.LdapComparator;
036    import org.apache.directory.shared.ldap.schema.MatchingRule;
037    import org.apache.directory.shared.ldap.schema.Normalizer;
038    import org.apache.directory.shared.ldap.schema.SchemaManager;
039    import org.apache.directory.shared.ldap.schema.comparators.ByteArrayComparator;
040    import org.apache.directory.shared.ldap.schema.comparators.StringComparator;
041    import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer;
042    import org.apache.directory.shared.ldap.util.StringTools;
043    
044    
045    /**
046     * An Evaluator which determines if candidates are matched by GreaterEqNode
047     * assertions.
048     *
049     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050     * @version $Rev$
051     */
052    public class EqualityEvaluator<T, ID> implements Evaluator<EqualityNode<T>, ServerEntry, ID>
053    {
054        private final EqualityNode<T> node;
055        private final Store<ServerEntry, ID> db;
056        private final SchemaManager schemaManager;
057        private final AttributeType type;
058        private final Normalizer normalizer;
059    
060        /** The comparator to use */
061        private final LdapComparator<?> comparator;
062    
063        /** The default byte[] comparator if no comparator has been defined */
064        private static final Comparator<byte[]> BINARY_COMPARATOR = new ByteArrayComparator( null );
065    
066        /** The default String comparator if no comparator has been defined */
067        private static final Comparator<String> STRING_COMPARATOR = new StringComparator( null );
068    
069        private final Index<T, ServerEntry, ID> idx;
070    
071    
072        @SuppressWarnings("unchecked")
073        public EqualityEvaluator( EqualityNode<T> node, Store<ServerEntry, ID> db, SchemaManager schemaManager )
074            throws Exception
075        {
076            this.db = db;
077            this.node = node;
078            this.schemaManager = schemaManager;
079    
080            if ( db.hasIndexOn( node.getAttribute() ) )
081            {
082                idx = ( Index<T, ServerEntry, ID> ) db.getIndex( node.getAttribute() );
083                type = null;
084                normalizer = null;
085                comparator = null;
086            }
087            else
088            {
089                idx = null;
090                type = schemaManager.lookupAttributeTypeRegistry( node.getAttribute() );
091    
092                MatchingRule mr = type.getEquality();
093    
094                if ( mr == null )
095                {
096                    normalizer = new NoOpNormalizer( type.getOid() );
097                    comparator = null;
098                }
099                else
100                {
101                    normalizer = mr.getNormalizer();
102                    comparator = mr.getLdapComparator();
103                }
104            }
105        }
106    
107    
108        public EqualityNode<T> getExpression()
109        {
110            return node;
111        }
112    
113    
114        public boolean evaluate( IndexEntry<?, ServerEntry, ID> indexEntry ) throws Exception
115        {
116            if ( idx != null )
117            {
118                return idx.forward( node.getValue().get(), indexEntry.getId() );
119            }
120    
121            ServerEntry entry = indexEntry.getObject();
122    
123            // resuscitate the entry if it has not been and set entry in IndexEntry
124            if ( null == entry )
125            {
126                entry = db.lookup( indexEntry.getId() );
127                indexEntry.setObject( entry );
128            }
129    
130            return evaluateEntry( entry );
131        }
132    
133    
134        public boolean evaluateEntry( ServerEntry entry ) throws Exception
135        {
136            // get the attribute
137            EntryAttribute attr = entry.get( type );
138    
139            // if the attribute does not exist just return false
140            if ( attr != null && evaluate( attr ) )
141            {
142                return true;
143            }
144    
145            // If we do not have the attribute, loop through the sub classes of
146            // the attributeType.  Perhaps the entry has an attribute value of a
147            // subtype (descendant) that will produce a match
148            if ( schemaManager.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) )
149            {
150                // TODO check to see if descendant handling is necessary for the
151                // index so we can match properly even when for example a name
152                // attribute is used instead of more specific commonName
153                Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants(
154                    node.getAttribute() );
155    
156                while ( descendants.hasNext() )
157                {
158                    AttributeType descendant = descendants.next();
159    
160                    attr = entry.get( descendant );
161    
162                    if ( attr != null && evaluate( attr ) )
163                    {
164                        return true;
165                    }
166                }
167            }
168    
169            // we fell through so a match was not found - assertion was false.
170            return false;
171        }
172    
173    
174        public boolean evaluateId( ID id ) throws Exception
175        {
176            if ( idx != null )
177            {
178                return idx.reverse( id );
179            }
180    
181            return evaluateEntry( db.lookup( id ) );
182        }
183    
184    
185        // TODO - determine if comparator and index entry should have the Value
186        // wrapper or the raw normalized value
187        private boolean evaluate( EntryAttribute attribute ) throws Exception
188        {
189            /*
190             * Cycle through the attribute values testing normalized version
191             * obtained from using the ordering or equality matching rule's
192             * normalizer.  The test uses the comparator obtained from the
193             * appropriate matching rule to perform the check.
194             */
195            for ( Value<?> value : attribute )
196            {
197                value.normalize( normalizer );
198    
199                //noinspection unchecked
200                if ( value.isBinary() )
201                {
202                    // Deal with a binary value
203                    byte[] serverValue = ( ( Value<byte[]> ) value ).getNormalizedValue();
204                    byte[] nodeValue = ( ( Value<byte[]> ) node.getValue() ).getNormalizedValue();
205    
206                    if ( comparator != null )
207                    {
208                        if ( ( ( ( LdapComparator<byte[]> ) comparator ).compare( serverValue, nodeValue ) == 0 ) )
209                        {
210                            return true;
211                        }
212                    }
213                    else
214                    {
215                        if ( BINARY_COMPARATOR.compare( serverValue, nodeValue ) == 0 )
216                        {
217                            return true;
218                        }
219                    }
220                }
221                else
222                {
223                    // Deal with a String value
224                    String serverValue = ( ( Value<String> ) value ).getNormalizedValue();
225                    String nodeValue = null;
226    
227                    if ( node.getValue().isBinary() )
228                    {
229                        nodeValue = StringTools.utf8ToString( ( ( Value<byte[]> ) node.getValue() ).getNormalizedValue() );
230                    }
231                    else
232                    {
233                        nodeValue = ( ( Value<String> ) node.getValue() ).getNormalizedValue();
234                    }
235    
236                    if ( comparator != null )
237                    {
238                        if ( ( ( LdapComparator<String> ) comparator ).compare( serverValue, nodeValue ) == 0 )
239                        {
240                            return true;
241                        }
242                    }
243                    else
244                    {
245                        if ( STRING_COMPARATOR.compare( serverValue, nodeValue ) == 0 )
246                        {
247                            return true;
248                        }
249                    }
250                }
251            }
252    
253            return false;
254        }
255    }