KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > naming > ldap > Rdn


1 /*
2  * @(#)Rdn.java 1.7 04/06/21
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.naming.ldap;
9
10 import java.util.Iterator JavaDoc;
11 import java.util.NoSuchElementException JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Collections JavaDoc;
14
15 import javax.naming.InvalidNameException JavaDoc;
16 import javax.naming.directory.BasicAttributes JavaDoc;
17 import javax.naming.directory.Attributes JavaDoc;
18 import javax.naming.directory.Attribute JavaDoc;
19 import javax.naming.NamingEnumeration JavaDoc;
20 import javax.naming.NamingException JavaDoc;
21
22 import java.io.Serializable JavaDoc;
23 import java.io.ObjectOutputStream JavaDoc;
24 import java.io.ObjectInputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26
27 /**
28  * This class represents a relative distinguished name, or RDN, which is a
29  * component of a distinguished name as specified by
30  * <a HREF="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
31  * An example of an RDN is "OU=Sales+CN=J.Smith". In this example,
32  * the RDN consist of multiple attribute type/value pairs. The
33  * RDN is parsed as described in the class description for
34  * {@link javax.naming.ldap.LdapName <tt>LdapName</tt>}.
35  * <p>
36  * The Rdn class represents an RDN as attribute type/value mappings,
37  * which can be viewed using
38  * {@link javax.naming.directory.Attributes Attributes}.
39  * In addition, it contains convenience methods that allow easy retrieval
40  * of type and value when the Rdn consist of a single type/value pair,
41  * which is how it appears in a typical usage.
42  * It also contains helper methods that allow escaping of the unformatted
43  * attribute value and unescaping of the value formatted according to the
44  * escaping syntax defined in RFC2253. For methods that take or return
45  * attribute value as an Object, the value is either a String
46  * (in unescaped form) or a byte array.
47  * <p>
48  * <code>Rdn</code> will properly parse all valid RDNs, but
49  * does not attempt to detect all possible violations when parsing
50  * invalid RDNs. It is "generous" in accepting invalid RDNs.
51  * The "validity" of a name is determined ultimately when it
52  * is supplied to an LDAP server, which may accept or
53  * reject the name based on factors such as its schema information
54  * and interoperability considerations.
55  *
56  * <p>
57  * The following code example shows how to construct an Rdn using the
58  * constructor that takes type and value as arguments:
59  * <pre>
60  * Rdn rdn = new Rdn("cn", "Juicy, Fruit");
61  * System.out.println(rdn.toString());
62  * </pre>
63  * The last line will print <tt>cn=Juicy\, Fruit</tt>. The
64  * {@link #unescapeValue(String) <tt>unescapeValue()</tt>} method can be
65  * used to unescape the escaped comma resulting in the original
66  * value <tt>"Juicy, Fruit"</tt>. The {@link #escapeValue(Object)
67  * <tt>escapeValue()</tt>} method adds the escape back preceding the comma.
68  * <p>
69  * This class can be instantiated by a string representation
70  * of the RDN defined in RFC 2253 as shown in the following code example:
71  * <pre>
72  * Rdn rdn = new Rdn("cn=Juicy\\, Fruit");
73  * System.out.println(rdn.toString());
74  * </pre>
75  * The last line will print <tt>cn=Juicy\, Fruit</tt>.
76  * <p>
77  * Concurrent multithreaded read-only access of an instance of
78  * <tt>Rdn</tt> need not be synchronized.
79  * <p>
80  * Unless otherwise noted, the behavior of passing a null argument
81  * to a constructor or method in this class will cause NullPointerException
82  * to be thrown.
83  *
84  * @version 1.7 04/06/21
85  * @since 1.5
86  */

87
88 public class Rdn implements Serializable JavaDoc, Comparable JavaDoc<Object JavaDoc> {
89
90     // private transient ArrayList<RdnEntry> entries;
91
private transient ArrayList JavaDoc entries;
92
93     // The common case.
94
private static final int DEFAULT_SIZE = 1;
95
96     private static final long serialVersionUID = -5994465067210009656L;
97
98     /**
99      * Constructs an Rdn from the given attribute set. See
100      * {@link javax.naming.directory.Attributes Attributes}.
101      * <p>
102      * The string attribute values are not interpretted as
103      * <a HREF="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>
104      * formatted RDN strings. That is, the values are used
105      * literally (not parsed) and assumed to be unescaped.
106      *
107      * @param attrSet The non-null and non-empty attributes containing
108      * type/value mappings.
109      * @throws InvalidNameException If contents of <tt>attrSet</tt> cannot
110      * be used to construct a valid RDN.
111      */

112     public Rdn(Attributes JavaDoc attrSet) throws InvalidNameException JavaDoc {
113     if (attrSet.size() == 0) {
114         throw new InvalidNameException JavaDoc("Attributes cannot be empty");
115     }
116     entries = new ArrayList JavaDoc(attrSet.size());
117     NamingEnumeration JavaDoc attrs = attrSet.getAll();
118     try {
119         for (int nEntries = 0; attrs.hasMore(); nEntries++) {
120         RdnEntry entry = new RdnEntry();
121         Attribute JavaDoc attr = (Attribute JavaDoc) attrs.next();
122         entry.type = attr.getID();
123         entry.value = attr.get();
124         entries.add(nEntries, entry);
125         }
126     } catch (NamingException JavaDoc e) {
127         InvalidNameException JavaDoc e2 = new InvalidNameException JavaDoc(
128                     e.getMessage());
129         e2.initCause(e);
130         throw e2;
131     }
132     sort(); // arrange entries for comparison
133
}
134
135     /**
136      * Constructs an Rdn from the given string.
137      * This constructor takes a string formatted according to the rules
138      * defined in <a HREF="http://ietf.org//rfc/rfc2253.txt">RFC 2253</a>
139      * and described in the class description for
140      * {@link javax.naming.ldap.LdapName}.
141      *
142      * @param rdnString The non-null and non-empty RFC2253 formatted string.
143      * @throws InvalidNameException If a syntax error occurs during
144      * parsing of the rdnString.
145      */

146     public Rdn(String JavaDoc rdnString) throws InvalidNameException JavaDoc {
147     entries = new ArrayList JavaDoc(DEFAULT_SIZE);
148     (new Rfc2253Parser JavaDoc(rdnString)).parseRdn(this);
149     }
150
151     /**
152      * Constructs an Rdn from the given <tt>rdn</tt>.
153      * The contents of the <tt>rdn</tt> are simply copied into the newly
154      * created Rdn.
155      * @param rdn The non-null Rdn to be copied.
156      */

157     public Rdn(Rdn JavaDoc rdn) {
158     entries = new ArrayList JavaDoc(rdn.entries.size());
159     entries.addAll(rdn.entries);
160     }
161
162     /**
163      * Constructs an Rdn from the given attribute type and
164      * value.
165      * The string attribute values are not interpretted as
166      * <a HREF="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>
167      * formatted RDN strings. That is, the values are used
168      * literally (not parsed) and assumed to be unescaped.
169      *
170      * @param type The non-null and non-empty string attribute type.
171      * @param value The non-null and non-empty attribute value.
172      * @throws InvalidNameException If type/value cannot be used to
173      * construct a valid RDN.
174      * @see #toString()
175      */

176     public Rdn(String JavaDoc type, Object JavaDoc value) throws InvalidNameException JavaDoc {
177     if (value == null) {
178         throw new NullPointerException JavaDoc("Cannot set value to null");
179     }
180     if (type.equals("") || isEmptyValue(value)) {
181         throw new InvalidNameException JavaDoc(
182         "type or value cannot be empty, type:" + type +
183         " value:" + value);
184     }
185     entries = new ArrayList JavaDoc(DEFAULT_SIZE);
186     put(type, value);
187     }
188
189     private boolean isEmptyValue(Object JavaDoc val) {
190     return ((val instanceof String JavaDoc) && val.equals("")) ||
191     ((val instanceof byte[]) && (((byte[]) val).length == 0));
192     }
193
194     // An empty constructor used by the parser
195
Rdn() {
196     entries = new ArrayList JavaDoc(DEFAULT_SIZE);
197     }
198
199     /*
200      * Adds the given attribute type and value to this Rdn.
201      * The string attribute values are not interpretted as
202      * <a HREF="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>
203      * formatted RDN strings. That is the values are used
204      * literally (not parsed) and assumed to be unescaped.
205      *
206      * @param type The non-null and non-empty string attribute type.
207      * @param value The non-null and non-empty attribute value.
208      * @return The updated Rdn, not a new one. Cannot be null.
209      * @see #toString()
210      */

211     Rdn JavaDoc put(String JavaDoc type, Object JavaDoc value) {
212
213     // create new Entry
214
RdnEntry newEntry = new RdnEntry();
215     newEntry.type = type;
216         if (value instanceof byte[]) { // clone the byte array
217
newEntry.value = ((byte[]) value).clone();
218     } else {
219         newEntry.value = value;
220     }
221     entries.add(newEntry);
222     return this;
223     }
224
225     void sort() {
226     if (entries.size() > 1) {
227         Collections.sort(entries);
228     }
229     }
230
231     /**
232      * Retrieves one of this Rdn's value.
233      * This is a convenience method for obtaining the value,
234      * when the RDN contains a single type and value mapping,
235      * which is the common RDN usage.
236      * <p>
237      * For a multi-valued RDN, this method returns value corresponding
238      * to the type returned by {@link #getType() getType()} method.
239      *
240      * @return The non-null attribute value.
241      */

242     public Object JavaDoc getValue() {
243     return ((RdnEntry) entries.get(0)).getValue();
244     }
245
246     /**
247      * Retrieves one of this Rdn's type.
248      * This is a convenience method for obtaining the type,
249      * when the RDN contains a single type and value mapping,
250      * which is the common RDN usage.
251      * <p>
252      * For a multi-valued RDN, the type/value pairs have
253      * no specific order defined on them. In that case, this method
254      * returns type of one of the type/value pairs.
255      * The {@link #getValue() getValue()} method returns the
256      * value corresponding to the type returned by this method.
257      *
258      * @return The non-null attribute type.
259      */

260     public String JavaDoc getType() {
261     return ((RdnEntry) entries.get(0)).getType();
262     }
263
264     /**
265      * Returns this Rdn as a string represented in a format defined by
266      * <a HREF="http://ietf.org//rfc/rfc2253.txt">RFC 2253</a> and described
267      * in the class description for {@link javax.naming.ldap.LdapName LdapName}.
268      *
269      * @return The string representation of the Rdn.
270      */

271     public String JavaDoc toString() {
272     StringBuilder JavaDoc builder = new StringBuilder JavaDoc();
273     int size = entries.size();
274     if (size > 0) {
275             builder.append(entries.get(0));
276     }
277     for (int next = 1; next < size; next++) {
278             builder.append('+');
279             builder.append(entries.get(next));
280         }
281         return builder.toString();
282     }
283
284     /**
285      * Compares this Rdn with the specified Object for order.
286      * Returns a negative integer, zero, or a positive integer as this
287      * Rdn is less than, equal to, or greater than the given Object.
288      * <p>
289      * If obj is null or not an instance of Rdn, ClassCastException
290      * is thrown.
291      * <p>
292      * The attribute type and value pairs of the RDNs are lined up
293      * against each other and compared lexicographically. The order of
294      * components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
295      * significant.
296      *
297      * @param obj The non-null object to compare against.
298      * @return A negative integer, zero, or a positive integer as this Rdn
299      * is less than, equal to, or greater than the given Object.
300      * @exception ClassCastException if obj is null or not a Rdn.
301      * <p>
302      */

303     public int compareTo(Object JavaDoc obj) {
304     if (!(obj instanceof Rdn JavaDoc)) {
305             throw new ClassCastException JavaDoc("The obj is not a Rdn");
306         }
307     if (obj == this) {
308         return 0;
309     }
310     Rdn JavaDoc that = (Rdn JavaDoc) obj;
311         int minSize = Math.min(entries.size(), that.entries.size());
312         for (int i = 0; i < minSize; i++) {
313
314         // Compare a single pair of type/value pairs.
315
int diff = ((RdnEntry) entries.get(i)).compareTo(
316                     that.entries.get(i));
317             if (diff != 0) {
318         return diff;
319             }
320         }
321     return (entries.size() - that.entries.size()); // longer RDN wins
322
}
323
324     /**
325      * Compares the specified Object with this Rdn for equality.
326      * Returns true if the given object is also a Rdn and the two Rdns
327      * represent the same attribute type and value mappings. The order of
328      * components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
329      * significant.
330      * <p>
331      * Type and value equalilty matching is done as below:
332      * <ul>
333      * <li> The types are compared for equality with their case ignored.
334      * <li> String values with different but equivalent usage of quoting,
335      * escaping, or UTF8-hex-encoding are considered equal.
336      * The case of the values is ignored during the comparison.
337      * </ul>
338      * <p>
339      * If obj is null or not an instance of Rdn, false is returned.
340      * <p>
341      * @param obj object to be compared for equality with this Rdn.
342      * @return true if the specified object is equal to this Rdn.
343      * @see #hashCode()
344      */

345     public boolean equals(Object JavaDoc obj) {
346     if (obj == this) {
347         return true;
348     }
349     if (!(obj instanceof Rdn JavaDoc)) {
350         return false;
351     }
352     Rdn JavaDoc that = (Rdn JavaDoc) obj;
353     if (entries.size() != that.size()) {
354         return false;
355     }
356         for (int i = 0; i < entries.size(); i++) {
357         if (!entries.get(i).equals(that.entries.get(i))) {
358         return false;
359         }
360     }
361     return true;
362     }
363
364     /**
365      * Returns the hash code of this RDN. Two RDNs that are
366      * equal (according to the equals method) will have the same
367      * hash code.
368      *
369      * @return An int representing the hash code of this Rdn.
370      * @see #equals
371      */

372     public int hashCode() {
373
374     // Sum up the hash codes of the components.
375
int hash = 0;
376
377         // For each type/value pair...
378
for (int i = 0; i < entries.size(); i++) {
379         hash += entries.get(i).hashCode();
380         }
381         return hash;
382     }
383
384     /**
385      * Retrieves the {@link javax.naming.directory.Attributes Attributes}
386      * view of the type/value mappings contained in this Rdn.
387      *
388      * @return The non-null attributes containing the type/value
389      * mappings of this Rdn.
390      */

391     public Attributes JavaDoc toAttributes() {
392     Attributes JavaDoc attrs = new BasicAttributes JavaDoc(true);
393         for (int i = 0; i < entries.size(); i++) {
394         RdnEntry entry = (RdnEntry) entries.get(i);
395         Attribute JavaDoc attr = attrs.put(entry.getType(), entry.getValue());
396         if (attr != null) {
397         attr.add(entry.getValue());
398         attrs.put(entry.getType(), attr);
399         }
400     }
401         return attrs;
402     }
403
404
405     private static class RdnEntry implements Comparable JavaDoc {
406     private String JavaDoc type;
407     private Object JavaDoc value;
408
409     // If non-null, a cannonical representation of the value suitable
410
// for comparison using String.compareTo()
411
private String JavaDoc comparable = null;
412
413     String JavaDoc getType() {
414         return type;
415     }
416
417     Object JavaDoc getValue() {
418         return value;
419     }
420
421     public int compareTo(Object JavaDoc obj) {
422
423         // Any change here affecting equality must be
424
// reflected in hashCode().
425
RdnEntry that = (RdnEntry) obj;
426
427             int diff = type.toUpperCase().compareTo(
428                         that.type.toUpperCase());
429             if (diff != 0) {
430                 return diff;
431             }
432         if (value.equals(that.value)) { // try shortcut
433
return 0;
434             }
435             return getValueComparable().compareTo(
436             that.getValueComparable());
437         }
438
439     public boolean equals(Object JavaDoc obj) {
440         if (obj == this) {
441         return true;
442         }
443         if (!(obj instanceof RdnEntry)) {
444         return false;
445         }
446
447             // Any change here must be reflected in hashCode()
448
RdnEntry that = (RdnEntry) obj;
449         return (type.equalsIgnoreCase(that.type)) &&
450             (getValueComparable().equals(
451             that.getValueComparable()));
452     }
453
454     public int hashCode() {
455         return (type.toUpperCase().hashCode() +
456         getValueComparable().hashCode());
457     }
458
459     public String JavaDoc toString() {
460         return type + "=" + escapeValue(value);
461     }
462
463     private String JavaDoc getValueComparable() {
464         if (comparable != null) {
465         return comparable; // return cached result
466
}
467
468         // cache result
469
if (value instanceof byte[]) {
470         comparable = escapeBinaryValue((byte[]) value);
471         } else {
472         comparable = ((String JavaDoc) value).toUpperCase();
473         }
474         return comparable;
475         }
476     }
477
478     /**
479      * Retrieves the number of attribute type/value pairs in this Rdn.
480      * @return The non-negative number of type/value pairs in this Rdn.
481      */

482     public int size() {
483         return entries.size();
484     }
485
486     /**
487      * Given the value of an attribute, returns a string escaped according
488      * to the rules specified in
489      * <a HREF="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
490      * <p>
491      * For example, if the val is "Sue, Grabbit and Runn", the escaped
492      * value returned by this method is "Sue\, Grabbit and Runn".
493      * <p>
494      * A string value is represented as a String and binary value
495      * as a byte array.
496      *
497      * @param val The non-null object to be escaped.
498      * @return Escaped string value.
499      * @throws ClassCastException if val is is not a String or byte array.
500      */

501     public static String JavaDoc escapeValue(Object JavaDoc val) {
502     return (val instanceof byte[])
503         ? escapeBinaryValue((byte[])val)
504         : escapeStringValue((String JavaDoc)val);
505     }
506
507     /*
508      * Given the value of a string-valued attribute, returns a
509      * string suitable for inclusion in a DN. This is accomplished by
510      * using backslash (\) to escape the following characters:
511      * leading and trailing whitespace
512      * , = + < > # ; " \
513      */

514     private static final String JavaDoc escapees = ",=+<>#;\"\\";
515
516     private static String JavaDoc escapeStringValue(String JavaDoc val) {
517
518         char[] chars = val.toCharArray();
519         StringBuilder JavaDoc builder = new StringBuilder JavaDoc(2 * val.length());
520
521         // Find leading and trailing whitespace.
522
int lead; // index of first char that is not leading whitespace
523
for (lead = 0; lead < chars.length; lead++) {
524         if (!isWhitespace(chars[lead])) {
525             break;
526         }
527         }
528         int trail; // index of last char that is not trailing whitespace
529
for (trail = chars.length - 1; trail >= 0; trail--) {
530         if (!isWhitespace(chars[trail])) {
531             break;
532         }
533         }
534
535         for (int i = 0; i < chars.length; i++) {
536         char c = chars[i];
537         if ((i < lead) || (i > trail) || (escapees.indexOf(c) >= 0)) {
538             builder.append('\\');
539         }
540         builder.append(c);
541         }
542         return builder.toString();
543     }
544
545     /*
546      * Given the value of a binary attribute, returns a string
547      * suitable for inclusion in a DN (such as "#CEB1DF80").
548      * TBD: This method should actually generate the ber encoding
549      * of the binary value
550      */

551     private static String JavaDoc escapeBinaryValue(byte[] val) {
552
553     StringBuilder JavaDoc builder = new StringBuilder JavaDoc(1 + 2 * val.length);
554     builder.append("#");
555
556     for (int i = 0; i < val.length; i++) {
557         byte b = val[i];
558         builder.append(Character.forDigit(0xF & (b >>> 4), 16));
559         builder.append(Character.forDigit(0xF & b, 16));
560     }
561     return builder.toString();
562     // return builder.toString().toUpperCase();
563
}
564
565     /**
566      * Given an attribute value string formated according to the rules
567      * specified in
568      * <a HREF="http://ietf.org//rfc/rfc2253.txt">RFC 2253</a>,
569      * returns the unformated value. Escapes and quotes are
570      * stripped away, and hex-encoded UTF-8 is converted to equivalent
571      * UTF-16 characters. Returns a string value as a String, and a
572      * binary value as a byte array.
573      * <p>
574      * Legal and illegal values are defined in RFC 2253.
575      * This method is generous in accepting the values and does not
576      * catch all illegal values.
577      * Therefore, passing in an illegal value might not necessarily
578      * trigger an <tt>IllegalArgumentException</tt>.
579      *
580      * @param val The non-null string to be unescaped.
581      * @return Unescaped value.
582      * @throws IllegalArgumentException When an Illegal value
583      * is provided.
584      */

585     public static Object JavaDoc unescapeValue(String JavaDoc val) {
586
587         char[] chars = val.toCharArray();
588         int beg = 0;
589         int end = chars.length;
590
591         // Trim off leading and trailing whitespace.
592
while ((beg < end) && isWhitespace(chars[beg])) {
593         ++beg;
594         }
595
596         while ((beg < end) && isWhitespace(chars[end - 1])) {
597         --end;
598         }
599
600         // Add back the trailing whitespace with a preceeding '\'
601
// (escaped or unescaped) that was taken off in the above
602
// loop. Whether or not to retain this whitespace is decided below.
603
if (end != chars.length &&
604             (beg < end) &&
605             chars[end - 1] == '\\') {
606         end++;
607         }
608         if (beg >= end) {
609         return "";
610         }
611
612         if (chars[beg] == '#') {
613         // Value is binary (eg: "#CEB1DF80").
614
return decodeHexPairs(chars, ++beg, end);
615         }
616
617         // Trim off quotes.
618
if ((chars[beg] == '\"') && (chars[end - 1] == '\"')) {
619         ++beg;
620         --end;
621         }
622
623         StringBuilder JavaDoc builder = new StringBuilder JavaDoc(end - beg);
624         int esc = -1; // index of the last escaped character
625

626         for (int i = beg; i < end; i++) {
627         if ((chars[i] == '\\') && (i + 1 < end)) {
628             if (!Character.isLetterOrDigit(chars[i + 1])) {
629             ++i; // skip backslash
630
builder.append(chars[i]); // snarf escaped char
631
esc = i;
632             } else {
633
634             // Convert hex-encoded UTF-8 to 16-bit chars.
635
byte[] utf8 = getUtf8Octets(chars, i, end);
636             if (utf8.length > 0) {
637                 try {
638                 builder.append(new String JavaDoc(utf8, "UTF8"));
639                 } catch (java.io.UnsupportedEncodingException JavaDoc e) {
640                 // shouldn't happen
641
}
642                 i += utf8.length * 3 - 1;
643             } else { // no utf8 bytes available, invalid DN
644

645                 // '/' has no meaning, throw exception
646
throw new IllegalArgumentException JavaDoc(
647                 "Not a valid attribute string value:" +
648                 val + ",improper usage of backslash");
649             }
650             }
651         } else {
652             builder.append(chars[i]); // snarf unescaped char
653
}
654         }
655
656         // Get rid of the unescaped trailing whitespace with the
657
// preceeding '\' character that was previously added back.
658
int len = builder.length();
659         if (isWhitespace(builder.charAt(len - 1)) && esc != (end - 1)) {
660         builder.setLength(len - 1);
661         }
662         return builder.toString();
663     }
664
665
666     /*
667      * Given an array of chars (with starting and ending indexes into it)
668      * representing bytes encoded as hex-pairs (such as "CEB1DF80"),
669      * returns a byte array containing the decoded bytes.
670      */

671     private static byte[] decodeHexPairs(char[] chars, int beg, int end) {
672         byte[] bytes = new byte[(end - beg) / 2];
673         for (int i = 0; beg + 1 < end; i++) {
674         int hi = Character.digit(chars[beg], 16);
675         int lo = Character.digit(chars[beg + 1], 16);
676         if (hi < 0 || lo < 0) {
677             break;
678         }
679         bytes[i] = (byte)((hi<<4) + lo);
680         beg += 2;
681         }
682         if (beg != end) {
683         throw new IllegalArgumentException JavaDoc(
684             "Illegal attribute value: " + new String JavaDoc(chars));
685         }
686         return bytes;
687     }
688
689     /*
690      * Given an array of chars (with starting and ending indexes into it),
691      * finds the largest prefix consisting of hex-encoded UTF-8 octets,
692      * and returns a byte array containing the corresponding UTF-8 octets.
693      *
694      * Hex-encoded UTF-8 octets look like this:
695      * \03\B1\DF\80
696      */

697     private static byte[] getUtf8Octets(char[] chars, int beg, int end) {
698         byte[] utf8 = new byte[(end - beg) / 3]; // allow enough room
699
int len = 0; // index of first unused byte in utf8
700

701         while ((beg + 2 < end) &&
702            (chars[beg++] == '\\')) {
703         int hi = Character.digit(chars[beg++], 16);
704         int lo = Character.digit(chars[beg++], 16);
705         if (hi < 0 || lo < 0) {
706            break;
707         }
708         utf8[len++] = (byte)((hi<<4) + lo);
709         }
710         if (len == utf8.length) {
711         return utf8;
712         } else {
713         byte[] res = new byte[len];
714         System.arraycopy(utf8, 0, res, 0, len);
715         return res;
716         }
717     }
718
719     /*
720      * Best guess as to what RFC 2253 means by "whitespace".
721      */

722     private static boolean isWhitespace(char c) {
723         return (c == ' ' || c == '\r');
724     }
725
726     /**
727      * Serializes only the unparsed RDN, for compactness and to avoid
728      * any implementation dependency.
729      *
730      * @serialData The RDN string
731      */

732     private void writeObject(ObjectOutputStream JavaDoc s)
733             throws java.io.IOException JavaDoc {
734     s.defaultWriteObject();
735         s.writeObject(toString());
736     }
737
738     private void readObject(ObjectInputStream JavaDoc s)
739             throws IOException JavaDoc, ClassNotFoundException JavaDoc {
740     s.defaultReadObject();
741     entries = new ArrayList JavaDoc(DEFAULT_SIZE);
742         String JavaDoc unparsed = (String JavaDoc) s.readObject();
743         try {
744         (new Rfc2253Parser JavaDoc(unparsed)).parseRdn(this);
745         } catch (InvalidNameException JavaDoc e) {
746             // shouldn't happen
747
throw new java.io.StreamCorruptedException JavaDoc(
748                     "Invalid name: " + unparsed);
749         }
750     }
751 }
752
Popular Tags