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.core.api.subtree; 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.name.Dn; 026import org.apache.directory.api.ldap.model.schema.SchemaManager; 027import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification; 028import org.apache.directory.server.core.api.event.Evaluator; 029import org.apache.directory.server.core.api.event.ExpressionEvaluator; 030 031 032/** 033 * An evaluator used to determine if an entry is included in the collection 034 * represented by a subtree specification. 035 * 036 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 037 */ 038public class SubtreeEvaluator 039{ 040 /** A refinement filter evaluator */ 041 private final Evaluator evaluator; 042 043 044 /** 045 * Creates a subtreeSpecification evaluatior which can be used to determine 046 * if an entry is included within the collection of a subtree. 047 * 048 * @param schemaManager The server schemaManager 049 */ 050 public SubtreeEvaluator( SchemaManager schemaManager ) 051 { 052 evaluator = new ExpressionEvaluator( schemaManager ); 053 } 054 055 056 /** 057 * Determines if an entry is selected by a subtree specification. 058 * 059 * @param subtree the subtree specification 060 * @param apDn the distinguished name of the administrative point containing the subentry 061 * @param entryDn the distinguished name of the candidate entry 062 * @param entry The entry to evaluate 063 * @return true if the entry is selected by the specification, false if it is not 064 * @throws LdapException if errors are encountered while evaluating selection 065 */ 066 public boolean evaluate( SubtreeSpecification subtree, Dn apDn, Dn entryDn, Entry entry ) 067 throws LdapException 068 { 069 /* ===================================================================== 070 * NOTE: Regarding the overall approach, we try to narrow down the 071 * possibilities by slowly pruning relative names off of the entryDn. 072 * For example we check first if the entry is a descendant of the AP. 073 * If so we use the relative name thereafter to calculate if it is 074 * a descendant of the base. This means shorter names to compare and 075 * less work to do while we continue to deduce inclusion by the subtree 076 * specification. 077 * ===================================================================== 078 */ 079 // First construct the subtree base, which is the concatenation of the 080 // AP Dn and the subentry base 081 Dn subentryBaseDn = apDn; 082 subentryBaseDn = subentryBaseDn.add( subtree.getBase() ); 083 084 if ( !entryDn.isDescendantOf( subentryBaseDn ) ) 085 { 086 // The entry Dn is not part of the subtree specification, get out 087 return false; 088 } 089 090 /* 091 * Evaluate based on minimum and maximum chop values. Here we simply 092 * need to compare the distances respectively with the size of the 093 * baseRelativeRdn. For the max distance entries with a baseRelativeRdn 094 * size greater than the max distance are rejected. For the min distance 095 * entries with a baseRelativeRdn size less than the minimum distance 096 * are rejected. 097 */ 098 int entryRelativeDnSize = entryDn.size() - subentryBaseDn.size(); 099 100 if ( ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX ) 101 && ( entryRelativeDnSize > subtree.getMaxBaseDistance() ) ) 102 { 103 return false; 104 } 105 106 if ( ( subtree.getMinBaseDistance() > 0 ) && ( entryRelativeDnSize < subtree.getMinBaseDistance() ) ) 107 { 108 return false; 109 } 110 111 /* 112 * For specific exclusions we must iterate through the set and check 113 * if the baseRelativeRdn is a descendant of the exclusion. The 114 * isDescendant() function will return true if the compared names 115 * are equal so for chopAfter exclusions we must check for equality 116 * as well and reject if the relative names are equal. 117 */ 118 // Now, get the entry's relative part 119 120 if ( !subtree.getChopBeforeExclusions().isEmpty() || !subtree.getChopAfterExclusions().isEmpty() ) 121 { 122 Dn entryRelativeDn = entryDn.getDescendantOf( apDn ).getDescendantOf( subtree.getBase() ); 123 124 for ( Dn chopBeforeDn : subtree.getChopBeforeExclusions() ) 125 { 126 if ( entryRelativeDn.isDescendantOf( chopBeforeDn ) ) 127 { 128 return false; 129 } 130 } 131 132 for ( Dn chopAfterDn : subtree.getChopAfterExclusions() ) 133 { 134 if ( entryRelativeDn.isDescendantOf( chopAfterDn ) && !chopAfterDn.equals( entryRelativeDn ) ) 135 { 136 return false; 137 } 138 } 139 } 140 141 /* 142 * The last remaining step is to check and see if the refinement filter 143 * selects the entry candidate based on objectClass attribute values. 144 * To do this we invoke the refinement evaluator members evaluate() method. 145 */ 146 if ( subtree.getRefinement() != null ) 147 { 148 return evaluator.evaluate( subtree.getRefinement(), entryDn, entry ); 149 } 150 151 /* 152 * If nothing has rejected the candidate entry and there is no refinement 153 * filter then the entry is included in the collection represented by the 154 * subtree specification so we return true. 155 */ 156 return true; 157 } 158}