1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.util;
19
20/**
21 * Breaks a string into tokens; new code should probably use {@link String#split}.
22 *
23 * <blockquote>
24 * <pre>
25 * // Legacy code:
26 * StringTokenizer st = new StringTokenizer("a:b:c", ":");
27 * while (st.hasMoreTokens()) {
28 *     System.err.println(st.nextToken());
29 * }
30 *
31 * // New code:
32 * for (String token : "a:b:c".split(":")) {
33 *     System.err.println(token);
34 * }
35 * </pre>
36 * </blockquote>
37 *
38 * @since 1.0
39 */
40public class StringTokenizer implements Enumeration<Object> {
41
42    private String string;
43
44    private String delimiters;
45
46    private boolean returnDelimiters;
47
48    private int position;
49
50    /**
51     * Constructs a new {@code StringTokenizer} for the parameter string using
52     * whitespace as the delimiter. The {@code returnDelimiters} flag is set to
53     * {@code false}.
54     *
55     * @param string
56     *            the string to be tokenized.
57     */
58    public StringTokenizer(String string) {
59        this(string, " \t\n\r\f", false);
60    }
61
62    /**
63     * Constructs a new {@code StringTokenizer} for the parameter string using
64     * the specified delimiters. The {@code returnDelimiters} flag is set to
65     * {@code false}. If {@code delimiters} is {@code null}, this constructor
66     * doesn't throw an {@code Exception}, but later calls to some methods might
67     * throw a {@code NullPointerException}.
68     *
69     * @param string
70     *            the string to be tokenized.
71     * @param delimiters
72     *            the delimiters to use.
73     */
74    public StringTokenizer(String string, String delimiters) {
75        this(string, delimiters, false);
76    }
77
78    /**
79     * Constructs a new {@code StringTokenizer} for the parameter string using
80     * the specified delimiters, returning the delimiters as tokens if the
81     * parameter {@code returnDelimiters} is {@code true}. If {@code delimiters}
82     * is null this constructor doesn't throw an {@code Exception}, but later
83     * calls to some methods might throw a {@code NullPointerException}.
84     *
85     * @param string
86     *            the string to be tokenized.
87     * @param delimiters
88     *            the delimiters to use.
89     * @param returnDelimiters
90     *            {@code true} to return each delimiter as a token.
91     */
92    public StringTokenizer(String string, String delimiters,
93            boolean returnDelimiters) {
94        if (string == null) {
95            throw new NullPointerException("string == null");
96        }
97        this.string = string;
98        this.delimiters = delimiters;
99        this.returnDelimiters = returnDelimiters;
100        this.position = 0;
101    }
102
103    /**
104     * Returns the number of unprocessed tokens remaining in the string.
105     *
106     * @return number of tokens that can be retreived before an {@code
107     *         Exception} will result from a call to {@code nextToken()}.
108     */
109    public int countTokens() {
110        int count = 0;
111        boolean inToken = false;
112        for (int i = position, length = string.length(); i < length; i++) {
113            if (delimiters.indexOf(string.charAt(i), 0) >= 0) {
114                if (returnDelimiters)
115                    count++;
116                if (inToken) {
117                    count++;
118                    inToken = false;
119                }
120            } else {
121                inToken = true;
122            }
123        }
124        if (inToken)
125            count++;
126        return count;
127    }
128
129    /**
130     * Returns {@code true} if unprocessed tokens remain. This method is
131     * implemented in order to satisfy the {@code Enumeration} interface.
132     *
133     * @return {@code true} if unprocessed tokens remain.
134     */
135    public boolean hasMoreElements() {
136        return hasMoreTokens();
137    }
138
139    /**
140     * Returns {@code true} if unprocessed tokens remain.
141     *
142     * @return {@code true} if unprocessed tokens remain.
143     */
144    public boolean hasMoreTokens() {
145        if (delimiters == null) {
146            throw new NullPointerException("delimiters == null");
147        }
148        int length = string.length();
149        if (position < length) {
150            if (returnDelimiters)
151                return true; // there is at least one character and even if
152            // it is a delimiter it is a token
153
154            // otherwise find a character which is not a delimiter
155            for (int i = position; i < length; i++)
156                if (delimiters.indexOf(string.charAt(i), 0) == -1)
157                    return true;
158        }
159        return false;
160    }
161
162    /**
163     * Returns the next token in the string as an {@code Object}. This method is
164     * implemented in order to satisfy the {@code Enumeration} interface.
165     *
166     * @return next token in the string as an {@code Object}
167     * @throws NoSuchElementException
168     *                if no tokens remain.
169     */
170    public Object nextElement() {
171        return nextToken();
172    }
173
174    /**
175     * Returns the next token in the string as a {@code String}.
176     *
177     * @return next token in the string as a {@code String}.
178     * @throws NoSuchElementException
179     *                if no tokens remain.
180     */
181    public String nextToken() {
182        if (delimiters == null) {
183            throw new NullPointerException("delimiters == null");
184        }
185        int i = position;
186        int length = string.length();
187
188        if (i < length) {
189            if (returnDelimiters) {
190                if (delimiters.indexOf(string.charAt(position), 0) >= 0)
191                    return String.valueOf(string.charAt(position++));
192                for (position++; position < length; position++)
193                    if (delimiters.indexOf(string.charAt(position), 0) >= 0)
194                        return string.substring(i, position);
195                return string.substring(i);
196            }
197
198            while (i < length && delimiters.indexOf(string.charAt(i), 0) >= 0)
199                i++;
200            position = i;
201            if (i < length) {
202                for (position++; position < length; position++)
203                    if (delimiters.indexOf(string.charAt(position), 0) >= 0)
204                        return string.substring(i, position);
205                return string.substring(i);
206            }
207        }
208        throw new NoSuchElementException();
209    }
210
211    /**
212     * Returns the next token in the string as a {@code String}. The delimiters
213     * used are changed to the specified delimiters.
214     *
215     * @param delims
216     *            the new delimiters to use.
217     * @return next token in the string as a {@code String}.
218     * @throws NoSuchElementException
219     *                if no tokens remain.
220     */
221    public String nextToken(String delims) {
222        this.delimiters = delims;
223        return nextToken();
224    }
225}
226