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 org.apache.directory.api.ldap.model.entry.Entry;
024import org.apache.directory.api.ldap.model.exception.LdapException;
025import org.apache.directory.api.ldap.model.filter.ScopeNode;
026import org.apache.directory.api.ldap.model.message.SearchScope;
027import org.apache.directory.server.core.api.partition.PartitionTxn;
028import org.apache.directory.server.i18n.I18n;
029import org.apache.directory.server.xdbm.IndexEntry;
030import org.apache.directory.server.xdbm.ParentIdAndRdn;
031import org.apache.directory.server.xdbm.Store;
032import org.apache.directory.server.xdbm.search.Evaluator;
033
034
035/**
036 * Evaluates one level scope assertions on candidates using an entry database.
037 * 
038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039 */
040public class OneLevelScopeEvaluator<E> implements Evaluator<ScopeNode>
041{
042    /** The ScopeNode containing initial search scope constraints */
043    private final ScopeNode node;
044
045    /** The entry identifier of the scope base */
046    private final String baseId;
047
048    /** True if the scope requires alias dereferencing while searching */
049    private final boolean dereferencing;
050
051    /** the entry db storing entries */
052    private final Store db;
053
054
055    /**
056     * Creates a one level scope node Evaluator for search expressions.
057     *
058     * @param node the scope node
059     * @param db the database used to evaluate scope node
060     */
061    public OneLevelScopeEvaluator( Store db, ScopeNode node )
062    {
063        this.node = node;
064
065        if ( node.getScope() != SearchScope.ONELEVEL )
066        {
067            throw new IllegalStateException( I18n.err( I18n.ERR_720 ) );
068        }
069
070        this.db = db;
071        baseId = node.getBaseId();
072        dereferencing = node.getDerefAliases().isDerefInSearching() || node.getDerefAliases().isDerefAlways();
073    }
074
075
076    /**
077     * Asserts whether or not a candidate has one level scope while taking
078     * alias dereferencing into account.
079     *
080     * TODO - terribly inefficient - would benefit from exposing the id of an
081     * entry within the Entry
082     *
083     * {@inheritDoc}
084     */
085    public boolean evaluate( Entry candidate ) throws LdapException
086    {
087        throw new UnsupportedOperationException( I18n.err( I18n.ERR_721 ) );
088    }
089
090
091    /**
092     * {@inheritDoc}
093     */
094    @Override
095    public boolean evaluate( PartitionTxn partitionTxn, IndexEntry<?, String> indexEntry ) throws LdapException
096    {
097        ParentIdAndRdn parent = db.getRdnIndex().reverseLookup( partitionTxn, indexEntry.getId() );
098        boolean isChild = parent.getParentId().equals( baseId );
099        Entry entry = indexEntry.getEntry();
100
101        // Fetch the entry
102        if ( null == entry )
103        {
104            entry = db.fetch( partitionTxn, indexEntry.getId() );
105
106            if ( null == entry )
107            {
108                // The entry is not anymore present : get out
109                return false;
110            }
111
112            indexEntry.setEntry( entry );
113        }
114
115        /*
116         * The candidate id could be any entry in the db.  If search
117         * dereferencing is not enabled then we return the results of the child
118         * test.
119         */
120        if ( !dereferencing )
121        {
122            return isChild;
123        }
124
125        /*
126         * From here down alias dereferencing is enabled.  We determine if the
127         * candidate id is an alias, if so we reject it since aliases should
128         * not be returned.
129         */
130        if ( null != db.getAliasIndex().reverseLookup( partitionTxn, indexEntry.getId() ) )
131        {
132            return false;
133        }
134
135        /*
136         * The candidate is NOT an alias at this point.  So if it is a child we
137         * just return true since it is in normal one level scope.
138         */
139        if ( isChild )
140        {
141            return true;
142        }
143
144        /*
145         * At this point the candidate is not a child and it is not an alias.
146         * We need to check if the candidate is in extended one level scope by
147         * performing a lookup on the one level alias index.  This index stores
148         * a tuple mapping the baseId to the id of objects brought into the
149         * one level scope of the base by an alias: ( baseId, aliasedObjId )
150         * If the candidate id is an object brought into one level scope then
151         * the lookup returns true accepting the candidate.  Otherwise the
152         * candidate is rejected with a false return because it is not in scope.
153         */
154        return db.getOneAliasIndex().forward( partitionTxn, baseId, indexEntry.getId() );
155    }
156
157
158    public ScopeNode getExpression()
159    {
160        return node;
161    }
162
163
164    /**
165     * Gets the id of the search base associated with the ScopeNode expression.
166     *
167     * @return identifier of the search base
168     */
169    public String getBaseId()
170    {
171        return baseId;
172    }
173
174
175    /**
176     * Gets whether or not dereferencing is enabled for this evaluator.
177     *
178     * @return true if dereferencing is enabled, false otherwise
179     */
180    public boolean isDereferencing()
181    {
182        return dereferencing;
183    }
184
185
186    /**
187     * @see Object#toString()
188     */
189    public String toString( String tabs )
190    {
191        StringBuilder sb = new StringBuilder();
192
193        sb.append( tabs ).append( "OneLevelScopEvaluator : " ).append( node ).append( "\n" );
194
195        return sb.toString();
196    }
197
198
199    /**
200     * @see Object#toString()
201     */
202    public String toString()
203    {
204        return toString( "" );
205    }
206}