33
34 import javax.naming.InvalidNameException;
35 import javax.naming.directory.BasicAttributes;
36 import javax.naming.directory.Attributes;
37 import javax.naming.directory.Attribute;
38 import javax.naming.NamingEnumeration;
39 import javax.naming.NamingException;
40
41 import java.io.Serializable;
42 import java.io.ObjectOutputStream;
43 import java.io.ObjectInputStream;
44 import java.io.IOException;
45
46 /**
47 * This class represents a relative distinguished name, or RDN, which is a
48 * component of a distinguished name as specified by
49 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
50 * An example of an RDN is "OU=Sales+CN=J.Smith". In this example,
51 * the RDN consist of multiple attribute type/value pairs. The
52 * RDN is parsed as described in the class description for
53 * {@link javax.naming.ldap.LdapName <tt>LdapName</tt>}.
54 * <p>
55 * The Rdn class represents an RDN as attribute type/value mappings,
56 * which can be viewed using
57 * {@link javax.naming.directory.Attributes Attributes}.
58 * In addition, it contains convenience methods that allow easy retrieval
59 * of type and value when the Rdn consist of a single type/value pair,
60 * which is how it appears in a typical usage.
61 * It also contains helper methods that allow escaping of the unformatted
62 * attribute value and unescaping of the value formatted according to the
63 * escaping syntax defined in RFC2253. For methods that take or return
64 * attribute value as an Object, the value is either a String
65 * (in unescaped form) or a byte array.
66 * <p>
67 * <code>Rdn</code> will properly parse all valid RDNs, but
68 * does not attempt to detect all possible violations when parsing
69 * invalid RDNs. It is "generous" in accepting invalid RDNs.
70 * The "validity" of a name is determined ultimately when it
71 * is supplied to an LDAP server, which may accept or
72 * reject the name based on factors such as its schema information
73 * and interoperability considerations.
74 *
75 * <p>
76 * The following code example shows how to construct an Rdn using the
77 * constructor that takes type and value as arguments:
78 * <pre>
79 * Rdn rdn = new Rdn("cn", "Juicy, Fruit");
80 * System.out.println(rdn.toString());
81 * </pre>
82 * The last line will print <tt>cn=Juicy\, Fruit</tt>. The
83 * {@link #unescapeValue(String) <tt>unescapeValue()</tt>} method can be
84 * used to unescape the escaped comma resulting in the original
85 * value <tt>"Juicy, Fruit"</tt>. The {@link #escapeValue(Object)
86 * <tt>escapeValue()</tt>} method adds the escape back preceding the comma.
87 * <p>
88 * This class can be instantiated by a string representation
89 * of the RDN defined in RFC 2253 as shown in the following code example:
90 * <pre>
91 * Rdn rdn = new Rdn("cn=Juicy\\, Fruit");
92 * System.out.println(rdn.toString());
93 * </pre>
94 * The last line will print <tt>cn=Juicy\, Fruit</tt>.
95 * <p>
96 * Concurrent multithreaded read-only access of an instance of
97 * <tt>Rdn</tt> need not be synchronized.
98 * <p>
99 * Unless otherwise noted, the behavior of passing a null argument
100 * to a constructor or method in this class will cause NullPointerException
101 * to be thrown.
102 *
103 * @since 1.5
104 */
105
106 public class Rdn implements Serializable, Comparable<Object> {
107
108 private transient ArrayList<RdnEntry> entries;
109
110 // The common case.
111 private static final int DEFAULT_SIZE = 1;
112
113 private static final long serialVersionUID = -5994465067210009656L;
114
115 /**
116 * Constructs an Rdn from the given attribute set. See
117 * {@link javax.naming.directory.Attributes Attributes}.
118 * <p>
119 * The string attribute values are not interpreted as
120 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
121 * formatted RDN strings. That is, the values are used
122 * literally (not parsed) and assumed to be unescaped.
123 *
124 * @param attrSet The non-null and non-empty attributes containing
125 * type/value mappings.
126 * @throws InvalidNameException If contents of <tt>attrSet</tt> cannot
127 * be used to construct a valid RDN.
128 */
129 public Rdn(Attributes attrSet) throws InvalidNameException {
130 if (attrSet.size() == 0) {
131 throw new InvalidNameException("Attributes cannot be empty");
132 }
133 entries = new ArrayList<>(attrSet.size());
134 NamingEnumeration<? extends Attribute> attrs = attrSet.getAll();
135 try {
136 for (int nEntries = 0; attrs.hasMore(); nEntries++) {
137 RdnEntry entry = new RdnEntry();
138 Attribute attr = attrs.next();
139 entry.type = attr.getID();
140 entry.value = attr.get();
141 entries.add(nEntries, entry);
142 }
143 } catch (NamingException e) {
144 InvalidNameException e2 = new InvalidNameException(
145 e.getMessage());
146 e2.initCause(e);
149 sort(); // arrange entries for comparison
150 }
151
152 /**
153 * Constructs an Rdn from the given string.
154 * This constructor takes a string formatted according to the rules
155 * defined in <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
156 * and described in the class description for
157 * {@link javax.naming.ldap.LdapName}.
158 *
159 * @param rdnString The non-null and non-empty RFC2253 formatted string.
160 * @throws InvalidNameException If a syntax error occurs during
161 * parsing of the rdnString.
162 */
163 public Rdn(String rdnString) throws InvalidNameException {
164 entries = new ArrayList<>(DEFAULT_SIZE);
165 (new Rfc2253Parser(rdnString)).parseRdn(this);
166 }
167
168 /**
169 * Constructs an Rdn from the given <tt>rdn</tt>.
170 * The contents of the <tt>rdn</tt> are simply copied into the newly
171 * created Rdn.
172 * @param rdn The non-null Rdn to be copied.
173 */
174 public Rdn(Rdn rdn) {
175 entries = new ArrayList<>(rdn.entries.size());
176 entries.addAll(rdn.entries);
177 }
178
179 /**
180 * Constructs an Rdn from the given attribute type and
181 * value.
182 * The string attribute values are not interpreted as
183 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
184 * formatted RDN strings. That is, the values are used
185 * literally (not parsed) and assumed to be unescaped.
186 *
187 * @param type The non-null and non-empty string attribute type.
188 * @param value The non-null and non-empty attribute value.
189 * @throws InvalidNameException If type/value cannot be used to
190 * construct a valid RDN.
566 byte b = val[i];
567 builder.append(Character.forDigit(0xF & (b >>> 4), 16));
568 builder.append(Character.forDigit(0xF & b, 16));
569 }
570 return builder.toString();
571 }
572
573 /**
574 * Given an attribute value string formatted according to the rules
575 * specified in
576 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>,
577 * returns the unformatted value. Escapes and quotes are
578 * stripped away, and hex-encoded UTF-8 is converted to equivalent
579 * UTF-16 characters. Returns a string value as a String, and a
580 * binary value as a byte array.
581 * <p>
582 * Legal and illegal values are defined in RFC 2253.
583 * This method is generous in accepting the values and does not
584 * catch all illegal values.
585 * Therefore, passing in an illegal value might not necessarily
586 * trigger an <tt>IllegalArgumentException</tt>.
587 *
588 * @param val The non-null string to be unescaped.
589 * @return Unescaped value.
590 * @throws IllegalArgumentException When an Illegal value
591 * is provided.
592 */
593 public static Object unescapeValue(String val) {
594
595 char[] chars = val.toCharArray();
596 int beg = 0;
597 int end = chars.length;
598
599 // Trim off leading and trailing whitespace.
600 while ((beg < end) && isWhitespace(chars[beg])) {
601 ++beg;
602 }
603
604 while ((beg < end) && isWhitespace(chars[end - 1])) {
605 --end;
606 }
|
33
34 import javax.naming.InvalidNameException;
35 import javax.naming.directory.BasicAttributes;
36 import javax.naming.directory.Attributes;
37 import javax.naming.directory.Attribute;
38 import javax.naming.NamingEnumeration;
39 import javax.naming.NamingException;
40
41 import java.io.Serializable;
42 import java.io.ObjectOutputStream;
43 import java.io.ObjectInputStream;
44 import java.io.IOException;
45
46 /**
47 * This class represents a relative distinguished name, or RDN, which is a
48 * component of a distinguished name as specified by
49 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
50 * An example of an RDN is "OU=Sales+CN=J.Smith". In this example,
51 * the RDN consist of multiple attribute type/value pairs. The
52 * RDN is parsed as described in the class description for
53 * {@link javax.naming.ldap.LdapName LdapName}.
54 * <p>
55 * The Rdn class represents an RDN as attribute type/value mappings,
56 * which can be viewed using
57 * {@link javax.naming.directory.Attributes Attributes}.
58 * In addition, it contains convenience methods that allow easy retrieval
59 * of type and value when the Rdn consist of a single type/value pair,
60 * which is how it appears in a typical usage.
61 * It also contains helper methods that allow escaping of the unformatted
62 * attribute value and unescaping of the value formatted according to the
63 * escaping syntax defined in RFC2253. For methods that take or return
64 * attribute value as an Object, the value is either a String
65 * (in unescaped form) or a byte array.
66 * <p>
67 * <code>Rdn</code> will properly parse all valid RDNs, but
68 * does not attempt to detect all possible violations when parsing
69 * invalid RDNs. It is "generous" in accepting invalid RDNs.
70 * The "validity" of a name is determined ultimately when it
71 * is supplied to an LDAP server, which may accept or
72 * reject the name based on factors such as its schema information
73 * and interoperability considerations.
74 *
75 * <p>
76 * The following code example shows how to construct an Rdn using the
77 * constructor that takes type and value as arguments:
78 * <pre>
79 * Rdn rdn = new Rdn("cn", "Juicy, Fruit");
80 * System.out.println(rdn.toString());
81 * </pre>
82 * The last line will print {@code cn=Juicy\, Fruit}. The
83 * {@link #unescapeValue(String) unescapeValue()} method can be
84 * used to unescape the escaped comma resulting in the original
85 * value {@code "Juicy, Fruit"}. The {@link #escapeValue(Object)
86 * escapeValue()} method adds the escape back preceding the comma.
87 * <p>
88 * This class can be instantiated by a string representation
89 * of the RDN defined in RFC 2253 as shown in the following code example:
90 * <pre>
91 * Rdn rdn = new Rdn("cn=Juicy\\, Fruit");
92 * System.out.println(rdn.toString());
93 * </pre>
94 * The last line will print {@code cn=Juicy\, Fruit}.
95 * <p>
96 * Concurrent multithreaded read-only access of an instance of
97 * {@code Rdn} need not be synchronized.
98 * <p>
99 * Unless otherwise noted, the behavior of passing a null argument
100 * to a constructor or method in this class will cause NullPointerException
101 * to be thrown.
102 *
103 * @since 1.5
104 */
105
106 public class Rdn implements Serializable, Comparable<Object> {
107
108 private transient ArrayList<RdnEntry> entries;
109
110 // The common case.
111 private static final int DEFAULT_SIZE = 1;
112
113 private static final long serialVersionUID = -5994465067210009656L;
114
115 /**
116 * Constructs an Rdn from the given attribute set. See
117 * {@link javax.naming.directory.Attributes Attributes}.
118 * <p>
119 * The string attribute values are not interpreted as
120 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
121 * formatted RDN strings. That is, the values are used
122 * literally (not parsed) and assumed to be unescaped.
123 *
124 * @param attrSet The non-null and non-empty attributes containing
125 * type/value mappings.
126 * @throws InvalidNameException If contents of {@code attrSet} cannot
127 * be used to construct a valid RDN.
128 */
129 public Rdn(Attributes attrSet) throws InvalidNameException {
130 if (attrSet.size() == 0) {
131 throw new InvalidNameException("Attributes cannot be empty");
132 }
133 entries = new ArrayList<>(attrSet.size());
134 NamingEnumeration<? extends Attribute> attrs = attrSet.getAll();
135 try {
136 for (int nEntries = 0; attrs.hasMore(); nEntries++) {
137 RdnEntry entry = new RdnEntry();
138 Attribute attr = attrs.next();
139 entry.type = attr.getID();
140 entry.value = attr.get();
141 entries.add(nEntries, entry);
142 }
143 } catch (NamingException e) {
144 InvalidNameException e2 = new InvalidNameException(
145 e.getMessage());
146 e2.initCause(e);
149 sort(); // arrange entries for comparison
150 }
151
152 /**
153 * Constructs an Rdn from the given string.
154 * This constructor takes a string formatted according to the rules
155 * defined in <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
156 * and described in the class description for
157 * {@link javax.naming.ldap.LdapName}.
158 *
159 * @param rdnString The non-null and non-empty RFC2253 formatted string.
160 * @throws InvalidNameException If a syntax error occurs during
161 * parsing of the rdnString.
162 */
163 public Rdn(String rdnString) throws InvalidNameException {
164 entries = new ArrayList<>(DEFAULT_SIZE);
165 (new Rfc2253Parser(rdnString)).parseRdn(this);
166 }
167
168 /**
169 * Constructs an Rdn from the given {@code rdn}.
170 * The contents of the {@code rdn} are simply copied into the newly
171 * created Rdn.
172 * @param rdn The non-null Rdn to be copied.
173 */
174 public Rdn(Rdn rdn) {
175 entries = new ArrayList<>(rdn.entries.size());
176 entries.addAll(rdn.entries);
177 }
178
179 /**
180 * Constructs an Rdn from the given attribute type and
181 * value.
182 * The string attribute values are not interpreted as
183 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
184 * formatted RDN strings. That is, the values are used
185 * literally (not parsed) and assumed to be unescaped.
186 *
187 * @param type The non-null and non-empty string attribute type.
188 * @param value The non-null and non-empty attribute value.
189 * @throws InvalidNameException If type/value cannot be used to
190 * construct a valid RDN.
566 byte b = val[i];
567 builder.append(Character.forDigit(0xF & (b >>> 4), 16));
568 builder.append(Character.forDigit(0xF & b, 16));
569 }
570 return builder.toString();
571 }
572
573 /**
574 * Given an attribute value string formatted according to the rules
575 * specified in
576 * <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>,
577 * returns the unformatted value. Escapes and quotes are
578 * stripped away, and hex-encoded UTF-8 is converted to equivalent
579 * UTF-16 characters. Returns a string value as a String, and a
580 * binary value as a byte array.
581 * <p>
582 * Legal and illegal values are defined in RFC 2253.
583 * This method is generous in accepting the values and does not
584 * catch all illegal values.
585 * Therefore, passing in an illegal value might not necessarily
586 * trigger an {@code IllegalArgumentException}.
587 *
588 * @param val The non-null string to be unescaped.
589 * @return Unescaped value.
590 * @throws IllegalArgumentException When an Illegal value
591 * is provided.
592 */
593 public static Object unescapeValue(String val) {
594
595 char[] chars = val.toCharArray();
596 int beg = 0;
597 int end = chars.length;
598
599 // Trim off leading and trailing whitespace.
600 while ((beg < end) && isWhitespace(chars[beg])) {
601 ++beg;
602 }
603
604 while ((beg < end) && isWhitespace(chars[end - 1])) {
605 --end;
606 }
|