Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.naming/share/classes/javax/naming/ldap/Rdn.java
41159 views
1
/*
2
* Copyright (c) 2003, 2020, 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 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
@java.io.Serial
114
private static final long serialVersionUID = -5994465067210009656L;
115
116
/**
117
* Constructs an Rdn from the given attribute set. See
118
* {@link javax.naming.directory.Attributes Attributes}.
119
* <p>
120
* The string attribute values are not interpreted as
121
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
122
* formatted RDN strings. That is, the values are used
123
* literally (not parsed) and assumed to be unescaped.
124
*
125
* @param attrSet The non-null and non-empty attributes containing
126
* type/value mappings.
127
* @throws InvalidNameException If contents of {@code attrSet} cannot
128
* be used to construct a valid RDN.
129
*/
130
public Rdn(Attributes attrSet) throws InvalidNameException {
131
if (attrSet.size() == 0) {
132
throw new InvalidNameException("Attributes cannot be empty");
133
}
134
entries = new ArrayList<>(attrSet.size());
135
NamingEnumeration<? extends Attribute> attrs = attrSet.getAll();
136
try {
137
for (int nEntries = 0; attrs.hasMore(); nEntries++) {
138
RdnEntry entry = new RdnEntry();
139
Attribute attr = attrs.next();
140
entry.type = attr.getID();
141
entry.value = attr.get();
142
entries.add(nEntries, entry);
143
}
144
} catch (NamingException e) {
145
InvalidNameException e2 = new InvalidNameException(
146
e.getMessage());
147
e2.initCause(e);
148
throw e2;
149
}
150
sort(); // arrange entries for comparison
151
}
152
153
/**
154
* Constructs an Rdn from the given string.
155
* This constructor takes a string formatted according to the rules
156
* defined in <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
157
* and described in the class description for
158
* {@link javax.naming.ldap.LdapName}.
159
*
160
* @param rdnString The non-null and non-empty RFC2253 formatted string.
161
* @throws InvalidNameException If a syntax error occurs during
162
* parsing of the rdnString.
163
*/
164
public Rdn(String rdnString) throws InvalidNameException {
165
entries = new ArrayList<>(DEFAULT_SIZE);
166
(new Rfc2253Parser(rdnString)).parseRdn(this);
167
}
168
169
/**
170
* Constructs an Rdn from the given {@code rdn}.
171
* The contents of the {@code rdn} are simply copied into the newly
172
* created Rdn.
173
* @param rdn The non-null Rdn to be copied.
174
*/
175
public Rdn(Rdn rdn) {
176
entries = new ArrayList<>(rdn.entries.size());
177
entries.addAll(rdn.entries);
178
}
179
180
/**
181
* Constructs an Rdn from the given attribute type and
182
* value.
183
* The string attribute values are not interpreted as
184
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
185
* formatted RDN strings. That is, the values are used
186
* literally (not parsed) and assumed to be unescaped.
187
*
188
* @param type The non-null and non-empty string attribute type.
189
* @param value The non-null and non-empty attribute value.
190
* @throws InvalidNameException If type/value cannot be used to
191
* construct a valid RDN.
192
* @see #toString()
193
*/
194
public Rdn(String type, Object value) throws InvalidNameException {
195
if (value == null) {
196
throw new NullPointerException("Cannot set value to null");
197
}
198
if (type.equals("") || isEmptyValue(value)) {
199
throw new InvalidNameException(
200
"type or value cannot be empty, type:" + type +
201
" value:" + value);
202
}
203
entries = new ArrayList<>(DEFAULT_SIZE);
204
put(type, value);
205
}
206
207
private boolean isEmptyValue(Object val) {
208
return ((val instanceof String) && val.equals("")) ||
209
((val instanceof byte[]) && (((byte[]) val).length == 0));
210
}
211
212
// An empty constructor used by the parser
213
Rdn() {
214
entries = new ArrayList<>(DEFAULT_SIZE);
215
}
216
217
/*
218
* Adds the given attribute type and value to this Rdn.
219
* The string attribute values are not interpreted as
220
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>
221
* formatted RDN strings. That is the values are used
222
* literally (not parsed) and assumed to be unescaped.
223
*
224
* @param type The non-null and non-empty string attribute type.
225
* @param value The non-null and non-empty attribute value.
226
* @return The updated Rdn, not a new one. Cannot be null.
227
* @see #toString()
228
*/
229
Rdn put(String type, Object value) {
230
231
// create new Entry
232
RdnEntry newEntry = new RdnEntry();
233
newEntry.type = type;
234
if (value instanceof byte[]) { // clone the byte array
235
newEntry.value = ((byte[]) value).clone();
236
} else {
237
newEntry.value = value;
238
}
239
entries.add(newEntry);
240
return this;
241
}
242
243
void sort() {
244
if (entries.size() > 1) {
245
Collections.sort(entries);
246
}
247
}
248
249
/**
250
* Retrieves one of this Rdn's value.
251
* This is a convenience method for obtaining the value,
252
* when the RDN contains a single type and value mapping,
253
* which is the common RDN usage.
254
* <p>
255
* For a multi-valued RDN, this method returns value corresponding
256
* to the type returned by {@link #getType() getType()} method.
257
*
258
* @return The non-null attribute value.
259
*/
260
public Object getValue() {
261
return entries.get(0).getValue();
262
}
263
264
/**
265
* Retrieves one of this Rdn's type.
266
* This is a convenience method for obtaining the type,
267
* when the RDN contains a single type and value mapping,
268
* which is the common RDN usage.
269
* <p>
270
* For a multi-valued RDN, the type/value pairs have
271
* no specific order defined on them. In that case, this method
272
* returns type of one of the type/value pairs.
273
* The {@link #getValue() getValue()} method returns the
274
* value corresponding to the type returned by this method.
275
*
276
* @return The non-null attribute type.
277
*/
278
public String getType() {
279
return entries.get(0).getType();
280
}
281
282
/**
283
* Returns this Rdn as a string represented in a format defined by
284
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a> and described
285
* in the class description for {@link javax.naming.ldap.LdapName LdapName}.
286
*
287
* @return The string representation of the Rdn.
288
*/
289
public String toString() {
290
StringBuilder builder = new StringBuilder();
291
int size = entries.size();
292
if (size > 0) {
293
builder.append(entries.get(0));
294
}
295
for (int next = 1; next < size; next++) {
296
builder.append('+');
297
builder.append(entries.get(next));
298
}
299
return builder.toString();
300
}
301
302
/**
303
* Compares this Rdn with the specified Object for order.
304
* Returns a negative integer, zero, or a positive integer as this
305
* Rdn is less than, equal to, or greater than the given Object.
306
* <p>
307
* If obj is null or not an instance of Rdn, ClassCastException
308
* is thrown.
309
* <p>
310
* The attribute type and value pairs of the RDNs are lined up
311
* against each other and compared lexicographically. The order of
312
* components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
313
* significant.
314
*
315
* @param obj The non-null object to compare against.
316
* @return A negative integer, zero, or a positive integer as this Rdn
317
* is less than, equal to, or greater than the given Object.
318
* @throws ClassCastException if obj is null or not a Rdn.
319
*/
320
public int compareTo(Object obj) {
321
if (!(obj instanceof Rdn)) {
322
throw new ClassCastException("The obj is not a Rdn");
323
}
324
if (obj == this) {
325
return 0;
326
}
327
Rdn that = (Rdn) obj;
328
int minSize = Math.min(entries.size(), that.entries.size());
329
for (int i = 0; i < minSize; i++) {
330
331
// Compare a single pair of type/value pairs.
332
int diff = entries.get(i).compareTo(that.entries.get(i));
333
if (diff != 0) {
334
return diff;
335
}
336
}
337
return (entries.size() - that.entries.size()); // longer RDN wins
338
}
339
340
/**
341
* Compares the specified Object with this Rdn for equality.
342
* Returns true if the given object is also a Rdn and the two Rdns
343
* represent the same attribute type and value mappings. The order of
344
* components in multi-valued Rdns (such as "ou=Sales+cn=Bob") is not
345
* significant.
346
* <p>
347
* Type and value equality matching is done as below:
348
* <ul>
349
* <li> The types are compared for equality with their case ignored.
350
* <li> String values with different but equivalent usage of quoting,
351
* escaping, or UTF8-hex-encoding are considered equal.
352
* The case of the values is ignored during the comparison.
353
* </ul>
354
* <p>
355
* If obj is null or not an instance of Rdn, false is returned.
356
*
357
* @param obj object to be compared for equality with this Rdn.
358
* @return true if the specified object is equal to this Rdn.
359
* @see #hashCode()
360
*/
361
public boolean equals(Object obj) {
362
if (obj == this) {
363
return true;
364
}
365
if (!(obj instanceof Rdn)) {
366
return false;
367
}
368
Rdn that = (Rdn) obj;
369
if (entries.size() != that.size()) {
370
return false;
371
}
372
for (int i = 0; i < entries.size(); i++) {
373
if (!entries.get(i).equals(that.entries.get(i))) {
374
return false;
375
}
376
}
377
return true;
378
}
379
380
/**
381
* Returns the hash code of this RDN. Two RDNs that are
382
* equal (according to the equals method) will have the same
383
* hash code.
384
*
385
* @return An int representing the hash code of this Rdn.
386
* @see #equals
387
*/
388
public int hashCode() {
389
390
// Sum up the hash codes of the components.
391
int hash = 0;
392
393
// For each type/value pair...
394
for (int i = 0; i < entries.size(); i++) {
395
hash += entries.get(i).hashCode();
396
}
397
return hash;
398
}
399
400
/**
401
* Retrieves the {@link javax.naming.directory.Attributes Attributes}
402
* view of the type/value mappings contained in this Rdn.
403
*
404
* @return The non-null attributes containing the type/value
405
* mappings of this Rdn.
406
*/
407
public Attributes toAttributes() {
408
Attributes attrs = new BasicAttributes(true);
409
for (int i = 0; i < entries.size(); i++) {
410
RdnEntry entry = entries.get(i);
411
Attribute attr = attrs.put(entry.getType(), entry.getValue());
412
if (attr != null) {
413
attr.add(entry.getValue());
414
attrs.put(attr);
415
}
416
}
417
return attrs;
418
}
419
420
421
private static class RdnEntry implements Comparable<RdnEntry> {
422
private String type;
423
private Object value;
424
425
// If non-null, a canonical representation of the value suitable
426
// for comparison using String.compareTo()
427
private String comparable = null;
428
429
String getType() {
430
return type;
431
}
432
433
Object getValue() {
434
return value;
435
}
436
437
public int compareTo(RdnEntry that) {
438
int diff = type.compareToIgnoreCase(that.type);
439
if (diff != 0) {
440
return diff;
441
}
442
if (value.equals(that.value)) { // try shortcut
443
return 0;
444
}
445
return getValueComparable().compareTo(
446
that.getValueComparable());
447
}
448
449
public boolean equals(Object obj) {
450
if (obj == this) {
451
return true;
452
}
453
if (!(obj instanceof RdnEntry)) {
454
return false;
455
}
456
457
// Any change here must be reflected in hashCode()
458
RdnEntry that = (RdnEntry) obj;
459
return (type.equalsIgnoreCase(that.type)) &&
460
(getValueComparable().equals(
461
that.getValueComparable()));
462
}
463
464
public int hashCode() {
465
return (type.toUpperCase(Locale.ENGLISH).hashCode() +
466
getValueComparable().hashCode());
467
}
468
469
public String toString() {
470
return type + "=" + escapeValue(value);
471
}
472
473
private String getValueComparable() {
474
if (comparable != null) {
475
return comparable; // return cached result
476
}
477
478
// cache result
479
if (value instanceof byte[]) {
480
comparable = escapeBinaryValue((byte[]) value);
481
} else {
482
comparable = ((String) value).toUpperCase(Locale.ENGLISH);
483
}
484
return comparable;
485
}
486
}
487
488
/**
489
* Retrieves the number of attribute type/value pairs in this Rdn.
490
* @return The non-negative number of type/value pairs in this Rdn.
491
*/
492
public int size() {
493
return entries.size();
494
}
495
496
/**
497
* Given the value of an attribute, returns a string escaped according
498
* to the rules specified in
499
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
500
* <p>
501
* For example, if the val is "Sue, Grabbit and Runn", the escaped
502
* value returned by this method is "Sue\, Grabbit and Runn".
503
* <p>
504
* A string value is represented as a String and binary value
505
* as a byte array.
506
*
507
* @param val The non-null object to be escaped.
508
* @return Escaped string value.
509
* @throws ClassCastException if val is not a String or byte array.
510
*/
511
public static String escapeValue(Object val) {
512
return (val instanceof byte[])
513
? escapeBinaryValue((byte[])val)
514
: escapeStringValue((String)val);
515
}
516
517
/*
518
* Given the value of a string-valued attribute, returns a
519
* string suitable for inclusion in a DN. This is accomplished by
520
* using backslash (\) to escape the following characters:
521
* leading and trailing whitespace
522
* , = + < > # ; " \
523
*/
524
private static final String escapees = ",=+<>#;\"\\";
525
526
private static String escapeStringValue(String val) {
527
528
char[] chars = val.toCharArray();
529
StringBuilder builder = new StringBuilder(2 * val.length());
530
531
// Find leading and trailing whitespace.
532
int lead; // index of first char that is not leading whitespace
533
for (lead = 0; lead < chars.length; lead++) {
534
if (!isWhitespace(chars[lead])) {
535
break;
536
}
537
}
538
int trail; // index of last char that is not trailing whitespace
539
for (trail = chars.length - 1; trail >= 0; trail--) {
540
if (!isWhitespace(chars[trail])) {
541
break;
542
}
543
}
544
545
for (int i = 0; i < chars.length; i++) {
546
char c = chars[i];
547
if ((i < lead) || (i > trail) || (escapees.indexOf(c) >= 0)) {
548
builder.append('\\');
549
}
550
builder.append(c);
551
}
552
return builder.toString();
553
}
554
555
/*
556
* Given the value of a binary attribute, returns a string
557
* suitable for inclusion in a DN (such as "#CEB1DF80").
558
* TBD: This method should actually generate the ber encoding
559
* of the binary value
560
*/
561
private static String escapeBinaryValue(byte[] val) {
562
563
StringBuilder builder = new StringBuilder(1 + 2 * val.length);
564
builder.append("#");
565
566
for (int i = 0; i < val.length; i++) {
567
byte b = val[i];
568
builder.append(Character.forDigit(0xF & (b >>> 4), 16));
569
builder.append(Character.forDigit(0xF & b, 16));
570
}
571
return builder.toString();
572
}
573
574
/**
575
* Given an attribute value string formatted according to the rules
576
* specified in
577
* <a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>,
578
* returns the unformatted value. Escapes and quotes are
579
* stripped away, and hex-encoded UTF-8 is converted to equivalent
580
* UTF-16 characters. Returns a string value as a String, and a
581
* binary value as a byte array.
582
* <p>
583
* Legal and illegal values are defined in RFC 2253.
584
* This method is generous in accepting the values and does not
585
* catch all illegal values.
586
* Therefore, passing in an illegal value might not necessarily
587
* trigger an {@code IllegalArgumentException}.
588
*
589
* @param val The non-null string to be unescaped.
590
* @return Unescaped value.
591
* @throws IllegalArgumentException When an Illegal value
592
* is provided.
593
*/
594
public static Object unescapeValue(String val) {
595
596
char[] chars = val.toCharArray();
597
int beg = 0;
598
int end = chars.length;
599
600
// Trim off leading and trailing whitespace.
601
while ((beg < end) && isWhitespace(chars[beg])) {
602
++beg;
603
}
604
605
while ((beg < end) && isWhitespace(chars[end - 1])) {
606
--end;
607
}
608
609
// Add back the trailing whitespace with a preceding '\'
610
// (escaped or unescaped) that was taken off in the above
611
// loop. Whether or not to retain this whitespace is decided below.
612
if (end != chars.length &&
613
(beg < end) &&
614
chars[end - 1] == '\\') {
615
end++;
616
}
617
if (beg >= end) {
618
return "";
619
}
620
621
if (chars[beg] == '#') {
622
// Value is binary (eg: "#CEB1DF80").
623
return decodeHexPairs(chars, ++beg, end);
624
}
625
626
// Trim off quotes.
627
if ((chars[beg] == '\"') && (chars[end - 1] == '\"')) {
628
++beg;
629
--end;
630
}
631
632
StringBuilder builder = new StringBuilder(end - beg);
633
int esc = -1; // index of the last escaped character
634
635
for (int i = beg; i < end; i++) {
636
if ((chars[i] == '\\') && (i + 1 < end)) {
637
if (!Character.isLetterOrDigit(chars[i + 1])) {
638
++i; // skip backslash
639
builder.append(chars[i]); // snarf escaped char
640
esc = i;
641
} else {
642
643
// Convert hex-encoded UTF-8 to 16-bit chars.
644
byte[] utf8 = getUtf8Octets(chars, i, end);
645
if (utf8.length > 0) {
646
try {
647
builder.append(new String(utf8, "UTF8"));
648
} catch (java.io.UnsupportedEncodingException e) {
649
// shouldn't happen
650
}
651
i += utf8.length * 3 - 1;
652
} else { // no utf8 bytes available, invalid DN
653
654
// '/' has no meaning, throw exception
655
throw new IllegalArgumentException(
656
"Not a valid attribute string value:" +
657
val + ",improper usage of backslash");
658
}
659
}
660
} else {
661
builder.append(chars[i]); // snarf unescaped char
662
}
663
}
664
665
// Get rid of the unescaped trailing whitespace with the
666
// preceding '\' character that was previously added back.
667
int len = builder.length();
668
if (isWhitespace(builder.charAt(len - 1)) && esc != (end - 1)) {
669
builder.setLength(len - 1);
670
}
671
return builder.toString();
672
}
673
674
675
/*
676
* Given an array of chars (with starting and ending indexes into it)
677
* representing bytes encoded as hex-pairs (such as "CEB1DF80"),
678
* returns a byte array containing the decoded bytes.
679
*/
680
private static byte[] decodeHexPairs(char[] chars, int beg, int end) {
681
byte[] bytes = new byte[(end - beg) / 2];
682
for (int i = 0; beg + 1 < end; i++) {
683
int hi = Character.digit(chars[beg], 16);
684
int lo = Character.digit(chars[beg + 1], 16);
685
if (hi < 0 || lo < 0) {
686
break;
687
}
688
bytes[i] = (byte)((hi<<4) + lo);
689
beg += 2;
690
}
691
if (beg != end) {
692
throw new IllegalArgumentException(
693
"Illegal attribute value: " + new String(chars));
694
}
695
return bytes;
696
}
697
698
/*
699
* Given an array of chars (with starting and ending indexes into it),
700
* finds the largest prefix consisting of hex-encoded UTF-8 octets,
701
* and returns a byte array containing the corresponding UTF-8 octets.
702
*
703
* Hex-encoded UTF-8 octets look like this:
704
* \03\B1\DF\80
705
*/
706
private static byte[] getUtf8Octets(char[] chars, int beg, int end) {
707
byte[] utf8 = new byte[(end - beg) / 3]; // allow enough room
708
int len = 0; // index of first unused byte in utf8
709
710
while ((beg + 2 < end) &&
711
(chars[beg++] == '\\')) {
712
int hi = Character.digit(chars[beg++], 16);
713
int lo = Character.digit(chars[beg++], 16);
714
if (hi < 0 || lo < 0) {
715
break;
716
}
717
utf8[len++] = (byte)((hi<<4) + lo);
718
}
719
if (len == utf8.length) {
720
return utf8;
721
} else {
722
byte[] res = new byte[len];
723
System.arraycopy(utf8, 0, res, 0, len);
724
return res;
725
}
726
}
727
728
/*
729
* Best guess as to what RFC 2253 means by "whitespace".
730
*/
731
private static boolean isWhitespace(char c) {
732
return (c == ' ' || c == '\r');
733
}
734
735
/**
736
* The writeObject method is called to save the state of the
737
* {@code Rdn} to a stream.
738
*
739
* Serializes only the unparsed RDN, for compactness and to avoid
740
* any implementation dependency.
741
*
742
* @serialData The unparsed RDN {@code String} representation.
743
*
744
* @param s the {@code ObjectOutputStream} to write to
745
* @throws java.io.IOException if an I/O error occurs
746
*/
747
@java.io.Serial
748
private void writeObject(ObjectOutputStream s)
749
throws java.io.IOException {
750
s.defaultWriteObject();
751
s.writeObject(toString());
752
}
753
754
/**
755
* The readObject method is called to restore the state of
756
* the {@code Rdn} from a stream.
757
*
758
* See {@code writeObject} for a description of the serial form.
759
*
760
* @param s the {@code ObjectInputStream} to read from
761
* @throws IOException if an I/O error occurs
762
* @throws ClassNotFoundException if the class of a serialized object
763
* could not be found
764
*/
765
@java.io.Serial
766
private void readObject(ObjectInputStream s)
767
throws IOException, ClassNotFoundException {
768
s.defaultReadObject();
769
entries = new ArrayList<>(DEFAULT_SIZE);
770
String unparsed = (String) s.readObject();
771
try {
772
(new Rfc2253Parser(unparsed)).parseRdn(this);
773
} catch (InvalidNameException e) {
774
// shouldn't happen
775
throw new java.io.StreamCorruptedException(
776
"Invalid name: " + unparsed);
777
}
778
}
779
}
780
781