blob: 2f0c1585d353962932053ddf000a494053e7539d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.core.api.subtree;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification;
import org.apache.directory.server.core.api.event.Evaluator;
import org.apache.directory.server.core.api.event.ExpressionEvaluator;
/**
* An evaluator used to determine if an entry is included in the collection
* represented by a subtree specification.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class SubtreeEvaluator
{
/** A refinement filter evaluator */
private final Evaluator evaluator;
/**
* Creates a subtreeSpecification evaluatior which can be used to determine
* if an entry is included within the collection of a subtree.
*
* @param schemaManager The server schemaManager
*/
public SubtreeEvaluator( SchemaManager schemaManager )
{
evaluator = new ExpressionEvaluator( schemaManager );
}
/**
* Determines if an entry is selected by a subtree specification.
*
* @param subtree the subtree specification
* @param apDn the distinguished name of the administrative point containing the subentry
* @param entryDn the distinguished name of the candidate entry
* @return true if the entry is selected by the specification, false if it is not
* @throws LdapException if errors are encountered while evaluating selection
*/
public boolean evaluate( SubtreeSpecification subtree, Dn apDn, Dn entryDn, Entry entry )
throws LdapException
{
/* =====================================================================
* NOTE: Regarding the overall approach, we try to narrow down the
* possibilities by slowly pruning relative names off of the entryDn.
* For example we check first if the entry is a descendant of the AP.
* If so we use the relative name thereafter to calculate if it is
* a descendant of the base. This means shorter names to compare and
* less work to do while we continue to deduce inclusion by the subtree
* specification.
* =====================================================================
*/
// First construct the subtree base, which is the concatenation of the
// AP Dn and the subentry base
Dn subentryBaseDn = apDn;
subentryBaseDn = subentryBaseDn.add( subtree.getBase() );
if ( !entryDn.isDescendantOf( subentryBaseDn ) )
{
// The entry Dn is not part of the subtree specification, get out
return false;
}
/*
* Evaluate based on minimum and maximum chop values. Here we simply
* need to compare the distances respectively with the size of the
* baseRelativeRdn. For the max distance entries with a baseRelativeRdn
* size greater than the max distance are rejected. For the min distance
* entries with a baseRelativeRdn size less than the minimum distance
* are rejected.
*/
int entryRelativeDnSize = entryDn.size() - subentryBaseDn.size();
if ( ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX ) &&
( entryRelativeDnSize > subtree.getMaxBaseDistance() ) )
{
return false;
}
if ( ( subtree.getMinBaseDistance() > 0 ) && ( entryRelativeDnSize < subtree.getMinBaseDistance() ) )
{
return false;
}
/*
* For specific exclusions we must iterate through the set and check
* if the baseRelativeRdn is a descendant of the exclusion. The
* isDescendant() function will return true if the compared names
* are equal so for chopAfter exclusions we must check for equality
* as well and reject if the relative names are equal.
*/
// Now, get the entry's relative part
if ( ( subtree.getChopBeforeExclusions().size() != 0 ) ||
( subtree.getChopAfterExclusions().size() != 0 ) )
{
Dn entryRelativeDn = entryDn.getDescendantOf( apDn ).getDescendantOf( subtree.getBase() );
for ( Dn chopBeforeDn : subtree.getChopBeforeExclusions() )
{
if ( entryRelativeDn.isDescendantOf( chopBeforeDn ) )
{
return false;
}
}
for ( Dn chopAfterDn : subtree.getChopAfterExclusions() )
{
if ( entryRelativeDn.isDescendantOf( chopAfterDn ) && !chopAfterDn.equals( entryRelativeDn ) )
{
return false;
}
}
}
/*
* The last remaining step is to check and see if the refinement filter
* selects the entry candidate based on objectClass attribute values.
* To do this we invoke the refinement evaluator members evaluate() method.
*/
if ( subtree.getRefinement() != null )
{
return evaluator.evaluate( subtree.getRefinement(), entryDn, entry );
}
/*
* If nothing has rejected the candidate entry and there is no refinement
* filter then the entry is included in the collection represented by the
* subtree specification so we return true.
*/
return true;
}
}