FileDocCategorySizeDatePackage
Rdn.javaAPI DocJava SE 5 API24128Fri Aug 26 14:57:40 BST 2005javax.naming.ldap

Rdn

public class Rdn extends Object implements Serializable, Comparable
This class represents a relative distinguished name, or RDN, which is a component of a distinguished name as specified by RFC 2253. An example of an RDN is "OU=Sales+CN=J.Smith". In this example, the RDN consist of multiple attribute type/value pairs. The RDN is parsed as described in the class description for {@link javax.naming.ldap.LdapName LdapName}.

The Rdn class represents an RDN as attribute type/value mappings, which can be viewed using {@link javax.naming.directory.Attributes Attributes}. In addition, it contains convenience methods that allow easy retrieval of type and value when the Rdn consist of a single type/value pair, which is how it appears in a typical usage. It also contains helper methods that allow escaping of the unformatted attribute value and unescaping of the value formatted according to the escaping syntax defined in RFC2253. For methods that take or return attribute value as an Object, the value is either a String (in unescaped form) or a byte array.

Rdn will properly parse all valid RDNs, but does not attempt to detect all possible violations when parsing invalid RDNs. It is "generous" in accepting invalid RDNs. The "validity" of a name is determined ultimately when it is supplied to an LDAP server, which may accept or reject the name based on factors such as its schema information and interoperability considerations.

The following code example shows how to construct an Rdn using the constructor that takes type and value as arguments:

Rdn rdn = new Rdn("cn", "Juicy, Fruit");
System.out.println(rdn.toString());
The last line will print cn=Juicy\, Fruit. The {@link #unescapeValue(String) unescapeValue()} method can be used to unescape the escaped comma resulting in the original value "Juicy, Fruit". The {@link #escapeValue(Object) escapeValue()} method adds the escape back preceding the comma.

This class can be instantiated by a string representation of the RDN defined in RFC 2253 as shown in the following code example:

Rdn rdn = new Rdn("cn=Juicy\\, Fruit");
System.out.println(rdn.toString());
The last line will print cn=Juicy\, Fruit.

Concurrent multithreaded read-only access of an instance of Rdn need not be synchronized.

Unless otherwise noted, the behavior of passing a null argument to a constructor or method in this class will cause NullPointerException to be thrown.

version
1.7 04/06/21
since
1.5

Fields Summary
private transient ArrayList
entries
private static final int
DEFAULT_SIZE
private static final long
serialVersionUID
private static final String
escapees
Constructors Summary
public Rdn(Attributes attrSet)
Constructs an Rdn from the given attribute set. See {@link javax.naming.directory.Attributes Attributes}.

The string attribute values are not interpretted as RFC 2253 formatted RDN strings. That is, the values are used literally (not parsed) and assumed to be unescaped.

param
attrSet The non-null and non-empty attributes containing type/value mappings.
throws
InvalidNameException If contents of attrSet cannot be used to construct a valid RDN.


                                                               		           
         
	if (attrSet.size() == 0) {
	    throw new InvalidNameException("Attributes cannot be empty");
	}
	entries = new ArrayList(attrSet.size());
	NamingEnumeration attrs = attrSet.getAll();
	try {
	    for (int nEntries = 0; attrs.hasMore(); nEntries++) {
		RdnEntry entry = new RdnEntry();
		Attribute attr = (Attribute) attrs.next();
		entry.type = attr.getID();
		entry.value = attr.get();
		entries.add(nEntries, entry);
	    }
	} catch (NamingException e) {
	    InvalidNameException e2 = new InvalidNameException(
					e.getMessage());
	    e2.initCause(e);
	    throw e2;
	}
	sort(); // arrange entries for comparison
    
public Rdn(String rdnString)
Constructs an Rdn from the given string. This constructor takes a string formatted according to the rules defined in RFC 2253 and described in the class description for {@link javax.naming.ldap.LdapName}.

param
rdnString The non-null and non-empty RFC2253 formatted string.
throws
InvalidNameException If a syntax error occurs during parsing of the rdnString.

	entries = new ArrayList(DEFAULT_SIZE);
	(new Rfc2253Parser(rdnString)).parseRdn(this);
    
public Rdn(Rdn rdn)
Constructs an Rdn from the given rdn. The contents of the rdn are simply copied into the newly created Rdn.

param
rdn The non-null Rdn to be copied.

	entries = new ArrayList(rdn.entries.size());
	entries.addAll(rdn.entries);
    
public Rdn(String type, Object value)
Constructs an Rdn from the given attribute type and value. The string attribute values are not interpretted as RFC 2253 formatted RDN strings. That is, the values are used literally (not parsed) and assumed to be unescaped.

param
type The non-null and non-empty string attribute type.
param
value The non-null and non-empty attribute value.
throws
InvalidNameException If type/value cannot be used to construct a valid RDN.
see
#toString()

	if (value == null) {
	    throw new NullPointerException("Cannot set value to null");
	}
	if (type.equals("") || isEmptyValue(value)) {
	    throw new InvalidNameException(
		"type or value cannot be empty, type:" + type +
		" value:" + value);
	}
	entries = new ArrayList(DEFAULT_SIZE);
	put(type, value);
    
Rdn()

	entries = new ArrayList(DEFAULT_SIZE);
    
Methods Summary
public intcompareTo(java.lang.Object obj)
Compares this Rdn with the specified Object for order. Returns a negative integer, zero, or a positive integer as this Rdn is less than, equal to, or greater than the given Object.

If obj is null or not an instance of Rdn, ClassCastException is thrown.

The attribute type and value pairs of the RDNs are lined up against each other and compared lexicographically. The order of components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not significant.

param
obj The non-null object to compare against.
return
A negative integer, zero, or a positive integer as this Rdn is less than, equal to, or greater than the given Object.
exception
ClassCastException if obj is null or not a Rdn.

	if (!(obj instanceof Rdn)) {
            throw new ClassCastException("The obj is not a Rdn");
        }
	if (obj == this) {
	    return 0;
	}
	Rdn that = (Rdn) obj;
        int minSize = Math.min(entries.size(), that.entries.size());
        for (int i = 0; i < minSize; i++) {

	    // Compare a single pair of type/value pairs.
            int diff = ((RdnEntry) entries.get(i)).compareTo(
					that.entries.get(i));
            if (diff != 0) {
		return diff;
            }
        }
	return (entries.size() - that.entries.size());  // longer RDN wins
    
private static byte[]decodeHexPairs(char[] chars, int beg, int end)

	    byte[] bytes = new byte[(end - beg) / 2];
	    for (int i = 0; beg + 1 < end; i++) {
		int hi = Character.digit(chars[beg], 16);
		int lo = Character.digit(chars[beg + 1], 16);
		if (hi < 0 || lo < 0) {
		    break;
		}
		bytes[i] = (byte)((hi<<4) + lo);
		beg += 2;
	    }
	    if (beg != end) {
		throw new IllegalArgumentException(
			"Illegal attribute value: " + new String(chars));
	    }
	    return bytes;
	
public booleanequals(java.lang.Object obj)
Compares the specified Object with this Rdn for equality. Returns true if the given object is also a Rdn and the two Rdns represent the same attribute type and value mappings. The order of components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not significant.

Type and value equalilty matching is done as below:

  • The types are compared for equality with their case ignored.
  • String values with different but equivalent usage of quoting, escaping, or UTF8-hex-encoding are considered equal. The case of the values is ignored during the comparison.

If obj is null or not an instance of Rdn, false is returned.

param
obj object to be compared for equality with this Rdn.
return
true if the specified object is equal to this Rdn.
see
#hashCode()

	if (obj == this) {
	    return true;
	}
	if (!(obj instanceof Rdn)) {
	    return false;
	}
	Rdn that = (Rdn) obj;
	if (entries.size() != that.size()) {
	    return false;
	}
        for (int i = 0; i < entries.size(); i++) {
	    if (!entries.get(i).equals(that.entries.get(i))) {
		return false;
	    }
	}
	return true;
    
private static java.lang.StringescapeBinaryValue(byte[] val)


	StringBuilder builder = new StringBuilder(1 + 2 * val.length);
	builder.append("#");

	for (int i = 0; i < val.length; i++) {
	    byte b = val[i];
	    builder.append(Character.forDigit(0xF & (b >>> 4), 16));
	    builder.append(Character.forDigit(0xF & b, 16));
	}
	return builder.toString();
	// return builder.toString().toUpperCase();
    
private static java.lang.StringescapeStringValue(java.lang.String val)


         

	    char[] chars = val.toCharArray();
	    StringBuilder builder = new StringBuilder(2 * val.length());

	    // Find leading and trailing whitespace.
	    int lead;	// index of first char that is not leading whitespace
	    for (lead = 0; lead < chars.length; lead++) {
		if (!isWhitespace(chars[lead])) {
		    break;
		}
	    }
	    int trail;	// index of last char that is not trailing whitespace
	    for (trail = chars.length - 1; trail >= 0; trail--) {
		if (!isWhitespace(chars[trail])) {
		    break;
		}
	    }

	    for (int i = 0; i < chars.length; i++) {
		char c = chars[i];
		if ((i < lead) || (i > trail) || (escapees.indexOf(c) >= 0)) {
		    builder.append('\\");
		}
		builder.append(c);
	    }
	    return builder.toString();
    
public static java.lang.StringescapeValue(java.lang.Object val)
Given the value of an attribute, returns a string escaped according to the rules specified in RFC 2253.

For example, if the val is "Sue, Grabbit and Runn", the escaped value returned by this method is "Sue\, Grabbit and Runn".

A string value is represented as a String and binary value as a byte array.

param
val The non-null object to be escaped.
return
Escaped string value.
throws
ClassCastException if val is is not a String or byte array.

	return (val instanceof byte[])
		? escapeBinaryValue((byte[])val)
		: escapeStringValue((String)val);
    
public java.lang.StringgetType()
Retrieves one of this Rdn's type. This is a convenience method for obtaining the type, when the RDN contains a single type and value mapping, which is the common RDN usage.

For a multi-valued RDN, the type/value pairs have no specific order defined on them. In that case, this method returns type of one of the type/value pairs. The {@link #getValue() getValue()} method returns the value corresponding to the type returned by this method.

return
The non-null attribute type.

	return ((RdnEntry) entries.get(0)).getType();
    
private static byte[]getUtf8Octets(char[] chars, int beg, int end)

	    byte[] utf8 = new byte[(end - beg) / 3];	// allow enough room
	    int len = 0;	// index of first unused byte in utf8

	    while ((beg + 2 < end) &&
		   (chars[beg++] == '\\")) {
		int hi = Character.digit(chars[beg++], 16);
		int lo = Character.digit(chars[beg++], 16);
		if (hi < 0 || lo < 0) {
		   break;
		}
		utf8[len++] = (byte)((hi<<4) + lo);
	    }
	    if (len == utf8.length) {
		return utf8;
	    } else {
		byte[] res = new byte[len];
		System.arraycopy(utf8, 0, res, 0, len);
		return res;
	    }
	
public java.lang.ObjectgetValue()
Retrieves one of this Rdn's value. This is a convenience method for obtaining the value, when the RDN contains a single type and value mapping, which is the common RDN usage.

For a multi-valued RDN, this method returns value corresponding to the type returned by {@link #getType() getType()} method.

return
The non-null attribute value.

	return ((RdnEntry) entries.get(0)).getValue();
    
public inthashCode()
Returns the hash code of this RDN. Two RDNs that are equal (according to the equals method) will have the same hash code.

return
An int representing the hash code of this Rdn.
see
#equals


	// Sum up the hash codes of the components.
        int hash = 0;

        // For each type/value pair...
        for (int i = 0; i < entries.size(); i++) {
	    hash += entries.get(i).hashCode();
        }
        return hash;
    
private booleanisEmptyValue(java.lang.Object val)

	return ((val instanceof String) && val.equals("")) ||
	((val instanceof byte[]) && (((byte[]) val).length == 0));
    
private static booleanisWhitespace(char c)

        return (c == ' " || c == '\r");
    
javax.naming.ldap.Rdnput(java.lang.String type, java.lang.Object value)


	// create new Entry
	RdnEntry newEntry = new RdnEntry();
	newEntry.type =  type;
        if (value instanceof byte[]) {	// clone the byte array
	    newEntry.value = ((byte[]) value).clone();
	} else {
	    newEntry.value = value;
	}
	entries.add(newEntry);
	return this;
    
private voidreadObject(java.io.ObjectInputStream s)

	s.defaultReadObject();
	entries = new ArrayList(DEFAULT_SIZE);
        String unparsed = (String) s.readObject();
        try {
	    (new Rfc2253Parser(unparsed)).parseRdn(this);
        } catch (InvalidNameException e) {
            // shouldn't happen
            throw new java.io.StreamCorruptedException(
                    "Invalid name: " + unparsed);
        }
    
public intsize()
Retrieves the number of attribute type/value pairs in this Rdn.

return
The non-negative number of type/value pairs in this Rdn.

        return entries.size();
    
voidsort()

	if (entries.size() > 1) {
	    Collections.sort(entries);
	}
    
public javax.naming.directory.AttributestoAttributes()
Retrieves the {@link javax.naming.directory.Attributes Attributes} view of the type/value mappings contained in this Rdn.

return
The non-null attributes containing the type/value mappings of this Rdn.

	Attributes attrs = new BasicAttributes(true);
        for (int i = 0; i < entries.size(); i++) {
	    RdnEntry entry = (RdnEntry) entries.get(i);
	    Attribute attr = attrs.put(entry.getType(), entry.getValue());
	    if (attr != null) {
		attr.add(entry.getValue());
		attrs.put(entry.getType(), attr);
	    }
	}
        return attrs;
    
public java.lang.StringtoString()
Returns this Rdn as a string represented in a format defined by RFC 2253 and described in the class description for {@link javax.naming.ldap.LdapName LdapName}.

return
The string representation of the Rdn.

	StringBuilder builder = new StringBuilder();
	int size = entries.size();
	if (size > 0) {
            builder.append(entries.get(0));
	}
	for (int next = 1; next < size; next++) {
            builder.append('+");
            builder.append(entries.get(next));
        }
        return builder.toString();
    
public static java.lang.ObjectunescapeValue(java.lang.String val)
Given an attribute value string formated according to the rules specified in RFC 2253, returns the unformated value. Escapes and quotes are stripped away, and hex-encoded UTF-8 is converted to equivalent UTF-16 characters. Returns a string value as a String, and a binary value as a byte array.

Legal and illegal values are defined in RFC 2253. This method is generous in accepting the values and does not catch all illegal values. Therefore, passing in an illegal value might not necessarily trigger an IllegalArgumentException.

param
val The non-null string to be unescaped.
return
Unescaped value.
throws
IllegalArgumentException When an Illegal value is provided.


	    char[] chars = val.toCharArray();
	    int beg = 0;
	    int end = chars.length;

	    // Trim off leading and trailing whitespace.
	    while ((beg < end) && isWhitespace(chars[beg])) {
		++beg;
	    }

	    while ((beg < end) && isWhitespace(chars[end - 1])) {
		--end;
	    }

	    // Add back the trailing whitespace with a preceeding '\'
	    // (escaped or unescaped) that was taken off in the above
	    // loop. Whether or not to retain this whitespace is decided below.
	    if (end != chars.length &&
		    (beg < end) &&
		    chars[end - 1] == '\\") {
		end++;
	    }
	    if (beg >= end) {
		return "";
	    }

	    if (chars[beg] == '#") {
		// Value is binary (eg: "#CEB1DF80").
		return decodeHexPairs(chars, ++beg, end);
	    }

	    // Trim off quotes.
	    if ((chars[beg] == '\"") && (chars[end - 1] == '\"")) {
		++beg;
		--end;
	    }

	    StringBuilder builder = new StringBuilder(end - beg);
	    int esc = -1; // index of the last escaped character

	    for (int i = beg; i < end; i++) {
		if ((chars[i] == '\\") && (i + 1 < end)) {
		    if (!Character.isLetterOrDigit(chars[i + 1])) {
			++i;				// skip backslash
			builder.append(chars[i]);	// snarf escaped char
			esc = i;
		    } else {

			// Convert hex-encoded UTF-8 to 16-bit chars.
			byte[] utf8 = getUtf8Octets(chars, i, end);
			if (utf8.length > 0) {
			    try {
				builder.append(new String(utf8, "UTF8"));
			    } catch (java.io.UnsupportedEncodingException e) {
				// shouldn't happen
			    }
			    i += utf8.length * 3 - 1;
			} else { // no utf8 bytes available, invalid DN

			    // '/' has no meaning, throw exception
			    throw new IllegalArgumentException(
				"Not a valid attribute string value:" +
				val + ",improper usage of backslash");
			}
		    }
		} else {
		    builder.append(chars[i]);	// snarf unescaped char
		}
	    }

	    // Get rid of the unescaped trailing whitespace with the
	    // preceeding '\' character that was previously added back.
	    int len = builder.length();
	    if (isWhitespace(builder.charAt(len - 1)) && esc != (end - 1)) {
		builder.setLength(len - 1);
	    }
	    return builder.toString();
	
private voidwriteObject(java.io.ObjectOutputStream s)
Serializes only the unparsed RDN, for compactness and to avoid any implementation dependency.

serialData
The RDN string

	s.defaultWriteObject();
        s.writeObject(toString());