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