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.evaluator;
021
022
023import java.util.Iterator;
024
025import org.apache.directory.api.ldap.model.entry.Attribute;
026import org.apache.directory.api.ldap.model.entry.Entry;
027import org.apache.directory.api.ldap.model.entry.Value;
028import org.apache.directory.api.ldap.model.exception.LdapException;
029import org.apache.directory.api.ldap.model.exception.LdapOtherException;
030import org.apache.directory.api.ldap.model.filter.LessEqNode;
031import org.apache.directory.api.ldap.model.schema.AttributeType;
032import org.apache.directory.api.ldap.model.schema.LdapComparator;
033import org.apache.directory.api.ldap.model.schema.MatchingRule;
034import org.apache.directory.api.ldap.model.schema.SchemaManager;
035import org.apache.directory.server.core.api.partition.PartitionTxn;
036import org.apache.directory.server.i18n.I18n;
037import org.apache.directory.server.xdbm.Index;
038import org.apache.directory.server.xdbm.IndexEntry;
039import org.apache.directory.server.xdbm.IndexNotFoundException;
040import org.apache.directory.server.xdbm.Store;
041
042
043/**
044 * An Evaluator which determines if candidates are matched by LessEqNode
045 * assertions.
046 *
047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
048 */
049public class LessEqEvaluator<T> extends LeafEvaluator<T>
050{
051    /**
052     * Creates a new LessEqEvaluator
053     * 
054     * @param node The LessEqNode
055     * @param db The Store
056     * @param schemaManager The SchemaManager
057     * @throws LdapException If the creation failed
058     */
059    @SuppressWarnings("unchecked")
060    public LessEqEvaluator( LessEqNode<T> node, Store db, SchemaManager schemaManager )
061        throws LdapException
062    {
063        super( node, db, schemaManager );
064
065        if ( db.hasIndexOn( attributeType ) )
066        {
067            try
068            { 
069                idx = ( Index<T, String> ) db.getIndex( attributeType );
070            }
071            catch ( IndexNotFoundException infe )
072            {
073                throw new LdapOtherException( infe.getMessage(), infe );
074            }
075        }
076        else
077        {
078            idx = null;
079        }
080
081        /*
082         * We prefer matching using the Normalizer and Comparator pair from
083         * the ordering matchingRule if one is available.  It may very well
084         * not be.  If so then we resort to using the Normalizer and
085         * Comparator from the equality matchingRule as a last resort.
086         */
087        MatchingRule mr = attributeType.getOrdering();
088
089        if ( mr == null )
090        {
091            mr = attributeType.getEquality();
092        }
093
094        if ( mr == null )
095        {
096            throw new IllegalStateException( I18n.err( I18n.ERR_717, node ) );
097        }
098
099        normalizer = mr.getNormalizer();
100        ldapComparator = mr.getLdapComparator();
101    }
102
103
104    /**
105     * {@inheritDoc}
106     */
107    @Override
108    public LessEqNode<T> getExpression()
109    {
110        return ( LessEqNode<T> ) node;
111    }
112
113
114    /**
115     * {@inheritDoc}
116     */
117    @Override
118    public boolean evaluate( PartitionTxn partitionTxn, IndexEntry<?, String> indexEntry ) throws LdapException
119    {
120        Entry entry = indexEntry.getEntry();
121
122        // resuscitate the entry if it has not been and set entry in IndexEntry
123        if ( null == entry )
124        {
125            entry = db.fetch( partitionTxn, indexEntry.getId() );
126
127            if ( null == entry )
128            {
129                // The entry is not anymore present : get out
130                return false;
131            }
132
133            indexEntry.setEntry( entry );
134        }
135
136        // get the attribute
137        Attribute attr = entry.get( attributeType );
138
139        // if the attribute does not exist just return false
140        //noinspection unchecked
141        if ( attr != null && evaluate( ( IndexEntry<Object, String> ) indexEntry, attr ) )
142        {
143            return true;
144        }
145
146        // If we do not have the attribute, loop through the sub classes of
147        // the attributeType.  Perhaps the entry has an attribute value of a
148        // subtype (descendant) that will produce a match
149        if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) )
150        {
151            // TODO check to see if descendant handling is necessary for the
152            // index so we can match properly even when for example a name
153            // attribute is used instead of more specific commonName
154            Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType );
155
156            while ( descendants.hasNext() )
157            {
158                AttributeType descendant = descendants.next();
159
160                attr = entry.get( descendant );
161
162                //noinspection unchecked
163                if ( attr != null && evaluate( ( IndexEntry<Object, String> ) indexEntry, attr ) )
164                {
165                    return true;
166                }
167            }
168        }
169
170        // we fell through so a match was not found - assertion was false.
171        return false;
172    }
173
174
175    /**
176     * {@inheritDoc}
177     */
178    @Override
179    public boolean evaluate( Entry entry ) throws LdapException
180    {
181        // get the attribute
182        Attribute attr = entry.get( attributeType );
183
184        // if the attribute does not exist just return false
185        if ( ( attr != null ) && evaluate( null, attr ) )
186        {
187            return true;
188        }
189
190        // If we do not have the attribute, loop through the sub classes of
191        // the attributeType.  Perhaps the entry has an attribute value of a
192        // subtype (descendant) that will produce a match
193        if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) )
194        {
195            // TODO check to see if descendant handling is necessary for the
196            // index so we can match properly even when for example a name
197            // attribute is used instead of more specific commonName
198            Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType );
199
200            while ( descendants.hasNext() )
201            {
202                AttributeType descendant = descendants.next();
203
204                attr = entry.get( descendant );
205
206                if ( attr != null && evaluate( null, attr ) )
207                {
208                    return true;
209                }
210            }
211        }
212
213        // we fell through so a match was not found - assertion was false.
214        return false;
215    }
216
217
218    // TODO - determine if comparator and index entry should have the Value
219    // wrapper or the raw normalized value
220    private boolean evaluate( IndexEntry<Object, String> indexEntry, Attribute attribute )
221    {
222        LdapComparator ldapComparator = attribute.getAttributeType().getOrdering().getLdapComparator();
223        /*
224         * Cycle through the attribute values testing normalized version
225         * obtained from using the ordering or equality matching rule's
226         * normalizer.  The test uses the comparator obtained from the
227         * appropriate matching rule to perform the check.
228         */
229        for ( Value value : attribute )
230        {
231            if ( ldapComparator.compare( value.getValue(), node.getValue().getValue() ) <= 0 )
232            {
233                if ( indexEntry != null )
234                {
235                    indexEntry.setKey( value.getValue() );
236                }
237                
238                return true;
239            }
240        }
241
242        return false;
243    }
244
245
246    /**
247     * @see Object#toString()
248     */
249    @Override
250    public String toString( String tabs )
251    {
252        StringBuilder sb = new StringBuilder();
253
254        sb.append( tabs ).append( "LessEqEvaluator : " ).append( super.toString() ).append( "\n" );
255
256        return sb.toString();
257    }
258
259
260    /**
261     * @see Object#toString()
262     */
263    @Override
264    public String toString()
265    {
266        return toString( "" );
267    }
268}