package net.lshift.spki.convert;

import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;

import net.lshift.spki.CanonicalSpkiInputStream;
import net.lshift.spki.InvalidInputException;
import net.lshift.spki.ParseException;
import net.lshift.spki.SpkiInputStream;

/**
 * Input stream that wraps an SpkiInputStream to add facilities
 * useful for conversion to classes.
 */
public class ConvertInputStream
    extends SpkiInputStream {
    private final SpkiInputStream delegate;
    private final Stack<TokenType> tokenStack = new Stack<TokenType>();
    private final Stack<byte[]> byteStack = new Stack<byte[]>();

    public ConvertInputStream(final SpkiInputStream delegate) {
        super();
        this.delegate = delegate;
    }

    public ConvertInputStream(final InputStream is) {
        this(new CanonicalSpkiInputStream(is));
    }

    @Override
    public TokenType doNext()
        throws IOException,
            ParseException {
        if (tokenStack.isEmpty()) {
            return delegate.next();
        } else {
            return tokenStack.pop();
        }
    }

    @Override
    public byte[] doAtomBytes()
        throws IOException,
            ParseException {
        if (byteStack.isEmpty()) {
            return delegate.atomBytes();
        } else {
            return byteStack.pop();
        }
    }

    @Override
    public void close()
        throws IOException {
        delegate.close();
    }

    public <T> T read(final Class<T> clazz)
        throws IOException, InvalidInputException {
        return Registry.getConverter(clazz).read(this);
    }

    public <T> T readRest(final Class<T> clazz)
            throws IOException, InvalidInputException {
        return ((ListConverter<T>)Registry.getConverter(clazz)).readRest(this);
    }

    public void pushback(final TokenType token) {
        switch (token) {
        case EOF:
            assertState(State.FINISHED);
            break;
        case ATOM:
            assertState(State.ATOM);
            break;
        default:
            assertState(State.TOKEN);
            break;
        }
        tokenStack.push(token);
        state = State.TOKEN;
    }

    public void pushback(final byte[] atom) {
        assertState(State.TOKEN);
        byteStack.push(atom);
        state = State.ATOM;
    }

    public void nextAssertType(final TokenType type)
        throws IOException, InvalidInputException {
        if (next() != type) {
            throw new ConvertException("Token was of unexpected type");
        }
    }

    public void assertAtom(final String name)
        throws InvalidInputException, IOException {
        nextAssertType(TokenType.ATOM);
        if (!name.equals(ConvertUtils.stringOrNull(atomBytes()))) {
            throw new ConvertException("Did not see expected atom: " + name);
        }
    }
}

@@ -86,32 +86,8 @@
 eam;
-%0Aimport java.util.Stack;
 %0A%0Aim
@@ -477,145 +477,8 @@
 ate;
-%0A    private final Stack%3CTokenType%3E tokenStack = new Stack%3CTokenType%3E();%0A    private final Stack%3Cbyte%5B%5D%3E byteStack = new Stack%3Cbyte%5B%5D%3E();
 %0A%0A  
@@ -819,384 +819,178 @@
     
-if (tokenStack.isEmpty()) %7B%0A            return delegate.next();%0A        %7D else %7B%0A            return tokenStack.pop();%0A        %7D%0A    %7D%0A%0A    @Override%0A    public byte%5B%5D doAtomBytes()%0A        throws IOException,%0A            ParseException %7B%0A        if (byteStack.isEmpty()) %7B%0A            return delegate.atomBytes();%0A        %7D else %7B%0A            return byteStack.pop();%0A        %7D
+return delegate.next();%0A    %7D%0A%0A    @Override%0A    public byte%5B%5D doAtomBytes()%0A        throws IOException,%0A            ParseException %7B%0A        return delegate.atomBytes();
 %0A   
@@ -1447,533 +1447,8 @@
  %7D%0A%0A
-    public void pushback(final TokenType token) %7B%0A        switch (token) %7B%0A        case EOF:%0A            assertState(State.FINISHED);%0A            break;%0A        case ATOM:%0A            assertState(State.ATOM);%0A            break;%0A        default:%0A            assertState(State.TOKEN);%0A            break;%0A        %7D%0A        tokenStack.push(token);%0A        state = State.TOKEN;%0A    %7D%0A%0A    public void pushback(final byte%5B%5D atom) %7B%0A        assertState(State.TOKEN);%0A        byteStack.push(atom);%0A        state = State.ATOM;%0A    %7D%0A%0A
     

Remove pushback from ConvertInputStream.