SubtreeSpecification.java revision a41662c1136b2bb4a4198df89e0e87d2be3ef099
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* An RFC 3672 subtree specification.
* <p>
* This implementation extends RFC 3672 by supporting search filters
* for specification filters. More specifically, the
* {@code Refinement} product has been extended as follows:
*
* <pre>
* Refinement = item / and / or / not / Filter
*
* Filter = dquote *SafeUTF8Character dquote
* </pre>
*
* @see <a href="http://tools.ietf.org/html/rfc3672">RFC 3672 -
* Subentries in the Lightweight Directory Access Protocol (LDAP)
* </a>
*/
mayInstantiate = false,
mayExtend = true,
mayInvoke = false)
public final class SubtreeSpecification
{
/**
* RFC 3672 subtree specification AND refinement. This type of
* refinement filters entries based on all of the underlying
* refinements being <code>true</code>.
*/
public static final class AndRefinement extends Refinement
{
/** The set of refinements which must all be true. */
/**
* Create a new AND refinement.
*
* @param refinementSet
* The set of refinements which must all be
* <code>true</code>.
*/
{
this.refinementSet = refinementSet;
}
/** {@inheritDoc} */
{
if (this == obj)
{
return true;
}
if (obj instanceof AndRefinement)
{
}
return false;
}
/** {@inheritDoc} */
public int hashCode()
{
return refinementSet.hashCode();
}
/** {@inheritDoc} */
{
{
{
return false;
}
}
// All sub-refinements matched.
return true;
}
/** {@inheritDoc} */
{
switch (refinementSet.size())
{
case 0:
// Do nothing.
break;
case 1:
break;
default:
.iterator();
{
}
break;
}
return builder;
}
}
/**
* A refinement which uses a search filter.
*/
public static final class FilterRefinement extends Refinement
{
/** The search filter. */
private final SearchFilter filter;
/**
* Create a new filter refinement.
*
* @param filter
* The search filter.
*/
{
}
/** {@inheritDoc} */
{
if (this == obj)
{
return true;
}
if (obj instanceof FilterRefinement)
{
}
return false;
}
/** {@inheritDoc} */
public int hashCode()
{
}
/** {@inheritDoc} */
{
try
{
}
catch (final DirectoryException e)
{
// TODO: need to decide what to do with the exception here.
// It's probably safe to ignore, but we could log it perhaps.
return false;
}
}
/** {@inheritDoc} */
{
return builder;
}
}
/**
* RFC 3672 subtree specification Item refinement. This type of
* refinement filters entries based on the presence of a specified
* object class.
*/
public static final class ItemRefinement extends Refinement
{
/** The item's object class. */
private final String objectClass;
/** The item's normalized object class. */
private final String normalizedObjectClass;
/**
* Create a new item refinement.
*
* @param objectClass
* The item's object class.
*/
{
this.objectClass = objectClass;
this.normalizedObjectClass = StaticUtils
}
/** {@inheritDoc} */
{
if (this == obj)
{
return true;
}
if (obj instanceof ItemRefinement)
{
return normalizedObjectClass
}
return false;
}
/** {@inheritDoc} */
public int hashCode()
{
return normalizedObjectClass.hashCode();
}
/** {@inheritDoc} */
{
}
/** {@inheritDoc} */
{
return builder;
}
}
/**
* RFC 3672 subtree specification NOT refinement. This type of
* refinement filters entries based on the underlying refinement
* being <code>false</code>
* .
*/
public static final class NotRefinement extends Refinement
{
/** The inverted refinement. */
private final Refinement refinement;
/**
* Create a new NOT refinement.
*
* @param refinement
* The refinement which must be <code>false</code>.
*/
{
this.refinement = refinement;
}
/** {@inheritDoc} */
{
if (this == obj)
{
return true;
}
if (obj instanceof NotRefinement)
{
}
return false;
}
/** {@inheritDoc} */
public int hashCode()
{
return refinement.hashCode();
}
/** {@inheritDoc} */
{
}
/** {@inheritDoc} */
{
}
}
/**
* RFC 3672 subtree specification OR refinement. This type of
* refinement filters entries based on at least one of the
* underlying refinements being <code>true</code>.
*/
public static final class OrRefinement extends Refinement
{
/** The set of refinements of which at least one must be true. */
/**
* Create a new OR refinement.
*
* @param refinementSet
* The set of refinements of which at least one must be
* <code>true</code>.
*/
{
this.refinementSet = refinementSet;
}
/** {@inheritDoc} */
{
if (this == obj)
{
return true;
}
if (obj instanceof AndRefinement)
{
}
return false;
}
/** {@inheritDoc} */
public int hashCode()
{
return refinementSet.hashCode();
}
/** {@inheritDoc} */
{
{
{
return true;
}
}
// No sub-refinements matched.
return false;
}
/** {@inheritDoc} */
{
switch (refinementSet.size())
{
case 0:
// Do nothing.
break;
case 1:
break;
default:
.iterator();
{
}
break;
}
return builder;
}
}
/**
* Abstract interface for RFC3672 specification filter refinements.
*/
public static abstract class Refinement
{
/**
* Create a new RFC3672 specification filter refinement.
*/
protected Refinement()
{
// No implementation required.
}
/** {@inheritDoc} */
/** {@inheritDoc} */
public abstract int hashCode();
/**
* Check if the refinement matches the given entry.
*
* @param entry
* The filterable entry.
* @return Returns <code>true</code> if the entry matches the
* refinement, or <code>false</code> otherwise.
*/
/** {@inheritDoc} */
{
}
/**
* Append the string representation of the refinement to the
* provided strin builder.
*
* @param builder
* The string builder.
* @return The string builder.
*/
}
/**
* Internal utility class which can be used by sub-classes to help
* parse string representations.
*/
protected static final class Parser
{
/** Text scanner used to parse the string value. */
/** Pattern used to detect left braces. */
/** Pattern used to parse left braces. */
/** Pattern used to detect right braces. */
/** Pattern used to parse right braces. */
/** Pattern used to detect comma separators. */
/** Pattern used to parse comma separators. */
/** Pattern used to detect colon separators. */
/** Pattern used to parse colon separators. */
/** Pattern used to detect integer values. */
/** Pattern used to parse integer values. */
/** Pattern used to detect name values. */
/** Pattern used to parse name values. */
/** Pattern used to detect RFC3641 string values. */
/** Pattern used to parse RFC3641 string values. */
.compile("\"([^\"]|(\"\"))*\"");
/**
* Create a new parser for a subtree specification string value.
*
* @param value
* The subtree specification string value.
*/
{
}
/**
* Determine if there are remaining tokens.
*
* @return <code>true</code> if and only if there are remaining
* tokens.
*/
public boolean hasNext()
{
}
/**
* Determine if the next token is a right-brace character.
*
* @return <code>true</code> if and only if the next token is a
* valid right brace character.
*/
public boolean hasNextRightBrace()
{
}
/**
* Scans the next token of the input as a non-negative
* <code>int</code> value.
*
* @return The name value scanned from the input.
* @throws InputMismatchException
* If the next token is not a valid non-negative integer
* string.
* @throws NoSuchElementException
* If input is exhausted.
*/
public int nextInt() throws InputMismatchException,
{
}
/**
* Scans the next token of the input as a key value.
*
* @return The lower-case key value scanned from the input.
* @throws InputMismatchException
* If the next token is not a valid key string.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
}
/**
* Scans the next token of the input as a name value.
* <p>
* A name is any string containing only alpha-numeric characters
* or hyphens, semi-colons, or underscores.
*
* @return The name value scanned from the input.
* @throws InputMismatchException
* If the next token is not a valid name string.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
}
/**
* Scans the next tokens of the input as a set of specific
* exclusions encoded according to the SpecificExclusion
* production in RFC 3672.
*
* @param chopBefore
* The set of chop before local names.
* @param chopAfter
* The set of chop after local names.
* @throws InputMismatchException
* If the common component did not have a valid syntax.
* @throws NoSuchElementException
* If input is exhausted.
* @throws DirectoryException
* If an error occurred when attempting to parse a
* DN value.
*/
{
// Skip leading open-brace.
// Parse each chop DN in the sequence.
boolean isFirstValue = true;
while (true)
{
// Make sure that there is a closing brace.
if (hasNextRightBrace())
{
break;
}
// Make sure that there is a comma separator if this is not
// the first element.
if (!isFirstValue)
{
}
else
{
isFirstValue = false;
}
// Parse each chop specification which is of the form
// <type>:<value>.
skipColon();
{
}
{
}
else
{
}
}
}
/**
* Scans the next token of the input as a string quoted according
* to the StringValue production in RFC 3641.
* <p>
* The return string has its outer double quotes removed and any
* escape inner double quotes unescaped.
*
* @return The string value scanned from the input.
* @throws InputMismatchException
* If the next token is not a valid string.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
}
/**
* Skip a colon separator.
*
* @throws InputMismatchException
* If the next token is not a colon separator character.
* @throws NoSuchElementException
* If input is exhausted.
*/
public void skipColon() throws InputMismatchException,
{
}
/**
* Skip a left-brace character.
*
* @throws InputMismatchException
* If the next token is not a left-brace character.
* @throws NoSuchElementException
* If input is exhausted.
*/
public void skipLeftBrace() throws InputMismatchException,
{
}
/**
* Skip a right-brace character.
*
* @throws InputMismatchException
* If the next token is not a right-brace character.
* @throws NoSuchElementException
* If input is exhausted.
*/
public void skipRightBrace() throws InputMismatchException,
{
}
/**
* Skip a comma separator.
*
* @throws InputMismatchException
* If the next token is not a comma separator character.
* @throws NoSuchElementException
* If input is exhausted.
*/
public void skipSeparator() throws InputMismatchException,
{
}
/**
* Parse the next token from the string using the specified
* patterns.
*
* @param head
* The pattern used to determine if the next token is a
* possible match.
* @param content
* The pattern used to parse the token content.
* @return The next token matching the <code>content</code>
* pattern.
* @throws InputMismatchException
* If the next token does not match the
* <code>content</code> pattern.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
{
}
{
}
if (s == null)
{
}
return s;
}
}
/**
* Parses the string argument as an RFC3672 subtree specification.
*
* @param rootDN
* The DN of the subtree specification's base entry.
* @param s
* The string to be parsed.
* @return The RFC3672 subtree specification represented by the
* string argument.
* @throws DirectoryException
* If the string does not contain a parsable relative
* subtree specification.
*/
final String s) throws DirectoryException
{
// Default values.
int minimum = -1;
int maximum = -1;
// Value must have an opening left brace.
boolean isValid = true;
try
{
// Parse each element of the value sequence.
boolean isFirst = true;
while (true)
{
if (parser.hasNextRightBrace())
{
// Make sure that there is a closing brace and no trailing text.
{
}
break;
}
// Make sure that there is a comma separator if this is not
// the first element.
if (!isFirst)
{
}
else
{
isFirst = false;
}
{
if (relativeBaseDN != null)
{
// Relative base DN specified more than once.
throw new InputMismatchException();
}
}
{
if (minimum != -1)
{
// Minimum specified more than once.
throw new InputMismatchException();
}
}
{
if (maximum != -1)
{
// Maximum specified more than once.
throw new InputMismatchException();
}
}
{
if (refinement != null)
{
// Refinements specified more than once.
throw new InputMismatchException();
}
// First try normal search filter before RFC3672
// refinements.
try
{
}
catch (final InputMismatchException e)
{
}
}
{
{
// Specific exclusions specified more than once.
throw new InputMismatchException();
}
}
else
{
throw new InputMismatchException();
}
}
// Make default minimum value is 0.
if (minimum < 0)
{
minimum = 0;
}
// Check that the maximum, if specified, is gte the minimum.
{
isValid = false;
}
}
catch (final NoSuchElementException e)
{
isValid = false;
}
if (isValid)
{
}
else
{
final LocalizableMessage message =
throw new DirectoryException(
}
}
/**
* Parse a single refinement.
*
* @param parser
* The active subtree specification parser.
* @return The parsed refinement.
* @throws InputMismatchException
* If the common component did not have a valid syntax.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
// Get the type of refinement.
// Skip the colon separator.
{
}
{
return new NotRefinement(refinement);
}
{
return new AndRefinement(refinements);
}
{
return new OrRefinement(refinements);
}
else
{
// Unknown refinement type.
throw new InputMismatchException();
}
}
/**
* Parse a list of refinements.
*
* @param parser
* The active subtree specification parser.
* @return The parsed refinement list.
* @throws InputMismatchException
* If the common component did not have a valid syntax.
* @throws NoSuchElementException
* If input is exhausted.
*/
{
// Skip leading open-brace.
// Parse each chop DN in the sequence.
boolean isFirstValue = true;
while (true)
{
// Make sure that there is a closing brace.
if (parser.hasNextRightBrace())
{
break;
}
// Make sure that there is a comma separator if this is not
// the first element.
if (!isFirstValue)
{
}
else
{
isFirstValue = false;
}
// Parse each sub-refinement.
}
return refinements;
}
/** The absolute base of the subtree. */
/** Optional minimum depth (<=0 means unlimited). */
private final int minimumDepth;
/** Optional maximum depth (<0 means unlimited). */
private final int maximumDepth;
/** Optional set of chop before absolute DNs (mapping to their local-names). */
/** Optional set of chop after absolute DNs (mapping to their local-names). */
/** The root DN. */
/** The optional relative base DN. */
private final DN relativeBaseDN;
/** The optional specification filter refinements. */
private final Refinement refinements;
/**
* Create a new RFC3672 subtree specification.
*
* @param rootDN
* The root DN of the subtree.
* @param relativeBaseDN
* The relative base DN (or {@code null} if not
* specified).
* @param minimumDepth
* The minimum depth (less than or equal to 0 means unlimited).
* @param maximumDepth
* The maximum depth (less than 0 means unlimited).
* @param chopBefore
* The set of chop before local names (relative to the
* relative base DN), or {@code null} if there are
* none.
* @param chopAfter
* The set of chop after local names (relative to the
* relative base DN), or {@code null} if there are
* none.
* @param refinements
* The optional specification filter refinements, or
* {@code null} if there are none.
*/
{
this.minimumDepth = minimumDepth;
this.maximumDepth = maximumDepth;
{
// Calculate the absolute DNs.
{
}
}
else
{
// No chop before specifications.
}
{
// Calculate the absolute DNs.
{
}
}
else
{
// No chop after specifications.
}
this.relativeBaseDN = relativeBaseDN;
this.refinements = refinements;
}
/**
* Indicates whether the provided object is logically equal to this
* subtree specification object.
*
* @param obj
* The object for which to make the determination.
* @return {@code true} if the provided object is logically equal
* to this subtree specification object, or {@code false}
* if not.
*/
{
if (this == obj)
{
return true;
}
if (obj instanceof SubtreeSpecification)
{
if (!commonComponentsEquals(other))
{
return false;
}
{
return false;
}
if (refinements != null)
{
}
else
{
}
}
return false;
}
/**
* Get the absolute base DN of the subtree specification.
*
* @return Returns the absolute base DN of the subtree
* specification.
*/
{
return baseDN;
}
/**
* Get the set of chop after relative DNs.
*
* @return Returns the set of chop after relative DNs.
*/
{
}
/**
* Get the set of chop before relative DNs.
*
* @return Returns the set of chop before relative DNs.
*/
{
return chopBefore.values();
}
/**
* Get the maximum depth of the subtree specification.
*
* @return Returns the maximum depth (less than 0 indicates unlimited depth).
*/
public int getMaximumDepth()
{
return maximumDepth;
}
/**
* Get the minimum depth of the subtree specification.
*
* @return Returns the minimum depth (<=0 indicates unlimited
* depth).
*/
public int getMinimumDepth()
{
return minimumDepth;
}
/**
* Get the specification filter refinements.
*
* @return Returns the specification filter refinements, or
* <code>null</code> if none were specified.
*/
public Refinement getRefinements()
{
return refinements;
}
/**
* Get the relative base DN.
*
* @return Returns the relative base DN or <code>null</code> if
* none was specified.
*/
public DN getRelativeBaseDN()
{
return relativeBaseDN;
}
/**
* Get the root DN.
*
* @return Returns the root DN.
*/
{
return rootDN;
}
/**
* Retrieves the hash code for this subtree specification object.
*
* @return The hash code for this subtree specification object.
*/
public int hashCode()
{
int hash = commonComponentsHashCode();
if (refinements != null)
{
}
return hash;
}
/**
* Determine if the specified DN is within the scope of the subtree
* specification.
*
* @param dn
* The distinguished name.
* @return Returns <code>true</code> if the DN is within the scope
* of the subtree specification, or <code>false</code>
* otherwise.
*/
{
{
return false;
}
// Check minimum and maximum depths.
if (minimumDepth > 0)
{
{
return false;
}
}
if (maximumDepth >= 0)
{
{
return false;
}
}
// Check exclusions.
{
{
return false;
}
}
{
{
return false;
}
}
// Everything seemed to match.
return true;
}
/**
* Determine if an entry is within the scope of the subtree
* specification.
*
* @param entry
* The entry.
* @return {@code true} if the entry is within the scope of the
* subtree specification, or {@code false} if not.
*/
{
}
/**
* Retrieves a string representation of this subtree specification
* object.
*
* @return A string representation of this subtree specification
* object.
*/
{
}
/**
* Append the string representation of the subtree specification to
* the provided string builder.
*
* @param builder
* The string builder.
* @return The string builder.
*/
{
boolean isFirstElement = true;
// Output the optional base DN.
{
isFirstElement = false;
}
// Output the optional specific exclusions.
{
if (!isFirstElement)
{
}
else
{
isFirstElement = false;
}
boolean isFirst = true;
{
if (!isFirst)
{
}
else
{
isFirst = false;
}
}
{
if (!isFirst)
{
}
else
{
isFirst = false;
}
}
}
// Output the optional minimum depth.
if (getMinimumDepth() > 0)
{
if (!isFirstElement)
{
}
else
{
isFirstElement = false;
}
}
// Output the optional maximum depth.
if (getMaximumDepth() >= 0)
{
if (!isFirstElement)
{
}
else
{
isFirstElement = false;
}
}
// Output the optional refinements.
if (refinements != null)
{
if (!isFirstElement)
{
}
else
{
isFirstElement = false;
}
}
return builder;
}
/**
* Determine if the common components of this subtree specification
* are equal to the common components of another subtree
* specification.
*
* @param other
* The other subtree specification.
* @return Returns <code>true</code> if they are equal.
*/
private boolean commonComponentsEquals(
final SubtreeSpecification other)
{
if (this == other)
{
return true;
}
}
/**
* Get a hash code of the subtree specification's common components.
*
* @return The computed hash code.
*/
private int commonComponentsHashCode()
{
return hash;
}
}