1. /*
  2. * @(#)LdapName.java 1.7 04/06/21
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.naming.ldap;
  8. import javax.naming.Name;
  9. import javax.naming.InvalidNameException;
  10. import java.util.Enumeration;
  11. import java.util.Collection;
  12. import java.util.ArrayList;
  13. import java.util.List;
  14. import java.util.Iterator;
  15. import java.util.ListIterator;
  16. import java.util.Collections;
  17. import java.io.ObjectOutputStream;
  18. import java.io.ObjectInputStream;
  19. import java.io.IOException;
  20. /**
  21. * This class represents a distinguished name as specified by
  22. * <a href="http://ietf.org//rfc/rfc2253.txt">RFC 2253</a>.
  23. * A distinguished name, or DN, is composed of an ordered list of
  24. * components called <em>relative distinguished name</em>s, or RDNs.
  25. * Details of a DN's syntax are described in RFC 2253.
  26. *<p>
  27. * This class resolves a few ambiguities found in RFC 2253
  28. * as follows:
  29. * <ul>
  30. * <li> RFC 2253 leaves the term "whitespace" undefined. The
  31. * ASCII space character 0x20 (" ") is used in its place.
  32. * <li> Whitespace is allowed on either side of ',', ';', '=', and '+'.
  33. * Such whitespace is accepted but not generated by this code,
  34. * and is ignored when comparing names.
  35. * <li> AttributeValue strings containing '=' or non-leading '#'
  36. * characters (unescaped) are accepted.
  37. * </ul>
  38. *<p>
  39. * String names passed to <code>LdapName</code> or returned by it
  40. * use the full Unicode character set. They may also contain
  41. * characters encoded into UTF-8 with each octet represented by a
  42. * three-character substring such as "\\B4".
  43. * They may not, however, contain characters encoded into UTF-8 with
  44. * each octet represented by a single character in the string: the
  45. * meaning would be ambiguous.
  46. *<p>
  47. * <code>LdapName</code> will properly parse all valid names, but
  48. * does not attempt to detect all possible violations when parsing
  49. * invalid names. It is "generous" in accepting invalid names.
  50. * The "validity" of a name is determined ultimately when it
  51. * is supplied to an LDAP server, which may accept or
  52. * reject the name based on factors such as its schema information
  53. * and interoperability considerations.
  54. *<p>
  55. * When names are tested for equality, attribute types, both binary
  56. * and string values, are case-insensitive.
  57. * String values with different but equivalent usage of quoting,
  58. * escaping, or UTF8-hex-encoding are considered equal. The order of
  59. * components in multi-valued RDNs (such as "ou=Sales+cn=Bob") is not
  60. * significant.
  61. * <p>
  62. * The components of a LDAP name, that is, RDNs, are numbered. The
  63. * indexes of a LDAP name with n RDNs range from 0 to n-1.
  64. * This range may be written as [0,n).
  65. * The right most RDN is at index 0, and the left most RDN is at
  66. * index n-1. For example, the distinguished name:
  67. * "CN=Steve Kille, O=Isode Limited, C=GB" is numbered in the following
  68. * sequence ranging from 0 to 2: {C=GB, O=Isode Limited, CN=Steve Kille}. An
  69. * empty LDAP name is represented by an empty RDN list.
  70. *<p>
  71. * Concurrent multithreaded read-only access of an instance of
  72. * <tt>LdapName</tt> need not be synchronized.
  73. *<p>
  74. * Unless otherwise noted, the behavior of passing a null argument
  75. * to a constructor or method in this class will cause a
  76. * NullPointerException to be thrown.
  77. *
  78. * @author Scott Seligman
  79. * @version 1.7 04/06/21
  80. * @since 1.5
  81. */
  82. public class LdapName implements Name {
  83. // private transient ArrayList<Rdn> rdns; // parsed name components
  84. private transient ArrayList rdns; // parsed name components
  85. private transient String unparsed; // if non-null, the DN in unparsed form
  86. private static final long serialVersionUID = -1595520034788997356L;
  87. /**
  88. * Constructs an LDAP name from the given distinguished name.
  89. *
  90. * @param name This is a non-null distinguished name formatted
  91. * according to the rules defined in
  92. * <a href="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>.
  93. *
  94. * @throws InvalidNameException if a syntax violation is detected.
  95. * @see Rdn#escapeValue(Object value)
  96. */
  97. public LdapName(String name) throws InvalidNameException {
  98. unparsed = name;
  99. parse();
  100. }
  101. /**
  102. * Constructs an LDAP name given its parsed RDN components.
  103. * <p>
  104. * The indexing of RDNs in the list follows the numbering of
  105. * RDNs described in the class description.
  106. *
  107. * @param rdns The non-null list of <tt>Rdn</tt>s forming this LDAP name.
  108. */
  109. public LdapName(List<Rdn> rdns) {
  110. // if (rdns instanceof ArrayList<Rdn>) {
  111. // this.rdns = rdns.clone();
  112. // } else if (rdns instanceof List<Rdn>) {
  113. // this.rdns = new ArrayList<Rdn>(rdns);
  114. // } else {
  115. // throw IllegalArgumentException(
  116. // "Invalid entries, list entries must be of type Rdn");
  117. // }
  118. this.rdns = new ArrayList(rdns.size());
  119. for (int i = 0; i < rdns.size(); i++) {
  120. Object obj = rdns.get(i);
  121. if (!(obj instanceof Rdn)) {
  122. throw new IllegalArgumentException("Entry:" + obj +
  123. " not a valid type;list entries must be of type Rdn");
  124. }
  125. this.rdns.add(obj);
  126. }
  127. }
  128. /*
  129. * Constructs an LDAP name given its parsed components (the elements
  130. * of "rdns" in the range [beg,end)) and, optionally
  131. * (if "name" is not null), the unparsed DN.
  132. *
  133. */
  134. // private LdapName(String name, List<Rdn> rdns, int beg, int end) {
  135. private LdapName(String name, ArrayList rdns, int beg, int end) {
  136. unparsed = name;
  137. // this.rdns = rdns.subList(beg, end);
  138. List sList = rdns.subList(beg, end);
  139. this.rdns = new ArrayList(sList);
  140. }
  141. /**
  142. * Retrieves the number of components in this LDAP name.
  143. * @return The non-negative number of components in this LDAP name.
  144. */
  145. public int size() {
  146. return rdns.size();
  147. }
  148. /**
  149. * Determines whether this LDAP name is empty.
  150. * An empty name is one with zero components.
  151. * @return true if this LDAP name is empty, false otherwise.
  152. */
  153. public boolean isEmpty() {
  154. return rdns.isEmpty();
  155. }
  156. /**
  157. * Retrieves the components of this name as an enumeration
  158. * of strings. The effect of updates to this name on this enumeration
  159. * is undefined. If the name has zero components, an empty (non-null)
  160. * enumeration is returned.
  161. * The order of the components returned by the enumeration is same as
  162. * the order in which the components are numbered as described in the
  163. * class description.
  164. *
  165. * @return A non-null enumeration of the components of this LDAP name.
  166. * Each element of the enumeration is of class String.
  167. */
  168. public Enumeration<String> getAll() {
  169. final Iterator iter = rdns.iterator();
  170. return new Enumeration<String>() {
  171. public boolean hasMoreElements() {
  172. return iter.hasNext();
  173. }
  174. public String nextElement() {
  175. return iter.next().toString();
  176. }
  177. };
  178. }
  179. /**
  180. * Retrieves a component of this LDAP name as a string.
  181. * @param posn The 0-based index of the component to retrieve.
  182. * Must be in the range [0,size()).
  183. * @return The non-null component at index posn.
  184. * @exception IndexOutOfBoundsException if posn is outside the
  185. * specified range.
  186. */
  187. public String get(int posn) {
  188. return rdns.get(posn).toString();
  189. }
  190. /**
  191. * Retrieves an RDN of this LDAP name as an Rdn.
  192. * @param posn The 0-based index of the RDN to retrieve.
  193. * Must be in the range [0,size()).
  194. * @return The non-null RDN at index posn.
  195. * @exception IndexOutOfBoundsException if posn is outside the
  196. * specified range.
  197. */
  198. public Rdn getRdn(int posn) {
  199. return (Rdn) rdns.get(posn);
  200. }
  201. /**
  202. * Creates a name whose components consist of a prefix of the
  203. * components of this LDAP name.
  204. * Subsequent changes to this name will not affect the name
  205. * that is returned and vice versa.
  206. * @param posn The 0-based index of the component at which to stop.
  207. * Must be in the range [0,size()].
  208. * @return An instance of <tt>LdapName</tt> consisting of the
  209. * components at indexes in the range [0,posn).
  210. * If posn is zero, an empty LDAP name is returned.
  211. * @exception IndexOutOfBoundsException
  212. * If posn is outside the specified range.
  213. */
  214. public Name getPrefix(int posn) {
  215. try {
  216. return new LdapName(null, rdns, 0, posn);
  217. } catch (IllegalArgumentException e) {
  218. throw new IndexOutOfBoundsException(
  219. "Posn: " + posn + ", Size: "+ rdns.size());
  220. }
  221. }
  222. /**
  223. * Creates a name whose components consist of a suffix of the
  224. * components in this LDAP name.
  225. * Subsequent changes to this name do not affect the name that is
  226. * returned and vice versa.
  227. *
  228. * @param posn The 0-based index of the component at which to start.
  229. * Must be in the range [0,size()].
  230. * @return An instance of <tt>LdapName</tt> consisting of the
  231. * components at indexes in the range [posn,size()).
  232. * If posn is equal to size(), an empty LDAP name is
  233. * returned.
  234. * @exception IndexOutOfBoundsException
  235. * If posn is outside the specified range.
  236. */
  237. public Name getSuffix(int posn) {
  238. try {
  239. return new LdapName(null, rdns, posn, rdns.size());
  240. } catch (IllegalArgumentException e) {
  241. throw new IndexOutOfBoundsException(
  242. "Posn: " + posn + ", Size: "+ rdns.size());
  243. }
  244. }
  245. /**
  246. * Determines whether this LDAP name starts with a specified LDAP name
  247. * prefix.
  248. * A name <tt>n</tt> is a prefix if it is equal to
  249. * <tt>getPrefix(n.size())</tt>--in other words this LDAP
  250. * name starts with 'n'. If n is null or not a RFC2253 formatted name
  251. * as described in the class description, false is returned.
  252. *
  253. * @param n The LDAP name to check.
  254. * @return true if <tt>n</tt> is a prefix of this LDAP name,
  255. * false otherwise.
  256. * @see #getPrefix(int posn)
  257. */
  258. public boolean startsWith(Name n) {
  259. if (n == null) {
  260. return false;
  261. }
  262. int len1 = rdns.size();
  263. int len2 = n.size();
  264. return (len1 >= len2 &&
  265. matches(0, len2, n));
  266. }
  267. /**
  268. * Determines whether the specified RDN sequence forms a prefix of this
  269. * LDAP name. Returns true if this LdapName is at least as long as rdns,
  270. * and for every position p in the range [0, rdns.size()) the component
  271. * getRdn(p) matches rdns.get(p). Returns false otherwise. If rdns is
  272. * null, false is returned.
  273. *
  274. * @param rdns The sequence of <tt>Rdn</tt>s to check.
  275. * @return true if <tt>rdns</tt> form a prefix of this LDAP name,
  276. * false otherwise.
  277. */
  278. public boolean startsWith(List<Rdn> rdns) {
  279. if (rdns == null) {
  280. return false;
  281. }
  282. int len1 = this.rdns.size();
  283. int len2 = rdns.size();
  284. return (len1 >= len2 &&
  285. doesListMatch(0, len2, rdns));
  286. }
  287. /**
  288. * Determines whether this LDAP name ends with a specified
  289. * LDAP name suffix.
  290. * A name <tt>n</tt> is a suffix if it is equal to
  291. * <tt>getSuffix(size()-n.size())</tt>--in other words this LDAP
  292. * name ends with 'n'. If n is null or not a RFC2253 formatted name
  293. * as described in the class description, false is returned.
  294. *
  295. * @param n The LDAP name to check.
  296. * @return true if <tt>n</tt> is a suffix of this name, false otherwise.
  297. * @see #getSuffix(int posn)
  298. */
  299. public boolean endsWith(Name n) {
  300. if (n == null) {
  301. return false;
  302. }
  303. int len1 = rdns.size();
  304. int len2 = n.size();
  305. return (len1 >= len2 &&
  306. matches(len1 - len2, len1, n));
  307. }
  308. /**
  309. * Determines whether the specified RDN sequence forms a suffix of this
  310. * LDAP name. Returns true if this LdapName is at least as long as rdns,
  311. * and for every position p in the range [size() - rdns.size(), size())
  312. * the component getRdn(p) matches rdns.get(p). Returns false otherwise.
  313. * If rdns is null, false is returned.
  314. *
  315. * @param rdns The sequence of <tt>Rdn</tt>s to check.
  316. * @return true if <tt>rdns</tt> form a suffix of this LDAP name,
  317. * false otherwise.
  318. */
  319. public boolean endsWith(List<Rdn> rdns) {
  320. if (rdns == null) {
  321. return false;
  322. }
  323. int len1 = this.rdns.size();
  324. int len2 = rdns.size();
  325. return (len1 >= len2 &&
  326. doesListMatch(len1 - len2, len1, rdns));
  327. }
  328. private boolean doesListMatch(int beg, int end, List rdns) {
  329. for (int i = beg; i < end; i++) {
  330. if (!this.rdns.get(i).equals(rdns.get(i - beg))) {
  331. return false;
  332. }
  333. }
  334. return true;
  335. }
  336. /*
  337. * Helper method for startsWith() and endsWith().
  338. * Returns true if components [beg,end) match the components of "n".
  339. * If "n" is not an LdapName, each of its components is parsed as
  340. * the string form of an RDN.
  341. * The following must hold: end - beg == n.size().
  342. */
  343. private boolean matches(int beg, int end, Name n) {
  344. if (n instanceof LdapName) {
  345. LdapName ln = (LdapName) n;
  346. return doesListMatch(beg, end, ln.rdns);
  347. } else {
  348. for (int i = beg; i < end; i++) {
  349. Rdn rdn;
  350. String rdnString = n.get(i - beg);
  351. try {
  352. rdn = (new Rfc2253Parser(rdnString)).parseRdn();
  353. } catch (InvalidNameException e) {
  354. return false;
  355. }
  356. if (!rdn.equals(rdns.get(i))) {
  357. return false;
  358. }
  359. }
  360. }
  361. return true;
  362. }
  363. /**
  364. * Adds the components of a name -- in order -- to the end of this name.
  365. *
  366. * @param suffix The non-null components to add.
  367. * @return The updated name (not a new instance).
  368. *
  369. * @throws InvalidNameException if <tt>suffix</tt> is not a valid LDAP
  370. * name, or if the addition of the components would violate the
  371. * syntax rules of this LDAP name.
  372. */
  373. public Name addAll(Name suffix) throws InvalidNameException {
  374. return addAll(size(), suffix);
  375. }
  376. /**
  377. * Adds the RDNs of a name -- in order -- to the end of this name.
  378. *
  379. * @param suffixRdns The non-null suffix <tt>Rdn</tt>s to add.
  380. * @return The updated name (not a new instance).
  381. */
  382. public Name addAll(List<Rdn> suffixRdns) {
  383. return addAll(size(), suffixRdns);
  384. }
  385. /**
  386. * Adds the components of a name -- in order -- at a specified position
  387. * within this name. Components of this LDAP name at or after the
  388. * index (if any) of the first new component are shifted up
  389. * (away from index 0) to accomodate the new components.
  390. *
  391. * @param suffix The non-null components to add.
  392. * @param posn The index at which to add the new component.
  393. * Must be in the range [0,size()].
  394. *
  395. * @return The updated name (not a new instance).
  396. *
  397. * @throws InvalidNameException if <tt>suffix</tt> is not a valid LDAP
  398. * name, or if the addition of the components would violate the
  399. * syntax rules of this LDAP name.
  400. * @throws IndexOutOfBoundsException.
  401. * If posn is outside the specified range.
  402. */
  403. public Name addAll(int posn, Name suffix)
  404. throws InvalidNameException {
  405. unparsed = null; // no longer valid
  406. if (suffix instanceof LdapName) {
  407. LdapName s = (LdapName) suffix;
  408. rdns.addAll(posn, s.rdns);
  409. } else {
  410. Enumeration comps = suffix.getAll();
  411. while (comps.hasMoreElements()) {
  412. rdns.add(posn++,
  413. (new Rfc2253Parser((String) comps.nextElement()).
  414. parseRdn()));
  415. }
  416. }
  417. return this;
  418. }
  419. /**
  420. * Adds the RDNs of a name -- in order -- at a specified position
  421. * within this name. RDNs of this LDAP name at or after the
  422. * index (if any) of the first new RDN are shifted up (away from index 0) to
  423. * accomodate the new RDNs.
  424. *
  425. * @param suffixRdns The non-null suffix <tt>Rdn</tt>s to add.
  426. * @param posn The index at which to add the suffix RDNs.
  427. * Must be in the range [0,size()].
  428. *
  429. * @return The updated name (not a new instance).
  430. * @throws IndexOutOfBoundsException.
  431. * If posn is outside the specified range.
  432. */
  433. public Name addAll(int posn, List<Rdn> suffixRdns) {
  434. unparsed = null;
  435. for (int i = 0; i < suffixRdns.size(); i++) {
  436. Object obj = suffixRdns.get(i);
  437. if (!(obj instanceof Rdn)) {
  438. throw new IllegalArgumentException("Entry:" + obj +
  439. " not a valid type;suffix list entries must be of type Rdn");
  440. }
  441. rdns.add(i + posn, obj);
  442. }
  443. return this;
  444. }
  445. /**
  446. * Adds a single component to the end of this LDAP name.
  447. *
  448. * @param comp The non-null component to add.
  449. * @return The updated LdapName, not a new instance.
  450. * Cannot be null.
  451. * @exception InvalidNameException If adding comp at end of the name
  452. * would violate the name's syntax.
  453. */
  454. public Name add(String comp) throws InvalidNameException {
  455. return add(size(), comp);
  456. }
  457. /**
  458. * Adds a single RDN to the end of this LDAP name.
  459. *
  460. * @param comp The non-null RDN to add.
  461. *
  462. * @return The updated LdapName, not a new instance.
  463. * Cannot be null.
  464. */
  465. public Name add(Rdn comp) {
  466. return add(size(), comp);
  467. }
  468. /**
  469. * Adds a single component at a specified position within this
  470. * LDAP name.
  471. * Components of this LDAP name at or after the index (if any) of the new
  472. * component are shifted up by one (away from index 0) to accommodate
  473. * the new component.
  474. *
  475. * @param comp The non-null component to add.
  476. * @param posn The index at which to add the new component.
  477. * Must be in the range [0,size()].
  478. * @return The updated LdapName, not a new instance.
  479. * Cannot be null.
  480. * @exception IndexOutOfBoundsException.
  481. * If posn is outside the specified range.
  482. * @exception InvalidNameException If adding comp at the
  483. * specified position would violate the name's syntax.
  484. */
  485. public Name add(int posn, String comp) throws InvalidNameException {
  486. Rdn rdn = (new Rfc2253Parser(comp)).parseRdn();
  487. rdns.add(posn, rdn);
  488. unparsed = null; // no longer valid
  489. return this;
  490. }
  491. /**
  492. * Adds a single RDN at a specified position within this
  493. * LDAP name.
  494. * RDNs of this LDAP name at or after the index (if any) of the new
  495. * RDN are shifted up by one (away from index 0) to accommodate
  496. * the new RDN.
  497. *
  498. * @param comp The non-null RDN to add.
  499. * @param posn The index at which to add the new RDN.
  500. * Must be in the range [0,size()].
  501. * @return The updated LdapName, not a new instance.
  502. * Cannot be null.
  503. * @exception IndexOutOfBoundsException
  504. * If posn is outside the specified range.
  505. */
  506. public Name add(int posn, Rdn comp) {
  507. if (comp == null) {
  508. throw new NullPointerException("Cannot set comp to null");
  509. }
  510. rdns.add(posn, comp);
  511. unparsed = null; // no longer valid
  512. return this;
  513. }
  514. /**
  515. * Removes a component from this LDAP name.
  516. * The component of this name at the specified position is removed.
  517. * Components with indexes greater than this position (if any)
  518. * are shifted down (toward index 0) by one.
  519. *
  520. * @param posn The index of the component to remove.
  521. * Must be in the range [0,size()).
  522. * @return The component removed (a String).
  523. *
  524. * @throws IndexOutOfBoundsException
  525. * if posn is outside the specified range.
  526. * @throws InvalidNameException if deleting the component
  527. * would violate the syntax rules of the name.
  528. */
  529. public Object remove(int posn) throws InvalidNameException {
  530. unparsed = null; // no longer valid
  531. return rdns.remove(posn).toString();
  532. }
  533. /**
  534. * Retrieves the list of relative distinguished names.
  535. * The contents of the list are unmodifiable.
  536. * The indexing of RDNs in the returned list follows the numbering of
  537. * RDNs as described in the class description.
  538. * If the name has zero components, an empty list is returned.
  539. *
  540. * @return The name as a list of RDNs which are instances of
  541. * the class {@link Rdn Rdn}.
  542. */
  543. public List<Rdn> getRdns() {
  544. return Collections.unmodifiableList(rdns);
  545. }
  546. /**
  547. * Generates a new copy of this name.
  548. * Subsequent changes to the components of this name will not
  549. * affect the new copy, and vice versa.
  550. *
  551. * @return A copy of the this LDAP name.
  552. */
  553. public Object clone() {
  554. return new LdapName(unparsed, rdns, 0, rdns.size());
  555. }
  556. /**
  557. * Returns a string representation of this LDAP name in a format
  558. * defined by <a href="http://ietf.org/rfc/rfc2253.txt">RFC 2253</a>
  559. * and described in the class description. If the name has zero
  560. * components an empty string is returned.
  561. *
  562. * @return The string representation of the LdapName.
  563. */
  564. public String toString() {
  565. if (unparsed != null) {
  566. return unparsed;
  567. }
  568. StringBuilder builder = new StringBuilder();
  569. int size = rdns.size();
  570. if ((size - 1) >= 0) {
  571. builder.append((Rdn) rdns.get(size - 1));
  572. }
  573. for (int next = size - 2; next >= 0; next--) {
  574. builder.append(',');
  575. builder.append((Rdn) rdns.get(next));
  576. }
  577. unparsed = builder.toString();
  578. return unparsed;
  579. }
  580. /**
  581. * Determines whether two LDAP names are equal.
  582. * If obj is null or not an LDAP name, false is returned.
  583. * <p>
  584. * Two LDAP names are equal if each RDN in one is equal
  585. * to the corresponding RDN in the other. This implies
  586. * both have the same number of RDNs, and each RDN's
  587. * equals() test against the corresponding RDN in the other
  588. * name returns true. See {@link Rdn#equals(Object obj)}
  589. * for a definition of RDN equality.
  590. *
  591. * @param obj The possibly null object to compare against.
  592. * @return true if obj is equal to this LDAP name,
  593. * false otherwise.
  594. * @see #hashCode
  595. */
  596. public boolean equals(Object obj) {
  597. // check possible shortcuts
  598. if (obj == this) {
  599. return true;
  600. }
  601. if (!(obj instanceof LdapName)) {
  602. return false;
  603. }
  604. LdapName that = (LdapName) obj;
  605. if (rdns.size() != that.rdns.size()) {
  606. return false;
  607. }
  608. if (unparsed != null && unparsed.equalsIgnoreCase(
  609. that.unparsed)) {
  610. return true;
  611. }
  612. // Compare RDNs one by one for equality
  613. for (int i = 0; i < rdns.size(); i++) {
  614. // Compare a single pair of RDNs.
  615. Rdn rdn1 = (Rdn) rdns.get(i);
  616. Rdn rdn2 = (Rdn) that.rdns.get(i);
  617. if (!rdn1.equals(rdn2)) {
  618. return false;
  619. }
  620. }
  621. return true;
  622. }
  623. /**
  624. * Compares this LdapName with the specified Object for order.
  625. * Returns a negative integer, zero, or a positive integer as this
  626. * Name is less than, equal to, or greater than the given Object.
  627. * <p>
  628. * If obj is null or not an instance of LdapName, ClassCastException
  629. * is thrown.
  630. * <p>
  631. * Ordering of LDAP names follows the lexicographical rules for
  632. * string comparison, with the extension that this applies to all
  633. * the RDNs in the LDAP name. All the RDNs are lined up in their
  634. * specified order and compared lexicographically.
  635. * See {@link Rdn#compareTo(Object obj) Rdn.compareTo(Object obj)}
  636. * for RDN comparison rules.
  637. * <p>
  638. * If this LDAP name is lexicographically lesser than obj,
  639. * a negative number is returned.
  640. * If this LDAP name is lexicographically greater than obj,
  641. * a positive number is returned.
  642. * @param obj The non-null LdapName instance to compare against.
  643. *
  644. * @return A negative integer, zero, or a positive integer as this Name
  645. * is less than, equal to, or greater than the given obj.
  646. * @exception ClassCastException if obj is null or not a LdapName.
  647. */
  648. public int compareTo(Object obj) {
  649. if (!(obj instanceof LdapName)) {
  650. throw new ClassCastException("The obj is not a LdapName");
  651. }
  652. // check possible shortcuts
  653. if (obj == this) {
  654. return 0;
  655. }
  656. LdapName that = (LdapName) obj;
  657. if (unparsed != null && unparsed.equalsIgnoreCase(
  658. that.unparsed)) {
  659. return 0;
  660. }
  661. // Compare RDNs one by one, lexicographically.
  662. int minSize = Math.min(rdns.size(), that.rdns.size());
  663. for (int i = 0; i < minSize; i++) {
  664. // Compare a single pair of RDNs.
  665. Rdn rdn1 = (Rdn)rdns.get(i);
  666. Rdn rdn2 = (Rdn)that.rdns.get(i);
  667. int diff = rdn1.compareTo(rdn2);
  668. if (diff != 0) {
  669. return diff;
  670. }
  671. }
  672. return (rdns.size() - that.rdns.size()); // longer DN wins
  673. }
  674. /**
  675. * Computes the hash code of this LDAP name.
  676. * The hash code is the sum of the hash codes of individual RDNs
  677. * of this name.
  678. *
  679. * @return An int representing the hash code of this name.
  680. * @see #equals
  681. */
  682. public int hashCode() {
  683. // Sum up the hash codes of the components.
  684. int hash = 0;
  685. // For each RDN...
  686. for (int i = 0; i < rdns.size(); i++) {
  687. Rdn rdn = (Rdn) rdns.get(i);
  688. hash += rdn.hashCode();
  689. }
  690. return hash;
  691. }
  692. /**
  693. * Serializes only the unparsed DN, for compactness and to avoid
  694. * any implementation dependency.
  695. *
  696. * @serialData The DN string
  697. */
  698. private void writeObject(ObjectOutputStream s)
  699. throws java.io.IOException {
  700. s.defaultWriteObject();
  701. s.writeObject(toString());
  702. }
  703. private void readObject(ObjectInputStream s)
  704. throws java.io.IOException, ClassNotFoundException {
  705. s.defaultReadObject();
  706. unparsed = (String)s.readObject();
  707. try {
  708. parse();
  709. } catch (InvalidNameException e) {
  710. // shouldn't happen
  711. throw new java.io.StreamCorruptedException(
  712. "Invalid name: " + unparsed);
  713. }
  714. }
  715. private void parse() throws InvalidNameException {
  716. // rdns = (ArrayList<Rdn>) (new RFC2253Parser(unparsed)).getDN();
  717. rdns = (ArrayList) (new Rfc2253Parser(unparsed)).parseDn();
  718. }
  719. }