1. /*
  2. * @(#)NameImpl.java 1.4 00/02/02
  3. *
  4. * Copyright 1999, 2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.naming;
  11. import java.util.Vector;
  12. import java.util.Enumeration;
  13. import java.util.Properties;
  14. import java.util.NoSuchElementException;
  15. /**
  16. * The implementation class for CompoundName and CompositeName.
  17. * This class is package private.
  18. *
  19. * @author Rosanna Lee
  20. * @author Scott Seligman
  21. * @author Aravindan Ranganathan
  22. * @version 1.4 00/02/02
  23. * @since 1.3
  24. */
  25. class NameImpl implements java.io.Serializable {
  26. private Vector components;
  27. private String syntaxDirection;
  28. private String syntaxSeparator;
  29. private String syntaxSeparator2;
  30. private boolean syntaxCaseInsensitive;
  31. private boolean syntaxTrimBlanks;
  32. private String syntaxEscape;
  33. private String syntaxBeginQuote1;
  34. private String syntaxEndQuote1;
  35. private String syntaxBeginQuote2;
  36. private String syntaxEndQuote2;
  37. private String syntaxAvaSeparator;
  38. private String syntaxTypevalSeparator;
  39. // escapingStyle gives the method used at creation time for
  40. // quoting or escaping characters in the name. It is set to the
  41. // first style of quote or escape encountered if and when the name
  42. // is parsed.
  43. private final int STYLE_NONE = 0;
  44. private final int STYLE_QUOTE1 = 1;
  45. private final int STYLE_QUOTE2 = 2;
  46. private final int STYLE_ESCAPE = 3;
  47. private int escapingStyle = STYLE_NONE;
  48. // Returns true if "match" is not null, and n contains "match" at
  49. // position i.
  50. private final boolean isA(String n, int i, String match) {
  51. return (match != null && n.startsWith(match, i));
  52. }
  53. private final boolean isMeta(String n, int i) {
  54. return (isA(n, i, syntaxEscape) ||
  55. isA(n, i, syntaxBeginQuote1) ||
  56. isA(n, i, syntaxBeginQuote2) ||
  57. isSeparator(n, i));
  58. }
  59. private final boolean isSeparator(String n, int i) {
  60. return (isA(n, i, syntaxSeparator) ||
  61. isA(n, i, syntaxSeparator2));
  62. }
  63. private final int skipSeparator(String name, int i) {
  64. if (isA(name, i, syntaxSeparator)) {
  65. i += syntaxSeparator.length();
  66. } else if (isA(name, i, syntaxSeparator2)) {
  67. i += syntaxSeparator2.length();
  68. }
  69. return (i);
  70. }
  71. private final int extractComp(String name, int i, int len, Vector comps)
  72. throws InvalidNameException {
  73. String beginQuote;
  74. String endQuote;
  75. boolean start = true;
  76. boolean one = false;
  77. StringBuffer answer = new StringBuffer(len);
  78. while (i < len) {
  79. // handle quoted strings
  80. if (start && ((one = isA(name, i, syntaxBeginQuote1)) ||
  81. isA(name, i, syntaxBeginQuote2))) {
  82. // record choice of quote chars being used
  83. beginQuote = one ? syntaxBeginQuote1 : syntaxBeginQuote2;
  84. endQuote = one ? syntaxEndQuote1 : syntaxEndQuote2;
  85. if (escapingStyle == STYLE_NONE) {
  86. escapingStyle = one ? STYLE_QUOTE1 : STYLE_QUOTE2;
  87. }
  88. // consume string until matching quote
  89. for (i += beginQuote.length();
  90. ((i < len) && !name.startsWith(endQuote, i));
  91. i++) {
  92. // skip escape character if it is escaping ending quote
  93. // otherwise leave as is.
  94. if (isA(name, i, syntaxEscape) &&
  95. isA(name, i + syntaxEscape.length(), endQuote)) {
  96. i += syntaxEscape.length();
  97. }
  98. answer.append(name.charAt(i)); // copy char
  99. }
  100. // no ending quote found
  101. if (i >= len)
  102. throw
  103. new InvalidNameException(name + ": no close quote");
  104. // new Exception("no close quote");
  105. i += endQuote.length();
  106. // verify that end-quote occurs at separator or end of string
  107. if (i == len || isSeparator(name, i)) {
  108. break;
  109. }
  110. // throw (new Exception(
  111. throw (new InvalidNameException(name +
  112. ": close quote appears before end of component"));
  113. } else if (isSeparator(name, i)) {
  114. break;
  115. } else if (isA(name, i, syntaxEscape)) {
  116. if (isMeta(name, i + syntaxEscape.length())) {
  117. // if escape preceeds meta, consume escape and let
  118. // meta through
  119. i += syntaxEscape.length();
  120. if (escapingStyle == STYLE_NONE) {
  121. escapingStyle = STYLE_ESCAPE;
  122. }
  123. } else if (i + syntaxEscape.length() >= len) {
  124. throw (new InvalidNameException(name +
  125. ": unescaped " + syntaxEscape + " at end of component"));
  126. }
  127. } else if (isA(name, i, syntaxTypevalSeparator) &&
  128. ((one = isA(name, i+syntaxTypevalSeparator.length(), syntaxBeginQuote1)) ||
  129. isA(name, i+syntaxTypevalSeparator.length(), syntaxBeginQuote2))) {
  130. // Handle quote occurring after typeval separator
  131. beginQuote = one ? syntaxBeginQuote1 : syntaxBeginQuote2;
  132. endQuote = one ? syntaxEndQuote1 : syntaxEndQuote2;
  133. i += syntaxTypevalSeparator.length();
  134. answer.append(syntaxTypevalSeparator+beginQuote); // add back
  135. // consume string until matching quote
  136. for (i += beginQuote.length();
  137. ((i < len) && !name.startsWith(endQuote, i));
  138. i++) {
  139. // skip escape character if it is escaping ending quote
  140. // otherwise leave as is.
  141. if (isA(name, i, syntaxEscape) &&
  142. isA(name, i + syntaxEscape.length(), endQuote)) {
  143. i += syntaxEscape.length();
  144. }
  145. answer.append(name.charAt(i)); // copy char
  146. }
  147. // no ending quote found
  148. if (i >= len)
  149. throw
  150. new InvalidNameException(name + ": typeval no close quote");
  151. i += endQuote.length();
  152. answer.append(endQuote); // add back
  153. // verify that end-quote occurs at separator or end of string
  154. if (i == len || isSeparator(name, i)) {
  155. break;
  156. }
  157. throw (new InvalidNameException(name.substring(i) +
  158. ": typeval close quote appears before end of component"));
  159. }
  160. answer.append(name.charAt(i++));
  161. start = false;
  162. }
  163. if (syntaxDirection.equals("right_to_left"))
  164. comps.insertElementAt(answer.toString(), 0);
  165. else
  166. comps.addElement(answer.toString());
  167. return i;
  168. }
  169. private static boolean getBoolean(Properties p, String name) {
  170. return toBoolean(p.getProperty(name));
  171. }
  172. private static boolean toBoolean(String name) {
  173. return ((name != null) && name.toLowerCase().equals("true"));
  174. }
  175. private final void recordNamingConvention(Properties p) {
  176. syntaxDirection = p.getProperty("jndi.syntax.direction", "flat");
  177. if (!syntaxDirection.equals("left_to_right") &&
  178. !syntaxDirection.equals("right_to_left") &&
  179. !syntaxDirection.equals("flat")) {
  180. throw new IllegalArgumentException(syntaxDirection +
  181. "is not a valid value for the jndi.syntax.direction property");
  182. }
  183. if (!syntaxDirection.equals("flat")) {
  184. syntaxSeparator = p.getProperty("jndi.syntax.separator");
  185. syntaxSeparator2 = p.getProperty("jndi.syntax.separator2");
  186. if (syntaxSeparator == null) {
  187. throw new IllegalArgumentException(
  188. "jndi.syntax.separator property required for non-flat syntax");
  189. }
  190. }
  191. syntaxEscape = p.getProperty("jndi.syntax.escape");
  192. syntaxCaseInsensitive = getBoolean(p, "jndi.syntax.ignorecase");
  193. syntaxTrimBlanks = getBoolean(p, "jndi.syntax.trimblanks");
  194. syntaxBeginQuote1 = p.getProperty("jndi.syntax.beginquote");
  195. syntaxEndQuote1 = p.getProperty("jndi.syntax.endquote");
  196. if (syntaxEndQuote1 == null && syntaxBeginQuote1 != null)
  197. syntaxEndQuote1 = syntaxBeginQuote1;
  198. else if (syntaxBeginQuote1 == null && syntaxEndQuote1 != null)
  199. syntaxBeginQuote1 = syntaxEndQuote1;
  200. syntaxBeginQuote2 = p.getProperty("jndi.syntax.beginquote2");
  201. syntaxEndQuote2 = p.getProperty("jndi.syntax.endquote2");
  202. if (syntaxEndQuote2 == null && syntaxBeginQuote2 != null)
  203. syntaxEndQuote2 = syntaxBeginQuote2;
  204. else if (syntaxBeginQuote2 == null && syntaxEndQuote2 != null)
  205. syntaxBeginQuote2 = syntaxEndQuote2;
  206. syntaxAvaSeparator = p.getProperty("jndi.syntax.separator.ava");
  207. syntaxTypevalSeparator =
  208. p.getProperty("jndi.syntax.separator.typeval");
  209. }
  210. NameImpl(Properties syntax) {
  211. recordNamingConvention(syntax);
  212. components = new Vector();
  213. }
  214. NameImpl(Properties syntax, String n) throws InvalidNameException {
  215. this(syntax);
  216. boolean rToL = syntaxDirection.equals("right_to_left");
  217. boolean compsAllEmpty = true;
  218. int len = n.length();
  219. for (int i = 0; i < len; ) {
  220. i = extractComp(n, i, len, components);
  221. String comp = rToL
  222. ? (String)components.firstElement()
  223. : (String)components.lastElement();
  224. if (comp.length() >= 1) {
  225. compsAllEmpty = false;
  226. }
  227. if (i < len) {
  228. i = skipSeparator(n, i);
  229. if ((i == len) && !compsAllEmpty) {
  230. // Trailing separator found. Add an empty component.
  231. if (rToL) {
  232. components.insertElementAt("", 0);
  233. } else {
  234. components.addElement("");
  235. }
  236. }
  237. }
  238. }
  239. }
  240. NameImpl(Properties syntax, Enumeration comps) {
  241. this(syntax);
  242. // %% comps could shrink in the middle.
  243. while (comps.hasMoreElements())
  244. components.addElement(comps.nextElement());
  245. }
  246. /*
  247. // Determines whether this component needs any escaping.
  248. private final boolean escapingNeeded(String comp) {
  249. int len = comp.length();
  250. for (int i = 0; i < len; i++) {
  251. if (i == 0) {
  252. if (isA(comp, 0, syntaxBeginQuote1) ||
  253. isA(comp, 0, syntaxBeginQuote2)) {
  254. return (true);
  255. }
  256. }
  257. if (isSeparator(comp, i)) {
  258. return (true);
  259. }
  260. if (isA(comp, i, syntaxEscape)) {
  261. i += syntaxEscape.length();
  262. if (i >= len || isMeta(comp, i)) {
  263. return (true);
  264. }
  265. }
  266. }
  267. return (false);
  268. }
  269. */
  270. private final String stringifyComp(String comp) {
  271. int len = comp.length();
  272. boolean escapeSeparator = false, escapeSeparator2 = false;
  273. String beginQuote = null, endQuote = null;
  274. StringBuffer strbuf = new StringBuffer(len);
  275. // determine whether there are any separators; if so escape
  276. // or quote them
  277. if (syntaxSeparator != null &&
  278. comp.indexOf(syntaxSeparator) >= 0) {
  279. if (syntaxBeginQuote1 != null) {
  280. beginQuote = syntaxBeginQuote1;
  281. endQuote = syntaxEndQuote1;
  282. } else if (syntaxBeginQuote2 != null) {
  283. beginQuote = syntaxBeginQuote2;
  284. endQuote = syntaxEndQuote2;
  285. } else if (syntaxEscape != null)
  286. escapeSeparator = true;
  287. }
  288. if (syntaxSeparator2 != null &&
  289. comp.indexOf(syntaxSeparator2) >= 0) {
  290. if (syntaxBeginQuote1 != null) {
  291. if (beginQuote == null) {
  292. beginQuote = syntaxBeginQuote1;
  293. endQuote = syntaxEndQuote1;
  294. }
  295. } else if (syntaxBeginQuote2 != null) {
  296. if (beginQuote == null) {
  297. beginQuote = syntaxBeginQuote2;
  298. endQuote = syntaxEndQuote2;
  299. }
  300. } else if (syntaxEscape != null)
  301. escapeSeparator2 = true;
  302. }
  303. // if quoting component,
  304. if (beginQuote != null) {
  305. // start string off with opening quote
  306. strbuf = strbuf.append(beginQuote);
  307. // component is being quoted, so we only need to worry about
  308. // escaping end quotes that occur in component
  309. for (int i = 0; i < len; ) {
  310. if (comp.startsWith(endQuote, i)) {
  311. // end-quotes must be escaped when inside a quoted string
  312. strbuf.append(syntaxEscape).append(endQuote);
  313. i += endQuote.length();
  314. } else {
  315. // no special treatment required
  316. strbuf.append(comp.charAt(i++));
  317. }
  318. }
  319. // end with closing quote
  320. strbuf.append(endQuote);
  321. } else {
  322. // When component is not quoted, add escape for:
  323. // 1. leading quote
  324. // 2. an escape preceding any meta char
  325. // 3. an escape at the end of a component
  326. // 4. separator
  327. // go through characters in component and escape where necessary
  328. boolean start = true;
  329. for (int i = 0; i < len; ) {
  330. // leading quote must be escaped
  331. if (start && isA(comp, i, syntaxBeginQuote1)) {
  332. strbuf.append(syntaxEscape).append(syntaxBeginQuote1);
  333. i += syntaxBeginQuote1.length();
  334. } else if (start && isA(comp, i, syntaxBeginQuote2)) {
  335. strbuf.append(syntaxEscape).append(syntaxBeginQuote2);
  336. i += syntaxBeginQuote2.length();
  337. } else
  338. // Escape an escape preceding meta characters, or at end.
  339. // Other escapes pass through.
  340. if (isA(comp, i, syntaxEscape)) {
  341. if (i + syntaxEscape.length() >= len) {
  342. // escape an ending escape
  343. strbuf.append(syntaxEscape);
  344. } else if (isMeta(comp, i + syntaxEscape.length())) {
  345. // escape meta strings
  346. strbuf.append(syntaxEscape);
  347. }
  348. strbuf.append(syntaxEscape);
  349. i += syntaxEscape.length();
  350. } else
  351. // escape unescaped separator
  352. if (escapeSeparator && comp.startsWith(syntaxSeparator, i)) {
  353. // escape separator
  354. strbuf.append(syntaxEscape).append(syntaxSeparator);
  355. i += syntaxSeparator.length();
  356. } else if (escapeSeparator2 &&
  357. comp.startsWith(syntaxSeparator2, i)) {
  358. // escape separator2
  359. strbuf.append(syntaxEscape).append(syntaxSeparator2);
  360. i += syntaxSeparator2.length();
  361. } else {
  362. // no special treatment required
  363. strbuf.append(comp.charAt(i++));
  364. }
  365. start = false;
  366. }
  367. }
  368. return (strbuf.toString());
  369. }
  370. public String toString() {
  371. StringBuffer answer = new StringBuffer();
  372. String comp;
  373. boolean compsAllEmpty = true;
  374. int size = components.size();
  375. for (int i = 0; i < size; i++) {
  376. if (syntaxDirection.equals("right_to_left")) {
  377. comp =
  378. stringifyComp((String) components.elementAt(size - 1 - i));
  379. } else {
  380. comp = stringifyComp((String) components.elementAt(i));
  381. }
  382. if ((i != 0) && (syntaxSeparator != null))
  383. answer.append(syntaxSeparator);
  384. if (comp.length() >= 1)
  385. compsAllEmpty = false;
  386. answer = answer.append(comp);
  387. }
  388. if (compsAllEmpty && (size >= 1) && (syntaxSeparator != null))
  389. answer = answer.append(syntaxSeparator);
  390. return (answer.toString());
  391. }
  392. public boolean equals(Object obj) {
  393. if ((obj != null) && (obj instanceof NameImpl)) {
  394. NameImpl target = (NameImpl)obj;
  395. if (target.size() == this.size()) {
  396. Enumeration mycomps = getAll();
  397. Enumeration comps = target.getAll();
  398. while (mycomps.hasMoreElements()) {
  399. // %% comps could shrink in the middle.
  400. String my = (String)mycomps.nextElement();
  401. String his = (String)comps.nextElement();
  402. if (syntaxTrimBlanks) {
  403. my = my.trim();
  404. his = his.trim();
  405. }
  406. if (syntaxCaseInsensitive) {
  407. if (!(my.equalsIgnoreCase(his)))
  408. return false;
  409. } else {
  410. if (!(my.equals(his)))
  411. return false;
  412. }
  413. }
  414. return true;
  415. }
  416. }
  417. return false;
  418. }
  419. /**
  420. * Compares obj to this NameImpl to determine ordering.
  421. * Takes into account syntactic properties such as
  422. * elimination of blanks, case-ignore, etc, if relevant.
  423. *
  424. * Note: using syntax of this NameImpl and ignoring
  425. * that of comparison target.
  426. */
  427. public int compareTo(NameImpl obj) {
  428. if (this == obj) {
  429. return 0;
  430. }
  431. int len1 = size();
  432. int len2 = obj.size();
  433. int n = Math.min(len1, len2);
  434. int index1 = 0, index2 = 0;
  435. while (n-- != 0) {
  436. String comp1 = get(index1++);
  437. String comp2 = obj.get(index2++);
  438. // normalize according to syntax
  439. if (syntaxTrimBlanks) {
  440. comp1 = comp1.trim();
  441. comp2 = comp2.trim();
  442. }
  443. if (syntaxCaseInsensitive) {
  444. comp1 = comp1.toLowerCase();
  445. comp2 = comp2.toLowerCase();
  446. }
  447. int local = comp1.compareTo(comp2);
  448. if (local != 0) {
  449. return local;
  450. }
  451. }
  452. return len1 - len2;
  453. }
  454. public int size() {
  455. return (components.size());
  456. }
  457. public Enumeration getAll() {
  458. return components.elements();
  459. }
  460. public String get(int posn) {
  461. return ((String) components.elementAt(posn));
  462. }
  463. public Enumeration getPrefix(int posn) {
  464. if (posn < 0 || posn > size()) {
  465. throw new ArrayIndexOutOfBoundsException(posn);
  466. }
  467. return new NameImplEnumerator(components, 0, posn);
  468. }
  469. public Enumeration getSuffix(int posn) {
  470. int cnt = size();
  471. if (posn < 0 || posn > cnt) {
  472. throw new ArrayIndexOutOfBoundsException(posn);
  473. }
  474. return new NameImplEnumerator(components, posn, cnt);
  475. }
  476. public boolean isEmpty() {
  477. return (components.isEmpty());
  478. }
  479. public boolean startsWith(int posn, Enumeration prefix) {
  480. if (posn < 0 || posn > size()) {
  481. return false;
  482. }
  483. try {
  484. Enumeration mycomps = getPrefix(posn);
  485. while (mycomps.hasMoreElements()) {
  486. String my = (String)mycomps.nextElement();
  487. String his = (String)prefix.nextElement();
  488. if (syntaxTrimBlanks) {
  489. my = my.trim();
  490. his = his.trim();
  491. }
  492. if (syntaxCaseInsensitive) {
  493. if (!(my.equalsIgnoreCase(his)))
  494. return false;
  495. } else {
  496. if (!(my.equals(his)))
  497. return false;
  498. }
  499. }
  500. } catch (NoSuchElementException e) {
  501. return false;
  502. }
  503. return true;
  504. }
  505. public boolean endsWith(int posn, Enumeration suffix) {
  506. // posn is number of elements in suffix
  507. // startIndex is the starting position in this name
  508. // at which to start the comparison. It is calculated by
  509. // subtracting 'posn' from size()
  510. int startIndex = size() - posn;
  511. if (startIndex < 0 || startIndex > size()) {
  512. return false;
  513. }
  514. try {
  515. Enumeration mycomps = getSuffix(startIndex);
  516. while (mycomps.hasMoreElements()) {
  517. String my = (String)mycomps.nextElement();
  518. String his = (String)suffix.nextElement();
  519. if (syntaxTrimBlanks) {
  520. my = my.trim();
  521. his = his.trim();
  522. }
  523. if (syntaxCaseInsensitive) {
  524. if (!(my.equalsIgnoreCase(his)))
  525. return false;
  526. } else {
  527. if (!(my.equals(his)))
  528. return false;
  529. }
  530. }
  531. } catch (NoSuchElementException e) {
  532. return false;
  533. }
  534. return true;
  535. }
  536. public boolean addAll(Enumeration comps) throws InvalidNameException {
  537. boolean added = false;
  538. while (comps.hasMoreElements()) {
  539. try {
  540. Object comp = comps.nextElement();
  541. if (size() > 0 && syntaxDirection.equals("flat")) {
  542. throw new InvalidNameException(
  543. "A flat name can only have a single component");
  544. }
  545. components.addElement(comp);
  546. added = true;
  547. } catch (NoSuchElementException e) {
  548. break; // "comps" has shrunk.
  549. }
  550. }
  551. return added;
  552. }
  553. public boolean addAll(int posn, Enumeration comps)
  554. throws InvalidNameException {
  555. boolean added = false;
  556. for (int i = posn; comps.hasMoreElements(); i++) {
  557. try {
  558. Object comp = comps.nextElement();
  559. if (size() > 0 && syntaxDirection.equals("flat")) {
  560. throw new InvalidNameException(
  561. "A flat name can only have a single component");
  562. }
  563. components.insertElementAt(comp, i);
  564. added = true;
  565. } catch (NoSuchElementException e) {
  566. break; // "comps" has shrunk.
  567. }
  568. }
  569. return added;
  570. }
  571. public void add(String comp) throws InvalidNameException {
  572. if (size() > 0 && syntaxDirection.equals("flat")) {
  573. throw new InvalidNameException(
  574. "A flat name can only have a single component");
  575. }
  576. components.addElement(comp);
  577. }
  578. public void add(int posn, String comp) throws InvalidNameException {
  579. if (size() > 0 && syntaxDirection.equals("flat")) {
  580. throw new InvalidNameException(
  581. "A flat name can only zero or one component");
  582. }
  583. components.insertElementAt(comp, posn);
  584. }
  585. public Object remove(int posn) {
  586. Object r = components.elementAt(posn);
  587. components.removeElementAt(posn);
  588. return r;
  589. }
  590. public int hashCode() {
  591. int hash = 0;
  592. for (Enumeration e = getAll(); e.hasMoreElements();) {
  593. String comp = (String)e.nextElement();
  594. if (syntaxTrimBlanks) {
  595. comp = comp.trim();
  596. }
  597. if (syntaxCaseInsensitive) {
  598. comp = comp.toLowerCase();
  599. }
  600. hash += comp.hashCode();
  601. }
  602. return hash;
  603. }
  604. }
  605. final
  606. class NameImplEnumerator implements Enumeration {
  607. Vector vector;
  608. int count;
  609. int limit;
  610. NameImplEnumerator(Vector v, int start, int lim) {
  611. vector = v;
  612. count = start;
  613. limit = lim;
  614. }
  615. public boolean hasMoreElements() {
  616. return count < limit;
  617. }
  618. public Object nextElement() {
  619. if (count < limit) {
  620. return vector.elementAt(count++);
  621. }
  622. throw new NoSuchElementException("NameImplEnumerator");
  623. }
  624. }