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).append(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 }