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