View Javadoc
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         StringBuffer answer = new StringBuffer(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 }