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