1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 * 19 */ 20 package org.apache.directory.server.core.subtree; 21 22 23 import java.util.Iterator; 24 25 import javax.naming.Name; 26 import javax.naming.NamingException; 27 28 import org.apache.directory.server.core.entry.ServerEntry; 29 import org.apache.directory.server.core.event.Evaluator; 30 import org.apache.directory.server.core.event.ExpressionEvaluator; 31 import org.apache.directory.server.schema.registries.AttributeTypeRegistry; 32 import org.apache.directory.server.schema.registries.OidRegistry; 33 import org.apache.directory.shared.ldap.name.LdapDN; 34 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification; 35 import org.apache.directory.shared.ldap.util.NamespaceTools; 36 37 38 /** 39 * An evaluator used to determine if an entry is included in the collection 40 * represented by a subtree specification. 41 * 42 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 43 * @version $Rev: 638228 $ 44 */ 45 public class SubtreeEvaluator 46 { 47 /** A refinement filter evaluator */ 48 private final Evaluator evaluator; 49 50 51 /** 52 * Creates a subtreeSpecification evaluatior which can be used to determine 53 * if an entry is included within the collection of a subtree. 54 * 55 * @param oidRegistry a registry used to lookup objectClass names for OIDs 56 * @param attrRegistry registry to be looked up 57 * @throws NamingException 58 */ 59 public SubtreeEvaluator( OidRegistry oidRegistry, AttributeTypeRegistry attrRegistry ) 60 { 61 evaluator = new ExpressionEvaluator(oidRegistry, attrRegistry ); 62 } 63 64 65 /** 66 * Determines if an entry is selected by a subtree specification. 67 * 68 * @param subtree the subtree specification 69 * @param apDn the distinguished name of the administrative point containing the subentry 70 * @param entryDn the distinguished name of the candidate entry 71 * @return true if the entry is selected by the specification, false if it is not 72 * @throws javax.naming.NamingException if errors are encountered while evaluating selection 73 */ 74 public boolean evaluate( SubtreeSpecification subtree, Name apDn, Name entryDn, ServerEntry entry ) 75 throws NamingException 76 { 77 // TODO: Try to make this cast unnecessary. 78 LdapDN entryLdapDn = (LdapDN) entryDn; 79 80 /* ===================================================================== 81 * NOTE: Regarding the overall approach, we try to narrow down the 82 * possibilities by slowly pruning relative names off of the entryDn. 83 * For example we check first if the entry is a descendant of the AP. 84 * If so we use the relative name thereafter to calculate if it is 85 * a descendant of the base. This means shorter names to compare and 86 * less work to do while we continue to deduce inclusion by the subtree 87 * specification. 88 * ===================================================================== 89 */ 90 91 /* 92 * First we simply check if the candidate entry is a descendant of the 93 * administrative point. In the process we calculate the relative 94 * distinguished name relative to the administrative point. 95 */ 96 Name apRelativeRdn; 97 98 if ( !NamespaceTools.isDescendant( apDn, entryDn ) ) 99 { 100 return false; 101 } 102 else if ( apDn.equals( entryDn ) ) 103 { 104 apRelativeRdn = new LdapDN(); 105 } 106 else 107 { 108 apRelativeRdn = NamespaceTools.getRelativeName( apDn, entryDn ); 109 } 110 111 /* 112 * We do the same thing with the base as we did with the administrative 113 * point: check if the entry is a descendant of the base and find the 114 * relative name of the entry with respect to the base rdn. With the 115 * baseRelativeRdn we can later make comparisons with specific exclusions. 116 */ 117 Name baseRelativeRdn; 118 119 if ( subtree.getBase() != null && subtree.getBase().size() == 0 ) 120 { 121 baseRelativeRdn = apRelativeRdn; 122 } 123 else if ( apRelativeRdn.equals( subtree.getBase() ) ) 124 { 125 baseRelativeRdn = new LdapDN(); 126 } 127 else if ( !NamespaceTools.isDescendant( subtree.getBase(), apRelativeRdn ) ) 128 { 129 return false; 130 } 131 else 132 { 133 baseRelativeRdn = NamespaceTools.getRelativeName( subtree.getBase(), apRelativeRdn ); 134 } 135 136 /* 137 * Evaluate based on minimum and maximum chop values. Here we simply 138 * need to compare the distances respectively with the size of the 139 * baseRelativeRdn. For the max distance entries with a baseRelativeRdn 140 * size greater than the max distance are rejected. For the min distance 141 * entries with a baseRelativeRdn size less than the minimum distance 142 * are rejected. 143 */ 144 if ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX ) 145 { 146 if ( subtree.getMaxBaseDistance() < baseRelativeRdn.size() ) 147 { 148 return false; 149 } 150 } 151 152 if ( subtree.getMinBaseDistance() > 0 ) 153 { 154 if ( baseRelativeRdn.size() < subtree.getMinBaseDistance() ) 155 { 156 return false; 157 } 158 } 159 160 /* 161 * For specific exclusions we must iterate through the set and check 162 * if the baseRelativeRdn is a descendant of the exclusion. The 163 * isDescendant() function will return true if the compared names 164 * are equal so for chopAfter exclusions we must check for equality 165 * as well and reject if the relative names are equal. 166 */ 167 Iterator list = subtree.getChopBeforeExclusions().iterator(); 168 169 while ( list.hasNext() ) 170 { 171 Name chopBefore = ( Name ) list.next(); 172 173 if ( NamespaceTools.isDescendant( chopBefore, baseRelativeRdn ) ) 174 { 175 return false; 176 } 177 } 178 179 list = subtree.getChopAfterExclusions().iterator(); 180 181 while ( list.hasNext() ) 182 { 183 Name chopAfter = ( Name ) list.next(); 184 185 if ( NamespaceTools.isDescendant( chopAfter, baseRelativeRdn ) && !chopAfter.equals( baseRelativeRdn ) ) 186 { 187 return false; 188 } 189 } 190 191 /* 192 * The last remaining step is to check and see if the refinement filter 193 * selects the entry candidate based on objectClass attribute values. 194 * To do this we invoke the refinement evaluator members evaluate() method. 195 */ 196 if ( subtree.getRefinement() != null ) 197 { 198 return evaluator.evaluate( subtree.getRefinement(), entryLdapDn.toNormName(), entry ); 199 } 200 201 /* 202 * If nothing has rejected the candidate entry and there is no refinement 203 * filter then the entry is included in the collection represented by the 204 * subtree specification so we return true. 205 */ 206 return true; 207 } 208 }