Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/naming/ldap/Rdn.java
38918 views
1
/*
2
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package javax.naming.ldap;
27
28
import java.util.Iterator;
29
import java.util.NoSuchElementException;
30
import java.util.ArrayList;
31
import java.util.Locale;
32
import java.util.Collections;
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);
147
throw e2;
148
}
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.
191
* @see #toString()
192
*/
193
public Rdn(String type, Object value) throws InvalidNameException {
194
if (value == null) {
195
throw new NullPointerException("Cannot set value to null");
196
}
197
if (type.equals("") || isEmptyValue(value)) {
198
throw new InvalidNameException(
199
"type or value cannot be empty, type:" + type +
200
" value:" + value);
201
}
202
entries = new ArrayList<>(DEFAULT_SIZE);
203
put(type, value);
204
}
205
206
private boolean isEmptyValue(Object val) {
207
return ((val instanceof String) && val.equals("")) ||
208
((val instanceof byte[]) && (((byte[]) val).length == 0));
209
}
210
211
// An empty constructor used by the parser
212
Rdn() {
213
entries = new ArrayList<>(DEFAULT_SIZE);
214
}
215
216
/*
217
* Adds the given attribute type and value to this Rdn.
218
* The string attribute values are not interpreted as
219
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
220
* formatted RDN strings. That is the values are used
221
* literally (not parsed) and assumed to be unescaped.
222
*
223
* @param type The non-null and non-empty string attribute type.
224
* @param value The non-null and non-empty attribute value.
225
* @return The updated Rdn, not a new one. Cannot be null.
226
* @see #toString()
227
*/
228
Rdn put(String type, Object value) {
229
230
// create new Entry
231
RdnEntry newEntry = new RdnEntry();
232
newEntry.type = type;
233
if (value instanceof byte[]) { // clone the byte array
234
newEntry.value = ((byte[]) value).clone();
235
} else {
236
newEntry.value = value;
237
}
238
entries.add(newEntry);
239
return this;
240
}
241
242
void sort() {
243
if (entries.size() > 1) {
244
Collections.sort(entries);
245
}
246
}
247
248
/**
249
* Retrieves one of this Rdn's value.
250
* This is a convenience method for obtaining the value,
251
* when the RDN contains a single type and value mapping,
252
* which is the common RDN usage.
253
* <p>
254
* For a multi-valued RDN, this method returns value corresponding
255
* to the type returned by {@link #getType() getType()} method.
256
*
257
* @return The non-null attribute value.
258
*/
259
public Object getValue() {
260
return entries.get(0).getValue();
261
}
262
263
/**
264
* Retrieves one of this Rdn's type.
265
* This is a convenience method for obtaining the type,
266
* when the RDN contains a single type and value mapping,
267
* which is the common RDN usage.
268
* <p>
269
* For a multi-valued RDN, the type/value pairs have
270
* no specific order defined on them. In that case, this method
271
* returns type of one of the type/value pairs.
272
* The {@link #getValue() getValue()} method returns the
273
* value corresponding to the type returned by this method.
274
*
275
* @return The non-null attribute type.
276
*/
277
public String getType() {
278
return entries.get(0).getType();
279
}
280
281
/**
282
* Returns this Rdn as a string represented in a format defined by
283
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a> and described
284
* in the class description for {@link javax.naming.ldap.LdapName LdapName}.
285
*
286
* @return The string representation of the Rdn.
287
*/
288
public String toString() {
289
StringBuilder builder = new StringBuilder();
290
int size = entries.size();
291
if (size > 0) {
292
builder.append(entries.get(0));
293
}
294
for (int next = 1; next < size; next++) {
295
builder.append('+');
296
builder.append(entries.get(next));
297
}
298
return builder.toString();
299
}
300
301
/**
302
* Compares this Rdn with the specified Object for order.
303
* Returns a negative integer, zero, or a positive integer as this
304
* Rdn is less than, equal to, or greater than the given Object.
305
* <p>
306
* If obj is null or not an instance of Rdn, ClassCastException
307
* is thrown.
308
* <p>
309
* The attribute type and value pairs of the RDNs are lined up
310
* against each other and compared lexicographically. The order of
311
* components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
312
* significant.
313
*
314
* @param obj The non-null object to compare against.
315
* @return A negative integer, zero, or a positive integer as this Rdn
316
* is less than, equal to, or greater than the given Object.
317
* @exception ClassCastException if obj is null or not a Rdn.
318
*/
319
public int compareTo(Object obj) {
320
if (!(obj instanceof Rdn)) {
321
throw new ClassCastException("The obj is not a Rdn");
322
}
323
if (obj == this) {
324
return 0;
325
}
326
Rdn that = (Rdn) obj;
327
int minSize = Math.min(entries.size(), that.entries.size());
328
for (int i = 0; i < minSize; i++) {
329
330
// Compare a single pair of type/value pairs.
331
int diff = entries.get(i).compareTo(that.entries.get(i));
332
if (diff != 0) {
333
return diff;
334
}
335
}
336
return (entries.size() - that.entries.size()); // longer RDN wins
337
}
338
339
/**
340
* Compares the specified Object with this Rdn for equality.
341
* Returns true if the given object is also a Rdn and the two Rdns
342
* represent the same attribute type and value mappings. The order of
343
* components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
344
* significant.
345
* <p>
346
* Type and value equality matching is done as below:
347
* <ul>
348
* <li> The types are compared for equality with their case ignored.
349
* <li> String values with different but equivalent usage of quoting,
350
* escaping, or UTF8-hex-encoding are considered equal.
351
* The case of the values is ignored during the comparison.
352
* </ul>
353
* <p>
354
* If obj is null or not an instance of Rdn, false is returned.
355
* <p>
356
* @param obj object to be compared for equality with this Rdn.
357
* @return true if the specified object is equal to this Rdn.
358
* @see #hashCode()
359
*/
360
public boolean equals(Object obj) {
361
if (obj == this) {
362
return true;
363
}
364
if (!(obj instanceof Rdn)) {
365
return false;
366
}
367
Rdn that = (Rdn) obj;
368
if (entries.size() != that.size()) {
369
return false;
370
}
371
for (int i = 0; i < entries.size(); i++) {
372
if (!entries.get(i).equals(that.entries.get(i))) {
373
return false;
374
}
375
}
376
return true;
377
}
378
379
/**
380
* Returns the hash code of this RDN. Two RDNs that are
381
* equal (according to the equals method) will have the same
382
* hash code.
383
*
384
* @return An int representing the hash code of this Rdn.
385
* @see #equals
386
*/
387
public int hashCode() {
388
389
// Sum up the hash codes of the components.
390
int hash = 0;
391
392
// For each type/value pair...
393
for (int i = 0; i < entries.size(); i++) {
394
hash += entries.get(i).hashCode();
395
}
396
return hash;
397
}
398
399
/**
400
* Retrieves the {@link javax.naming.directory.Attributes Attributes}
401
* view of the type/value mappings contained in this Rdn.
402
*
403
* @return The non-null attributes containing the type/value
404
* mappings of this Rdn.
405
*/
406
public Attributes toAttributes() {
407
Attributes attrs = new BasicAttributes(true);
408
for (int i = 0; i < entries.size(); i++) {
409
RdnEntry entry = entries.get(i);
410
Attribute attr = attrs.put(entry.getType(), entry.getValue());
411
if (attr != null) {
412
attr.add(entry.getValue());
413
attrs.put(attr);
414
}
415
}
416
return attrs;
417
}
418
419
420
private static class RdnEntry implements Comparable<RdnEntry> {
421
private String type;
422
private Object value;
423
424
// If non-null, a cannonical representation of the value suitable
425
// for comparison using String.compareTo()
426
private String comparable = null;
427
428
String getType() {
429
return type;
430
}
431
432
Object getValue() {
433
return value;
434
}
435
436
public int compareTo(RdnEntry that) {
437
int diff = type.compareToIgnoreCase(that.type);
438
if (diff != 0) {
439
return diff;
440
}
441
if (value.equals(that.value)) { // try shortcut
442
return 0;
443
}
444
return getValueComparable().compareTo(
445
that.getValueComparable());
446
}
447
448
public boolean equals(Object obj) {
449
if (obj == this) {
450
return true;
451
}
452
if (!(obj instanceof RdnEntry)) {
453
return false;
454
}
455
456
// Any change here must be reflected in hashCode()
457
RdnEntry that = (RdnEntry) obj;
458
return (type.equalsIgnoreCase(that.type)) &&
459
(getValueComparable().equals(
460
that.getValueComparable()));
461
}
462
463
public int hashCode() {
464
return (type.toUpperCase(Locale.ENGLISH).hashCode() +
465
getValueComparable().hashCode());
466
}
467
468
public String toString() {
469
return type + "=" + escapeValue(value);
470
}
471
472
private String getValueComparable() {
473
if (comparable != null) {
474
return comparable; // return cached result
475
}
476
477
// cache result
478
if (value instanceof byte[]) {
479
comparable = escapeBinaryValue((byte[]) value);
480
} else {
481
comparable = ((String) value).toUpperCase(Locale.ENGLISH);
482
}
483
return comparable;
484
}
485
}
486
487
/**
488
* Retrieves the number of attribute type/value pairs in this Rdn.
489
* @return The non-negative number of type/value pairs in this Rdn.
490
*/
491
public int size() {
492
return entries.size();
493
}
494
495
/**
496
* Given the value of an attribute, returns a string escaped according
497
* to the rules specified in
498
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
499
* <p>
500
* For example, if the val is "Sue, Grabbit and Runn", the escaped
501
* value returned by this method is "Sue\, Grabbit and Runn".
502
* <p>
503
* A string value is represented as a String and binary value
504
* as a byte array.
505
*
506
* @param val The non-null object to be escaped.
507
* @return Escaped string value.
508
* @throws ClassCastException if val is is not a String or byte array.
509
*/
510
public static String escapeValue(Object val) {
511
return (val instanceof byte[])
512
? escapeBinaryValue((byte[])val)
513
: escapeStringValue((String)val);
514
}
515
516
/*
517
* Given the value of a string-valued attribute, returns a
518
* string suitable for inclusion in a DN. This is accomplished by
519
* using backslash (\) to escape the following characters:
520
* leading and trailing whitespace
521
* , = + < > # ; " \
522
*/
523
private static final String escapees = ",=+<>#;\"\\";
524
525
private static String escapeStringValue(String val) {
526
527
char[] chars = val.toCharArray();
528
StringBuilder builder = new StringBuilder(2 * val.length());
529
530
// Find leading and trailing whitespace.
531
int lead; // index of first char that is not leading whitespace
532
for (lead = 0; lead < chars.length; lead++) {
533
if (!isWhitespace(chars[lead])) {
534
break;
535
}
536
}
537
int trail; // index of last char that is not trailing whitespace
538
for (trail = chars.length - 1; trail >= 0; trail--) {
539
if (!isWhitespace(chars[trail])) {
540
break;
541
}
542
}
543
544
for (int i = 0; i < chars.length; i++) {
545
char c = chars[i];
546
if ((i < lead) || (i > trail) || (escapees.indexOf(c) >= 0)) {
547
builder.append('\\');
548
}
549
builder.append(c);
550
}
551
return builder.toString();
552
}
553
554
/*
555
* Given the value of a binary attribute, returns a string
556
* suitable for inclusion in a DN (such as "#CEB1DF80").
557
* TBD: This method should actually generate the ber encoding
558
* of the binary value
559
*/
560
private static String escapeBinaryValue(byte[] val) {
561
562
StringBuilder builder = new StringBuilder(1 + 2 * val.length);
563
builder.append("#");
564
565
for (int i = 0; i < val.length; i++) {
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 formated according to the rules
575
* specified in
576
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>,
577
* returns the unformated 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
}
607
608
// Add back the trailing whitespace with a preceding '\'
609
// (escaped or unescaped) that was taken off in the above
610
// loop. Whether or not to retain this whitespace is decided below.
611
if (end != chars.length &&
612
(beg < end) &&
613
chars[end - 1] == '\\') {
614
end++;
615
}
616
if (beg >= end) {
617
return "";
618
}
619
620
if (chars[beg] == '#') {
621
// Value is binary (eg: "#CEB1DF80").
622
return decodeHexPairs(chars, ++beg, end);
623
}
624
625
// Trim off quotes.
626
if ((chars[beg] == '\"') && (chars[end - 1] == '\"')) {
627
++beg;
628
--end;
629
}
630
631
StringBuilder builder = new StringBuilder(end - beg);
632
int esc = -1; // index of the last escaped character
633
634
for (int i = beg; i < end; i++) {
635
if ((chars[i] == '\\') && (i + 1 < end)) {
636
if (!Character.isLetterOrDigit(chars[i + 1])) {
637
++i; // skip backslash
638
builder.append(chars[i]); // snarf escaped char
639
esc = i;
640
} else {
641
642
// Convert hex-encoded UTF-8 to 16-bit chars.
643
byte[] utf8 = getUtf8Octets(chars, i, end);
644
if (utf8.length > 0) {
645
try {
646
builder.append(new String(utf8, "UTF8"));
647
} catch (java.io.UnsupportedEncodingException e) {
648
// shouldn't happen
649
}
650
i += utf8.length * 3 - 1;
651
} else { // no utf8 bytes available, invalid DN
652
653
// '/' has no meaning, throw exception
654
throw new IllegalArgumentException(
655
"Not a valid attribute string value:" +
656
val + ",improper usage of backslash");
657
}
658
}
659
} else {
660
builder.append(chars[i]); // snarf unescaped char
661
}
662
}
663
664
// Get rid of the unescaped trailing whitespace with the
665
// preceding '\' character that was previously added back.
666
int len = builder.length();
667
if (isWhitespace(builder.charAt(len - 1)) && esc != (end - 1)) {
668
builder.setLength(len - 1);
669
}
670
return builder.toString();
671
}
672
673
674
/*
675
* Given an array of chars (with starting and ending indexes into it)
676
* representing bytes encoded as hex-pairs (such as "CEB1DF80"),
677
* returns a byte array containing the decoded bytes.
678
*/
679
private static byte[] decodeHexPairs(char[] chars, int beg, int end) {
680
byte[] bytes = new byte[(end - beg) / 2];
681
for (int i = 0; beg + 1 < end; i++) {
682
int hi = Character.digit(chars[beg], 16);
683
int lo = Character.digit(chars[beg + 1], 16);
684
if (hi < 0 || lo < 0) {
685
break;
686
}
687
bytes[i] = (byte)((hi<<4) + lo);
688
beg += 2;
689
}
690
if (beg != end) {
691
throw new IllegalArgumentException(
692
"Illegal attribute value: " + new String(chars));
693
}
694
return bytes;
695
}
696
697
/*
698
* Given an array of chars (with starting and ending indexes into it),
699
* finds the largest prefix consisting of hex-encoded UTF-8 octets,
700
* and returns a byte array containing the corresponding UTF-8 octets.
701
*
702
* Hex-encoded UTF-8 octets look like this:
703
* \03\B1\DF\80
704
*/
705
private static byte[] getUtf8Octets(char[] chars, int beg, int end) {
706
byte[] utf8 = new byte[(end - beg) / 3]; // allow enough room
707
int len = 0; // index of first unused byte in utf8
708
709
while ((beg + 2 < end) &&
710
(chars[beg++] == '\\')) {
711
int hi = Character.digit(chars[beg++], 16);
712
int lo = Character.digit(chars[beg++], 16);
713
if (hi < 0 || lo < 0) {
714
break;
715
}
716
utf8[len++] = (byte)((hi<<4) + lo);
717
}
718
if (len == utf8.length) {
719
return utf8;
720
} else {
721
byte[] res = new byte[len];
722
System.arraycopy(utf8, 0, res, 0, len);
723
return res;
724
}
725
}
726
727
/*
728
* Best guess as to what RFC 2253 means by "whitespace".
729
*/
730
private static boolean isWhitespace(char c) {
731
return (c == ' ' || c == '\r');
732
}
733
734
/**
735
* Serializes only the unparsed RDN, for compactness and to avoid
736
* any implementation dependency.
737
*
738
* @serialData The RDN string
739
*/
740
private void writeObject(ObjectOutputStream s)
741
throws java.io.IOException {
742
s.defaultWriteObject();
743
s.writeObject(toString());
744
}
745
746
private void readObject(ObjectInputStream s)
747
throws IOException, ClassNotFoundException {
748
s.defaultReadObject();
749
entries = new ArrayList<>(DEFAULT_SIZE);
750
String unparsed = (String) s.readObject();
751
try {
752
(new Rfc2253Parser(unparsed)).parseRdn(this);
753
} catch (InvalidNameException e) {
754
// shouldn't happen
755
throw new java.io.StreamCorruptedException(
756
"Invalid name: " + unparsed);
757
}
758
}
759
}
760
761