package gherkin;

import gherkin.ast.Location;

import java.util.Collections;
import java.util.List;

public class ParserException extends RuntimeException {
    public final Location location;

    protected ParserException(String message) {
        super(message);
        location = null;
    }

    protected ParserException(String message, Location location) {
        super(getMessage(message, location));
        this.location = location;
    }

    private static String getMessage(String message, Location location) {
        return String.format("(%s:%s): %s", location.getLine(), location.getColumn(), message);
    }

    public static class AstBuilderException extends ParserException {
        public AstBuilderException(String message, Location location) {
            super(message, location);
        }
    }

    public static class NoSuchLanguageException extends ParserException {
        public NoSuchLanguageException(String language, Location location) {
            super("Language not supported: " + language, location);
        }
    }

    public static class UnexpectedTokenException extends ParserException {
        public String stateComment;

        public final Token receivedToken;
        public final List<String> expectedTokenTypes;

        public UnexpectedTokenException(Token receivedToken, List<String> expectedTokenTypes, String stateComment) {
            super(getMessage(receivedToken, expectedTokenTypes), receivedToken.location);
            this.receivedToken = receivedToken;
            this.expectedTokenTypes = expectedTokenTypes;
            this.stateComment = stateComment;
        }

        private static String getMessage(Token receivedToken, List<String> expectedTokenTypes) {
            return String.format("expected: %s, got '%s'",
                    StringUtils.join(", ", expectedTokenTypes),
                    receivedToken.getTokenValue().trim());
        }
    }

    public static class UnexpectedEOFException extends ParserException {
        public final String stateComment;
        public final List<String> expectedTokenTypes;

        public UnexpectedEOFException(Token receivedToken, List<String> expectedTokenTypes, String stateComment) {
            super(getMessage(expectedTokenTypes), receivedToken.location);
            this.expectedTokenTypes = expectedTokenTypes;
            this.stateComment = stateComment;
        }

        private static String getMessage(List<String> expectedTokenTypes) {
            return String.format("unexpected end of file, expected: %s",
                    StringUtils.join(", ", expectedTokenTypes));
        }
    }

    public static class CompositeParserException extends ParserException {
        public final List<ParserException> errors;

        public CompositeParserException(List<ParserException> errors) {
            super(getMessage(errors));
            this.errors = Collections.unmodifiableList(errors);
        }

        private static String getMessage(List<ParserException> errors) {
            if (errors == null) throw new NullPointerException("errors");

            StringUtils.ToString<ParserException> exceptionToString = new StringUtils.ToString<ParserException>() {
                @Override
                public String toString(ParserException e) {
                    return e.getMessage();
                }
            };
            return "Parser errors:\n" + StringUtils.join(exceptionToString, "\n", errors);
        }
    }
}

@@ -1439,32 +1439,44 @@
 tedTokenTypes), 
+getLocation(
 receivedToken.lo
@@ -1468,33 +1468,25 @@
 eceivedToken
-.location
+)
 );%0A         
@@ -1919,32 +1919,309 @@
 im());%0A        %7D
+%0A%0A        private static Location getLocation(Token receivedToken) %7B%0A            return receivedToken.location.getColumn() %3E 1 %0A                ? receivedToken.location%0A                : new Location(receivedToken.location.getLine(), receivedToken.line.indent() + 1);%0A        %7D
 %0A    %7D%0A%0A    publ

improve error column information (java)