RDN.java revision 998747bfaaa3c6b28bbfaf0e282e6c0ccbf46bc0
/*
* 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 2013-2015 ForgeRock AS
*/
/**
* This class defines a data structure for storing and interacting
* with the relative distinguished names associated with entries in
* the Directory Server.
*/
mayInstantiate=true,
mayExtend=false,
mayInvoke=true)
public final class RDN
implements Comparable<RDN>
{
/** The set of attribute types for the elements in this RDN. */
private AttributeType[] attributeTypes;
/** The set of values for the elements in this RDN. */
private ByteString[] attributeValues;
/** The number of values for this RDN. */
private int numValues;
/** The string representation of the normalized form of this RDN. */
private String normalizedRDN;
/** The string representation of this RDN. */
/** The set of user-provided names for the attributes in this RDN. */
private String[] attributeNames;
/**
* Creates a new RDN with the provided information.
*
* @param attributeType The attribute type for this RDN. It must
* not be {@code null}.
* @param attributeValue The value for this RDN. It must not be
* {@code null}.
*/
{
numValues = 1;
}
/**
* Creates a new RDN with the provided information.
*
* @param attributeType The attribute type for this RDN. It must
* not be {@code null}.
* @param attributeName The user-provided name for this RDN. It
* must not be {@code null}.
* @param attributeValue The value for this RDN. It must not be
* {@code null}.
*/
{
numValues = 1;
}
/**
* Creates a new RDN with the provided information. The number of
* type, name, and value elements must be nonzero and equal.
*
* @param attributeTypes The set of attribute types for this RDN.
* It must not be empty or {@code null}.
* @param attributeNames The set of user-provided names for this
* RDN. It must have the same number of
* elements as the {@code attributeTypes}
* argument.
* @param attributeValues The set of values for this RDN. It must
* have the same number of elements as the
* {@code attributeTypes} argument.
*/
{
}
/**
* Creates a new RDN with the provided information. The number of
* type, name, and value elements must be nonzero and equal.
*
* @param attributeTypes The set of attribute types for this RDN.
* It must not be empty or {@code null}.
* @param attributeNames The set of user-provided names for this
* RDN. It must have the same number of
* elements as the {@code attributeTypes}
* argument.
* @param attributeValues The set of values for this RDN. It must
* have the same number of elements as the
* {@code attributeTypes} argument.
*/
{
this.attributeTypes = attributeTypes;
this.attributeNames = attributeNames;
this.attributeValues = attributeValues;
}
/**
* Creates a new RDN with the provided information.
*
* @param attributeType The attribute type for this RDN. It must
* not be {@code null}.
* @param attributeValue The value for this RDN. It must not be
* {@code null}.
*
* @return The RDN created with the provided information.
*/
{
}
/**
* Retrieves the number of attribute-value pairs contained in this
* RDN.
*
* @return The number of attribute-value pairs contained in this
* RDN.
*/
public int getNumValues()
{
return numValues;
}
/**
* Indicates whether this RDN includes the specified attribute type.
*
* @param attributeType The attribute type for which to make the
* determination.
*
* @return <CODE>true</CODE> if the RDN includes the specified
* attribute type, or <CODE>false</CODE> if not.
*/
{
for (AttributeType t : attributeTypes)
{
if (t.equals(attributeType))
{
return true;
}
}
return false;
}
/**
* Indicates whether this RDN includes the specified attribute type.
*
* @param lowerName The name or OID for the attribute type for
* which to make the determination, formatted in
* all lowercase characters.
*
* @return <CODE>true</CODE> if the RDN includes the specified
* attribute type, or <CODE>false</CODE> if not.
*/
{
for (AttributeType t : attributeTypes)
{
if (t.hasNameOrOID(lowerName))
{
return true;
}
}
for (String s : attributeNames)
{
if (s.equalsIgnoreCase(lowerName))
{
return true;
}
}
return false;
}
/**
* Retrieves the attribute type at the specified position in the set
* of attribute types for this RDN.
*
* @param pos The position of the attribute type to retrieve.
*
* @return The attribute type at the specified position in the set
* of attribute types for this RDN.
*/
{
return attributeTypes[pos];
}
/**
* Retrieves the name for the attribute type at the specified
* position in the set of attribute types for this RDN.
*
* @param pos The position of the attribute type for which to
* retrieve the name.
*
* @return The name for the attribute type at the specified
* position in the set of attribute types for this RDN.
*/
{
return attributeNames[pos];
}
/**
* Retrieves the attribute value that is associated with the
* specified attribute type.
*
* @param attributeType The attribute type for which to retrieve
* the corresponding value.
*
* @return The value for the requested attribute type, or
* <CODE>null</CODE> if the specified attribute type is not
* present in the RDN.
*/
{
for (int i=0; i < numValues; i++)
{
{
return attributeValues[i];
}
}
return null;
}
/**
* Retrieves the value for the attribute type at the specified
* position in the set of attribute types for this RDN.
*
* @param pos The position of the attribute type for which to
* retrieve the value.
*
* @return The value for the attribute type at the specified
* position in the set of attribute types for this RDN.
*/
{
return attributeValues[pos];
}
/**
* Indicates whether this RDN is multivalued.
*
* @return <CODE>true</CODE> if this RDN is multivalued, or
* <CODE>false</CODE> if not.
*/
public boolean isMultiValued()
{
return numValues > 1;
}
/**
* Indicates whether this RDN contains the specified type-value
* pair.
*
* @param type The attribute type for which to make the
* determination.
* @param value The value for which to make the determination.
*
* @return <CODE>true</CODE> if this RDN contains the specified
* attribute value, or <CODE>false</CODE> if not.
*/
{
for (int i=0; i < numValues; i++)
{
{
return true;
}
}
return false;
}
/**
* Adds the provided type-value pair from this RDN. Note that this
* is intended only for internal use when constructing DN values.
*
* @param type The attribute type of the pair to add.
* @param name The user-provided name of the pair to add.
* @param value The attribute value of the pair to add.
*
* @return <CODE>true</CODE> if the type-value pair was added to
* this RDN, or <CODE>false</CODE> if it was not (e.g., it
* was already present).
*/
{
for (int i=0; i < numValues; i++)
{
{
return false;
}
}
numValues++;
return true;
}
/**
* Retrieves a version of the provided value in a form that is
* properly escaped for use in a DN or RDN.
*
* @param valueBS The value to be represented in a DN-safe form.
*
* @return A version of the provided value in a form that is
* properly escaped for use in a DN or RDN.
*/
return "";
}
// Only copy the string value if required.
boolean needsEscaping = false;
if ((c == ' ') || (c == '#')) {
needsEscaping = true;
break needsEscaping;
}
needsEscaping = true;
break needsEscaping;
}
for (int i = 0; i < length; i++) {
if (c < ' ') {
needsEscaping = true;
break needsEscaping;
} else {
switch (c) {
case ',':
case '+':
case '"':
case '\\':
case '<':
case '>':
case ';':
needsEscaping = true;
break needsEscaping;
}
}
}
}
if (!needsEscaping) {
return value;
}
// We need to copy and escape the string (allow for at least one
// escaped character).
// If the lead character is a space or a # it must be escaped.
int start = 0;
if ((c == ' ') || (c == '#')) {
start = 1;
}
// Escape remaining characters as necessary.
if (c < ' ') {
}
} else {
case ',':
case '+':
case '"':
case '\\':
case '<':
case '>':
case ';':
break;
default:
break;
}
}
}
// If the last character is a space it must be escaped.
}
}
/**
* Decodes the provided string as an RDN.
*
* @param rdnString
* The string to decode as an RDN.
* @return The decoded RDN.
* @throws DirectoryException
* If a problem occurs while trying to decode the provided
* string as a RDN.
*/
{
// A null or empty RDN is not acceptable.
{
}
if (length == 0)
{
}
// Iterate through the RDN string. The first thing to do is to
// get rid of any leading spaces.
int pos = 0;
while (c == ' ')
{
pos++;
{
// This means that the RDN was completely comprised of spaces,
// which is not valid.
}
else
{
}
}
// We know that it's not an empty RDN, so we can do the real processing.
// First, parse the attribute name. We can borrow the DN code for this.
// Make sure that we're not at the end of the RDN string because
// that would be invalid.
{
}
// Skip over any spaces between the attribute name and its value.
while (c == ' ')
{
pos++;
{
// This means that we hit the end of the string before finding a '='.
// This is illegal because there is no attribute-value separator.
}
else
{
}
}
// The next character must be an equal sign. If it is not, then
// that's an error.
if (c == '=')
{
pos++;
}
else
{
}
// Skip over any spaces between the equal sign and the value.
{
pos++;
}
// If we are at the end of the RDN string, then that must mean
// that the attribute value was empty.
{
}
// Parse the value for this RDN component. This can be done using
// the DN code.
// Create the new RDN with the provided information. However,
// don't return it yet because this could be a multi-valued RDN.
{
// This must be an attribute type that we don't know about.
// In that case, we'll create a new attribute using the default
// syntax. If this is a problem, it will be caught later either
// by not finding the target entry or by not allowing the entry
// to be added.
}
// Skip over any spaces that might be after the attribute value.
{
pos++;
}
// Most likely, this is the end of the RDN. If so, then return it.
{
return rdn;
}
// If the next character is a comma or semicolon, then that is not
// allowed. It would be legal for a DN but not an RDN.
if ((c == ',') || (c == ';'))
{
}
// If the next character is anything but a plus sign, then it is illegal.
if (c != '+')
{
}
// If we have gotten here, then it is a multi-valued RDN. Parse
// that we've already created.
while (true)
{
// Skip over the plus sign and any spaces that may follow it
// before the next attribute name.
pos++;
{
pos++;
}
// Parse the attribute name.
attributeName = new StringBuilder();
// Make sure we're not at the end of the RDN.
{
}
// Skip over any spaces between the attribute name and the equal sign.
while (c == ' ')
{
pos++;
{
// This means that we hit the end of the string before finding a '='.
// This is illegal because there is no attribute-value separator.
}
else
{
}
}
// The next character must be an equal sign.
if (c == '=')
{
pos++;
}
else
{
}
// Skip over any spaces after the equal sign.
{
pos++;
}
// If we are at the end of the RDN string, then that must mean
// that the attribute value was empty. This will probably never
// happen in a real-world environment, but technically isn't
// illegal. If it does happen, then go ahead and return the RDN.
{
{
// This must be an attribute type that we don't know about.
// In that case, we'll create a new attribute using the
// default syntax. If this is a problem, it will be caught
// later either by not finding the target entry or by not
// allowing the entry to be added.
}
return rdn;
}
// Parse the value for this RDN component.
parsedValue.clear();
{
// This must be an attribute type that we don't know about.
// In that case, we'll create a new attribute using the
// default syntax. If this is a problem, it will be caught
// later either by not finding the target entry or by not
// allowing the entry to be added.
}
// Skip over any spaces that might be after the attribute value.
{
pos++;
}
// If we're at the end of the string, then return the RDN.
{
return rdn;
}
// If the next character is a comma or semicolon, then that is
// not allowed. It would be legal for a DN but not an RDN.
if ((c == ',') || (c == ';'))
{
}
// If the next character is anything but a plus sign, then it is illegal.
if (c != '+')
{
}
}
}
/**
* Creates a duplicate of this RDN that can be modified without
* impacting this RDN.
*
* @return A duplicate of this RDN that can be modified without
* impacting this RDN.
*/
{
}
/**
* Indicates whether the provided object is equal to this RDN. It
* will only be considered equal if it is an RDN object that
* contains the same number of elements in the same order with the
* same types and normalized values.
*
* @param o The object for which to make the determination.
*
* @return <CODE>true</CODE> if it is determined that the provided
* object is equal to this RDN, or <CODE>false</CODE> if
* not.
*/
{
if (this == o)
{
return true;
}
if (o instanceof RDN)
{
}
return false;
}
/**
* Retrieves the hash code for this RDN. It will be calculated as
* the sum of the hash codes of the types and values.
*
* @return The hash code for this RDN.
*/
public int hashCode()
{
// Avoid an algorithm that requires the AVAs to be sorted.
int hash = 0;
{
}
return hash;
}
/** Returns normalized value for attribute at provided position. */
{
if (matchingRule != null)
{
try
{
}
catch (final DecodeException de)
{
// Unable to normalize, use default
}
}
return attributeValue;
}
/**
* Retrieves a string representation of this RDN.
*
* @return A string representation of this RDN.
*/
{
{
for (int i=1; i < numValues; i++)
{
}
}
return rdnString;
}
/**
* Appends a string representation of this RDN to the provided
* buffer.
*
* @param buffer The buffer to which the string representation
* should be appended.
*/
{
}
/**
* Retrieves a normalized string representation of this RDN.
*
* @return A normalized string representation of this RDN.
*/
public String toNormalizedString()
{
if (normalizedRDN == null)
{
}
return normalizedRDN;
}
/**
* Appends a normalized string representation of this RDN to the
* provided buffer.
*
* @param buffer The buffer to which to append the information.
*/
{
if (normalizedRDN != null)
{
return;
}
{
}
else
{
// Normalization sorts RDNs alphabetically
{
}
{
}
}
{
}
}
/**
* Adds a normalized byte string representation of this RDN to the provided builder.
*
* @param builder
* Builder to add this representation to.
* @return the builder
*/
{
}
else
{
// Normalization sorts RDNs
{
ByteStringBuilder b = new ByteStringBuilder();
normalizeAVAToByteString(i, b);
}
{
}
}
return builder;
}
/**
* Adds a normalized byte string representation of the AVA corresponding to provided position
* in this RDN to the provided builder.
*
* @param position
* Position of AVA in this RDN
* @param builder
* Builder to add the representation to.
* @return the builder
*/
{
{
}
return builder;
}
/**
* Return a new byte string with bytes 0x00, 0x01 and 0x02 escaped.
* <p>
* These bytes are reserved to represent respectively the RDN separator, the
* AVA separator and the escape byte in a normalized byte string.
*/
{
if (!needEscaping(value))
{
return value;
}
{
if (isByteToEscape(b))
{
}
}
return builder.toByteString();
}
{
boolean needEscaping = false;
{
if (isByteToEscape(b))
{
needEscaping = true;
break;
}
}
return needEscaping;
}
private boolean isByteToEscape(final byte b)
{
return b == DN.NORMALIZED_RDN_SEPARATOR || b == DN.NORMALIZED_AVA_SEPARATOR || b == DN.NORMALIZED_ESC_BYTE;
}
/**
* Appends a normalized string representation of this RDN to the
* provided buffer.
*
* @param position The position of the attribute type and value to
* retrieve.
* @param builder The buffer to which to append the information.
* @return the builder
*/
{
{
return builder;
}
if (!hasAttributeName || !isHumanReadable)
{
}
else
{
// try to decode value as UTF-8 string
{
try
{
// URL encoding encodes space char as '+' instead of using hex code
}
catch (UnsupportedEncodingException e)
{
// should never happen
}
}
else
{
}
}
return builder;
}
/**
* Compares this RDN with the provided RDN based on an alphabetic
* comparison of the attribute names and values.
*
* @param rdn The RDN against which to compare this RDN.
*
* @return A negative integer if this RDN should come before the
* provided RDN, a positive integer if this RDN should come
* after the provided RDN, or zero if there is no
* difference with regard to ordering.
*/
{
{
// fast path
{
}
else
{
}
}
{
}
{
}
while (true)
{
{
// there is a difference => return result
}
if (valueComparison != 0)
{
// we found a difference => return result
return valueComparison;
}
{
{
return -1;
}
return 0;
}
{
return 1;
}
}
}
/**
* Compares two attribute values by using the provided MatchingRule if
* it is not null, or relying on alphabetical ordering otherwise.
*
* @param value1
* the first attribute value to compare
* @param value2
* the second attribute value to compare
* @param type
* the type whose MatchingRule is to be used for comparison
* @return A negative integer if this value1 should come before the value2, a
* positive integer if value1 should come after value2, or zero if
* there is no difference with regard to ordering.
*/
{
{
try
{
}
catch (DecodeException e)
{
logger.traceException(e);
}
}
}
}