1. /*
  2. * @(#)CompoundName.java 1.11 04/05/05
  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;
  8. import java.util.Enumeration;
  9. import java.util.Properties;
  10. /**
  11. * This class represents a compound name -- a name from
  12. * a hierarchical name space.
  13. * Each component in a compound name is an atomic name.
  14. * <p>
  15. * The components of a compound name are numbered. The indexes of a
  16. * compound name with N components range from 0 up to, but not including, N.
  17. * This range may be written as [0,N).
  18. * The most significant component is at index 0.
  19. * An empty compound name has no components.
  20. *<p>
  21. * <h4>Compound Name Syntax</h4>
  22. * The syntax of a compound name is specified using a set of properties:
  23. *<dl>
  24. * <dt>jndi.syntax.direction
  25. * <dd>Direction for parsing ("right_to_left", "left_to_right", "flat").
  26. * If unspecified, defaults to "flat", which means the namespace is flat
  27. * with no hierarchical structure.
  28. *
  29. * <dt>jndi.syntax.separator
  30. * <dd>Separator between atomic name components.
  31. * Required unless direction is "flat".
  32. *
  33. * <dt>jndi.syntax.ignorecase
  34. * <dd>If present, "true" means ignore the case when comparing name
  35. * components. If its value is not "true", or if the property is not
  36. * present, case is considered when comparing name components.
  37. *
  38. * <dt>jndi.syntax.escape
  39. * <dd>If present, specifies the escape string for overriding separator,
  40. * escapes and quotes.
  41. *
  42. * <dt>jndi.syntax.beginquote
  43. * <dd>If present, specifies the string delimiting start of a quoted string.
  44. *
  45. * <dt>jndi.syntax.endquote
  46. * <dd>String delimiting end of quoted string.
  47. * If present, specifies the string delimiting the end of a quoted string.
  48. * If not present, use syntax.beginquote as end quote.
  49. * <dt>jndi.syntax.beginquote2
  50. * <dd>Alternative set of begin/end quotes.
  51. *
  52. * <dt>jndi.syntax.endquote2
  53. * <dd>Alternative set of begin/end quotes.
  54. *
  55. * <dt>jndi.syntax.trimblanks
  56. * <dd>If present, "true" means trim any leading and trailing whitespaces
  57. * in a name component for comparison purposes. If its value is not
  58. * "true", or if the property is not present, blanks are significant.
  59. * <dt>jndi.syntax.separator.ava
  60. * <dd>If present, specifies the string that separates
  61. * attribute-value-assertions when specifying multiple attribute/value
  62. * pairs. (e.g. "," in age=65,gender=male).
  63. * <dt>jndi.syntax.separator.typeval
  64. * <dd>If present, specifies the string that separators attribute
  65. * from value (e.g. "=" in "age=65")
  66. *</dl>
  67. * These properties are interpreted according to the following rules:
  68. *<ol>
  69. *<li>
  70. * In a string without quotes or escapes, any instance of the
  71. * separator delimits two atomic names. Each atomic name is referred
  72. * to as a <em>component</em>.
  73. *<li>
  74. * A separator, quote or escape is escaped if preceded immediately
  75. * (on the left) by the escape.
  76. *<li>
  77. * If there are two sets of quotes, a specific begin-quote must be matched
  78. * by its corresponding end-quote.
  79. *<li>
  80. * A non-escaped begin-quote which precedes a component must be
  81. * matched by a non-escaped end-quote at the end of the component.
  82. * A component thus quoted is referred to as a
  83. * <em>quoted component</em>. It is parsed by
  84. * removing the being- and end- quotes, and by treating the intervening
  85. * characters as ordinary characters unless one of the rules involving
  86. * quoted components listed below applies.
  87. *<li>
  88. * Quotes embedded in non-quoted components are treated as ordinary strings
  89. * and need not be matched.
  90. *<li>
  91. * A separator that is escaped or appears between non-escaped
  92. * quotes is treated as an ordinary string and not a separator.
  93. *<li>
  94. * An escape string within a quoted component acts as an escape only when
  95. * followed by the corresponding end-quote string.
  96. * This can be used to embed an escaped quote within a quoted component.
  97. *<li>
  98. * An escaped escape string is not treated as an escape string.
  99. *<li>
  100. * An escape string that does not precede a meta string (quotes or separator)
  101. * and is not at the end of a component is treated as an ordinary string.
  102. *<li>
  103. * A leading separator (the compound name string begins with
  104. * a separator) denotes a leading empty atomic component (consisting
  105. * of an empty string).
  106. * A trailing separator (the compound name string ends with
  107. * a separator) denotes a trailing empty atomic component.
  108. * Adjacent separators denote an empty atomic component.
  109. *</ol>
  110. * <p>
  111. * The string form of the compound name follows the syntax described above.
  112. * When the components of the compound name are turned into their
  113. * string representation, the reserved syntax rules described above are
  114. * applied (e.g. embedded separators are escaped or quoted)
  115. * so that when the same string is parsed, it will yield the same components
  116. * of the original compound name.
  117. *<p>
  118. *<h4>Multithreaded Access</h4>
  119. * A <tt>CompoundName</tt> instance is not synchronized against concurrent
  120. * multithreaded access. Multiple threads trying to access and modify a
  121. * <tt>CompoundName</tt> should lock the object.
  122. *
  123. * @author Rosanna Lee
  124. * @author Scott Seligman
  125. * @version 1.11 04/05/05
  126. * @since 1.3
  127. */
  128. public class CompoundName implements Name {
  129. /**
  130. * Implementation of this compound name.
  131. * This field is initialized by the constructors and cannot be null.
  132. * It should be treated as a read-only variable by subclasses.
  133. */
  134. protected transient NameImpl impl;
  135. /**
  136. * Syntax properties for this compound name.
  137. * This field is initialized by the constructors and cannot be null.
  138. * It should be treated as a read-only variable by subclasses.
  139. * Any necessary changes to mySyntax should be made within constructors
  140. * and not after the compound name has been instantiated.
  141. */
  142. protected transient Properties mySyntax;
  143. /**
  144. * Constructs a new compound name instance using the components
  145. * specified in comps and syntax. This protected method is intended to be
  146. * to be used by subclasses of CompoundName when they override
  147. * methods such as clone(), getPrefix(), getSuffix().
  148. *
  149. * @param comps A non-null enumeration of the components to add.
  150. * Each element of the enumeration is of class String.
  151. * The enumeration will be consumed to extract its
  152. * elements.
  153. * @param syntax A non-null properties that specify the syntax of
  154. * this compound name. See class description for
  155. * contents of properties.
  156. */
  157. protected CompoundName(Enumeration<String> comps, Properties syntax) {
  158. if (syntax == null) {
  159. throw new NullPointerException();
  160. }
  161. mySyntax = syntax;
  162. impl = new NameImpl(syntax, comps);
  163. }
  164. /**
  165. * Constructs a new compound name instance by parsing the string n
  166. * using the syntax specified by the syntax properties supplied.
  167. *
  168. * @param n The non-null string to parse.
  169. * @param syntax A non-null list of properties that specify the syntax of
  170. * this compound name. See class description for
  171. * contents of properties.
  172. * @exception InvalidNameException If 'n' violates the syntax specified
  173. * by <code>syntax</code>.
  174. */
  175. public CompoundName(String n, Properties syntax) throws InvalidNameException {
  176. if (syntax == null) {
  177. throw new NullPointerException();
  178. }
  179. mySyntax = syntax;
  180. impl = new NameImpl(syntax, n);
  181. }
  182. /**
  183. * Generates the string representation of this compound name, using
  184. * the syntax rules of the compound name. The syntax rules
  185. * are described in the class description.
  186. * An empty component is represented by an empty string.
  187. *
  188. * The string representation thus generated can be passed to
  189. * the CompoundName constructor with the same syntax properties
  190. * to create a new equivalent compound name.
  191. *
  192. * @return A non-null string representation of this compound name.
  193. */
  194. public String toString() {
  195. return (impl.toString());
  196. }
  197. /**
  198. * Determines whether obj is syntactically equal to this compound name.
  199. * If obj is null or not a CompoundName, false is returned.
  200. * Two compound names are equal if each component in one is "equal"
  201. * to the corresponding component in the other.
  202. *<p>
  203. * Equality is also defined in terms of the syntax of this compound name.
  204. * The default implementation of CompoundName uses the syntax properties
  205. * jndi.syntax.ignorecase and jndi.syntax.trimblanks when comparing
  206. * two components for equality. If case is ignored, two strings
  207. * with the same sequence of characters but with different cases
  208. * are considered equal. If blanks are being trimmed, leading and trailing
  209. * blanks are ignored for the purpose of the comparison.
  210. *<p>
  211. * Both compound names must have the same number of components.
  212. *<p>
  213. * Implementation note: Currently the syntax properties of the two compound
  214. * names are not compared for equality. They might be in the future.
  215. *
  216. * @param obj The possibly null object to compare against.
  217. * @return true if obj is equal to this compound name, false otherwise.
  218. * @see #compareTo(java.lang.Object obj)
  219. */
  220. public boolean equals(Object obj) {
  221. // %%% check syntax too?
  222. return (obj != null &&
  223. obj instanceof CompoundName &&
  224. impl.equals(((CompoundName)obj).impl));
  225. }
  226. /**
  227. * Computes the hash code of this compound name.
  228. * The hash code is the sum of the hash codes of the "canonicalized"
  229. * forms of individual components of this compound name.
  230. * Each component is "canonicalized" according to the
  231. * compound name's syntax before its hash code is computed.
  232. * For a case-insensitive name, for example, the uppercased form of
  233. * a name has the same hash code as its lowercased equivalent.
  234. *
  235. * @return An int representing the hash code of this name.
  236. */
  237. public int hashCode() {
  238. return impl.hashCode();
  239. }
  240. /**
  241. * Creates a copy of this compound name.
  242. * Changes to the components of this compound name won't
  243. * affect the new copy and vice versa.
  244. * The clone and this compound name share the same syntax.
  245. *
  246. * @return A non-null copy of this compound name.
  247. */
  248. public Object clone() {
  249. return (new CompoundName(getAll(), mySyntax));
  250. }
  251. /**
  252. * Compares this CompoundName with the specified Object for order.
  253. * Returns a
  254. * negative integer, zero, or a positive integer as this Name is less
  255. * than, equal to, or greater than the given Object.
  256. * <p>
  257. * If obj is null or not an instance of CompoundName, ClassCastException
  258. * is thrown.
  259. * <p>
  260. * See equals() for what it means for two compound names to be equal.
  261. * If two compound names are equal, 0 is returned.
  262. *<p>
  263. * Ordering of compound names depend on the syntax of the compound name.
  264. * By default, they follow lexicographical rules for string comparison
  265. * with the extension that this applies to all the components in the
  266. * compound name and that comparison of individual components is
  267. * affected by the jndi.syntax.ignorecase and jndi.syntax.trimblanks
  268. * properties, identical to how they affect equals().
  269. * If this compound name is "lexicographically" lesser than obj,
  270. * a negative number is returned.
  271. * If this compound name is "lexicographically" greater than obj,
  272. * a positive number is returned.
  273. *<p>
  274. * Implementation note: Currently the syntax properties of the two compound
  275. * names are not compared when checking order. They might be in the future.
  276. * @param obj The non-null object to compare against.
  277. * @return a negative integer, zero, or a positive integer as this Name
  278. * is less than, equal to, or greater than the given Object.
  279. * @exception ClassCastException if obj is not a CompoundName.
  280. * @see #equals(java.lang.Object)
  281. */
  282. public int compareTo(Object obj) {
  283. if (!(obj instanceof CompoundName)) {
  284. throw new ClassCastException("Not a CompoundName");
  285. }
  286. return impl.compareTo(((CompoundName)obj).impl);
  287. }
  288. /**
  289. * Retrieves the number of components in this compound name.
  290. *
  291. * @return The nonnegative number of components in this compound name.
  292. */
  293. public int size() {
  294. return (impl.size());
  295. }
  296. /**
  297. * Determines whether this compound name is empty.
  298. * A compound name is empty if it has zero components.
  299. *
  300. * @return true if this compound name is empty, false otherwise.
  301. */
  302. public boolean isEmpty() {
  303. return (impl.isEmpty());
  304. }
  305. /**
  306. * Retrieves the components of this compound name as an enumeration
  307. * of strings.
  308. * The effects of updates to this compound name on this enumeration
  309. * is undefined.
  310. *
  311. * @return A non-null enumeration of the components of this
  312. * compound name. Each element of the enumeration is of class String.
  313. */
  314. public Enumeration<String> getAll() {
  315. return (impl.getAll());
  316. }
  317. /**
  318. * Retrieves a component of this compound name.
  319. *
  320. * @param posn The 0-based index of the component to retrieve.
  321. * Must be in the range [0,size()).
  322. * @return The component at index posn.
  323. * @exception ArrayIndexOutOfBoundsException if posn is outside the
  324. * specified range.
  325. */
  326. public String get(int posn) {
  327. return (impl.get(posn));
  328. }
  329. /**
  330. * Creates a compound name whose components consist of a prefix of the
  331. * components in this compound name.
  332. * The result and this compound name share the same syntax.
  333. * Subsequent changes to
  334. * this compound name does not affect the name that is returned and
  335. * vice versa.
  336. *
  337. * @param posn The 0-based index of the component at which to stop.
  338. * Must be in the range [0,size()].
  339. * @return A compound name consisting of the components at indexes in
  340. * the range [0,posn).
  341. * @exception ArrayIndexOutOfBoundsException
  342. * If posn is outside the specified range.
  343. */
  344. public Name getPrefix(int posn) {
  345. Enumeration comps = impl.getPrefix(posn);
  346. return (new CompoundName(comps, mySyntax));
  347. }
  348. /**
  349. * Creates a compound name whose components consist of a suffix of the
  350. * components in this compound name.
  351. * The result and this compound name share the same syntax.
  352. * Subsequent changes to
  353. * this compound name does not affect the name that is returned.
  354. *
  355. * @param posn The 0-based index of the component at which to start.
  356. * Must be in the range [0,size()].
  357. * @return A compound name consisting of the components at indexes in
  358. * the range [posn,size()). If posn is equal to
  359. * size(), an empty compound name is returned.
  360. * @exception ArrayIndexOutOfBoundsException
  361. * If posn is outside the specified range.
  362. */
  363. public Name getSuffix(int posn) {
  364. Enumeration comps = impl.getSuffix(posn);
  365. return (new CompoundName(comps, mySyntax));
  366. }
  367. /**
  368. * Determines whether a compound name is a prefix of this compound name.
  369. * A compound name 'n' is a prefix if it is equal to
  370. * getPrefix(n.size())--in other words, this compound name
  371. * starts with 'n'.
  372. * If n is null or not a compound name, false is returned.
  373. *<p>
  374. * Implementation note: Currently the syntax properties of n
  375. * are not used when doing the comparison. They might be in the future.
  376. * @param n The possibly null compound name to check.
  377. * @return true if n is a CompoundName and
  378. * is a prefix of this compound name, false otherwise.
  379. */
  380. public boolean startsWith(Name n) {
  381. if (n instanceof CompoundName) {
  382. return (impl.startsWith(n.size(), n.getAll()));
  383. } else {
  384. return false;
  385. }
  386. }
  387. /**
  388. * Determines whether a compound name is a suffix of this compound name.
  389. * A compound name 'n' is a suffix if it it is equal to
  390. * getSuffix(size()-n.size())--in other words, this
  391. * compound name ends with 'n'.
  392. * If n is null or not a compound name, false is returned.
  393. *<p>
  394. * Implementation note: Currently the syntax properties of n
  395. * are not used when doing the comparison. They might be in the future.
  396. * @param n The possibly null compound name to check.
  397. * @return true if n is a CompoundName and
  398. * is a suffix of this compound name, false otherwise.
  399. */
  400. public boolean endsWith(Name n) {
  401. if (n instanceof CompoundName) {
  402. return (impl.endsWith(n.size(), n.getAll()));
  403. } else {
  404. return false;
  405. }
  406. }
  407. /**
  408. * Adds the components of a compound name -- in order -- to the end of
  409. * this compound name.
  410. *<p>
  411. * Implementation note: Currently the syntax properties of suffix
  412. * is not used or checked. They might be in the future.
  413. * @param suffix The non-null components to add.
  414. * @return The updated CompoundName, not a new one. Cannot be null.
  415. * @exception InvalidNameException If suffix is not a compound name,
  416. * or if the addition of the components violates the syntax
  417. * of this compound name (e.g. exceeding number of components).
  418. */
  419. public Name addAll(Name suffix) throws InvalidNameException {
  420. if (suffix instanceof CompoundName) {
  421. impl.addAll(suffix.getAll());
  422. return this;
  423. } else {
  424. throw new InvalidNameException("Not a compound name: " +
  425. suffix.toString());
  426. }
  427. }
  428. /**
  429. * Adds the components of a compound name -- in order -- at a specified
  430. * position within this compound name.
  431. * Components of this compound name at or after the index of the first
  432. * new component are shifted up (away from index 0)
  433. * to accommodate the new components.
  434. *<p>
  435. * Implementation note: Currently the syntax properties of suffix
  436. * is not used or checked. They might be in the future.
  437. *
  438. * @param n The non-null components to add.
  439. * @param posn The index in this name at which to add the new
  440. * components. Must be in the range [0,size()].
  441. * @return The updated CompoundName, not a new one. Cannot be null.
  442. * @exception ArrayIndexOutOfBoundsException
  443. * If posn is outside the specified range.
  444. * @exception InvalidNameException If n is not a compound name,
  445. * or if the addition of the components violates the syntax
  446. * of this compound name (e.g. exceeding number of components).
  447. */
  448. public Name addAll(int posn, Name n) throws InvalidNameException {
  449. if (n instanceof CompoundName) {
  450. impl.addAll(posn, n.getAll());
  451. return this;
  452. } else {
  453. throw new InvalidNameException("Not a compound name: " +
  454. n.toString());
  455. }
  456. }
  457. /**
  458. * Adds a single component to the end of this compound name.
  459. *
  460. * @param comp The non-null component to add.
  461. * @return The updated CompoundName, not a new one. Cannot be null.
  462. * @exception InvalidNameException If adding comp at end of the name
  463. * would violate the compound name's syntax.
  464. */
  465. public Name add(String comp) throws InvalidNameException{
  466. impl.add(comp);
  467. return this;
  468. }
  469. /**
  470. * Adds a single component at a specified position within this
  471. * compound name.
  472. * Components of this compound name at or after the index of the new
  473. * component are shifted up by one (away from index 0)
  474. * to accommodate the new component.
  475. *
  476. * @param comp The non-null component to add.
  477. * @param posn The index at which to add the new component.
  478. * Must be in the range [0,size()].
  479. * @exception ArrayIndexOutOfBoundsException
  480. * If posn is outside the specified range.
  481. * @return The updated CompoundName, not a new one. Cannot be null.
  482. * @exception InvalidNameException If adding comp at the specified position
  483. * would violate the compound name's syntax.
  484. */
  485. public Name add(int posn, String comp) throws InvalidNameException{
  486. impl.add(posn, comp);
  487. return this;
  488. }
  489. /**
  490. * Deletes a component from this compound name.
  491. * The component of this compound name at position 'posn' is removed,
  492. * and components at indices greater than 'posn'
  493. * are shifted down (towards index 0) by one.
  494. *
  495. * @param posn The index of the component to delete.
  496. * Must be in the range [0,size()).
  497. * @return The component removed (a String).
  498. * @exception ArrayIndexOutOfBoundsException
  499. * If posn is outside the specified range (includes case where
  500. * compound name is empty).
  501. * @exception InvalidNameException If deleting the component
  502. * would violate the compound name's syntax.
  503. */
  504. public Object remove(int posn) throws InvalidNameException {
  505. return impl.remove(posn);
  506. }
  507. /**
  508. * Overridden to avoid implementation dependency.
  509. * @serialData The syntax <tt>Properties</tt>, followed by
  510. * the number of components (an <tt>int</tt>), and the individual
  511. * components (each a <tt>String</tt>).
  512. */
  513. private void writeObject(java.io.ObjectOutputStream s)
  514. throws java.io.IOException {
  515. s.writeObject(mySyntax);
  516. s.writeInt(size());
  517. Enumeration comps = getAll();
  518. while (comps.hasMoreElements()) {
  519. s.writeObject(comps.nextElement());
  520. }
  521. }
  522. /**
  523. * Overridden to avoid implementation dependency.
  524. */
  525. private void readObject(java.io.ObjectInputStream s)
  526. throws java.io.IOException, ClassNotFoundException {
  527. mySyntax = (Properties)s.readObject();
  528. impl = new NameImpl(mySyntax);
  529. int n = s.readInt(); // number of components
  530. try {
  531. while (--n >= 0) {
  532. add((String)s.readObject());
  533. }
  534. } catch (InvalidNameException e) {
  535. throw (new java.io.StreamCorruptedException("Invalid name"));
  536. }
  537. }
  538. /**
  539. * Use serialVersionUID from JNDI 1.1.1 for interoperability
  540. */
  541. private static final long serialVersionUID = 3513100557083972036L;
  542. /*
  543. // For testing
  544. public static void main(String[] args) {
  545. Properties dotSyntax = new Properties();
  546. dotSyntax.put("jndi.syntax.direction", "right_to_left");
  547. dotSyntax.put("jndi.syntax.separator", ".");
  548. dotSyntax.put("jndi.syntax.ignorecase", "true");
  549. dotSyntax.put("jndi.syntax.escape", "\\");
  550. // dotSyntax.put("jndi.syntax.beginquote", "\"");
  551. // dotSyntax.put("jndi.syntax.beginquote2", "'");
  552. Name first = null;
  553. try {
  554. for (int i = 0; i < args.length; i++) {
  555. Name name;
  556. Enumeration e;
  557. System.out.println("Given name: " + args[i]);
  558. name = new CompoundName(args[i], dotSyntax);
  559. if (first == null) {
  560. first = name;
  561. }
  562. e = name.getComponents();
  563. while (e.hasMoreElements()) {
  564. System.out.println("Element: " + e.nextElement());
  565. }
  566. System.out.println("Constructed name: " + name.toString());
  567. System.out.println("Compare " + first.toString() + " with "
  568. + name.toString() + " = " + first.compareTo(name));
  569. }
  570. } catch (Exception ne) {
  571. ne.printStackTrace();
  572. }
  573. }
  574. */
  575. }