1. /*
  2. * @(#)NameImpl.java 1.9 03/12/19
  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.Vector;
  9. import java.util.Enumeration;
  10. import java.util.Properties;
  11. import java.util.NoSuchElementException;
  12. /**
  13. * The implementation class for CompoundName and CompositeName.
  14. * This class is package private.
  15. *
  16. * @author Rosanna Lee
  17. * @author Scott Seligman
  18. * @author Aravindan Ranganathan
  19. * @version 1.9 03/12/19
  20. * @since 1.3
  21. */
  22. class NameImpl {
  23. private static final byte LEFT_TO_RIGHT = 1;
  24. private static final byte RIGHT_TO_LEFT = 2;
  25. private static final byte FLAT = 0;
  26. private Vector components;
  27. private byte syntaxDirection = LEFT_TO_RIGHT;
  28. private String syntaxSeparator = "/";
  29. private String syntaxSeparator2 = null;
  30. private boolean syntaxCaseInsensitive = false;
  31. private boolean syntaxTrimBlanks = false;
  32. private String syntaxEscape = "\\";
  33. private String syntaxBeginQuote1 = "\"";
  34. private String syntaxEndQuote1 = "\"";
  35. private String syntaxBeginQuote2 = "'";
  36. private String syntaxEndQuote2 = "'";
  37. private String syntaxAvaSeparator = null;
  38. private String syntaxTypevalSeparator = null;
  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 static final int STYLE_NONE = 0;
  44. private static final int STYLE_QUOTE1 = 1;
  45. private static final int STYLE_QUOTE2 = 2;
  46. private static 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 precedes 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 == 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. String syntaxDirectionStr =
  177. p.getProperty("jndi.syntax.direction", "flat");
  178. if (syntaxDirectionStr.equals("left_to_right")) {
  179. syntaxDirection = LEFT_TO_RIGHT;
  180. } else if (syntaxDirectionStr.equals("right_to_left")) {
  181. syntaxDirection = RIGHT_TO_LEFT;
  182. } else if (syntaxDirectionStr.equals("flat")) {
  183. syntaxDirection = FLAT;
  184. } else {
  185. throw new IllegalArgumentException(syntaxDirectionStr +
  186. "is not a valid value for the jndi.syntax.direction property");
  187. }
  188. if (syntaxDirection != FLAT) {
  189. syntaxSeparator = p.getProperty("jndi.syntax.separator");
  190. syntaxSeparator2 = p.getProperty("jndi.syntax.separator2");
  191. if (syntaxSeparator == null) {
  192. throw new IllegalArgumentException(
  193. "jndi.syntax.separator property required for non-flat syntax");
  194. }
  195. } else {
  196. syntaxSeparator = null;
  197. }
  198. syntaxEscape = p.getProperty("jndi.syntax.escape");
  199. syntaxCaseInsensitive = getBoolean(p, "jndi.syntax.ignorecase");
  200. syntaxTrimBlanks = getBoolean(p, "jndi.syntax.trimblanks");
  201. syntaxBeginQuote1 = p.getProperty("jndi.syntax.beginquote");
  202. syntaxEndQuote1 = p.getProperty("jndi.syntax.endquote");
  203. if (syntaxEndQuote1 == null && syntaxBeginQuote1 != null)
  204. syntaxEndQuote1 = syntaxBeginQuote1;
  205. else if (syntaxBeginQuote1 == null && syntaxEndQuote1 != null)
  206. syntaxBeginQuote1 = syntaxEndQuote1;
  207. syntaxBeginQuote2 = p.getProperty("jndi.syntax.beginquote2");
  208. syntaxEndQuote2 = p.getProperty("jndi.syntax.endquote2");
  209. if (syntaxEndQuote2 == null && syntaxBeginQuote2 != null)
  210. syntaxEndQuote2 = syntaxBeginQuote2;
  211. else if (syntaxBeginQuote2 == null && syntaxEndQuote2 != null)
  212. syntaxBeginQuote2 = syntaxEndQuote2;
  213. syntaxAvaSeparator = p.getProperty("jndi.syntax.separator.ava");
  214. syntaxTypevalSeparator =
  215. p.getProperty("jndi.syntax.separator.typeval");
  216. }
  217. NameImpl(Properties syntax) {
  218. if (syntax != null) {
  219. recordNamingConvention(syntax);
  220. }
  221. components = new Vector();
  222. }
  223. NameImpl(Properties syntax, String n) throws InvalidNameException {
  224. this(syntax);
  225. boolean rToL = (syntaxDirection == RIGHT_TO_LEFT);
  226. boolean compsAllEmpty = true;
  227. int len = n.length();
  228. for (int i = 0; i < len; ) {
  229. i = extractComp(n, i, len, components);
  230. String comp = rToL
  231. ? (String)components.firstElement()
  232. : (String)components.lastElement();
  233. if (comp.length() >= 1) {
  234. compsAllEmpty = false;
  235. }
  236. if (i < len) {
  237. i = skipSeparator(n, i);
  238. if ((i == len) && !compsAllEmpty) {
  239. // Trailing separator found. Add an empty component.
  240. if (rToL) {
  241. components.insertElementAt("", 0);
  242. } else {
  243. components.addElement("");
  244. }
  245. }
  246. }
  247. }
  248. }
  249. NameImpl(Properties syntax, Enumeration comps) {
  250. this(syntax);
  251. // %% comps could shrink in the middle.
  252. while (comps.hasMoreElements())
  253. components.addElement(comps.nextElement());
  254. }
  255. /*
  256. // Determines whether this component needs any escaping.
  257. private final boolean escapingNeeded(String comp) {
  258. int len = comp.length();
  259. for (int i = 0; i < len; i++) {
  260. if (i == 0) {
  261. if (isA(comp, 0, syntaxBeginQuote1) ||
  262. isA(comp, 0, syntaxBeginQuote2)) {
  263. return (true);
  264. }
  265. }
  266. if (isSeparator(comp, i)) {
  267. return (true);
  268. }
  269. if (isA(comp, i, syntaxEscape)) {
  270. i += syntaxEscape.length();
  271. if (i >= len || isMeta(comp, i)) {
  272. return (true);
  273. }
  274. }
  275. }
  276. return (false);
  277. }
  278. */
  279. private final String stringifyComp(String comp) {
  280. int len = comp.length();
  281. boolean escapeSeparator = false, escapeSeparator2 = false;
  282. String beginQuote = null, endQuote = null;
  283. StringBuffer strbuf = new StringBuffer(len);
  284. // determine whether there are any separators; if so escape
  285. // or quote them
  286. if (syntaxSeparator != null &&
  287. comp.indexOf(syntaxSeparator) >= 0) {
  288. if (syntaxBeginQuote1 != null) {
  289. beginQuote = syntaxBeginQuote1;
  290. endQuote = syntaxEndQuote1;
  291. } else if (syntaxBeginQuote2 != null) {
  292. beginQuote = syntaxBeginQuote2;
  293. endQuote = syntaxEndQuote2;
  294. } else if (syntaxEscape != null)
  295. escapeSeparator = true;
  296. }
  297. if (syntaxSeparator2 != null &&
  298. comp.indexOf(syntaxSeparator2) >= 0) {
  299. if (syntaxBeginQuote1 != null) {
  300. if (beginQuote == null) {
  301. beginQuote = syntaxBeginQuote1;
  302. endQuote = syntaxEndQuote1;
  303. }
  304. } else if (syntaxBeginQuote2 != null) {
  305. if (beginQuote == null) {
  306. beginQuote = syntaxBeginQuote2;
  307. endQuote = syntaxEndQuote2;
  308. }
  309. } else if (syntaxEscape != null)
  310. escapeSeparator2 = true;
  311. }
  312. // if quoting component,
  313. if (beginQuote != null) {
  314. // start string off with opening quote
  315. strbuf = strbuf.append(beginQuote);
  316. // component is being quoted, so we only need to worry about
  317. // escaping end quotes that occur in component
  318. for (int i = 0; i < len; ) {
  319. if (comp.startsWith(endQuote, i)) {
  320. // end-quotes must be escaped when inside a quoted string
  321. strbuf.append(syntaxEscape).append(endQuote);
  322. i += endQuote.length();
  323. } else {
  324. // no special treatment required
  325. strbuf.append(comp.charAt(i++));
  326. }
  327. }
  328. // end with closing quote
  329. strbuf.append(endQuote);
  330. } else {
  331. // When component is not quoted, add escape for:
  332. // 1. leading quote
  333. // 2. an escape preceding any meta char
  334. // 3. an escape at the end of a component
  335. // 4. separator
  336. // go through characters in component and escape where necessary
  337. boolean start = true;
  338. for (int i = 0; i < len; ) {
  339. // leading quote must be escaped
  340. if (start && isA(comp, i, syntaxBeginQuote1)) {
  341. strbuf.append(syntaxEscape).append(syntaxBeginQuote1);
  342. i += syntaxBeginQuote1.length();
  343. } else if (start && isA(comp, i, syntaxBeginQuote2)) {
  344. strbuf.append(syntaxEscape).append(syntaxBeginQuote2);
  345. i += syntaxBeginQuote2.length();
  346. } else
  347. // Escape an escape preceding meta characters, or at end.
  348. // Other escapes pass through.
  349. if (isA(comp, i, syntaxEscape)) {
  350. if (i + syntaxEscape.length() >= len) {
  351. // escape an ending escape
  352. strbuf.append(syntaxEscape);
  353. } else if (isMeta(comp, i + syntaxEscape.length())) {
  354. // escape meta strings
  355. strbuf.append(syntaxEscape);
  356. }
  357. strbuf.append(syntaxEscape);
  358. i += syntaxEscape.length();
  359. } else
  360. // escape unescaped separator
  361. if (escapeSeparator && comp.startsWith(syntaxSeparator, i)) {
  362. // escape separator
  363. strbuf.append(syntaxEscape).append(syntaxSeparator);
  364. i += syntaxSeparator.length();
  365. } else if (escapeSeparator2 &&
  366. comp.startsWith(syntaxSeparator2, i)) {
  367. // escape separator2
  368. strbuf.append(syntaxEscape).append(syntaxSeparator2);
  369. i += syntaxSeparator2.length();
  370. } else {
  371. // no special treatment required
  372. strbuf.append(comp.charAt(i++));
  373. }
  374. start = false;
  375. }
  376. }
  377. return (strbuf.toString());
  378. }
  379. public String toString() {
  380. StringBuffer answer = new StringBuffer();
  381. String comp;
  382. boolean compsAllEmpty = true;
  383. int size = components.size();
  384. for (int i = 0; i < size; i++) {
  385. if (syntaxDirection == RIGHT_TO_LEFT) {
  386. comp =
  387. stringifyComp((String) components.elementAt(size - 1 - i));
  388. } else {
  389. comp = stringifyComp((String) components.elementAt(i));
  390. }
  391. if ((i != 0) && (syntaxSeparator != null))
  392. answer.append(syntaxSeparator);
  393. if (comp.length() >= 1)
  394. compsAllEmpty = false;
  395. answer = answer.append(comp);
  396. }
  397. if (compsAllEmpty && (size >= 1) && (syntaxSeparator != null))
  398. answer = answer.append(syntaxSeparator);
  399. return (answer.toString());
  400. }
  401. public boolean equals(Object obj) {
  402. if ((obj != null) && (obj instanceof NameImpl)) {
  403. NameImpl target = (NameImpl)obj;
  404. if (target.size() == this.size()) {
  405. Enumeration mycomps = getAll();
  406. Enumeration comps = target.getAll();
  407. while (mycomps.hasMoreElements()) {
  408. // %% comps could shrink in the middle.
  409. String my = (String)mycomps.nextElement();
  410. String his = (String)comps.nextElement();
  411. if (syntaxTrimBlanks) {
  412. my = my.trim();
  413. his = his.trim();
  414. }
  415. if (syntaxCaseInsensitive) {
  416. if (!(my.equalsIgnoreCase(his)))
  417. return false;
  418. } else {
  419. if (!(my.equals(his)))
  420. return false;
  421. }
  422. }
  423. return true;
  424. }
  425. }
  426. return false;
  427. }
  428. /**
  429. * Compares obj to this NameImpl to determine ordering.
  430. * Takes into account syntactic properties such as
  431. * elimination of blanks, case-ignore, etc, if relevant.
  432. *
  433. * Note: using syntax of this NameImpl and ignoring
  434. * that of comparison target.
  435. */
  436. public int compareTo(NameImpl obj) {
  437. if (this == obj) {
  438. return 0;
  439. }
  440. int len1 = size();
  441. int len2 = obj.size();
  442. int n = Math.min(len1, len2);
  443. int index1 = 0, index2 = 0;
  444. while (n-- != 0) {
  445. String comp1 = get(index1++);
  446. String comp2 = obj.get(index2++);
  447. // normalize according to syntax
  448. if (syntaxTrimBlanks) {
  449. comp1 = comp1.trim();
  450. comp2 = comp2.trim();
  451. }
  452. if (syntaxCaseInsensitive) {
  453. comp1 = comp1.toLowerCase();
  454. comp2 = comp2.toLowerCase();
  455. }
  456. int local = comp1.compareTo(comp2);
  457. if (local != 0) {
  458. return local;
  459. }
  460. }
  461. return len1 - len2;
  462. }
  463. public int size() {
  464. return (components.size());
  465. }
  466. public Enumeration getAll() {
  467. return components.elements();
  468. }
  469. public String get(int posn) {
  470. return ((String) components.elementAt(posn));
  471. }
  472. public Enumeration getPrefix(int posn) {
  473. if (posn < 0 || posn > size()) {
  474. throw new ArrayIndexOutOfBoundsException(posn);
  475. }
  476. return new NameImplEnumerator(components, 0, posn);
  477. }
  478. public Enumeration getSuffix(int posn) {
  479. int cnt = size();
  480. if (posn < 0 || posn > cnt) {
  481. throw new ArrayIndexOutOfBoundsException(posn);
  482. }
  483. return new NameImplEnumerator(components, posn, cnt);
  484. }
  485. public boolean isEmpty() {
  486. return (components.isEmpty());
  487. }
  488. public boolean startsWith(int posn, Enumeration prefix) {
  489. if (posn < 0 || posn > size()) {
  490. return false;
  491. }
  492. try {
  493. Enumeration mycomps = getPrefix(posn);
  494. while (mycomps.hasMoreElements()) {
  495. String my = (String)mycomps.nextElement();
  496. String his = (String)prefix.nextElement();
  497. if (syntaxTrimBlanks) {
  498. my = my.trim();
  499. his = his.trim();
  500. }
  501. if (syntaxCaseInsensitive) {
  502. if (!(my.equalsIgnoreCase(his)))
  503. return false;
  504. } else {
  505. if (!(my.equals(his)))
  506. return false;
  507. }
  508. }
  509. } catch (NoSuchElementException e) {
  510. return false;
  511. }
  512. return true;
  513. }
  514. public boolean endsWith(int posn, Enumeration suffix) {
  515. // posn is number of elements in suffix
  516. // startIndex is the starting position in this name
  517. // at which to start the comparison. It is calculated by
  518. // subtracting 'posn' from size()
  519. int startIndex = size() - posn;
  520. if (startIndex < 0 || startIndex > size()) {
  521. return false;
  522. }
  523. try {
  524. Enumeration mycomps = getSuffix(startIndex);
  525. while (mycomps.hasMoreElements()) {
  526. String my = (String)mycomps.nextElement();
  527. String his = (String)suffix.nextElement();
  528. if (syntaxTrimBlanks) {
  529. my = my.trim();
  530. his = his.trim();
  531. }
  532. if (syntaxCaseInsensitive) {
  533. if (!(my.equalsIgnoreCase(his)))
  534. return false;
  535. } else {
  536. if (!(my.equals(his)))
  537. return false;
  538. }
  539. }
  540. } catch (NoSuchElementException e) {
  541. return false;
  542. }
  543. return true;
  544. }
  545. public boolean addAll(Enumeration comps) throws InvalidNameException {
  546. boolean added = false;
  547. while (comps.hasMoreElements()) {
  548. try {
  549. Object comp = comps.nextElement();
  550. if (size() > 0 && syntaxDirection == FLAT) {
  551. throw new InvalidNameException(
  552. "A flat name can only have a single component");
  553. }
  554. components.addElement(comp);
  555. added = true;
  556. } catch (NoSuchElementException e) {
  557. break; // "comps" has shrunk.
  558. }
  559. }
  560. return added;
  561. }
  562. public boolean addAll(int posn, Enumeration comps)
  563. throws InvalidNameException {
  564. boolean added = false;
  565. for (int i = posn; comps.hasMoreElements(); i++) {
  566. try {
  567. Object comp = comps.nextElement();
  568. if (size() > 0 && syntaxDirection == FLAT) {
  569. throw new InvalidNameException(
  570. "A flat name can only have a single component");
  571. }
  572. components.insertElementAt(comp, i);
  573. added = true;
  574. } catch (NoSuchElementException e) {
  575. break; // "comps" has shrunk.
  576. }
  577. }
  578. return added;
  579. }
  580. public void add(String comp) throws InvalidNameException {
  581. if (size() > 0 && syntaxDirection == FLAT) {
  582. throw new InvalidNameException(
  583. "A flat name can only have a single component");
  584. }
  585. components.addElement(comp);
  586. }
  587. public void add(int posn, String comp) throws InvalidNameException {
  588. if (size() > 0 && syntaxDirection == FLAT) {
  589. throw new InvalidNameException(
  590. "A flat name can only zero or one component");
  591. }
  592. components.insertElementAt(comp, posn);
  593. }
  594. public Object remove(int posn) {
  595. Object r = components.elementAt(posn);
  596. components.removeElementAt(posn);
  597. return r;
  598. }
  599. public int hashCode() {
  600. int hash = 0;
  601. for (Enumeration e = getAll(); e.hasMoreElements();) {
  602. String comp = (String)e.nextElement();
  603. if (syntaxTrimBlanks) {
  604. comp = comp.trim();
  605. }
  606. if (syntaxCaseInsensitive) {
  607. comp = comp.toLowerCase();
  608. }
  609. hash += comp.hashCode();
  610. }
  611. return hash;
  612. }
  613. }
  614. final
  615. class NameImplEnumerator implements Enumeration {
  616. Vector vector;
  617. int count;
  618. int limit;
  619. NameImplEnumerator(Vector v, int start, int lim) {
  620. vector = v;
  621. count = start;
  622. limit = lim;
  623. }
  624. public boolean hasMoreElements() {
  625. return count < limit;
  626. }
  627. public Object nextElement() {
  628. if (count < limit) {
  629. return vector.elementAt(count++);
  630. }
  631. throw new NoSuchElementException("NameImplEnumerator");
  632. }
  633. }