/* Copyright (C) 1998, 1999  Cygnus Solutions

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

package java.util;

/**
 * @author Warren Levy <warrenl@cygnus.com>
 * @date August 24, 1998.
 */
/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
 * "The Java Language Specification", ISBN 0-201-63451-1
 * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
 * Status:  Believed complete and correct
 */

public class StringTokenizer implements Enumeration
{
  /* String to be parsed */
  private String inputString;

  /* String to be parsed put into a char array for efficient access */
  private char[] chArray;

  /* Set of delimiter characters for separating tokens */
  private String delimiters;

  /* Whether delimiters in this instance are treated as tokens themselves */
  private boolean returnDelimiters;

  /* Index into the input string to start parsing for the next token */
  private int inputStringIndex;

  public StringTokenizer(String str)
  {
    this(str, " \t\n\r", false);
  }

  public StringTokenizer(String str, String delims)
  {
    this(str, delims, false);
  }

  public StringTokenizer(String str, String delims, boolean retDelim)
  {
    inputString = str;
    delimiters = delims;
    returnDelimiters = retDelim;
    inputStringIndex = 0;

    // Work on a copy of the remaining string in a char array
    // to gain efficiency of using primitives
    chArray = new char[inputString.length()];
    inputString.getChars(0, inputString.length(), chArray, 0);
  }

  public int countTokens()
  {
    int count = 0;
    int delimiterCount = 0;
    boolean tokenFound = false;		// Set when a non-delimiter is found
    int offset = inputStringIndex;

    // Note for efficiency, we count up the delimiters rather than check
    // returnDelimiters every time we encounter one.  That way, we can
    // just do the conditional once at the end of the method
    while (offset < chArray.length)
      {
	if (isDelimiter(chArray[offset++]))
	  {
	    if (tokenFound)
	      {
		// Got to the end of a token
	        count++;
	        tokenFound = false;
	      }

	    delimiterCount++;		// Increment for this delimiter
	  }
	else
	  {
	    tokenFound = true;

	    // Get to the end of the token
	    while (offset < chArray.length && !isDelimiter(chArray[offset]))
	      offset++;
	  }
      }

    // Make sure to count the last token 
    if (tokenFound)
      count++;

    // if counting delmiters add them into the token count
    return returnDelimiters ? count + delimiterCount : count;
  }

  public boolean hasMoreElements()
  {
    return hasMoreTokens();
  }

  public boolean hasMoreTokens()
  {
    int offset = inputStringIndex;

    while (offset < chArray.length)
      if (!isDelimiter(chArray[offset++]) || returnDelimiters)
	{
	  // update the current position with the start of the next token
	  inputStringIndex = --offset;

	  return true;
	}

    return false;
  }

  public Object nextElement()
  {
    return nextToken();
  }

  public String nextToken()
  {
    int offset = inputStringIndex;
    int startSubstr = -1;

    // Make sure we have more chars left to parse
    // and then find the start of the next token
    while (offset < chArray.length && startSubstr < 0)
      {
	// Find the start of the token; skipping initial delimiters
	if (!isDelimiter(chArray[offset++]))
	  startSubstr = offset - 1;
	else if (returnDelimiters)
	  {
	    // The single char delimiter is treated as a token
	    inputStringIndex = offset;		// update the current position

	    return inputString.substring(offset - 1, inputStringIndex);
	  }
      }

    // Now look for the end of the token
    while (offset < chArray.length)
      {
	if (isDelimiter(chArray[offset++]))
	  {
	    // Found the end of token
            inputStringIndex = offset - 1;	// update the current position

            return inputString.substring(startSubstr, inputStringIndex);
	  }
      }

    // Got to the end of the string without finding the start of a token
    if (startSubstr < 0)
      throw new NoSuchElementException();

    // Got to the end of the string before a delimiter
    inputStringIndex = offset;		// update the current position

    return inputString.substring(startSubstr, inputStringIndex);
  }

  public String nextToken(String delims)
  {
    // First replace with new set of delimiters
    delimiters = delims;

    return nextToken();
  }

  // This private method could be inlined but the other methods are
  // more readable this way, so we'll take the hit on efficiency.
  private boolean isDelimiter(char ch)
  {
    return delimiters.indexOf(ch) >= 0;
  }
}
