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 */ 020 package org.apache.directory.server.core.subtree; 021 022 023 import java.util.Iterator; 024 025 import org.apache.directory.server.core.event.Evaluator; 026 import org.apache.directory.server.core.event.ExpressionEvaluator; 027 import org.apache.directory.shared.ldap.entry.ServerEntry; 028 import org.apache.directory.shared.ldap.exception.LdapException; 029 import org.apache.directory.shared.ldap.name.DN; 030 import org.apache.directory.shared.ldap.schema.SchemaManager; 031 import org.apache.directory.shared.ldap.schema.registries.OidRegistry; 032 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification; 033 import org.apache.directory.shared.ldap.util.NamespaceTools; 034 035 036 /** 037 * An evaluator used to determine if an entry is included in the collection 038 * represented by a subtree specification. 039 * 040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 041 * @version $Rev: 927146 $ 042 */ 043 public class SubtreeEvaluator 044 { 045 /** A refinement filter evaluator */ 046 private final Evaluator evaluator; 047 048 049 /** 050 * Creates a subtreeSpecification evaluatior which can be used to determine 051 * if an entry is included within the collection of a subtree. 052 * 053 * @param oidRegistry a registry used to lookup objectClass names for OIDs 054 * @param attrRegistry registry to be looked up 055 */ 056 public SubtreeEvaluator( OidRegistry oidRegistry, SchemaManager schemaManager ) 057 { 058 evaluator = new ExpressionEvaluator( oidRegistry, schemaManager ); 059 } 060 061 062 /** 063 * Determines if an entry is selected by a subtree specification. 064 * 065 * @param subtree the subtree specification 066 * @param apDn the distinguished name of the administrative point containing the subentry 067 * @param entryDn the distinguished name of the candidate entry 068 * @return true if the entry is selected by the specification, false if it is not 069 * @throws LdapException if errors are encountered while evaluating selection 070 */ 071 public boolean evaluate( SubtreeSpecification subtree, DN apDn, DN entryDn, ServerEntry entry ) 072 throws LdapException 073 { 074 // TODO: Try to make this cast unnecessary. 075 DN dnEntryDn = (DN) entryDn; 076 077 /* ===================================================================== 078 * NOTE: Regarding the overall approach, we try to narrow down the 079 * possibilities by slowly pruning relative names off of the entryDn. 080 * For example we check first if the entry is a descendant of the AP. 081 * If so we use the relative name thereafter to calculate if it is 082 * a descendant of the base. This means shorter names to compare and 083 * less work to do while we continue to deduce inclusion by the subtree 084 * specification. 085 * ===================================================================== 086 */ 087 088 /* 089 * First we simply check if the candidate entry is a descendant of the 090 * administrative point. In the process we calculate the relative 091 * distinguished name relative to the administrative point. 092 */ 093 DN apRelativeRdn; 094 095 if ( !NamespaceTools.isDescendant( apDn, entryDn ) ) 096 { 097 return false; 098 } 099 else if ( apDn.equals( entryDn ) ) 100 { 101 apRelativeRdn = new DN(); 102 } 103 else 104 { 105 apRelativeRdn = NamespaceTools.getRelativeName( apDn, entryDn ); 106 } 107 108 /* 109 * We do the same thing with the base as we did with the administrative 110 * point: check if the entry is a descendant of the base and find the 111 * relative name of the entry with respect to the base rdn. With the 112 * baseRelativeRdn we can later make comparisons with specific exclusions. 113 */ 114 DN baseRelativeRdn; 115 116 if ( subtree.getBase() != null && subtree.getBase().size() == 0 ) 117 { 118 baseRelativeRdn = apRelativeRdn; 119 } 120 else if ( apRelativeRdn.equals( subtree.getBase() ) ) 121 { 122 baseRelativeRdn = new DN(); 123 } 124 else if ( !NamespaceTools.isDescendant( subtree.getBase(), apRelativeRdn ) ) 125 { 126 return false; 127 } 128 else 129 { 130 baseRelativeRdn = NamespaceTools.getRelativeName( subtree.getBase(), apRelativeRdn ); 131 } 132 133 /* 134 * Evaluate based on minimum and maximum chop values. Here we simply 135 * need to compare the distances respectively with the size of the 136 * baseRelativeRdn. For the max distance entries with a baseRelativeRdn 137 * size greater than the max distance are rejected. For the min distance 138 * entries with a baseRelativeRdn size less than the minimum distance 139 * are rejected. 140 */ 141 if ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX ) 142 { 143 if ( subtree.getMaxBaseDistance() < baseRelativeRdn.size() ) 144 { 145 return false; 146 } 147 } 148 149 if ( subtree.getMinBaseDistance() > 0 ) 150 { 151 if ( baseRelativeRdn.size() < subtree.getMinBaseDistance() ) 152 { 153 return false; 154 } 155 } 156 157 /* 158 * For specific exclusions we must iterate through the set and check 159 * if the baseRelativeRdn is a descendant of the exclusion. The 160 * isDescendant() function will return true if the compared names 161 * are equal so for chopAfter exclusions we must check for equality 162 * as well and reject if the relative names are equal. 163 */ 164 Iterator list = subtree.getChopBeforeExclusions().iterator(); 165 166 while ( list.hasNext() ) 167 { 168 DN chopBefore = ( DN ) list.next(); 169 170 if ( NamespaceTools.isDescendant( chopBefore, baseRelativeRdn ) ) 171 { 172 return false; 173 } 174 } 175 176 list = subtree.getChopAfterExclusions().iterator(); 177 178 while ( list.hasNext() ) 179 { 180 DN chopAfter = ( DN ) list.next(); 181 182 if ( NamespaceTools.isDescendant( chopAfter, baseRelativeRdn ) && !chopAfter.equals( baseRelativeRdn ) ) 183 { 184 return false; 185 } 186 } 187 188 /* 189 * The last remaining step is to check and see if the refinement filter 190 * selects the entry candidate based on objectClass attribute values. 191 * To do this we invoke the refinement evaluator members evaluate() method. 192 */ 193 if ( subtree.getRefinement() != null ) 194 { 195 return evaluator.evaluate( subtree.getRefinement(), dnEntryDn.getNormName(), entry ); 196 } 197 198 /* 199 * If nothing has rejected the candidate entry and there is no refinement 200 * filter then the entry is included in the collection represented by the 201 * subtree specification so we return true. 202 */ 203 return true; 204 } 205 }