import java.util.NoSuchElementException;

import junit.framework.TestCase;

/**
 * File TokenizerTest.java
 * Created on Jan 10, 2006
 */
public class TokenizerTest extends TestCase {
    Tokenizer t;

    Token fooToken = new Token(Type.NAME, "foo");
    Token foo2Token = new Token(Type.NAME, "foo2");
    Token barToken = new Token(Type.NAME, "bar");
    Token bazToken = new Token(Type.NAME, "baz");
    Token oneToken = new Token(Type.NUMBER, "1");
    Token twoToken = new Token(Type.NUMBER, "2");
    Token threeToken = new Token(Type.NUMBER, "3");
    Token oneTwoThreeToken = new Token(Type.NUMBER, "123");
    Token bangToken = new Token(Type.SYMBOL, "!");
    Token eolToken = new Token(Type.EOL, "\n");
    Token endOfInputToken = new Token(Type.END_OF_INPUT, "");

    /**
     * Test method for constructor Tokenizer(String)'
     */
    public void testTokenizer() {
        
    }

    /**
     * Test method for 'Tokenizer.hasNext()'
     */
    public void testHasNextEol() {        
        t = new Tokenizer("");
        assertTrue(t.hasNext());
        t.next();
        assertFalse(t.hasNext());
        
        t = new Tokenizer("  ");
        assertTrue(t.hasNext());
        t.next();
        assertFalse(t.hasNext());
        
        t = new Tokenizer(" foo ");
        assertTrue(t.hasNext());
        t.next();
        assertTrue(t.hasNext());
        t.next();
        assertFalse(t.hasNext());

        t = new Tokenizer("foo bar \t baz \n ");
        for (int i = 0; i < 5; i++) {
            assertTrue(t.hasNext());
            t.next();
        }
        assertFalse(t.hasNext());
    }
    
    /**
     * Test method for 'Tokenizer.next()')
     */
    public void testHasNext() {
        t = new Tokenizer("1 2 \t 3 \n four % 6");
        for (int i = 0; i < 8; i++) {
            assertTrue(t.hasNext());
            t.next();
        }
        assertFalse(t.hasNext());
    }
    
    /**
     * Test method for 'Tokenizer.next()' with names (identifiers)
     */
    public void testNext() {
        t = new Tokenizer("foo bar baz");
        for (int i = 0; i < 4; i++) {
            t.next();
        }
        try {
            t.next();
            fail();
        }
        catch (NoSuchElementException e) {
        }
    }
    
    /**
     * Test method for 'Tokenizer.next()' with names (identifiers)
     * and extra whitespace.
     */
    public void testNextName() {
        t = new Tokenizer("foo bar baz");
        assertEquals(fooToken, t.next());
        assertEquals(barToken, t.next());
        assertEquals(bazToken, t.next());
        assertEquals(endOfInputToken, t.next());

        t = new Tokenizer("foo bar  \n \t ");
        assertEquals(fooToken, t.next());
        assertEquals(barToken, t.next());
        assertEquals(eolToken, t.next());
        assertEquals(endOfInputToken, t.next());
    }
    
    /**
     * Test method for 'Tokenizer.next()' with non-English letters
     * (This is why we need the methods <code>isLetter</code> and/or
     * <code>isLetterOrDigit</code> from the <code>Character</code> class.
     * <br>3/17/06 -- Replaced character encodings with Java Unicode
     */
    public void testNextName2() {
        t = new Tokenizer("French \u00EAtre tr\u00E8s diff\u00E9rents");
        assertEquals(makeName("French"), t.next());
        assertEquals(makeName("\u00EAtre"), t.next());
        assertEquals(makeName("tr\u00E8s"), t.next());
        assertEquals(makeName("diff\u00E9rents"), t.next());
        
        t = new LogoTokenizer("German S\u00FCrd Stra\u00DFe");
        assertEquals(makeName("German"), t.next());
        assertEquals(makeName("S\u00FCrd"), t.next());
        assertEquals(makeName("Stra\u00DFe"), t.next());
    }
    
    private Token makeName(String value) {
        return new Token(Type.NAME, value);
    }
    
    /**
     * Test method for 'Tokenizer.next()' with numbers (integers)
     */
    public void testNextNumber() {
        t = new Tokenizer(" 1 2 3 123");
        assertEquals(oneToken, t.next());
        assertEquals(twoToken, t.next());
        assertEquals(threeToken, t.next());
        assertEquals(oneTwoThreeToken, t.next());
        assertEquals(endOfInputToken, t.next());
        
        t = new Tokenizer(" -3 ");
        assertEquals(new Token(Type.SYMBOL, "-"), t.next());
        assertEquals(threeToken, t.next());
        
    }

    /**
     * Test method for 'Tokenizer.next()' with symbols
     */
    public void testNextSymbol() {
        t = new Tokenizer("!@#$%^&*()_+\"\\");
        assertEquals(bangToken, t.next());
        assertEquals(new Token(Type.SYMBOL, "@"), t.next());
        for (int i = 0; i < 10; i++) {
            assertEquals(Type.SYMBOL, ((Token) t.next()).type);
        }
        assertEquals(new Token(Type.SYMBOL, "\""), t.next());
        assertEquals(new Token(Type.SYMBOL, "\\"), t.next());
    }
    
    /**
     * Additional test method for 'Tokenizer.next()' with more symbols
     */
    public void testNextSymbol2() {
        checkSymbols(":;~`-=,./<>?{}[]|");
    }
    
    private void checkSymbols(String symbols) {
        Tokenizer t = new Tokenizer(symbols);
        for (int i = 0; i < symbols.length(); i++) {
            String expectedValue = symbols.substring(i, i + 1);
            Token expectedToken = new Token(Type.SYMBOL, expectedValue);
            assertEquals(expectedToken, t.next());
        }
        assertEquals(endOfInputToken, t.next());
        assertFalse(t.hasNext());
    }

    /**
     * Test method for 'Tokenizer.next()' with mixed types
     */
    public void testNextMixed() {
        t = new Tokenizer("foo 2 foo2=123!");
        assertTrue(t.hasNext());
        assertEquals(fooToken, t.next());
        assertEquals(twoToken, t.next());
        assertEquals(foo2Token, t.next());
        assertEquals(new Token(Type.SYMBOL, "="), t.next());
        assertEquals(oneTwoThreeToken, t.next());
        assertEquals(bangToken, t.next());
        assertEquals(endOfInputToken, t.next());
        assertFalse(t.hasNext());
    }

    /**
     * Test method for 'Tokenizer.remove()'
     */
    public void testRemove() {
        t = new Tokenizer("foo");
        t.next();
        try {
            t.remove();
            fail();
        }
        catch (UnsupportedOperationException e) {
        }
    }

    /**
     * Test method for 'Tokenizer.putBack(int)'
     */
    public void testPutBack() {
        t = new Tokenizer("foo bar!1 !");
        assertEquals(fooToken, t.next());
        assertEquals(barToken, t.next());
        assertEquals(bangToken, t.next());
        assertEquals(oneToken, t.next());
        assertEquals(bangToken, t.next());
        
        t.putBack(5);
        assertEquals(fooToken, t.next());
        assertEquals(barToken, t.next());
        assertEquals(bangToken, t.next());
        assertEquals(oneToken, t.next());
        assertEquals(bangToken, t.next());
        assertEquals(endOfInputToken, t.next());
        assertFalse(t.hasNext());
        
        t.putBack(1);
        assertTrue(t.hasNext());
        assertEquals(endOfInputToken, t.next());

        t.putBack(6);
        assertEquals(fooToken, t.next());
        
        try {
            t.putBack(2);
            fail();            
        }
        catch (IllegalArgumentException e) {
        }
    }

}

