StringTokenizer.java Source code

Java tutorial

Introduction

Here is the source code for StringTokenizer.java

Source

/*
 * A replacement for java.util.StringTokenizer
 * Copyright (C) 2001 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Java+Utilities
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 */
import java.util.NoSuchElementException;

/**
 * The string tokenizer class allows an application to break a string into
 * tokens.
 * More information about this class is available from <a target="_top" href=
 * "http://ostermiller.org/utils/StringTokenizer.html">ostermiller.org</a>.
 * <p>
 * The tokenization method is much simpler than the one used by the
 * <code>StreamTokenizer</code> class. The <code>StringTokenizer</code> methods
 * do not distinguish among identifiers, numbers, and quoted strings, nor do
 * they recognize and skip comments.
 * <p>
 * The set of delimiters (the characters that separate tokens) may be specified
 * either at creation time or on a per-token basis.
 * <p>
 * There are two kinds of delimiters: token delimiters and non-token delimiters.
 * A token is either one token delimiter character, or a maximal sequence of
 * consecutive characters that are not delimiters.
 * <p>
 * A <code>StringTokenizer</code> object internally maintains a current
 * position within the string to be tokenized. Some operations advance this
 * current position past the characters processed.
 * <p>
 * The implementation is not thread safe; if a <code>StringTokenizer</code>
 * object is intended to be used in multiple threads, an appropriate wrapper
 * must be provided.
 * <p>
 * The following is one example of the use of the tokenizer. It also
 * demonstrates the usefulness of having both token and non-token delimiters in
 * one <code>StringTokenizer</code>.
 * <p>
 * The code:
 * <blockquote><code>
 * String s = " &nbsp;( &nbsp; aaa  \t &nbsp;* (b+c1 ))";<br>
 * StringTokenizer tokenizer = new StringTokenizer(s, " \t\n\r\f", "()+*");<br>
 * while (tokenizer.hasMoreTokens()) {<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(tokenizer.nextToken());<br>
 * };
 * </code></blockquote>
 * <p>
 * prints the following output:
 * <blockquote>
 * (<br>
 * aaa<br>
 * *<br>
 * (<br>
 * b<br>
 * +<br>
 * c1<br>
 * )<br>
 * )
 * </blockquote>
 * <p>
 * </b>Compatibility with <code>java.util.StringTokenizer</code></b>
 * <p>
 * In the original version of <code>java.util.StringTokenizer</code>, the method
 * <code>nextToken()</code> left the current position after the returned token,
 * and the method <code>hasMoreTokens()</code> moved (as a side effect) the
 * current position before the beginning of the next token. Thus, the code:
 * <blockquote><code>
 * String s = "x=a,b,c";<br>
 * java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(s,"=");<br>
 * System.out.println(tokenizer.nextToken());<br>
 * while (tokenizer.hasMoreTokens()) {<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(tokenizer.nextToken(","));<br>
 * };
 * </code></blockquote>
 * <p>
 * prints the following output:
 * <blockquote>
 * x<br>
 * a<br>
 * b<br>
 * c
 * </blockquote>
 * <p>
 * The Java SDK 1.3 implementation removed the undesired side effect of
 * <code>hasMoreTokens</code> method: now, it does not advance current position.
 * However, after these changes the output of the above code was:
 * <blockquote>
 * x<br>
 * =a<br>
 * b<br>
 * c
 * </blockquote>
 * <p>
 * and there was no good way to produce a second token without "=".
 * <p>
 * To solve the problem, this implementation introduces a new method
 * <code>skipDelimiters()</code>. To produce the original output, the above code
 * should be modified as:
 * <blockquote><code>
 * String s = "x=a,b,c";<br>
 * StringTokenizer tokenizer = new StringTokenizer(s,"=");<br>
 * System.out.println(tokenizer.nextToken());<br>
 * tokenizer.skipDelimiters();<br>
 * while (tokenizer.hasMoreTokens()) {<br>
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(tokenizer.nextToken(","));<br>
 * };
 * </code></blockquote>
 *
 * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
 * @since ostermillerutils 1.00.00
 */
public class StringTokenizer implements java.util.Enumeration<String>, java.util.Iterator<String> {

    /**
     * The string to be tokenized.
     * The code relies on this to never be null.
     *
     * @since ostermillerutils 1.00.00
     */
    protected String text;

    /**
     * The length of the text.
     * Cached for performance.  This should be set whenever the
     * string we are working with is changed.
     *
     * @since ostermillerutils 1.00.00
     */
    protected int strLength;

    /**
     * The set of non-token delimiters.
     *
     * @since ostermillerutils 1.00.00
     */
    protected String nontokenDelims;

    /**
     * The set of token delimiters.
     *
     * @since ostermillerutils 1.00.00
     */
    protected String tokenDelims;

    /**
     * One of two variables used to maintain state through
     * the tokenizing process.
     * <P>
     * Represents the position at which we should start looking for
     * the next token(the position of the character immediately
     * following the end of the last token, or 0 to start), or
     * -1 if the entire string has been examined.
     *
     * @since ostermillerutils 1.00.00
     */
    protected int position;

    /**
     * One of two variables used to maintain state through
     * the tokenizing process.
     * <p>
     * true if and only if is found that an empty token should
     * be returned or if empty token was the last thing returned.
     * <p>
     * If returnEmptyTokens in false, then this variable will
     * always be false.
     *
     * @since ostermillerutils 1.00.00
     */
    protected boolean emptyReturned;

    /**
     * Stores the value of the delimiter character with the
     * highest value. It is used to optimize the detection of delimiter
     * characters.  The common case will be that the int values of delimiters
     * will be less than that of most characters in the string (, or space less
     * than any letter for example).  Given this, we can check easily check
     * to see if a character is not a delimiter by comparing it to the max
     * delimiter.  If it is greater than the max delimiter, then it is no
     * a delimiter otherwise we have to do some more in depth analysis. (for example
     * search the delimiter string.)  This will reduce the running time of
     * the algorithm not to depend on the length of the delimiter string
     * for the common case.
     *
     * @since ostermillerutils 1.00.00
     */
    protected char maxDelimChar;

    /**
     * Whether empty tokens should be returned.
     * for example, if "" should be returned when text starts with
     * a delimiter, has two delimiters next to each other, or
     * ends with a delimiter.
     *
     * @since ostermillerutils 1.00.00
     */
    protected boolean returnEmptyTokens;

    /**
     * Indicates at which position the delimiters last changed.  This
     * will effect how null tokens are returned.  Any
     * time that delimiters are changed, the string will be treated as if
     * it is being parsed from position zero, for example, null strings are possible
     * at the very beginning.
     *
     * @since ostermillerutils 1.00.00
     */
    protected int delimsChangedPosition;

    /**
     * A cache of the token count.  This variable should be -1 if the token
     * have not yet been counted. It should be greater than or equal to zero
     * if the tokens have been counted.
     *
     * @since ostermillerutils 1.00.00
     */
    protected int tokenCount;

    /**
     * Constructs a string tokenizer for the specified string. Both token and
     * non-token delimiters are specified.
     * <p>
     * The current position is set at the beginning of the string.
     *
     * @param text a string to be parsed.
     * @param nontokenDelims the non-token delimiters, i.e. the delimiters that only separate
     *     tokens and are not returned as separate tokens.
     * @param tokenDelims the token delimiters, i.e. delimiters that both separate tokens,
     *     and are themselves returned as tokens.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public StringTokenizer(String text, String nontokenDelims, String tokenDelims) {
        this(text, nontokenDelims, tokenDelims, false);
    }

    /**
     * Constructs a string tokenizer for the specified string. Both token and
     * non-token delimiters are specified and whether or not empty tokens are returned
     * is specified.
     * <p>
     * Empty tokens are tokens that are between consecutive delimiters.
     * <p>
     * It is a primary constructor (i.e. all other constructors are defined in terms
     * of it.)
     * <p>
     * The current position is set at the beginning of the string.
     *
     * @param text a string to be parsed.
     * @param nontokenDelims the non-token delimiters, i.e. the delimiters that only separate
     *     tokens and are not returned as separate tokens.
     * @param tokenDelims the token delimiters, i.e. delimiters that both separate tokens,
     *     and are themselves returned as tokens.
     * @param returnEmptyTokens true if empty tokens may be returned; false otherwise.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public StringTokenizer(String text, String nontokenDelims, String tokenDelims, boolean returnEmptyTokens) {
        setDelims(nontokenDelims, tokenDelims);
        setText(text);
        setReturnEmptyTokens(returnEmptyTokens);
    }

    /**
     * Constructs a string tokenizer for the specified string. Either token or
     * non-token delimiters are specified.
     * <p>
     * Is equivalent to:
     * <ul>
     * <li> If the third parameter is <code>false</code> --
     *      <code>StringTokenizer(text, delimiters, null)</code>
     * <li> If the third parameter is <code>true</code> --
     *      <code>StringTokenizer(text, null, delimiters)</code>
     * </ul>
     *
     * @param text a string to be parsed.
     * @param delims the delimiters.
     * @param delimsAreTokens
     *     flag indicating whether the second parameter specifies token or
     *     non-token delimiters: <code>false</code> -- the second parameter
     *     specifies non-token delimiters, the set of token delimiters is
     *     empty; <code>true</code> -- the second parameter specifies token
     *     delimiters, the set of non-token delimiters is empty.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public StringTokenizer(String text, String delims, boolean delimsAreTokens) {
        this(text, (delimsAreTokens ? null : delims), (delimsAreTokens ? delims : null));
    }

    /**
     * Constructs a string tokenizer for the specified string. The characters in the
     * <code>nontokenDelims</code> argument are the delimiters for separating
     * tokens. Delimiter characters themselves will not be treated as tokens.
     * <p>
     * Is equivalent to <code>StringTokenizer(text,nontokenDelims, null)</code>.
     *
     * @param text a string to be parsed.
     * @param nontokenDelims the non-token delimiters.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public StringTokenizer(String text, String nontokenDelims) {
        this(text, nontokenDelims, null);
    }

    /**
     * Constructs a string tokenizer for the specified string. The tokenizer uses
     * " \t\n\r\f" as a delimiter set of non-token delimiters, and an empty token
     * delimiter set.
     * <p>
     * Is equivalent to <code>StringTokenizer(text, " \t\n\r\f", null);
     *
     * @param text a string to be parsed.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public StringTokenizer(String text) {
        this(text, " \t\n\r\f", null);
    }

    /**
     * Set the text to be tokenized in this StringTokenizer.
     * <p>
     * This is useful when for StringTokenizer re-use so that new string tokenizers do not
     * have to be created for each string you want to tokenizer.
     * <p>
     * The string will be tokenized from the beginning of the string.
     *
     * @param text a string to be parsed.
     * @throws NullPointerException if text is null.
     *
     * @since ostermillerutils 1.00.00
     */
    public void setText(String text) {
        if (text == null) {
            throw new NullPointerException();
        }
        this.text = text;
        strLength = text.length();
        emptyReturned = false;
        // set the position to start evaluation to zero
        // unless the string has no length, in which case
        // the entire string has already been examined.
        position = (strLength > 0 ? 0 : -1);
        // because the text was changed since the last time the delimiters
        // were changed we need to set the delimiter changed position
        delimsChangedPosition = 0;
        // The token count changes when the text changes
        tokenCount = -1;
    }

    /**
     * Set the delimiters for this StringTokenizer.
     * The position must be initialized before this method is used.
     * (setText does this and it is called from the constructor)
     *
     * @param nontokenDelims delimiters that should not be returned as tokens.
     * @param tokenDelims delimiters that should be returned as tokens.
     *
     * @since ostermillerutils 1.00.00
     */
    private void setDelims(String nontokenDelims, String tokenDelims) {
        this.nontokenDelims = nontokenDelims;
        this.tokenDelims = tokenDelims;
        // If we change delimiters, we do not want to start fresh,
        // without returning empty tokens.
        // the delimiter changed position can never be less than
        // zero, unlike position.
        delimsChangedPosition = (position != -1 ? position : strLength);
        // set the max delimiter
        maxDelimChar = 0;
        for (int i = 0; nontokenDelims != null && i < nontokenDelims.length(); i++) {
            if (maxDelimChar < nontokenDelims.charAt(i)) {
                maxDelimChar = nontokenDelims.charAt(i);
            }
        }
        for (int i = 0; tokenDelims != null && i < tokenDelims.length(); i++) {
            if (maxDelimChar < tokenDelims.charAt(i)) {
                maxDelimChar = tokenDelims.charAt(i);
            }
        }
        // Changing the delimiters may change the number of tokens
        tokenCount = -1;
    }

    /**
     * Tests if there are more tokens available from this tokenizer's string.
     * If this method returns <tt>true</tt>, then a subsequent call to
     * <tt>nextToken</tt> with no argument will successfully return a token.
     * <p>
     * The current position is not changed.
     *
     * @return <code>true</code> if and only if there is at least one token in the
     *          string after the current position; <code>false</code> otherwise.
     *
     * @since ostermillerutils 1.00.00
     */
    public boolean hasMoreTokens() {

        // handle the easy case in which the number
        // of tokens has been counted.
        if (tokenCount == 0) {
            return false;
        } else if (tokenCount > 0) {
            return true;
        }

        // copy over state variables from the class to local
        // variables so that the state of this object can be
        // restored to the state that it was in before this
        // method was called.
        int savedPosition = position;
        boolean savedEmptyReturned = emptyReturned;

        int workingPosition = position;
        boolean workingEmptyReturned = emptyReturned;
        boolean onToken = advancePosition();
        while (position != workingPosition || emptyReturned != workingEmptyReturned) {
            if (onToken) {
                // restore object state
                position = savedPosition;
                emptyReturned = savedEmptyReturned;
                return true;
            }
            workingPosition = position;
            workingEmptyReturned = emptyReturned;
            onToken = advancePosition();
        }

        // restore object state
        position = savedPosition;
        emptyReturned = savedEmptyReturned;
        return false;
    }

    /**
     * Returns the next token from this string tokenizer.
     * <p>
     * The current position is set after the token returned.
     *
     * @return the next token from this string tokenizer.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     *
     * @since ostermillerutils 1.00.00
     */
    public String nextToken() {
        int workingPosition = position;
        boolean workingEmptyReturned = emptyReturned;
        boolean onToken = advancePosition();
        while (position != workingPosition || emptyReturned != workingEmptyReturned) {
            if (onToken) {
                // returning a token decreases the token count
                tokenCount--;
                return (emptyReturned ? ""
                        : text.substring(workingPosition, (position != -1) ? position : strLength));
            }
            workingPosition = position;
            workingEmptyReturned = emptyReturned;
            onToken = advancePosition();
        }
        throw new NoSuchElementException();
    }

    /**
     * Advances the current position so it is before the next token.
     * <p>
     * This method skips non-token delimiters but does not skip
     * token delimiters.
     * <p>
     * This method is useful when switching to the new delimiter sets (see the
     * second example in the class comment.)
     *
     * @return <code>true</code> if there are more tokens, <code>false</code> otherwise.
     *
     * @since ostermillerutils 1.00.00
     */
    public boolean skipDelimiters() {
        int workingPosition = position;
        boolean workingEmptyReturned = emptyReturned;
        boolean onToken = advancePosition();

        // skipping delimiters may cause the number of tokens to change
        tokenCount = -1;

        while (position != workingPosition || emptyReturned != workingEmptyReturned) {
            if (onToken) {
                // restore the state to just as it was before we found
                // this token and return
                position = workingPosition;
                emptyReturned = workingEmptyReturned;
                return true;
            }
            workingPosition = position;
            workingEmptyReturned = emptyReturned;
            onToken = advancePosition();
        }

        // the end of the string was reached
        // without finding any tokens
        return false;
    }

    /**
     * Calculates the number of times that this tokenizer's <code>nextToken</code>
     * method can be called before it generates an exception. The current position
     * is not advanced.
     *
     * @return the number of tokens remaining in the string using the current
     *    delimiter set.
     *
     * @see #nextToken()
     * @since ostermillerutils 1.00.00
     */
    public int countTokens() {

        // return the cached token count if a cache
        // is available.
        if (this.tokenCount >= 0) {
            return this.tokenCount;
        }

        int tokenCount = 0;

        // copy over state variables from the class to local
        // variables so that the state of this object can be
        // restored to the state that it was in before this
        // method was called.
        int savedPosition = position;
        boolean savedEmptyReturned = emptyReturned;

        int workingPosition = position;
        boolean workingEmptyReturned = emptyReturned;
        boolean onToken = advancePosition();
        while (position != workingPosition || emptyReturned != workingEmptyReturned) {
            if (onToken) {
                tokenCount++;
            }
            workingPosition = position;
            workingEmptyReturned = emptyReturned;
            onToken = advancePosition();
        }

        // restore object state
        position = savedPosition;
        emptyReturned = savedEmptyReturned;

        // Save the token count in case this is called again
        // so we wouldn't have to do so much work.
        this.tokenCount = tokenCount;

        return tokenCount;
    }

    /**
     * Set the delimiters used to this set of (non-token) delimiters.
     *
     * @param delims the new set of non-token delimiters (the set of token delimiters will be empty).
     *
     * @since ostermillerutils 1.00.00
     */
    public void setDelimiters(String delims) {
        setDelims(delims, null);
    }

    /**
     * Set the delimiters used to this set of delimiters.
     *
     * @param delims the new set of delimiters.
     * @param delimsAreTokens flag indicating whether the first parameter specifies
     *    token or non-token delimiters: false -- the first parameter specifies non-token
     *    delimiters, the set of token delimiters is empty; true -- the first parameter
     *    specifies token delimiters, the set of non-token delimiters is empty.
     *
     * @since ostermillerutils 1.00.00
     */
    public void setDelimiters(String delims, boolean delimsAreTokens) {
        setDelims((delimsAreTokens ? null : delims), (delimsAreTokens ? delims : null));
    }

    /**
     * Set the delimiters used to this set of delimiters.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     *
     * @since ostermillerutils 1.00.00
     */
    public void setDelimiters(String nontokenDelims, String tokenDelims) {
        setDelims(nontokenDelims, tokenDelims);
    }

    /**
     * Set the delimiters used to this set of delimiters.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     * @param returnEmptyTokens true if empty tokens may be returned; false otherwise.
     *
     * @since ostermillerutils 1.00.00
     */
    public void setDelimiters(String nontokenDelims, String tokenDelims, boolean returnEmptyTokens) {
        setDelims(nontokenDelims, tokenDelims);
        setReturnEmptyTokens(returnEmptyTokens);
    }

    /**
     * Calculates the number of times that this tokenizer's <code>nextToken</code>
     * method can be called before it generates an exception using the given set of
     * (non-token) delimiters.  The delimiters given will be used for future calls to
     * nextToken() unless new delimiters are given. The current position
     * is not advanced.
     *
     * @param delims the new set of non-token delimiters (the set of token delimiters will be empty).
     * @return the number of tokens remaining in the string using the new
     *    delimiter set.
     *
     * @see #countTokens()
     * @since ostermillerutils 1.00.00
     */
    public int countTokens(String delims) {
        setDelims(delims, null);
        return countTokens();
    }

    /**
     * Calculates the number of times that this tokenizer's <code>nextToken</code>
     * method can be called before it generates an exception using the given set of
     * delimiters.  The delimiters given will be used for future calls to
     * nextToken() unless new delimiters are given. The current position
     * is not advanced.
     *
     * @param delims the new set of delimiters.
     * @param delimsAreTokens flag indicating whether the first parameter specifies
     *    token or non-token delimiters: false -- the first parameter specifies non-token
     *    delimiters, the set of token delimiters is empty; true -- the first parameter
     *    specifies token delimiters, the set of non-token delimiters is empty.
     * @return the number of tokens remaining in the string using the new
     *    delimiter set.
     *
     * @see #countTokens()
     * @since ostermillerutils 1.00.00
     */
    public int countTokens(String delims, boolean delimsAreTokens) {
        setDelims((delimsAreTokens ? null : delims), (delimsAreTokens ? delims : null));
        return countTokens();
    }

    /**
     * Calculates the number of times that this tokenizer's <code>nextToken</code>
     * method can be called before it generates an exception using the given set of
     * delimiters.  The delimiters given will be used for future calls to
     * nextToken() unless new delimiters are given. The current position
     * is not advanced.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     * @return the number of tokens remaining in the string using the new
     *    delimiter set.
     *
     * @see #countTokens()
     * @since ostermillerutils 1.00.00
     */
    public int countTokens(String nontokenDelims, String tokenDelims) {
        setDelims(nontokenDelims, tokenDelims);
        return countTokens();
    }

    /**
     * Calculates the number of times that this tokenizer's <code>nextToken</code>
     * method can be called before it generates an exception using the given set of
     * delimiters.  The delimiters given will be used for future calls to
     * nextToken() unless new delimiters are given. The current position
     * is not advanced.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     * @param returnEmptyTokens true if empty tokens may be returned; false otherwise.
     * @return the number of tokens remaining in the string using the new
     *    delimiter set.
     *
     * @see #countTokens()
     * @since ostermillerutils 1.00.00
     */
    public int countTokens(String nontokenDelims, String tokenDelims, boolean returnEmptyTokens) {
        setDelims(nontokenDelims, tokenDelims);
        setReturnEmptyTokens(returnEmptyTokens);
        return countTokens();
    }

    /**
     * Advances the state of the tokenizer to the next token or delimiter.  This method only
     * modifies the class variables position, and emptyReturned.  The type of token that
     * should be emitted can be deduced by examining the changes to these two variables.
     * If there are no more tokens, the state of these variables does not change at all.
     *
     * @return true if we are at a juncture at which a token may be emitted, false otherwise.
     *
     * @since ostermillerutils 1.00.00
     */
    private boolean advancePosition() {
        // if we are returning empty tokens, we are just starting to tokenizer
        // and there is a delimiter at the beginning of the string or the string
        // is empty we need to indicate that there is an empty token at the beginning.
        // The beginning is defined as where the delimiters were last changed.
        if (returnEmptyTokens && !emptyReturned
                && (delimsChangedPosition == position || (position == -1 && strLength == delimsChangedPosition))) {
            if (strLength == delimsChangedPosition) {
                // Case in which the string (since delimiter change)
                // is empty, but because we are returning empty
                // tokens, a single empty token should be returned.
                emptyReturned = true;
                return true;
            }
            char c = text.charAt(position);
            if (c <= maxDelimChar && (nontokenDelims != null && nontokenDelims.indexOf(c) != -1)
                    || (tokenDelims != null && tokenDelims.indexOf(c) != -1)) {
                // There is delimiter at the very start of the string
                // so we must return an empty token at the beginning.
                emptyReturned = true;
                return true;
            }
        }
        // The main loop
        // Do this as long as parts of the string have yet to be examined
        while (position != -1) {
            char c = text.charAt(position);
            if (returnEmptyTokens && !emptyReturned && position > delimsChangedPosition) {
                char c1 = text.charAt(position - 1);
                // Examine the current character and the one before it.
                // If both of them are delimiters, then we need to return
                // an empty delimiter.  Note that characters that were examined
                // before the delimiters changed should not be reexamined.
                if (c <= maxDelimChar && c1 <= maxDelimChar
                        && ((nontokenDelims != null && nontokenDelims.indexOf(c) != -1)
                                || (tokenDelims != null && tokenDelims.indexOf(c) != -1))
                        && ((nontokenDelims != null && nontokenDelims.indexOf(c1) != -1)
                                || (tokenDelims != null && tokenDelims.indexOf(c1) != -1))) {
                    emptyReturned = true;
                    /*System.out.println("Empty token.");*/
                    return true;
                }
            }

            int nextDelimiter = (position < strLength - 1 ? indexOfNextDelimiter(position + 1) : -1);
            if (c > maxDelimChar || ((nontokenDelims == null || nontokenDelims.indexOf(c) == -1)
                    && (tokenDelims == null || tokenDelims.indexOf(c) == -1))) {
                // token found
                /*System.out.println("Token: '" +
                  text.substring(position, (nextDelimiter == -1 ? strLength : nextDelimiter)) +
                  "' at " + position + ".");*/
                position = nextDelimiter;
                emptyReturned = false;
                return true;
            } else if (tokenDelims != null && tokenDelims.indexOf(c) != -1) {
                // delimiter that can be returned as a token found
                emptyReturned = false;
                /*System.out.println("Delimiter: '" + c + "' at " + position + ".");*/
                position = (position < strLength - 1 ? position + 1 : -1);
                return true;
            } else {
                // delimiter that is not a token found.
                emptyReturned = false;
                position = (position < strLength - 1 ? position + 1 : -1);
                return false;
            }
        }
        // handle the case that a token is at the end of the string and we should
        // return empty tokens.
        if (returnEmptyTokens && !emptyReturned && strLength > 0) {
            char c = text.charAt(strLength - 1);
            if (c <= maxDelimChar && (nontokenDelims != null && nontokenDelims.indexOf(c) != -1)
                    || (tokenDelims != null && tokenDelims.indexOf(c) != -1)) {
                // empty token at the end of the string found.
                emptyReturned = true;
                /*System.out.println("Empty token at end.");*/
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the next token in this string tokenizer's string.
     * <p>
     * First, the sets of token and non-token delimiters are changed to be the
     * <code>tokenDelims</code> and <code>nontokenDelims</code>, respectively.
     * Then the next token (with respect to new delimiters) in the string after the
     * current position is returned.
     * <p>
     * The current position is set after the token returned.
     * <p>
     * The new delimiter sets remains the used ones after this call.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     * @return the next token, after switching to the new delimiter set.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     * @see #nextToken()
     *
     * @since ostermillerutils 1.00.00
     */
    public String nextToken(String nontokenDelims, String tokenDelims) {
        setDelims(nontokenDelims, tokenDelims);
        return nextToken();
    }

    /**
     * Returns the next token in this string tokenizer's string.
     * <p>
     * First, the sets of token and non-token delimiters are changed to be the
     * <code>tokenDelims</code> and <code>nontokenDelims</code>, respectively;
     * and whether or not to return empty tokens is set.
     * Then the next token (with respect to new delimiters) in the string after the
     * current position is returned.
     * <p>
     * The current position is set after the token returned.
     * <p>
     * The new delimiter set remains the one used for this call and empty tokens are
     * returned in the future as they are in this call.
     *
     * @param nontokenDelims the new set of non-token delimiters.
     * @param tokenDelims the new set of token delimiters.
     * @param returnEmptyTokens true if empty tokens may be returned; false otherwise.
     * @return the next token, after switching to the new delimiter set.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     * @see #nextToken()
     *
     * @since ostermillerutils 1.00.00
     */
    public String nextToken(String nontokenDelims, String tokenDelims, boolean returnEmptyTokens) {
        setDelims(nontokenDelims, tokenDelims);
        setReturnEmptyTokens(returnEmptyTokens);
        return nextToken();
    }

    /**
     * Returns the next token in this string tokenizer's string.
     * <p>
     * Is equivalent to:
     * <ul>
     * <li> If the second parameter is <code>false</code> --
     *      <code>nextToken(delimiters, null)</code>
     * <li> If the second parameter is <code>true</code> --
     *      <code>nextToken(null, delimiters)</code>
     * </ul>
     * <p>
     * @param delims the new set of token or non-token delimiters.
     * @param delimsAreTokens
     *     flag indicating whether the first parameter specifies token or
     *     non-token delimiters: <code>false</code> -- the first parameter
     *     specifies non-token delimiters, the set of token delimiters is
     *     empty; <code>true</code> -- the first parameter specifies token
     *     delimiters, the set of non-token delimiters is empty.
     * @return the next token, after switching to the new delimiter set.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     *
     * @see #nextToken(String,String)
     * @since ostermillerutils 1.00.00
     */
    public String nextToken(String delims, boolean delimsAreTokens) {
        return (delimsAreTokens ? nextToken(null, delims) : nextToken(delims, null));
    }

    /**
     * Returns the next token in this string tokenizer's string.
     * <p>
     * Is equivalent to <code>nextToken(delimiters, null)</code>.
     *
     * @param nontokenDelims the new set of non-token delimiters (the set of
     *     token delimiters will be empty).
     * @return the next token, after switching to the new delimiter set.
     * @throws NoSuchElementException if there are no more tokens in this
     *     tokenizer's string.
     *
     * @see #nextToken(String,String)
     * @since ostermillerutils 1.00.00
     */
    public String nextToken(String nontokenDelims) {
        return nextToken(nontokenDelims, null);
    }

    /**
     * Similar to String.indexOf(int, String) but will look for
     * any character from string rather than the entire string.
     *
     * @param start index in text at which to begin the search
     * @return index of the first delimiter from the start index (inclusive), or -1
     *     if there are no more delimiters in the string
     *
     * @since ostermillerutils 1.00.00
     */
    private int indexOfNextDelimiter(int start) {
        char c;
        int next;
        for (next = start; (c = text.charAt(next)) > maxDelimChar
                || ((nontokenDelims == null || nontokenDelims.indexOf(c) == -1)
                        && (tokenDelims == null || tokenDelims.indexOf(c) == -1)); next++) {
            if (next == strLength - 1) {
                // we have reached the end of the string without
                // finding a delimiter
                return (-1);
            }
        }
        return next;
    }

    /**
     * Returns the same value as the <code>hasMoreTokens()</code> method. It exists
     * so that this class can implement the <code>Enumeration</code> interface.
     *
     * @return <code>true</code> if there are more tokens;
     *    <code>false</code> otherwise.
     *
     * @see java.util.Enumeration
     * @see #hasMoreTokens()
     * @since ostermillerutils 1.00.00
     */
    public boolean hasMoreElements() {
        return hasMoreTokens();
    }

    /**
     * Returns the same value as the <code>nextToken()</code> method, except that
     * its declared return value is <code>Object</code> rather than
     * <code>String</code>. It exists so that this class can implement the
     * <code>Enumeration</code> interface.
     *
     * @return the next token in the string.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     *
     * @see java.util.Enumeration
     * @see #nextToken()
     * @since ostermillerutils 1.00.00
     */
    public String nextElement() {
        return nextToken();
    }

    /**
     * Returns the same value as the <code>hasMoreTokens()</code> method. It exists
     * so that this class can implement the <code>Iterator</code> interface.
     *
     * @return <code>true</code> if there are more tokens;
     *     <code>false</code> otherwise.
     *
     * @see java.util.Iterator
     * @see #hasMoreTokens()
     * @since ostermillerutils 1.00.00
     */
    public boolean hasNext() {
        return hasMoreTokens();
    }

    /**
     * Returns the same value as the <code>nextToken()</code> method, except that
     * its declared return value is <code>Object</code> rather than
     * <code>String</code>. It exists so that this class can implement the
     * <code>Iterator</code> interface.
     *
     * @return the next token in the string.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     *
     * @see java.util.Iterator
     * @see #nextToken()
     * @since ostermillerutils 1.00.00
     */
    public String next() {
        return nextToken();
    }

    /**
     * This implementation always throws <code>UnsupportedOperationException</code>.
     * It exists so that this class can implement the <code>Iterator</code> interface.
     *
     * @throws UnsupportedOperationException always is thrown.
     *
     * @see java.util.Iterator
     * @since ostermillerutils 1.00.00
     */
    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
     * Set whether empty tokens should be returned from this point in
     * in the tokenizing process onward.
     * <P>
     * Empty tokens occur when two delimiters are next to each other
     * or a delimiter occurs at the beginning or end of a string. If
     * empty tokens are set to be returned, and a comma is the non token
     * delimiter, the following table shows how many tokens are in each
     * string.<br>
     * <table><tr><th>String<th><th>Number of tokens<th></tr>
     * <tr><td align=right>"one,two"<td><td>2 - normal case with no empty tokens.<td></tr>
     * <tr><td align=right>"one,,three"<td><td>3 including the empty token in the middle.<td></tr>
     * <tr><td align=right>"one,"<td><td>2 including the empty token at the end.<td></tr>
     * <tr><td align=right>",two"<td><td>2 including the empty token at the beginning.<td></tr>
     * <tr><td align=right>","<td><td>2 including the empty tokens at the beginning and the ends.<td></tr>
     * <tr><td align=right>""<td><td>1 - all strings will have at least one token if empty tokens are returned.<td></tr></table>
     *
     * @param returnEmptyTokens true iff empty tokens should be returned.
     *
     * @since ostermillerutils 1.00.00
     */
    public void setReturnEmptyTokens(boolean returnEmptyTokens) {
        // this could effect the number of tokens
        tokenCount = -1;
        this.returnEmptyTokens = returnEmptyTokens;
    }

    /**
     * Get the the index of the character immediately
     * following the end of the last token.  This is the position at which this tokenizer will begin looking
     * for the next token when a <code>nextToken()</code> method is invoked.
     *
     * @return the current position or -1 if the entire string has been tokenized.
     *
     * @since ostermillerutils 1.00.00
     */
    public int getCurrentPosition() {
        return this.position;
    }

    /**
     * Retrieve all of the remaining tokens in a String array.
     * This method uses the options that are currently set for
     * the tokenizer and will advance the state of the tokenizer
     * such that <code>hasMoreTokens()</code> will return false.
     *
     * @return an array of tokens from this tokenizer.
     *
     * @since ostermillerutils 1.00.00
     */
    public String[] toArray() {
        String[] tokenArray = new String[countTokens()];
        for (int i = 0; hasMoreTokens(); i++) {
            tokenArray[i] = nextToken();
        }
        return tokenArray;
    }

    /**
     * Retrieves the rest of the text as a single token.
     * After calling this method hasMoreTokens() will always return false.
     *
     * @return any part of the text that has not yet been tokenized.
     *
     * @since ostermillerutils 1.00.00
     */
    public String restOfText() {
        return nextToken(null, null);
    }

    /**
     * Returns the same value as nextToken() but does not alter
     * the internal state of the Tokenizer.  Subsequent calls
     * to peek() or a call to nextToken() will return the same
     * token again.
     *
     * @return the next token from this string tokenizer.
     * @throws NoSuchElementException if there are no more tokens in this tokenizer's string.
     *
     * @since ostermillerutils 1.00.00
     */
    public String peek() {
        // copy over state variables from the class to local
        // variables so that the state of this object can be
        // restored to the state that it was in before this
        // method was called.
        int savedPosition = position;
        boolean savedEmptyReturned = emptyReturned;
        int savedtokenCount = tokenCount;

        // get the next token
        String retval = nextToken();

        // restore the state
        position = savedPosition;
        emptyReturned = savedEmptyReturned;
        tokenCount = savedtokenCount;

        // return the nextToken;
        return (retval);
    }
}