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