001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.io.Serializable; 026import java.util.ArrayList; 027import java.util.Comparator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.ldap.sdk.schema.Schema; 032import com.unboundid.util.Debug; 033import com.unboundid.util.NotMutable; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036import com.unboundid.util.Validator; 037 038import static com.unboundid.ldap.sdk.LDAPMessages.*; 039 040 041 042/** 043 * This class provides a data structure for holding information about an LDAP 044 * distinguished name (DN). A DN consists of a comma-delimited list of zero or 045 * more RDN components. See 046 * <A HREF="http://www.ietf.org/rfc/rfc4514.txt">RFC 4514</A> for more 047 * information about representing DNs and RDNs as strings. 048 * <BR><BR> 049 * Examples of valid DNs (excluding the quotation marks, which are provided for 050 * clarity) include: 051 * <UL> 052 * <LI>"" -- This is the zero-length DN (also called the null DN), which may 053 * be used to refer to the directory server root DSE.</LI> 054 * <LI>"{@code o=example.com}". This is a DN with a single, single-valued 055 * RDN. The RDN attribute is "{@code o}" and the RDN value is 056 * "{@code example.com}".</LI> 057 * <LI>"{@code givenName=John+sn=Doe,ou=People,dc=example,dc=com}". This is a 058 * DN with four different RDNs ("{@code givenName=John+sn=Doe"}, 059 * "{@code ou=People}", "{@code dc=example}", and "{@code dc=com}". The 060 * first RDN is multivalued with attribute-value pairs of 061 * "{@code givenName=John}" and "{@code sn=Doe}".</LI> 062 * </UL> 063 * Note that there is some inherent ambiguity in the string representations of 064 * distinguished names. In particular, there may be differences in spacing 065 * (particularly around commas and equal signs, as well as plus signs in 066 * multivalued RDNs), and also differences in capitalization in attribute names 067 * and/or values. For example, the strings 068 * "{@code uid=john.doe,ou=people,dc=example,dc=com}" and 069 * "{@code UID = JOHN.DOE , OU = PEOPLE , DC = EXAMPLE , DC = COM}" actually 070 * refer to the same distinguished name. To deal with these differences, the 071 * normalized representation may be used. The normalized representation is a 072 * standardized way of representing a DN, and it is obtained by eliminating any 073 * unnecessary spaces and converting all non-case-sensitive characters to 074 * lowercase. The normalized representation of a DN may be obtained using the 075 * {@link DN#toNormalizedString} method, and two DNs may be compared to 076 * determine if they are equal using the standard {@link DN#equals} method. 077 * <BR><BR> 078 * Distinguished names are hierarchical. The rightmost RDN refers to the root 079 * of the directory information tree (DIT), and each successive RDN to the left 080 * indicates the addition of another level of hierarchy. For example, in the 081 * DN "{@code uid=john.doe,ou=People,o=example.com}", the entry 082 * "{@code o=example.com}" is at the root of the DIT, the entry 083 * "{@code ou=People,o=example.com}" is an immediate descendant of the 084 * "{@code o=example.com}" entry, and the 085 * "{@code uid=john.doe,ou=People,o=example.com}" entry is an immediate 086 * descendant of the "{@code ou=People,o=example.com}" entry. Similarly, the 087 * entry "{@code uid=jane.doe,ou=People,o=example.com}" would be considered a 088 * peer of the "{@code uid=john.doe,ou=People,o=example.com}" entry because they 089 * have the same parent. 090 * <BR><BR> 091 * Note that in some cases, the root of the DIT may actually contain a DN with 092 * multiple RDNs. For example, in the DN 093 * "{@code uid=john.doe,ou=People,dc=example,dc=com}", the directory server may 094 * or may not actually have a "{@code dc=com}" entry. In many such cases, the 095 * base entry may actually be just "{@code dc=example,dc=com}". The DNs of the 096 * entries that are at the base of the directory information tree are called 097 * "naming contexts" or "suffixes" and they are generally available in the 098 * {@code namingContexts} attribute of the root DSE. See the {@link RootDSE} 099 * class for more information about interacting with the server root DSE. 100 * <BR><BR> 101 * This class provides methods for making determinations based on the 102 * hierarchical relationships of DNs. For example, the 103 * {@link DN#isAncestorOf} and {@link DN#isDescendantOf} methods may be used to 104 * determine whether two DNs have a hierarchical relationship. In addition, 105 * this class implements the {@link Comparable} and {@link Comparator} 106 * interfaces so that it may be used to easily sort DNs (ancestors will always 107 * be sorted before descendants, and peers will always be sorted 108 * lexicographically based on their normalized representations). 109 */ 110@NotMutable() 111@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 112public final class DN 113 implements Comparable<DN>, Comparator<DN>, Serializable 114{ 115 /** 116 * The RDN array that will be used for the null DN. 117 */ 118 private static final RDN[] NO_RDNS = new RDN[0]; 119 120 121 122 /** 123 * A pre-allocated DN object equivalent to the null DN. 124 */ 125 public static final DN NULL_DN = new DN(); 126 127 128 129 /** 130 * The serial version UID for this serializable class. 131 */ 132 private static final long serialVersionUID = -5272968942085729346L; 133 134 135 136 // The set of RDN components that make up this DN. 137 private final RDN[] rdns; 138 139 // The schema to use to generate the normalized string representation of this 140 // DN, if any. 141 private final Schema schema; 142 143 // The string representation of this DN. 144 private final String dnString; 145 146 // The normalized string representation of this DN. 147 private volatile String normalizedString; 148 149 150 151 /** 152 * Creates a new DN with the provided set of RDNs. 153 * 154 * @param rdns The RDN components for this DN. It must not be {@code null}. 155 */ 156 public DN(final RDN... rdns) 157 { 158 Validator.ensureNotNull(rdns); 159 160 this.rdns = rdns; 161 if (rdns.length == 0) 162 { 163 dnString = ""; 164 normalizedString = ""; 165 schema = null; 166 } 167 else 168 { 169 Schema s = null; 170 final StringBuilder buffer = new StringBuilder(); 171 for (final RDN rdn : rdns) 172 { 173 if (buffer.length() > 0) 174 { 175 buffer.append(','); 176 } 177 rdn.toString(buffer, false); 178 179 if (s == null) 180 { 181 s = rdn.getSchema(); 182 } 183 } 184 185 dnString = buffer.toString(); 186 schema = s; 187 } 188 } 189 190 191 192 /** 193 * Creates a new DN with the provided set of RDNs. 194 * 195 * @param rdns The RDN components for this DN. It must not be {@code null}. 196 */ 197 public DN(final List<RDN> rdns) 198 { 199 Validator.ensureNotNull(rdns); 200 201 if (rdns.isEmpty()) 202 { 203 this.rdns = NO_RDNS; 204 dnString = ""; 205 normalizedString = ""; 206 schema = null; 207 } 208 else 209 { 210 this.rdns = rdns.toArray(new RDN[rdns.size()]); 211 212 Schema s = null; 213 final StringBuilder buffer = new StringBuilder(); 214 for (final RDN rdn : this.rdns) 215 { 216 if (buffer.length() > 0) 217 { 218 buffer.append(','); 219 } 220 rdn.toString(buffer, false); 221 222 if (s == null) 223 { 224 s = rdn.getSchema(); 225 } 226 } 227 228 dnString = buffer.toString(); 229 schema = s; 230 } 231 } 232 233 234 235 /** 236 * Creates a new DN below the provided parent DN with the given RDN. 237 * 238 * @param rdn The RDN for the new DN. It must not be {@code null}. 239 * @param parentDN The parent DN for the new DN to create. It must not be 240 * {@code null}. 241 */ 242 public DN(final RDN rdn, final DN parentDN) 243 { 244 Validator.ensureNotNull(rdn, parentDN); 245 246 rdns = new RDN[parentDN.rdns.length + 1]; 247 rdns[0] = rdn; 248 System.arraycopy(parentDN.rdns, 0, rdns, 1, parentDN.rdns.length); 249 250 Schema s = null; 251 final StringBuilder buffer = new StringBuilder(); 252 for (final RDN r : rdns) 253 { 254 if (buffer.length() > 0) 255 { 256 buffer.append(','); 257 } 258 r.toString(buffer, false); 259 260 if (s == null) 261 { 262 s = r.getSchema(); 263 } 264 } 265 266 dnString = buffer.toString(); 267 schema = s; 268 } 269 270 271 272 /** 273 * Creates a new DN from the provided string representation. 274 * 275 * @param dnString The string representation to use to create this DN. It 276 * must not be {@code null}. 277 * 278 * @throws LDAPException If the provided string cannot be parsed as a valid 279 * DN. 280 */ 281 public DN(final String dnString) 282 throws LDAPException 283 { 284 this(dnString, null); 285 } 286 287 288 289 /** 290 * Creates a new DN from the provided string representation. 291 * 292 * @param dnString The string representation to use to create this DN. It 293 * must not be {@code null}. 294 * @param schema The schema to use to generate the normalized string 295 * representation of this DN. It may be {@code null} if no 296 * schema is available. 297 * 298 * @throws LDAPException If the provided string cannot be parsed as a valid 299 * DN. 300 */ 301 public DN(final String dnString, final Schema schema) 302 throws LDAPException 303 { 304 Validator.ensureNotNull(dnString); 305 306 this.dnString = dnString; 307 this.schema = schema; 308 309 final ArrayList<RDN> rdnList = new ArrayList<>(5); 310 311 final int length = dnString.length(); 312 if (length == 0) 313 { 314 rdns = NO_RDNS; 315 normalizedString = ""; 316 return; 317 } 318 319 int pos = 0; 320 boolean expectMore = false; 321rdnLoop: 322 while (pos < length) 323 { 324 // Skip over any spaces before the attribute name. 325 while ((pos < length) && (dnString.charAt(pos) == ' ')) 326 { 327 pos++; 328 } 329 330 if (pos >= length) 331 { 332 // This is only acceptable if we haven't read anything yet. 333 if (rdnList.isEmpty()) 334 { 335 break; 336 } 337 else 338 { 339 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 340 ERR_DN_ENDS_WITH_COMMA.get(dnString)); 341 } 342 } 343 344 // Read the attribute name, until we find a space or equal sign. 345 int rdnEndPos; 346 int attrStartPos = pos; 347 final int rdnStartPos = pos; 348 while (pos < length) 349 { 350 final char c = dnString.charAt(pos); 351 if ((c == ' ') || (c == '=')) 352 { 353 break; 354 } 355 else if ((c == ',') || (c == ';')) 356 { 357 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 358 ERR_DN_UNEXPECTED_COMMA.get(dnString, pos)); 359 } 360 361 pos++; 362 } 363 364 String attrName = dnString.substring(attrStartPos, pos); 365 if (attrName.isEmpty()) 366 { 367 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 368 ERR_DN_NO_ATTR_IN_RDN.get(dnString)); 369 } 370 371 372 // Skip over any spaces before the equal sign. 373 while ((pos < length) && (dnString.charAt(pos) == ' ')) 374 { 375 pos++; 376 } 377 378 if ((pos >= length) || (dnString.charAt(pos) != '=')) 379 { 380 // We didn't find an equal sign. 381 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 382 ERR_DN_NO_EQUAL_SIGN.get(dnString, attrName)); 383 } 384 385 // Skip over the equal sign, and then any spaces leading up to the 386 // attribute value. 387 pos++; 388 while ((pos < length) && (dnString.charAt(pos) == ' ')) 389 { 390 pos++; 391 } 392 393 394 // Read the value for this RDN component. 395 ASN1OctetString value; 396 if (pos >= length) 397 { 398 value = new ASN1OctetString(); 399 rdnEndPos = pos; 400 } 401 else if (dnString.charAt(pos) == '#') 402 { 403 // It is a hex-encoded value, so we'll read until we find the end of the 404 // string or the first non-hex character, which must be a space, a 405 // comma, or a plus sign. Then, parse the bytes of the hex-encoded 406 // value as a BER element, and take the value of that element. 407 final byte[] valueArray = RDN.readHexString(dnString, ++pos); 408 409 try 410 { 411 value = ASN1OctetString.decodeAsOctetString(valueArray); 412 } 413 catch (final Exception e) 414 { 415 Debug.debugException(e); 416 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 417 ERR_RDN_HEX_STRING_NOT_BER_ENCODED.get(dnString, attrName), e); 418 } 419 420 pos += (valueArray.length * 2); 421 rdnEndPos = pos; 422 } 423 else 424 { 425 // It is a string value, which potentially includes escaped characters. 426 final StringBuilder buffer = new StringBuilder(); 427 pos = RDN.readValueString(dnString, pos, buffer); 428 value = new ASN1OctetString(buffer.toString()); 429 rdnEndPos = pos; 430 } 431 432 433 // Skip over any spaces until we find a comma, a plus sign, or the end of 434 // the value. 435 while ((pos < length) && (dnString.charAt(pos) == ' ')) 436 { 437 pos++; 438 } 439 440 if (pos >= length) 441 { 442 // It's a single-valued RDN, and we're at the end of the DN. 443 rdnList.add(new RDN(attrName, value, schema, 444 getTrimmedRDN(dnString, rdnStartPos,rdnEndPos))); 445 expectMore = false; 446 break; 447 } 448 449 switch (dnString.charAt(pos)) 450 { 451 case '+': 452 // It is a multivalued RDN, so we're not done reading either the DN 453 // or the RDN. 454 pos++; 455 break; 456 457 case ',': 458 case ';': 459 // We hit the end of the single-valued RDN, but there's still more of 460 // the DN to be read. 461 rdnList.add(new RDN(attrName, value, schema, 462 getTrimmedRDN(dnString, rdnStartPos,rdnEndPos))); 463 pos++; 464 expectMore = true; 465 continue rdnLoop; 466 467 default: 468 // It's an illegal character. This should never happen. 469 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 470 ERR_DN_UNEXPECTED_CHAR.get(dnString, dnString.charAt(pos), pos)); 471 } 472 473 if (pos >= length) 474 { 475 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 476 ERR_DN_ENDS_WITH_PLUS.get(dnString)); 477 } 478 479 480 // If we've gotten here, then we're dealing with a multivalued RDN. 481 // Create lists to hold the names and values, and then loop until we hit 482 // the end of the RDN. 483 final ArrayList<String> nameList = new ArrayList<>(5); 484 final ArrayList<ASN1OctetString> valueList = new ArrayList<>(5); 485 nameList.add(attrName); 486 valueList.add(value); 487 488 while (pos < length) 489 { 490 // Skip over any spaces before the attribute name. 491 while ((pos < length) && (dnString.charAt(pos) == ' ')) 492 { 493 pos++; 494 } 495 496 if (pos >= length) 497 { 498 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 499 ERR_DN_ENDS_WITH_PLUS.get(dnString)); 500 } 501 502 // Read the attribute name, until we find a space or equal sign. 503 attrStartPos = pos; 504 while (pos < length) 505 { 506 final char c = dnString.charAt(pos); 507 if ((c == ' ') || (c == '=')) 508 { 509 break; 510 } 511 else if ((c == ',') || (c == ';')) 512 { 513 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 514 ERR_DN_UNEXPECTED_COMMA.get(dnString, pos)); 515 } 516 517 pos++; 518 } 519 520 attrName = dnString.substring(attrStartPos, pos); 521 if (attrName.isEmpty()) 522 { 523 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 524 ERR_DN_NO_ATTR_IN_RDN.get(dnString)); 525 } 526 527 528 // Skip over any spaces before the equal sign. 529 while ((pos < length) && (dnString.charAt(pos) == ' ')) 530 { 531 pos++; 532 } 533 534 if ((pos >= length) || (dnString.charAt(pos) != '=')) 535 { 536 // We didn't find an equal sign. 537 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 538 ERR_DN_NO_EQUAL_SIGN.get(dnString, attrName)); 539 } 540 541 // Skip over the equal sign, and then any spaces leading up to the 542 // attribute value. 543 pos++; 544 while ((pos < length) && (dnString.charAt(pos) == ' ')) 545 { 546 pos++; 547 } 548 549 550 // Read the value for this RDN component. 551 if (pos >= length) 552 { 553 value = new ASN1OctetString(); 554 rdnEndPos = pos; 555 } 556 else if (dnString.charAt(pos) == '#') 557 { 558 // It is a hex-encoded value, so we'll read until we find the end of 559 // the string or the first non-hex character, which must be a space, a 560 // comma, or a plus sign. Then, parse the bytes of the hex-encoded 561 // value as a BER element, and take the value of that element. 562 final byte[] valueArray = RDN.readHexString(dnString, ++pos); 563 564 try 565 { 566 value = ASN1OctetString.decodeAsOctetString(valueArray); 567 } 568 catch (final Exception e) 569 { 570 Debug.debugException(e); 571 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 572 ERR_RDN_HEX_STRING_NOT_BER_ENCODED.get(dnString, attrName), e); 573 } 574 575 pos += (valueArray.length * 2); 576 rdnEndPos = pos; 577 } 578 else 579 { 580 // It is a string value, which potentially includes escaped 581 // characters. 582 final StringBuilder buffer = new StringBuilder(); 583 pos = RDN.readValueString(dnString, pos, buffer); 584 value = new ASN1OctetString(buffer.toString()); 585 rdnEndPos = pos; 586 } 587 588 589 // Skip over any spaces until we find a comma, a plus sign, or the end 590 // of the value. 591 while ((pos < length) && (dnString.charAt(pos) == ' ')) 592 { 593 pos++; 594 } 595 596 nameList.add(attrName); 597 valueList.add(value); 598 599 if (pos >= length) 600 { 601 // We've hit the end of the RDN and the end of the DN. 602 final String[] names = nameList.toArray(new String[nameList.size()]); 603 final ASN1OctetString[] values = 604 valueList.toArray(new ASN1OctetString[valueList.size()]); 605 rdnList.add(new RDN(names, values, schema, 606 getTrimmedRDN(dnString, rdnStartPos,rdnEndPos))); 607 expectMore = false; 608 break rdnLoop; 609 } 610 611 switch (dnString.charAt(pos)) 612 { 613 case '+': 614 // There are still more RDN components to be read, so we're not done 615 // yet. 616 pos++; 617 618 if (pos >= length) 619 { 620 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 621 ERR_DN_ENDS_WITH_PLUS.get(dnString)); 622 } 623 break; 624 625 case ',': 626 case ';': 627 // We've hit the end of the RDN, but there is still more of the DN 628 // to be read. 629 final String[] names = 630 nameList.toArray(new String[nameList.size()]); 631 final ASN1OctetString[] values = 632 valueList.toArray(new ASN1OctetString[valueList.size()]); 633 rdnList.add(new RDN(names, values, schema, 634 getTrimmedRDN(dnString, rdnStartPos,rdnEndPos))); 635 pos++; 636 expectMore = true; 637 continue rdnLoop; 638 639 default: 640 // It's an illegal character. This should never happen. 641 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 642 ERR_DN_UNEXPECTED_CHAR.get(dnString, dnString.charAt(pos), 643 pos)); 644 } 645 } 646 } 647 648 // If we are expecting more information to be provided, then it means that 649 // the string ended with a comma or semicolon. 650 if (expectMore) 651 { 652 throw new LDAPException(ResultCode.INVALID_DN_SYNTAX, 653 ERR_DN_ENDS_WITH_COMMA.get(dnString)); 654 } 655 656 // At this point, we should have all of the RDNs to use to create this DN. 657 rdns = new RDN[rdnList.size()]; 658 rdnList.toArray(rdns); 659 } 660 661 662 663 /** 664 * Retrieves a trimmed version of the string representation of the RDN in the 665 * specified portion of the provided DN string. Only non-escaped trailing 666 * spaces will be removed. 667 * 668 * @param dnString The string representation of the DN from which to extract 669 * the string representation of the RDN. 670 * @param start The position of the first character in the RDN. 671 * @param end The position marking the end of the RDN. 672 * 673 * @return A properly-trimmed string representation of the RDN. 674 */ 675 private static String getTrimmedRDN(final String dnString, final int start, 676 final int end) 677 { 678 final String rdnString = dnString.substring(start, end); 679 if (! rdnString.endsWith(" ")) 680 { 681 return rdnString; 682 } 683 684 final StringBuilder buffer = new StringBuilder(rdnString); 685 while ((buffer.charAt(buffer.length() - 1) == ' ') && 686 (buffer.charAt(buffer.length() - 2) != '\\')) 687 { 688 buffer.setLength(buffer.length() - 1); 689 } 690 691 return buffer.toString(); 692 } 693 694 695 696 /** 697 * Indicates whether the provided string represents a valid DN. 698 * 699 * @param s The string for which to make the determination. It must not be 700 * {@code null}. 701 * 702 * @return {@code true} if the provided string represents a valid DN, or 703 * {@code false} if not. 704 */ 705 public static boolean isValidDN(final String s) 706 { 707 try 708 { 709 new DN(s); 710 return true; 711 } 712 catch (final LDAPException le) 713 { 714 return false; 715 } 716 } 717 718 719 720 /** 721 * Retrieves the leftmost (i.e., furthest from the naming context) RDN 722 * component for this DN. 723 * 724 * @return The leftmost RDN component for this DN, or {@code null} if this DN 725 * does not have any RDNs (i.e., it is the null DN). 726 */ 727 public RDN getRDN() 728 { 729 if (rdns.length == 0) 730 { 731 return null; 732 } 733 else 734 { 735 return rdns[0]; 736 } 737 } 738 739 740 741 /** 742 * Retrieves the string representation of the leftmost (i.e., furthest from 743 * the naming context) RDN component for this DN. 744 * 745 * @return The string representation of the leftmost RDN component for this 746 * DN, or {@code null} if this DN does not have any RDNs (i.e., it is 747 * the null DN). 748 */ 749 public String getRDNString() 750 { 751 if (rdns.length == 0) 752 { 753 return null; 754 } 755 else 756 { 757 return rdns[0].toString(); 758 } 759 } 760 761 762 763 /** 764 * Retrieves the string representation of the leftmost (i.e., furthest from 765 * the naming context) RDN component for the DN with the provided string 766 * representation. 767 * 768 * @param s The string representation of the DN to process. It must not be 769 * {@code null}. 770 * 771 * @return The string representation of the leftmost RDN component for this 772 * DN, or {@code null} if this DN does not have any RDNs (i.e., it is 773 * the null DN). 774 * 775 * @throws LDAPException If the provided string cannot be parsed as a DN. 776 */ 777 public static String getRDNString(final String s) 778 throws LDAPException 779 { 780 return new DN(s).getRDNString(); 781 } 782 783 784 785 /** 786 * Retrieves the set of RDNs that comprise this DN. 787 * 788 * @return The set of RDNs that comprise this DN. 789 */ 790 public RDN[] getRDNs() 791 { 792 return rdns; 793 } 794 795 796 797 /** 798 * Retrieves the set of RDNs that comprise the DN with the provided string 799 * representation. 800 * 801 * @param s The string representation of the DN for which to retrieve the 802 * RDNs. It must not be {@code null}. 803 * 804 * @return The set of RDNs that comprise the DN with the provided string 805 * representation. 806 * 807 * @throws LDAPException If the provided string cannot be parsed as a DN. 808 */ 809 public static RDN[] getRDNs(final String s) 810 throws LDAPException 811 { 812 return new DN(s).getRDNs(); 813 } 814 815 816 817 /** 818 * Retrieves the set of string representations of the RDNs that comprise this 819 * DN. 820 * 821 * @return The set of string representations of the RDNs that comprise this 822 * DN. 823 */ 824 public String[] getRDNStrings() 825 { 826 final String[] rdnStrings = new String[rdns.length]; 827 for (int i=0; i < rdns.length; i++) 828 { 829 rdnStrings[i] = rdns[i].toString(); 830 } 831 return rdnStrings; 832 } 833 834 835 836 /** 837 * Retrieves the set of string representations of the RDNs that comprise this 838 * DN. 839 * 840 * @param s The string representation of the DN for which to retrieve the 841 * RDN strings. It must not be {@code null}. 842 * 843 * @return The set of string representations of the RDNs that comprise this 844 * DN. 845 * 846 * @throws LDAPException If the provided string cannot be parsed as a DN. 847 */ 848 public static String[] getRDNStrings(final String s) 849 throws LDAPException 850 { 851 return new DN(s).getRDNStrings(); 852 } 853 854 855 856 /** 857 * Indicates whether this DN represents the null DN, which does not have any 858 * RDN components. 859 * 860 * @return {@code true} if this DN represents the null DN, or {@code false} 861 * if not. 862 */ 863 public boolean isNullDN() 864 { 865 return (rdns.length == 0); 866 } 867 868 869 870 /** 871 * Retrieves the DN that is the parent for this DN. Note that neither the 872 * null DN nor DNs consisting of a single RDN component will be considered to 873 * have parent DNs. 874 * 875 * @return The DN that is the parent for this DN, or {@code null} if there 876 * is no parent. 877 */ 878 public DN getParent() 879 { 880 switch (rdns.length) 881 { 882 case 0: 883 case 1: 884 return null; 885 886 case 2: 887 return new DN(rdns[1]); 888 889 case 3: 890 return new DN(rdns[1], rdns[2]); 891 892 case 4: 893 return new DN(rdns[1], rdns[2], rdns[3]); 894 895 case 5: 896 return new DN(rdns[1], rdns[2], rdns[3], rdns[4]); 897 898 default: 899 final RDN[] parentRDNs = new RDN[rdns.length - 1]; 900 System.arraycopy(rdns, 1, parentRDNs, 0, parentRDNs.length); 901 return new DN(parentRDNs); 902 } 903 } 904 905 906 907 /** 908 * Retrieves the DN that is the parent for the DN with the provided string 909 * representation. Note that neither the null DN nor DNs consisting of a 910 * single RDN component will be considered to have parent DNs. 911 * 912 * @param s The string representation of the DN for which to retrieve the 913 * parent. It must not be {@code null}. 914 * 915 * @return The DN that is the parent for this DN, or {@code null} if there 916 * is no parent. 917 * 918 * @throws LDAPException If the provided string cannot be parsed as a DN. 919 */ 920 public static DN getParent(final String s) 921 throws LDAPException 922 { 923 return new DN(s).getParent(); 924 } 925 926 927 928 /** 929 * Retrieves the string representation of the DN that is the parent for this 930 * DN. Note that neither the null DN nor DNs consisting of a single RDN 931 * component will be considered to have parent DNs. 932 * 933 * @return The DN that is the parent for this DN, or {@code null} if there 934 * is no parent. 935 */ 936 public String getParentString() 937 { 938 final DN parentDN = getParent(); 939 if (parentDN == null) 940 { 941 return null; 942 } 943 else 944 { 945 return parentDN.toString(); 946 } 947 } 948 949 950 951 /** 952 * Retrieves the string representation of the DN that is the parent for the 953 * DN with the provided string representation. Note that neither the null DN 954 * nor DNs consisting of a single RDN component will be considered to have 955 * parent DNs. 956 * 957 * @param s The string representation of the DN for which to retrieve the 958 * parent. It must not be {@code null}. 959 * 960 * @return The DN that is the parent for this DN, or {@code null} if there 961 * is no parent. 962 * 963 * @throws LDAPException If the provided string cannot be parsed as a DN. 964 */ 965 public static String getParentString(final String s) 966 throws LDAPException 967 { 968 return new DN(s).getParentString(); 969 } 970 971 972 973 /** 974 * Indicates whether this DN is an ancestor of the provided DN. It will be 975 * considered an ancestor of the provided DN if the array of RDN components 976 * for the provided DN ends with the elements that comprise the array of RDN 977 * components for this DN (i.e., if the provided DN is subordinate to, or 978 * optionally equal to, this DN). The null DN will be considered an ancestor 979 * for all other DNs (with the exception of the null DN if {@code allowEquals} 980 * is {@code false}). 981 * 982 * @param dn The DN for which to make the determination. 983 * @param allowEquals Indicates whether a DN should be considered an 984 * ancestor of itself. 985 * 986 * @return {@code true} if this DN may be considered an ancestor of the 987 * provided DN, or {@code false} if not. 988 */ 989 public boolean isAncestorOf(final DN dn, final boolean allowEquals) 990 { 991 int thisPos = rdns.length - 1; 992 int thatPos = dn.rdns.length - 1; 993 994 if (thisPos < 0) 995 { 996 // This DN must be the null DN, which is an ancestor for all other DNs 997 // (and equal to the null DN, which we may still classify as being an 998 // ancestor). 999 return (allowEquals || (thatPos >= 0)); 1000 } 1001 1002 if ((thisPos > thatPos) || ((thisPos == thatPos) && (! allowEquals))) 1003 { 1004 // This DN has more RDN components than the provided DN, so it can't 1005 // possibly be an ancestor, or has the same number of components and equal 1006 // DNs shouldn't be considered ancestors. 1007 return false; 1008 } 1009 1010 while (thisPos >= 0) 1011 { 1012 if (! rdns[thisPos--].equals(dn.rdns[thatPos--])) 1013 { 1014 return false; 1015 } 1016 } 1017 1018 // If we've gotten here, then we can consider this DN to be an ancestor of 1019 // the provided DN. 1020 return true; 1021 } 1022 1023 1024 1025 /** 1026 * Indicates whether this DN is an ancestor of the DN with the provided string 1027 * representation. It will be considered an ancestor of the provided DN if 1028 * the array of RDN components for the provided DN ends with the elements that 1029 * comprise the array of RDN components for this DN (i.e., if the provided DN 1030 * is subordinate to, or optionally equal to, this DN). The null DN will be 1031 * considered an ancestor for all other DNs (with the exception of the null DN 1032 * if {@code allowEquals} is {@code false}). 1033 * 1034 * @param s The string representation of the DN for which to make 1035 * the determination. 1036 * @param allowEquals Indicates whether a DN should be considered an 1037 * ancestor of itself. 1038 * 1039 * @return {@code true} if this DN may be considered an ancestor of the 1040 * provided DN, or {@code false} if not. 1041 * 1042 * @throws LDAPException If the provided string cannot be parsed as a DN. 1043 */ 1044 public boolean isAncestorOf(final String s, final boolean allowEquals) 1045 throws LDAPException 1046 { 1047 return isAncestorOf(new DN(s), allowEquals); 1048 } 1049 1050 1051 1052 /** 1053 * Indicates whether the DN represented by the first string is an ancestor of 1054 * the DN represented by the second string. The first DN will be considered 1055 * an ancestor of the second DN if the array of RDN components for the first 1056 * DN ends with the elements that comprise the array of RDN components for the 1057 * second DN (i.e., if the first DN is subordinate to, or optionally equal to, 1058 * the second DN). The null DN will be considered an ancestor for all other 1059 * DNs (with the exception of the null DN if {@code allowEquals} is 1060 * {@code false}). 1061 * 1062 * @param s1 The string representation of the first DN for which to 1063 * make the determination. 1064 * @param s2 The string representation of the second DN for which 1065 * to make the determination. 1066 * @param allowEquals Indicates whether a DN should be considered an 1067 * ancestor of itself. 1068 * 1069 * @return {@code true} if the first DN may be considered an ancestor of the 1070 * second DN, or {@code false} if not. 1071 * 1072 * @throws LDAPException If either of the provided strings cannot be parsed 1073 * as a DN. 1074 */ 1075 public static boolean isAncestorOf(final String s1, final String s2, 1076 final boolean allowEquals) 1077 throws LDAPException 1078 { 1079 return new DN(s1).isAncestorOf(new DN(s2), allowEquals); 1080 } 1081 1082 1083 1084 /** 1085 * Indicates whether this DN is a descendant of the provided DN. It will be 1086 * considered a descendant of the provided DN if the array of RDN components 1087 * for this DN ends with the elements that comprise the RDN components for the 1088 * provided DN (i.e., if this DN is subordinate to, or optionally equal to, 1089 * the provided DN). The null DN will not be considered a descendant for any 1090 * other DNs (with the exception of the null DN if {@code allowEquals} is 1091 * {@code true}). 1092 * 1093 * @param dn The DN for which to make the determination. 1094 * @param allowEquals Indicates whether a DN should be considered a 1095 * descendant of itself. 1096 * 1097 * @return {@code true} if this DN may be considered a descendant of the 1098 * provided DN, or {@code false} if not. 1099 */ 1100 public boolean isDescendantOf(final DN dn, final boolean allowEquals) 1101 { 1102 int thisPos = rdns.length - 1; 1103 int thatPos = dn.rdns.length - 1; 1104 1105 if (thatPos < 0) 1106 { 1107 // The provided DN must be the null DN, which will be considered an 1108 // ancestor for all other DNs (and equal to the null DN), making this DN 1109 // considered a descendant for that DN. 1110 return (allowEquals || (thisPos >= 0)); 1111 } 1112 1113 if ((thisPos < thatPos) || ((thisPos == thatPos) && (! allowEquals))) 1114 { 1115 // This DN has fewer DN components than the provided DN, so it can't 1116 // possibly be a descendant, or it has the same number of components and 1117 // equal DNs shouldn't be considered descendants. 1118 return false; 1119 } 1120 1121 while (thatPos >= 0) 1122 { 1123 if (! rdns[thisPos--].equals(dn.rdns[thatPos--])) 1124 { 1125 return false; 1126 } 1127 } 1128 1129 // If we've gotten here, then we can consider this DN to be a descendant of 1130 // the provided DN. 1131 return true; 1132 } 1133 1134 1135 1136 /** 1137 * Indicates whether this DN is a descendant of the DN with the provided 1138 * string representation. It will be considered a descendant of the provided 1139 * DN if the array of RDN components for this DN ends with the elements that 1140 * comprise the RDN components for the provided DN (i.e., if this DN is 1141 * subordinate to, or optionally equal to, the provided DN). The null DN will 1142 * not be considered a descendant for any other DNs (with the exception of the 1143 * null DN if {@code allowEquals} is {@code true}). 1144 * 1145 * @param s The string representation of the DN for which to make 1146 * the determination. 1147 * @param allowEquals Indicates whether a DN should be considered a 1148 * descendant of itself. 1149 * 1150 * @return {@code true} if this DN may be considered a descendant of the 1151 * provided DN, or {@code false} if not. 1152 * 1153 * @throws LDAPException If the provided string cannot be parsed as a DN. 1154 */ 1155 public boolean isDescendantOf(final String s, final boolean allowEquals) 1156 throws LDAPException 1157 { 1158 return isDescendantOf(new DN(s), allowEquals); 1159 } 1160 1161 1162 1163 /** 1164 * Indicates whether the DN represented by the first string is a descendant of 1165 * the DN represented by the second string. The first DN will be considered a 1166 * descendant of the second DN if the array of RDN components for the first DN 1167 * ends with the elements that comprise the RDN components for the second DN 1168 * (i.e., if the first DN is subordinate to, or optionally equal to, the 1169 * second DN). The null DN will not be considered a descendant for any other 1170 * DNs (with the exception of the null DN if {@code allowEquals} is 1171 * {@code true}). 1172 * 1173 * @param s1 The string representation of the first DN for which to 1174 * make the determination. 1175 * @param s2 The string representation of the second DN for which 1176 * to make the determination. 1177 * @param allowEquals Indicates whether a DN should be considered an 1178 * ancestor of itself. 1179 * 1180 * @return {@code true} if this DN may be considered a descendant of the 1181 * provided DN, or {@code false} if not. 1182 * 1183 * @throws LDAPException If either of the provided strings cannot be parsed 1184 * as a DN. 1185 */ 1186 public static boolean isDescendantOf(final String s1, final String s2, 1187 final boolean allowEquals) 1188 throws LDAPException 1189 { 1190 return new DN(s1).isDescendantOf(new DN(s2), allowEquals); 1191 } 1192 1193 1194 1195 /** 1196 * Indicates whether this DN falls within the range of the provided search 1197 * base DN and scope. 1198 * 1199 * @param baseDN The base DN for which to make the determination. It must 1200 * not be {@code null}. 1201 * @param scope The scope for which to make the determination. It must not 1202 * be {@code null}. 1203 * 1204 * @return {@code true} if this DN is within the range of the provided base 1205 * and scope, or {@code false} if not. 1206 * 1207 * @throws LDAPException If a problem occurs while making the determination. 1208 */ 1209 public boolean matchesBaseAndScope(final String baseDN, 1210 final SearchScope scope) 1211 throws LDAPException 1212 { 1213 return matchesBaseAndScope(new DN(baseDN), scope); 1214 } 1215 1216 1217 1218 /** 1219 * Indicates whether this DN falls within the range of the provided search 1220 * base DN and scope. 1221 * 1222 * @param baseDN The base DN for which to make the determination. It must 1223 * not be {@code null}. 1224 * @param scope The scope for which to make the determination. It must not 1225 * be {@code null}. 1226 * 1227 * @return {@code true} if this DN is within the range of the provided base 1228 * and scope, or {@code false} if not. 1229 * 1230 * @throws LDAPException If a problem occurs while making the determination. 1231 */ 1232 public boolean matchesBaseAndScope(final DN baseDN, final SearchScope scope) 1233 throws LDAPException 1234 { 1235 Validator.ensureNotNull(baseDN, scope); 1236 1237 switch (scope.intValue()) 1238 { 1239 case SearchScope.BASE_INT_VALUE: 1240 return equals(baseDN); 1241 1242 case SearchScope.ONE_INT_VALUE: 1243 return baseDN.equals(getParent()); 1244 1245 case SearchScope.SUB_INT_VALUE: 1246 return isDescendantOf(baseDN, true); 1247 1248 case SearchScope.SUBORDINATE_SUBTREE_INT_VALUE: 1249 return isDescendantOf(baseDN, false); 1250 1251 default: 1252 throw new LDAPException(ResultCode.PARAM_ERROR, 1253 ERR_DN_MATCHES_UNSUPPORTED_SCOPE.get(dnString, 1254 String.valueOf(scope))); 1255 } 1256 } 1257 1258 1259 1260 /** 1261 * Generates a hash code for this DN. 1262 * 1263 * @return The generated hash code for this DN. 1264 */ 1265 @Override() public int hashCode() 1266 { 1267 return toNormalizedString().hashCode(); 1268 } 1269 1270 1271 1272 /** 1273 * Indicates whether the provided object is equal to this DN. In order for 1274 * the provided object to be considered equal, it must be a non-null DN with 1275 * the same set of RDN components. 1276 * 1277 * @param o The object for which to make the determination. 1278 * 1279 * @return {@code true} if the provided object is considered equal to this 1280 * DN, or {@code false} if not. 1281 */ 1282 @Override() 1283 public boolean equals(final Object o) 1284 { 1285 if (o == null) 1286 { 1287 return false; 1288 } 1289 1290 if (this == o) 1291 { 1292 return true; 1293 } 1294 1295 if (! (o instanceof DN)) 1296 { 1297 return false; 1298 } 1299 1300 final DN dn = (DN) o; 1301 return (toNormalizedString().equals(dn.toNormalizedString())); 1302 } 1303 1304 1305 1306 /** 1307 * Indicates whether the DN with the provided string representation is equal 1308 * to this DN. 1309 * 1310 * @param s The string representation of the DN to compare with this DN. 1311 * 1312 * @return {@code true} if the DN with the provided string representation is 1313 * equal to this DN, or {@code false} if not. 1314 * 1315 * @throws LDAPException If the provided string cannot be parsed as a DN. 1316 */ 1317 public boolean equals(final String s) 1318 throws LDAPException 1319 { 1320 if (s == null) 1321 { 1322 return false; 1323 } 1324 1325 return equals(new DN(s)); 1326 } 1327 1328 1329 1330 /** 1331 * Indicates whether the two provided strings represent the same DN. 1332 * 1333 * @param s1 The string representation of the first DN for which to make the 1334 * determination. It must not be {@code null}. 1335 * @param s2 The string representation of the second DN for which to make 1336 * the determination. It must not be {@code null}. 1337 * 1338 * @return {@code true} if the provided strings represent the same DN, or 1339 * {@code false} if not. 1340 * 1341 * @throws LDAPException If either of the provided strings cannot be parsed 1342 * as a DN. 1343 */ 1344 public static boolean equals(final String s1, final String s2) 1345 throws LDAPException 1346 { 1347 return new DN(s1).equals(new DN(s2)); 1348 } 1349 1350 1351 1352 /** 1353 * Indicates whether the two provided strings represent the same DN. 1354 * 1355 * @param s1 The string representation of the first DN for which to make 1356 * the determination. It must not be {@code null}. 1357 * @param s2 The string representation of the second DN for which to 1358 * make the determination. It must not be {@code null}. 1359 * @param schema The schema to use while making the determination. It may 1360 * be {@code null} if no schema is available. 1361 * 1362 * @return {@code true} if the provided strings represent the same DN, or 1363 * {@code false} if not. 1364 * 1365 * @throws LDAPException If either of the provided strings cannot be parsed 1366 * as a DN. 1367 */ 1368 public static boolean equals(final String s1, final String s2, 1369 final Schema schema) 1370 throws LDAPException 1371 { 1372 return new DN(s1, schema).equals(new DN(s2, schema)); 1373 } 1374 1375 1376 1377 /** 1378 * Retrieves a string representation of this DN. 1379 * 1380 * @return A string representation of this DN. 1381 */ 1382 @Override() 1383 public String toString() 1384 { 1385 return dnString; 1386 } 1387 1388 1389 1390 /** 1391 * Retrieves a string representation of this DN with minimal encoding for 1392 * special characters. Only those characters specified in RFC 4514 section 1393 * 2.4 will be escaped. No escaping will be used for non-ASCII characters or 1394 * non-printable ASCII characters. 1395 * 1396 * @return A string representation of this DN with minimal encoding for 1397 * special characters. 1398 */ 1399 public String toMinimallyEncodedString() 1400 { 1401 final StringBuilder buffer = new StringBuilder(); 1402 toString(buffer, true); 1403 return buffer.toString(); 1404 } 1405 1406 1407 1408 /** 1409 * Appends a string representation of this DN to the provided buffer. 1410 * 1411 * @param buffer The buffer to which to append the string representation of 1412 * this DN. 1413 */ 1414 public void toString(final StringBuilder buffer) 1415 { 1416 toString(buffer, false); 1417 } 1418 1419 1420 1421 /** 1422 * Appends a string representation of this DN to the provided buffer. 1423 * 1424 * @param buffer The buffer to which the string representation is 1425 * to be appended. 1426 * @param minimizeEncoding Indicates whether to restrict the encoding of 1427 * special characters to the bare minimum required 1428 * by LDAP (as per RFC 4514 section 2.4). If this 1429 * is {@code true}, then only leading and trailing 1430 * spaces, double quotes, plus signs, commas, 1431 * semicolons, greater-than, less-than, and 1432 * backslash characters will be encoded. 1433 */ 1434 public void toString(final StringBuilder buffer, 1435 final boolean minimizeEncoding) 1436 { 1437 for (int i=0; i < rdns.length; i++) 1438 { 1439 if (i > 0) 1440 { 1441 buffer.append(','); 1442 } 1443 1444 rdns[i].toString(buffer, minimizeEncoding); 1445 } 1446 } 1447 1448 1449 1450 /** 1451 * Retrieves a normalized string representation of this DN. 1452 * 1453 * @return A normalized string representation of this DN. 1454 */ 1455 public String toNormalizedString() 1456 { 1457 if (normalizedString == null) 1458 { 1459 final StringBuilder buffer = new StringBuilder(); 1460 toNormalizedString(buffer); 1461 normalizedString = buffer.toString(); 1462 } 1463 1464 return normalizedString; 1465 } 1466 1467 1468 1469 /** 1470 * Appends a normalized string representation of this DN to the provided 1471 * buffer. 1472 * 1473 * @param buffer The buffer to which to append the normalized string 1474 * representation of this DN. 1475 */ 1476 public void toNormalizedString(final StringBuilder buffer) 1477 { 1478 for (int i=0; i < rdns.length; i++) 1479 { 1480 if (i > 0) 1481 { 1482 buffer.append(','); 1483 } 1484 1485 buffer.append(rdns[i].toNormalizedString()); 1486 } 1487 } 1488 1489 1490 1491 /** 1492 * Retrieves a normalized representation of the DN with the provided string 1493 * representation. 1494 * 1495 * @param s The string representation of the DN to normalize. It must not 1496 * be {@code null}. 1497 * 1498 * @return The normalized representation of the DN with the provided string 1499 * representation. 1500 * 1501 * @throws LDAPException If the provided string cannot be parsed as a DN. 1502 */ 1503 public static String normalize(final String s) 1504 throws LDAPException 1505 { 1506 return normalize(s, null); 1507 } 1508 1509 1510 1511 /** 1512 * Retrieves a normalized representation of the DN with the provided string 1513 * representation. 1514 * 1515 * @param s The string representation of the DN to normalize. It must 1516 * not be {@code null}. 1517 * @param schema The schema to use to generate the normalized string 1518 * representation of the DN. It may be {@code null} if no 1519 * schema is available. 1520 * 1521 * @return The normalized representation of the DN with the provided string 1522 * representation. 1523 * 1524 * @throws LDAPException If the provided string cannot be parsed as a DN. 1525 */ 1526 public static String normalize(final String s, final Schema schema) 1527 throws LDAPException 1528 { 1529 return new DN(s, schema).toNormalizedString(); 1530 } 1531 1532 1533 1534 /** 1535 * Compares the provided DN to this DN to determine their relative order in 1536 * a sorted list. 1537 * 1538 * @param dn The DN to compare against this DN. It must not be 1539 * {@code null}. 1540 * 1541 * @return A negative integer if this DN should come before the provided DN 1542 * in a sorted list, a positive integer if this DN should come after 1543 * the provided DN in a sorted list, or zero if the provided DN can 1544 * be considered equal to this DN. 1545 */ 1546 @Override() 1547 public int compareTo(final DN dn) 1548 { 1549 return compare(this, dn); 1550 } 1551 1552 1553 1554 /** 1555 * Compares the provided DN values to determine their relative order in a 1556 * sorted list. 1557 * 1558 * @param dn1 The first DN to be compared. It must not be {@code null}. 1559 * @param dn2 The second DN to be compared. It must not be {@code null}. 1560 * 1561 * @return A negative integer if the first DN should come before the second 1562 * DN in a sorted list, a positive integer if the first DN should 1563 * come after the second DN in a sorted list, or zero if the two DN 1564 * values can be considered equal. 1565 */ 1566 @Override() 1567 public int compare(final DN dn1, final DN dn2) 1568 { 1569 Validator.ensureNotNull(dn1, dn2); 1570 1571 // We want the comparison to be in reverse order, so that DNs will be sorted 1572 // hierarchically. 1573 int pos1 = dn1.rdns.length - 1; 1574 int pos2 = dn2.rdns.length - 1; 1575 if (pos1 < 0) 1576 { 1577 if (pos2 < 0) 1578 { 1579 // Both DNs are the null DN, so they are equal. 1580 return 0; 1581 } 1582 else 1583 { 1584 // The first DN is the null DN and the second isn't, so the first DN 1585 // comes first. 1586 return -1; 1587 } 1588 } 1589 else if (pos2 < 0) 1590 { 1591 // The second DN is the null DN, which always comes first. 1592 return 1; 1593 } 1594 1595 1596 while ((pos1 >= 0) && (pos2 >= 0)) 1597 { 1598 final int compValue = dn1.rdns[pos1].compareTo(dn2.rdns[pos2]); 1599 if (compValue != 0) 1600 { 1601 return compValue; 1602 } 1603 1604 pos1--; 1605 pos2--; 1606 } 1607 1608 1609 // If we've gotten here, then one of the DNs is equal to or a descendant of 1610 // the other. 1611 if (pos1 < 0) 1612 { 1613 if (pos2 < 0) 1614 { 1615 // They're both the same length, so they should be considered equal. 1616 return 0; 1617 } 1618 else 1619 { 1620 // The first is shorter than the second, so it should come first. 1621 return -1; 1622 } 1623 } 1624 else 1625 { 1626 // The second RDN is shorter than the first, so it should come first. 1627 return 1; 1628 } 1629 } 1630 1631 1632 1633 /** 1634 * Compares the DNs with the provided string representations to determine 1635 * their relative order in a sorted list. 1636 * 1637 * @param s1 The string representation for the first DN to be compared. It 1638 * must not be {@code null}. 1639 * @param s2 The string representation for the second DN to be compared. It 1640 * must not be {@code null}. 1641 * 1642 * @return A negative integer if the first DN should come before the second 1643 * DN in a sorted list, a positive integer if the first DN should 1644 * come after the second DN in a sorted list, or zero if the two DN 1645 * values can be considered equal. 1646 * 1647 * @throws LDAPException If either of the provided strings cannot be parsed 1648 * as a DN. 1649 */ 1650 public static int compare(final String s1, final String s2) 1651 throws LDAPException 1652 { 1653 return compare(s1, s2, null); 1654 } 1655 1656 1657 1658 /** 1659 * Compares the DNs with the provided string representations to determine 1660 * their relative order in a sorted list. 1661 * 1662 * @param s1 The string representation for the first DN to be compared. 1663 * It must not be {@code null}. 1664 * @param s2 The string representation for the second DN to be compared. 1665 * It must not be {@code null}. 1666 * @param schema The schema to use to generate the normalized string 1667 * representations of the DNs. It may be {@code null} if no 1668 * schema is available. 1669 * 1670 * @return A negative integer if the first DN should come before the second 1671 * DN in a sorted list, a positive integer if the first DN should 1672 * come after the second DN in a sorted list, or zero if the two DN 1673 * values can be considered equal. 1674 * 1675 * @throws LDAPException If either of the provided strings cannot be parsed 1676 * as a DN. 1677 */ 1678 public static int compare(final String s1, final String s2, 1679 final Schema schema) 1680 throws LDAPException 1681 { 1682 return new DN(s1, schema).compareTo(new DN(s2, schema)); 1683 } 1684}