]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/util/StringTokenizer.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / util / StringTokenizer.java
1 /**
2 *******************************************************************************
3 * Copyright (C) 1996-2011, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7
8 package com.ibm.icu.util;
9
10 import java.util.Enumeration;
11 import java.util.NoSuchElementException;
12
13 import com.ibm.icu.text.UTF16;
14 import com.ibm.icu.text.UnicodeSet;
15
16 /**
17  * {@icuenhanced java.util.Calendar}.{@icu _usage_}
18  *
19  * <p>The string tokenizer class allows an application to break a string 
20  * into tokens by performing code point comparison. 
21  * The <code>StringTokenizer</code> methods do not distinguish 
22  * among identifiers, numbers, and quoted strings, nor do they recognize 
23  * and skip comments.</p>
24  * <p>
25  * The set of delimiters (the codepoints that separate tokens) may be 
26  * specified either at creation time or on a per-token basis. 
27  * </p>
28  * <p>
29  * An instance of <code>StringTokenizer</code> behaves in one of three ways, 
30  * depending on whether it was created with the <code>returnDelims</code> 
31  * and <code>coalesceDelims</code>
32  * flags having the value <code>true</code> or <code>false</code>: 
33  * <ul>
34  * <li>If returnDelims is <code>false</code>, delimiter code points serve to 
35  * separate tokens. A token is a maximal sequence of consecutive 
36  * code points that are not delimiters. 
37  * <li>If returnDelims is <code>true</code>, delimiter code points are 
38  * themselves considered to be tokens. In this case, if coalesceDelims is
39  * <code>true</code>, such tokens will be the maximal sequence of consecutive
40  * code points that <em>are</em> delimiters.  If coalesceDelims is false,
41  * a token will be received for each delimiter code point.
42  * </ul>
43  * <p>A token is thus either one 
44  * delimiter code point, a maximal sequence of consecutive code points that
45  * are delimiters, or a maximal sequence of consecutive code 
46  * points that are not delimiters.
47  * </p>
48  * <p>
49  * A <tt>StringTokenizer</tt> object internally maintains a current 
50  * position within the string to be tokenized. Some operations advance this 
51  * current position past the code point processed.
52  * </p>
53  * <p>
54  * A token is returned by taking a substring of the string that was used to 
55  * create the <tt>StringTokenizer</tt> object.
56  * </p>
57  * <p>
58  * Example of the use of the default delimiter tokenizer.
59  * <blockquote><pre>
60  * StringTokenizer st = new StringTokenizer("this is a test");
61  * while (st.hasMoreTokens()) {
62  *     println(st.nextToken());
63  *     }
64  * </pre></blockquote>
65  * </p>
66  * <p>
67  * prints the following output:
68  * <blockquote><pre>
69  *     this
70  *     is
71  *     a
72  *     test
73  * </pre></blockquote>
74  * </p>
75  * <p>
76  * Example of the use of the tokenizer with user specified delimiter.
77  * <blockquote><pre>
78  *     StringTokenizer st = new StringTokenizer(
79  *     "this is a test with supplementary characters &#92;ud800&#92;ud800&#92;udc00&#92;udc00",
80  *         " &#92;ud800&#92;udc00");
81  *     while (st.hasMoreTokens()) {
82  *         println(st.nextToken());
83  *     }
84  * </pre></blockquote>
85  * </p>
86  * <p>
87  * prints the following output:
88  * <blockquote><pre>
89  *     this
90  *     is
91  *     a
92  *     test
93  *     with
94  *     supplementary
95  *     characters
96  *     &#92;ud800
97  *     &#92;udc00
98  * </pre></blockquote>
99  * </p>
100  * @author syn wee
101  * @stable ICU 2.4
102  */
103 public final class StringTokenizer implements Enumeration<Object>
104 {
105     // public constructors ---------------------------------------------
106      
107     /**
108      * {@icu} Constructs a string tokenizer for the specified string. All 
109      * characters in the delim argument are the delimiters for separating 
110      * tokens. 
111      * <p>If the returnDelims flag is false, the delimiter characters are 
112      * skipped and only serve as separators between tokens.</p>
113      * <p>If the returnDelims flag is true, then the delimiter characters 
114      * are also returned as tokens, one per delimiter.
115      * @param str a string to be parsed.
116      * @param delim the delimiters.
117      * @param returndelims flag indicating whether to return the delimiters 
118      *        as tokens.
119      * @exception NullPointerException if str is null
120      * @stable ICU 2.4
121      */
122     public StringTokenizer(String str, UnicodeSet delim, boolean returndelims)
123     {
124         this(str, delim, returndelims, false);
125     }
126
127     /**
128      * {@icu} Constructs a string tokenizer for the specified string. All 
129      * characters in the delim argument are the delimiters for separating 
130      * tokens. 
131      * <p>If the returnDelims flag is false, the delimiter characters are 
132      * skipped and only serve as separators between tokens.</p>
133      * <p>If the returnDelims flag is true, then the delimiter characters 
134      * are also returned as tokens.  If coalescedelims is true, one token
135      * is returned for each run of delimiter characters, otherwise one
136      * token is returned per delimiter.  Since surrogate pairs can be
137      * delimiters, the returned token might be two chars in length.</p>
138      * @param str a string to be parsed.
139      * @param delim the delimiters.
140      * @param returndelims flag indicating whether to return the delimiters 
141      *        as tokens.
142      * @param coalescedelims flag indicating whether to return a run of 
143      *        delimiters as a single token or as one token per delimiter.  
144      *        This only takes effect if returndelims is true.
145      * @exception NullPointerException if str is null
146      * @internal
147      * @deprecated This API is ICU internal only.
148      */
149     public StringTokenizer(String str, UnicodeSet delim, boolean returndelims, boolean coalescedelims)
150     {
151         m_source_ = str;
152         m_length_ = str.length();
153         if (delim == null) {
154             m_delimiters_ = EMPTY_DELIMITER_;
155         }
156         else {
157             m_delimiters_ = delim;   
158         }
159         m_returnDelimiters_ = returndelims;
160         m_coalesceDelimiters_ = coalescedelims;
161         m_tokenOffset_ = -1;
162         m_tokenSize_ = -1;
163         if (m_length_ == 0) {
164             // string length 0, no tokens
165             m_nextOffset_ = -1;
166         }
167         else {
168             m_nextOffset_ = 0;
169             if (!returndelims) {
170                 m_nextOffset_ = getNextNonDelimiter(0);
171             }
172         }
173     }
174     
175     /**
176      * {@icu} Constructs a string tokenizer for the specified string. The 
177      * characters in the delim argument are the delimiters for separating 
178      * tokens. 
179      * <p>Delimiter characters themselves will not be treated as tokens.</p>
180      * @param str a string to be parsed.
181      * @param delim the delimiters.
182      * @exception NullPointerException if str is null
183      * @stable ICU 2.4
184      */
185     public StringTokenizer(String str, UnicodeSet delim)
186     {
187         this(str, delim, false, false);
188     }
189        
190     /**
191      * <p>Constructs a string tokenizer for the specified string. All 
192      * characters in the delim argument are the delimiters for separating 
193      * tokens.</p> 
194      * <p>If the returnDelims flag is false, the delimiter characters are 
195      * skipped and only serve as separators between tokens.</p>
196      * <p>If the returnDelims flag is true, then the delimiter characters 
197      * are also returned as tokens, one per delimiter.
198      * @param str a string to be parsed.
199      * @param delim the delimiters.
200      * @param returndelims flag indicating whether to return the delimiters 
201      *        as tokens.
202      * @exception NullPointerException if str is null
203      * @stable ICU 2.4
204      */
205     public StringTokenizer(String str, String delim, boolean returndelims)
206     {
207         this(str, delim, returndelims, false); // java default behavior
208     }
209
210     /**
211      * <p>Constructs a string tokenizer for the specified string. All 
212      * characters in the delim argument are the delimiters for separating 
213      * tokens.</p> 
214      * <p>If the returnDelims flag is false, the delimiter characters are 
215      * skipped and only serve as separators between tokens.</p>
216      * <p>If the returnDelims flag is true, then the delimiter characters 
217      * are also returned as tokens.  If coalescedelims is true, one token
218      * is returned for each run of delimiter characters, otherwise one
219      * token is returned per delimiter.  Since surrogate pairs can be
220      * delimiters, the returned token might be two chars in length.</p>
221      * @param str a string to be parsed.
222      * @param delim the delimiters.
223      * @param returndelims flag indicating whether to return the delimiters 
224      *        as tokens.
225      * @param coalescedelims flag indicating whether to return a run of 
226      *        delimiters as a single token or as one token per delimiter.  
227      *        This only takes effect if returndelims is true.
228      * @exception NullPointerException if str is null
229      * @internal
230      * @deprecated This API is ICU internal only.
231      */
232     public StringTokenizer(String str, String delim, boolean returndelims, boolean coalescedelims)
233     {
234         // don't ignore whitespace
235         m_delimiters_ = EMPTY_DELIMITER_;
236         if (delim != null && delim.length() > 0) {
237             m_delimiters_ = new UnicodeSet();
238             m_delimiters_.addAll(delim);
239             checkDelimiters();
240         }
241         m_coalesceDelimiters_ = coalescedelims;
242         m_source_ = str;
243         m_length_ = str.length();
244         m_returnDelimiters_ = returndelims;
245         m_tokenOffset_ = -1;
246         m_tokenSize_ = -1;
247         if (m_length_ == 0) {
248             // string length 0, no tokens
249             m_nextOffset_ = -1;
250         }
251         else {
252             m_nextOffset_ = 0;
253             if (!returndelims) {
254                 m_nextOffset_ = getNextNonDelimiter(0);
255             }
256         }
257     }
258     
259     /**
260      * <p>Constructs a string tokenizer for the specified string. The 
261      * characters in the delim argument are the delimiters for separating 
262      * tokens.</p> 
263      * <p>Delimiter characters themselves will not be treated as tokens.</p>
264      * @param str a string to be parsed.
265      * @param delim the delimiters.
266      * @exception NullPointerException if str is null
267      * @stable ICU 2.4
268      */
269     public StringTokenizer(String str, String delim)
270     {
271         // don't ignore whitespace
272         this(str, delim, false, false);
273     }
274
275     /**
276      * <p>Constructs a string tokenizer for the specified string. 
277      * The tokenizer uses the default delimiter set, which is 
278      * " &#92;t&#92;n&#92;r&#92;f": 
279      * the space character, the tab character, the newline character, the 
280      * carriage-return character, and the form-feed character.</p> 
281      * <p>Delimiter characters themselves will not be treated as tokens.</p>
282      * @param str a string to be parsed
283      * @exception NullPointerException if str is null
284      * @stable ICU 2.4
285      */
286     public StringTokenizer(String str) 
287     {
288         this(str, DEFAULT_DELIMITERS_, false, false);
289     }
290     
291     // public methods --------------------------------------------------
292     
293     /**
294      * Tests if there are more tokens available from this tokenizer's 
295      * string. 
296      * If this method returns <tt>true</tt>, then a subsequent call to 
297      * <tt>nextToken</tt> with no argument will successfully return a token.
298      * @return <code>true</code> if and only if there is at least one token 
299      *         in the string after the current position; <code>false</code> 
300      *         otherwise.
301      * @stable ICU 2.4
302      */
303     public boolean hasMoreTokens() 
304     {
305         return m_nextOffset_ >= 0;
306     }
307     
308     /**
309      * Returns the next token from this string tokenizer.
310      * @return the next token from this string tokenizer.
311      * @exception NoSuchElementException if there are no more tokens in 
312      *            this tokenizer's string.
313      * @stable ICU 2.4
314      */
315     public String nextToken() 
316     {
317         if (m_tokenOffset_ < 0) {
318             if (m_nextOffset_ < 0) {
319                 throw new NoSuchElementException("No more tokens in String");   
320             }
321             // pre-calculations of tokens not done
322             if (m_returnDelimiters_) {
323                 int tokenlimit = 0;
324                 int c = UTF16.charAt(m_source_, m_nextOffset_);
325                 boolean contains = delims == null 
326                     ? m_delimiters_.contains(c) 
327                     : c < delims.length && delims[c];
328                 if (contains) {
329                      if (m_coalesceDelimiters_) {
330                         tokenlimit = getNextNonDelimiter(m_nextOffset_);
331                      } else {
332                         tokenlimit = m_nextOffset_ + UTF16.getCharCount(c);
333                         if (tokenlimit == m_length_) {
334                             tokenlimit = -1;
335                         }
336                      }
337                 }
338                 else {
339                     tokenlimit = getNextDelimiter(m_nextOffset_);
340                 }
341                 String result;
342                 if (tokenlimit < 0) {
343                     result = m_source_.substring(m_nextOffset_);
344                 }
345                 else {
346                     result = m_source_.substring(m_nextOffset_, tokenlimit);
347                 }
348                 m_nextOffset_ = tokenlimit;
349                 return result;
350             }
351             else {
352                 int tokenlimit = getNextDelimiter(m_nextOffset_);
353                 String result;
354                 if (tokenlimit < 0) {
355                     result = m_source_.substring(m_nextOffset_);
356                     m_nextOffset_ = tokenlimit;
357                 }
358                 else {
359                     result = m_source_.substring(m_nextOffset_, tokenlimit);
360                     m_nextOffset_ = getNextNonDelimiter(tokenlimit);
361                 }
362                 
363                 return result;
364             }
365         }
366         // count was called before and we have all the tokens
367         if (m_tokenOffset_ >= m_tokenSize_) {
368             throw new NoSuchElementException("No more tokens in String");
369         }
370         String result;
371         if (m_tokenLimit_[m_tokenOffset_] >= 0) {
372             result = m_source_.substring(m_tokenStart_[m_tokenOffset_],
373                                          m_tokenLimit_[m_tokenOffset_]);
374         }
375         else {
376             result = m_source_.substring(m_tokenStart_[m_tokenOffset_]);
377         }
378         m_tokenOffset_ ++;
379         m_nextOffset_ = -1;
380         if (m_tokenOffset_ < m_tokenSize_) {
381             m_nextOffset_ = m_tokenStart_[m_tokenOffset_];
382         }
383         return result;
384     }
385     
386     /**
387      * Returns the next token in this string tokenizer's string. First, 
388      * the set of characters considered to be delimiters by this 
389      * <tt>StringTokenizer</tt> object is changed to be the characters in 
390      * the string <tt>delim</tt>. Then the next token in the string
391      * after the current position is returned. The current position is 
392      * advanced beyond the recognized token.  The new delimiter set 
393      * remains the default after this call. 
394      * @param delim the new delimiters.
395      * @return the next token, after switching to the new delimiter set.
396      * @exception NoSuchElementException if there are no more tokens in 
397      *            this tokenizer's string.
398      * @stable ICU 2.4
399      */
400     public String nextToken(String delim) 
401     {
402         m_delimiters_ = EMPTY_DELIMITER_;
403         if (delim != null && delim.length() > 0) {
404             m_delimiters_ = new UnicodeSet();
405             m_delimiters_.addAll(delim);
406         }
407         return nextToken(m_delimiters_);
408     }
409     
410     /**
411      * {@icu} Returns the next token in this string tokenizer's string. First, 
412      * the set of characters considered to be delimiters by this 
413      * <tt>StringTokenizer</tt> object is changed to be the characters in 
414      * the string <tt>delim</tt>. Then the next token in the string
415      * after the current position is returned. The current position is 
416      * advanced beyond the recognized token.  The new delimiter set 
417      * remains the default after this call. 
418      * @param delim the new delimiters.
419      * @return the next token, after switching to the new delimiter set.
420      * @exception NoSuchElementException if there are no more tokens in 
421      *            this tokenizer's string.
422      * @stable ICU 2.4
423      */
424     public String nextToken(UnicodeSet delim) 
425     {
426         m_delimiters_ = delim;
427         checkDelimiters();
428         m_tokenOffset_ = -1;
429         m_tokenSize_ = -1;
430         if (!m_returnDelimiters_) {
431             m_nextOffset_ = getNextNonDelimiter(m_nextOffset_);
432         }
433         return nextToken();
434     }
435     
436     /**
437      * Returns the same value as the <code>hasMoreTokens</code> method. 
438      * It exists so that this class can implement the 
439      * <code>Enumeration</code> interface. 
440      * @return <code>true</code> if there are more tokens;
441      *         <code>false</code> otherwise.
442      * @see #hasMoreTokens()
443      * @stable ICU 2.4
444      */
445     public boolean hasMoreElements() 
446     {
447         return hasMoreTokens();
448     }
449     
450     /**
451      * Returns the same value as the <code>nextToken</code> method, except 
452      * that its declared return value is <code>Object</code> rather than 
453      * <code>String</code>. It exists so that this class can implement the 
454      * <code>Enumeration</code> interface. 
455      * @return the next token in the string.
456      * @exception NoSuchElementException if there are no more tokens in 
457      *            this tokenizer's string.
458      * @see #nextToken()
459      * @stable ICU 2.4
460      */
461     public Object nextElement() 
462     {
463         return nextToken();
464     }
465     
466     /**
467      * Calculates the number of times that this tokenizer's 
468      * <code>nextToken</code> method can be called before it generates an 
469      * exception. The current position is not advanced.
470      * @return the number of tokens remaining in the string using the 
471      *         current delimiter set.
472      * @see #nextToken()
473      * @stable ICU 2.4
474      */
475     public int countTokens() 
476     {
477         int result = 0;
478         if (hasMoreTokens()) {
479             if (m_tokenOffset_ >= 0) {
480                 return m_tokenSize_ - m_tokenOffset_;
481             }
482             if (m_tokenStart_ == null) {
483                 m_tokenStart_ = new int[TOKEN_SIZE_];
484                 m_tokenLimit_ = new int[TOKEN_SIZE_];
485             }
486             do {
487                 if (m_tokenStart_.length == result) {
488                     int temptokenindex[] = m_tokenStart_;
489                     int temptokensize[] = m_tokenLimit_;
490                     int originalsize = temptokenindex.length;
491                     int newsize = originalsize + TOKEN_SIZE_;
492                     m_tokenStart_ = new int[newsize];
493                     m_tokenLimit_ = new int[newsize];
494                     System.arraycopy(temptokenindex, 0, m_tokenStart_, 0, 
495                                      originalsize);
496                     System.arraycopy(temptokensize, 0, m_tokenLimit_, 0, 
497                                      originalsize);
498                 }
499                 m_tokenStart_[result] = m_nextOffset_;
500                 if (m_returnDelimiters_) {
501                     int c = UTF16.charAt(m_source_, m_nextOffset_);
502                     boolean contains = delims == null 
503                         ? m_delimiters_.contains(c) 
504                         : c < delims.length && delims[c];
505                     if (contains) {
506                         if (m_coalesceDelimiters_) {
507                             m_tokenLimit_[result] = getNextNonDelimiter(
508                                                                 m_nextOffset_);
509                         } else {
510                             int p = m_nextOffset_ + 1;
511                             if (p == m_length_) {
512                                 p = -1;
513                             }
514                             m_tokenLimit_[result] = p;
515
516                         }
517                     }
518                     else {
519                         m_tokenLimit_[result] = getNextDelimiter(m_nextOffset_);
520                     }
521                     m_nextOffset_ = m_tokenLimit_[result];
522                 }
523                 else {
524                     m_tokenLimit_[result] = getNextDelimiter(m_nextOffset_);
525                     m_nextOffset_ = getNextNonDelimiter(m_tokenLimit_[result]);
526                 }
527                 result ++;
528             } while (m_nextOffset_ >= 0);
529             m_tokenOffset_ = 0;
530             m_tokenSize_ = result;
531             m_nextOffset_ = m_tokenStart_[0];
532         }
533         return result;
534     }
535     
536     // private data members -------------------------------------------------
537     
538     /**
539      * Current offset to the token array. If the array token is not set up yet,
540      * this value is a -1
541      */
542     private int m_tokenOffset_;
543     /**
544      * Size of the token array. If the array token is not set up yet,
545      * this value is a -1
546      */
547     private int m_tokenSize_;
548     /**
549      * Array of pre-calculated tokens start indexes in source string terminated 
550      * by -1.
551      * This is only set up during countTokens() and only stores the remaining
552      * tokens, not all tokens including parsed ones
553      */
554     private int m_tokenStart_[];
555     /**
556      * Array of pre-calculated tokens limit indexes in source string.
557      * This is only set up during countTokens() and only stores the remaining
558      * tokens, not all tokens including parsed ones
559      */
560     private int m_tokenLimit_[];
561     /**
562      * UnicodeSet containing delimiters
563      */
564     private UnicodeSet m_delimiters_;
565     /**
566      * String to parse for tokens
567      */
568     private String m_source_;
569     /**
570      * Length of m_source_
571      */
572     private int m_length_;
573     /**
574      * Current position in string to parse for tokens
575      */
576     private int m_nextOffset_;
577     /**
578      * Flag indicator if delimiters are to be treated as tokens too
579      */
580     private boolean m_returnDelimiters_;
581
582     /**
583      * Flag indicating whether to coalesce runs of delimiters into single tokens
584      */
585     private boolean m_coalesceDelimiters_;
586
587     /**
588      * Default set of delimiters &#92;t&#92;n&#92;r&#92;f
589      */
590     private static final UnicodeSet DEFAULT_DELIMITERS_
591         = new UnicodeSet(0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x20);   // UnicodeSet("[ \t\n\r\f]", false)
592     /**
593      * Array size increments
594      */
595     private static final int TOKEN_SIZE_ = 100;
596     /**
597      * A empty delimiter UnicodeSet, used when user specified null delimiters
598      */
599     private static final UnicodeSet EMPTY_DELIMITER_ = UnicodeSet.EMPTY;
600     
601     // private methods ------------------------------------------------------
602     
603     /**
604      * Gets the index of the next delimiter after offset
605      * @param offset to the source string
606      * @return offset of the immediate next delimiter, otherwise 
607      *         (- source string length - 1) if there
608      *         are no more delimiters after m_nextOffset
609      */
610     private int getNextDelimiter(int offset)
611     {
612         if (offset >= 0) {
613             int result = offset; 
614             int c = 0;
615             if (delims == null) {
616                 do {
617                     c = UTF16.charAt(m_source_, result);
618                     if (m_delimiters_.contains(c)) {
619                         break;
620                     }
621                     result ++;
622                 } while (result < m_length_);
623             } else {
624                 do {
625                     c = UTF16.charAt(m_source_, result);
626                     if (c < delims.length && delims[c]) {
627                         break;
628                     }
629                     result ++;
630                 } while (result < m_length_);
631             }                
632             if (result < m_length_) {
633                 return result;
634             }
635         }
636         return -1 - m_length_;
637     }
638     
639     /**
640      * Gets the index of the next non-delimiter after m_nextOffset_
641      * @param offset to the source string
642      * @return offset of the immediate next non-delimiter, otherwise 
643      *         (- source string length - 1) if there
644      *         are no more delimiters after m_nextOffset
645      */
646     private int getNextNonDelimiter(int offset)
647     {
648         if (offset >= 0) {
649             int result = offset; 
650             int c = 0;
651             if (delims == null) {
652                 do {
653                     c = UTF16.charAt(m_source_, result);
654                     if (!m_delimiters_.contains(c)) {
655                         break;
656                     }
657                     result ++;
658                 } while (result < m_length_);
659             } else {
660                 do {
661                     c = UTF16.charAt(m_source_, result);
662                     if (!(c < delims.length && delims[c])) {
663                         break;
664                     }
665                     result ++;
666                 } while (result < m_length_);
667             }
668             if (result < m_length_) {
669                 return result;
670             }
671         }
672         return -1 - m_length_;
673     }
674
675     void checkDelimiters() {
676         if (m_delimiters_ == null || m_delimiters_.size() == 0) {
677             delims = new boolean[0];
678         } else {
679             int maxChar = m_delimiters_.getRangeEnd(m_delimiters_.getRangeCount()-1);
680             if (maxChar < 0x7f) {
681                 delims = new boolean[maxChar+1];
682                 for (int i = 0, ch; -1 != (ch = m_delimiters_.charAt(i)); ++i) {
683                     delims[ch] = true;
684                 }
685             } else {
686                 delims = null;
687             }
688         }
689     }
690     private boolean[] delims;
691 }