1 /*
2 * Copyright 2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 /*
19 * $Id: ScopeEvaluator.java 264732 2005-08-30 08:04:51Z akarasulu $
20 *
21 * -- (c) LDAPd Group --
22 * -- Please refer to the LICENSE.txt file in the root directory of --
23 * -- any LDAPd project for copyright and distribution information. --
24 *
25 * Created on Oct 9, 2003
26 */
27 package org.apache.ldap.server.partition.impl.btree;
28
29
30 import java.math.BigInteger;
31
32 import javax.naming.NamingException;
33 import javax.naming.directory.SearchControls;
34
35 import org.apache.ldap.common.filter.ExprNode;
36 import org.apache.ldap.common.filter.ScopeNode;
37 import org.apache.ldap.common.message.DerefAliasesEnum;
38
39
40 /***
41 * Evaluates ScopeNode assertions on candidates using a database.
42 *
43 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44 * @version $Rev: 264732 $
45 */
46 public class ScopeEvaluator implements Evaluator
47 {
48 /*** Database used to evaluate scope with */
49 private BTreeContextPartition db;
50
51
52 /***
53 * Creates a scope node evaluator for search expressions.
54 *
55 * @param db the database used to evaluate scope node
56 */
57 public ScopeEvaluator( BTreeContextPartition db )
58 {
59 this.db = db;
60 }
61
62
63 /***
64 * @see org.apache.ldap.server.partition.impl.btree.Evaluator#evaluate(ExprNode, org.apache.ldap.server.partition.impl.btree.IndexRecord)
65 */
66 public boolean evaluate( ExprNode node, IndexRecord record )
67 throws NamingException
68 {
69 ScopeNode snode = ( ScopeNode ) node;
70
71 switch( snode.getScope() )
72 {
73 case( SearchControls.OBJECT_SCOPE ):
74 String dn = db.getEntryDn( record.getEntryId() );
75 return dn.equals( snode.getBaseDn() );
76 case( SearchControls.ONELEVEL_SCOPE ):
77 return assertOneLevelScope( snode, record.getEntryId() );
78 case( SearchControls.SUBTREE_SCOPE ):
79 return assertSubtreeScope( snode, record.getEntryId() );
80 default:
81 throw new NamingException( "Unrecognized search scope!" );
82 }
83 }
84
85
86 /***
87 * Asserts whether or not a candidate has one level scope while taking
88 * alias dereferencing into account.
89 *
90 * @param node the scope node containing the base and alias handling mode
91 * @param id the candidate to assert which can be any db entry's id
92 * @return true if the candidate is within one level scope whether or not
93 * alias dereferencing is enabled.
94 * @throws NamingException if the index lookups fail.
95 */
96 public boolean assertSubtreeScope( final ScopeNode node,
97 final BigInteger id ) throws NamingException
98 {
99 String dn = db.getEntryDn( id );
100 DerefAliasesEnum mode = node.getDerefAliases();
101 Object baseId = db.getEntryId( node.getBaseDn() );
102 boolean isDescendant = dn.endsWith( node.getBaseDn() );
103
104 /*
105 * The candidate id could be any entry in the db. If search
106 * dereferencing is not enabled then we return the results of the
107 * descendant test.
108 */
109 if ( ! mode.derefInSearching() )
110 {
111 return isDescendant;
112 }
113
114 /*
115 * From here down alias dereferencing is enabled. We determine if the
116 * candidate id is an alias, if so we reject it since aliases should
117 * not be returned.
118 */
119 Index idx = db.getAliasIndex();
120 if ( null != idx.reverseLookup( id ) )
121 {
122 return false;
123 }
124
125 /*
126 * The candidate is NOT an alias at this point. So if it is a
127 * descendant we just return it since it is in normal subtree scope.
128 */
129 if ( isDescendant )
130 {
131 return true;
132 }
133
134 /*
135 * At this point the candidate is not a descendant and it is not an
136 * alias. We need to check if the candidate is in extended subtree
137 * scope by performing a lookup on the subtree alias index. This index
138 * stores a tuple mapping the baseId to the ids of objects brought
139 * into subtree scope of the base by an alias:
140 *
141 * ( baseId, aliasedObjId )
142 *
143 * If the candidate id is an object brought into subtree scope then
144 * the lookup returns true accepting the candidate. Otherwise the
145 * candidate is rejected with a false return because it is not in scope.
146 */
147 idx = db.getSubAliasIndex();
148 return idx.hasValue( baseId, id );
149 }
150
151
152 /***
153 * Asserts whether or not a candidate has one level scope while taking
154 * alias dereferencing into account.
155 *
156 * @param node the scope node containing the base and alias handling mode
157 * @param id the candidate to assert which can be any db entry's id
158 * @return true if the candidate is within one level scope whether or not
159 * alias dereferencing is enabled.
160 * @throws NamingException if the index lookups fail.
161 */
162 public boolean assertOneLevelScope( final ScopeNode node,
163 final BigInteger id ) throws NamingException
164 {
165 DerefAliasesEnum mode = node.getDerefAliases();
166 Object baseId = db.getEntryId( node.getBaseDn() );
167 Index idx = db.getHierarchyIndex();
168 boolean isChild = idx.hasValue( baseId, id );
169
170 /*
171 * The candidate id could be any entry in the db. If search
172 * dereferencing is not enabled then we return the results of the child
173 * test.
174 */
175 if ( ! mode.derefInSearching() )
176 {
177 return isChild;
178 }
179
180 /*
181 * From here down alias dereferencing is enabled. We determine if the
182 * candidate id is an alias, if so we reject it since aliases should
183 * not be returned.
184 */
185 idx = db.getAliasIndex();
186 if ( null != idx.reverseLookup( id ) )
187 {
188 return false;
189 }
190
191 /*
192 * The candidate is NOT an alias at this point. So if it is a child we
193 * just return it since it is in normal one level scope.
194 */
195 if ( isChild )
196 {
197 return true;
198 }
199
200 /*
201 * At this point the candidate is not a child and it is not an alias.
202 * We need to check if the candidate is in extended one level scope by
203 * performing a lookup on the one level alias index. This index stores
204 * a tuple mapping the baseId to the id of objects brought into the
205 * one level scope of the base by an alias: ( baseId, aliasedObjId )
206 * If the candidate id is an object brought into one level scope then
207 * the lookup returns true accepting the candidate. Otherwise the
208 * candidate is rejected with a false return because it is not in scope.
209 */
210 idx = db.getOneAliasIndex();
211 return idx.hasValue( baseId, id );
212 }
213 }