/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2009 Sun Microsystems, Inc. */ package org.opends.sdk; import static com.sun.opends.sdk.messages.Messages.*; import static com.sun.opends.sdk.util.StaticUtils.*; import java.util.*; import org.opends.sdk.schema.*; import com.sun.opends.sdk.util.*; /** * A relative distinguished name (RDN) as defined in RFC 4512 section * 2.3 is the name of an entry relative to its immediate superior. An * RDN is composed of an unordered set of one or more attribute value * assertions (AVA) consisting of an attribute description with zero * options and an attribute value. These AVAs are chosen to match * attribute values (each a distinguished value) of the entry. *
* An entry's relative distinguished name must be unique among all * immediate subordinates of the entry's immediate superior (i.e. all * siblings). *
* The following are examples of string representations of RDNs: * *
* uid=12345 * ou=Engineering * cn=Kurt Zeilenga+L=Redwood Shores ** * The last is an example of a multi-valued RDN; that is, an RDN * composed of multiple AVAs. * * @see RFC * 4512 - Lightweight Directory Access Protocol (LDAP): Directory * Information Models */ public final class RDN implements Iterable
* If {@code attributeValue} is not an instance of {@code
* ByteString} then it will be converted using the
* {@link ByteString#valueOf(Object)} method.
*
* @param attributeType
* The attribute type.
* @param attributeValue
* The attribute value.
* @throws UnknownSchemaElementException
* If {@code attributeType} was not found in the default
* schema.
* @throws NullPointerException
* If {@code attributeType} or {@code attributeValue} was
* {@code null}.
*/
public AVA(String attributeType, Object attributeValue)
throws UnknownSchemaElementException, NullPointerException
{
Validator.ensureNotNull(attributeType, attributeValue);
this.attributeType = Schema.getDefaultSchema().getAttributeType(
attributeType);
this.attributeValue = ByteString.valueOf(attributeValue);
}
/**
* {@inheritDoc}
*/
public int compareTo(AVA ava)
{
int result = attributeType.compareTo(ava.attributeType);
if (result == 0)
{
final ByteString nv1 = getNormalizeValue();
final ByteString nv2 = ava.getNormalizeValue();
result = nv1.compareTo(nv2);
}
return result;
}
/**
* {@inheritDoc}
*/
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj instanceof AVA)
{
return compareTo((AVA) obj) == 0;
}
else
{
return false;
}
}
/**
* Returns the attribute type associated with this AVA.
*
* @return The attribute type associated with this AVA.
*/
public AttributeType getAttributeType()
{
return attributeType;
}
/**
* Returns the attribute value associated with this AVA.
*
* @return The attribute value associated with this AVA.
*/
public ByteString getAttributeValue()
{
return attributeValue;
}
/**
* {@inheritDoc}
*/
public int hashCode()
{
return attributeType.hashCode() * 31
+ getNormalizeValue().hashCode();
}
/**
* {@inheritDoc}
*/
public String toString()
{
final StringBuilder builder = new StringBuilder();
return toString(builder).toString();
}
private ByteString getNormalizeValue()
{
final MatchingRule matchingRule = attributeType
.getEqualityMatchingRule();
if (matchingRule != null)
{
try
{
return matchingRule.normalizeAttributeValue(attributeValue);
}
catch (final DecodeException de)
{
// Ignore - we'll drop back to the user provided value.
}
}
return attributeValue;
}
private StringBuilder toNormalizedString(StringBuilder builder)
{
return toString(builder, true);
}
private StringBuilder toString(StringBuilder builder)
{
return toString(builder, false);
}
private StringBuilder toString(StringBuilder builder,
boolean normalize)
{
final ByteString value = normalize ? getNormalizeValue()
: attributeValue;
if (!attributeType.getNames().iterator().hasNext())
{
builder.append(attributeType.getOID());
builder.append("=#");
StaticUtils.toHex(value, builder);
}
else
{
final String name = attributeType.getNameOrOID();
if (normalize)
{
// Normalizing.
StaticUtils.toLowerCase(name, builder);
}
else
{
builder.append(name);
}
builder.append("=");
final Syntax syntax = attributeType.getSyntax();
if (!syntax.isHumanReadable())
{
builder.append("#");
StaticUtils.toHex(value, builder);
}
else
{
final String str = value.toString();
char c;
for (int si = 0; si < str.length(); si++)
{
c = str.charAt(si);
if (c == ' ' || c == '#' || c == '"' || c == '+'
|| c == ',' || c == ';' || c == '<' || c == '='
|| c == '>' || c == '\\' || c == '\u0000')
{
builder.append('\\');
}
builder.append(c);
}
}
}
return builder;
}
}
private static final char[] SPECIAL_CHARS = new char[] { '\"', '+',
',', ';', '<', '>', ' ', '#', '=', '\\' };
private static final char[] DELIMITER_CHARS = new char[] { '+', ',',
';' };
private static final char[] DQUOTE_CHAR = new char[] { '\"' };
private static final Comparator
* If {@code attributeValue} is not an instance of {@code ByteString}
* then it will be converted using the
* {@link ByteString#valueOf(Object)} method.
*
* @param attributeType
* The attribute type.
* @param attributeValue
* The attribute value.
* @throws UnknownSchemaElementException
* If {@code attributeType} was not found in the default
* schema.
* @throws NullPointerException
* If {@code attributeType} or {@code attributeValue} was
* {@code null}.
*/
public RDN(String attributeType, Object attributeValue)
throws UnknownSchemaElementException, NullPointerException
{
this.avas = new AVA[] { new AVA(attributeType, attributeValue) };
}
private RDN(AVA[] avas, String stringValue)
{
this.avas = avas;
this.stringValue = stringValue;
}
/**
* {@inheritDoc}
*/
public int compareTo(RDN rdn)
{
final int sz1 = avas.length;
final int sz2 = rdn.avas.length;
if (sz1 != sz2)
{
return sz1 - sz2;
}
if (sz1 == 1)
{
return avas[0].compareTo(rdn.avas[0]);
}
// Need to sort the AVAs before comparing.
final AVA[] a1 = new AVA[sz1];
System.arraycopy(avas, 0, a1, 0, sz1);
Arrays.sort(a1, ATV_COMPARATOR);
final AVA[] a2 = new AVA[sz1];
System.arraycopy(rdn.avas, 0, a2, 0, sz1);
Arrays.sort(a2, ATV_COMPARATOR);
for (int i = 0; i < sz1; i++)
{
final int result = a1[i].compareTo(a2[i]);
if (result != 0)
{
return result;
}
}
return 0;
}
/**
* {@inheritDoc}
*/
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj instanceof RDN)
{
return compareTo((RDN) obj) == 0;
}
else
{
return false;
}
}
/**
* Returns the attribute value contained in this RDN which is
* associated with the provided attribute type, or {@code null} if
* this RDN does not include such an attribute value.
*
* @param attributeType
* The attribute type.
* @return The attribute value.
*/
public ByteString getAttributeValue(AttributeType attributeType)
{
for (final AVA ava : avas)
{
if (ava.getAttributeType().equals(attributeType))
{
return ava.getAttributeValue();
}
}
return null;
}
/**
* Returns the first AVA contained in this RDN.
*
* @return The first AVA contained in this RDN.
*/
public AVA getFirstAVA()
{
return avas[0];
}
/**
* {@inheritDoc}
*/
public int hashCode()
{
// Avoid an algorithm that requires the AVAs to be sorted.
int hash = 0;
for (int i = 0; i < avas.length; i++)
{
hash += avas[i].hashCode();
}
return hash;
}
/**
* Returns {@code true} if this RDN contains more than one AVA.
*
* @return {@code true} if this RDN contains more than one AVA,
* otherwise {@code false}.
*/
public boolean isMultiValued()
{
return avas.length > 1;
}
/**
* Returns an iterator of the AVAs contained in this RDN. The AVAs
* will be returned in the user provided order.
*
* Attempts to remove AVAs using an iterator's {@code remove()} method
* are not permitted and will result in an {@code
* UnsupportedOperationException} being thrown.
*
* @return An iterator of the AVAs contained in this RDN.
*/
public Iterator