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