import java.io.*;
import java.util.*;


/**
 * Reads delimited items (numbers, words, phrases, etc.), returning one at a time. 
 * This class combines the standard Reader and Iterator interfaces.
 * 
 * @see Reader
 * @see Iterator
 * @author Robert C. Duvall
 */
public class Scanner extends Reader implements java.util.Iterator
{
    //////////////////////////////////////////////////////////////
    // constants
    public final static String WHITE_SPACE = " \t\n\r";
    public final static int LOOKAHEAD_SIZE = 1048576;

    //////////////////////////////////////////////////////////////
    // state
    private BufferedReader myInput;
    private StringTokenizer myTokenizer;
    private String myDelimiters;
    private String myItem;
    private String myLine;


    //////////////////////////////////////////////////////////////
    // constructors
    /**
     * Create Scanner for that reads data from console input.
     */
    public Scanner ()
    {
        this(new InputStreamReader(System.in));
    }

    /**
     * Create Scanner for that reads data from a file.
     * 
     * @param file    specifies file that supplies data
     */
    public Scanner (File file)
    {
        try
        {
            init(new FileReader(file), WHITE_SPACE);
        }
        catch (FileNotFoundException e)
        {
            throw new RuntimeException("File not found " + e);
        }
    }

    /**
     * Create Scanner for that reads data from a string.
     * 
     * @param str    specifies string that supplies data
     */
    public Scanner (String str)
    {
        this(new StringReader(str));
    }

    /**
     * Create Scanner for that reads data from any kind of reader (i.e., a web site).
     * 
     * @param realReader specifies reader that supplies data
     */
    public Scanner (Reader realReader)
    {
        this(realReader, WHITE_SPACE);
    }

    /**
     * Create Scanner for that reads data from any kind of reader (i.e., a web site)
     * that is delimited by given set of characters.
     * 
     * @param realReader specifies reader that supplies data
     * @param characters set of characters on which to separate items
     */
    public Scanner (Reader realReader, String characters)
    {
        init(realReader, characters);
    }


    //////////////////////////////////////////////////////////////
    // Reader interface methods
    /**
     * Read characters into a portion of an array. This method will block until
     * some input is available, an I/O error occurs, or the end of the stream is
     * reached.
     * 
     * @param cbuf   destination buffer
     * @param off    offset at which to start storing characters
     * @param len    maximum number of characters to read
     * 
     * @return number of characters read, or -1 if end of stream has been reached
     */
    public int read (char[] cbuf, int off, int len) throws IOException
    {
        return myInput.read(cbuf, off, len);
    }

    /**
     * Close the stream. Once a stream has been closed, further read(), ready(),
     * mark(), or reset() invocations will throw an IOException. Closing a
     * previously-closed stream, however, has no effect.
     */
    public void close ()
    {
        try
        {
            myInput.close();
        }
        catch (IOException e)
        {
            System.out.println(e);
        }
    }

    /**
     * Reset to beginning of data
     */
    public void reset ()
    {
        try
        {
            myInput.reset();
            myInput.mark(LOOKAHEAD_SIZE);

            myLine = null;
            myTokenizer = null;
            myItem = null;
        }
        catch (IOException e)
        {
            System.out.println(e);
        }
    }

    /**
     * Change delimiters used to separate items
     *
     * @param characters set of characters that delimit items
     */
    public void useDelimiter (String characters)
    {
        myDelimiters = characters;
    }


    //////////////////////////////////////////////////////////////
    // Iterator interface methods
    /**
     * Check if more items are available
     * 
     * @return true iff there are more items
     */
    public boolean hasNext ()
    {
        try
        {
            if (myItem == null)
            {
                myItem = getNextItem();
            }
            return myItem != null;
        }
        catch (IOException e)
        {
            return false;
        }
    }

    /**
     * Get next item, could be anything
     * 
     * @return next delimited item
     */
    public Object next ()
    {
        if (hasNext())
        {
            String current = myItem;
            myItem = null;
            return current;
        }
        else
        {
            throw new NoSuchElementException();
        }
    }

    /**
     * Get next delimited item as a string
     * 
     * @return next delimited item, must be a string
     */
    public String nextString ()
    {
        return (String)next();
    }

    /**
     * Get next delimited item as an integer value
     * 
     * @return next delimited whole number
     */
    public int nextInt ()
    {
        return Integer.parseInt(nextString());
    }

    /**
     * Get next delimited item as an double value.
     * 
     * @return next delimited real number
     */
    public double nextDouble ()
    {
        return Double.parseDouble(nextString());
    }

    /**
     * Not implemented, throws UnsupportedOperationException
     * 
     * @see UnsupportedOperationException
     */
    public void remove ()
    {
        throw new UnsupportedOperationException();
    }


    //////////////////////////////////////////////////////////////
    // Helper methods
    /**
     * If possible, get next item even if it is on another line.
     * Subclasses can customize how to get next item.
     */
    protected String getNextItem () throws IOException
    {
        while (myTokenizer == null || !myTokenizer.hasMoreTokens())
        {
            myLine = getNextLine(myInput);
            if (myLine != null)
            {
                myTokenizer = new StringTokenizer(myLine, myDelimiters);
            }
            else
            {
                return null;
            }
        }

        // BUGBUG: does not work if input spread over many lines
        return myTokenizer.nextToken(myDelimiters);
    }

    /**
     * If possible, get next line of data.
     * Allow subclasses to customize how to read next line.
     * 
     * @return next useful line in file
     */
    protected String getNextLine (BufferedReader input) throws IOException
    {
        String result = input.readLine();
        if (result != null)
        {
            return result.trim();
        }
        return null;
    }


    /**
     * Initialize all instance variables (used by all constructors).
     * 
     * @param realReader specifies reader that supplies data
     * @param characters set of characters on which to separate items
     */
    private void init (Reader realReader, String characters)
    {
        if (realReader instanceof BufferedReader)
        {
            myInput = (BufferedReader)realReader;
        }
        else
        {
            myInput = new BufferedReader(realReader);
        }

        try
        {
            useDelimiter(characters);
            myInput.mark(LOOKAHEAD_SIZE);
            reset();
        }
        catch (IOException io)
        {
            throw new RuntimeException("reset not supported " + io);
        }
    }


    //////////////////////////////////////////////////////////////
    // Main
    // Allow this class to be run directly by Java.
    // Read and echo the given file.
    public static void main (String args[])
    {
        Scanner input;
        if (args.length == 0)
        {
            input = new Scanner();
        }
        else
        {
            input = new Scanner(new File(args[0]));
        }

        input.useDelimiter(WHITE_SPACE);
        while (input.hasNext())
        {
            System.out.println("**" + input.nextString() + "**");
        }

        input.useDelimiter("\n");
        input.reset();
        while (input.hasNext())
        {
            System.out.println("**" + input.nextString() + "**");
        }

        input.reset();
        while (input.hasNext())
        {
            Scanner line = new Scanner(input.nextString());
            System.out.println("##" + line.nextString() + "##");
            line.useDelimiter("-");
            while (line.hasNext())
            {
                System.out.println("##" + line.nextString() + "##");
            }
            System.out.println("------");
        }
    }
}
