PrettyException.java

/*
 * Copyright 2015-2016 Providence Authors
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package net.morimekta.providence.serializer.pretty;

import net.morimekta.providence.serializer.SerializerException;
import net.morimekta.util.Strings;
import net.morimekta.util.lexer.LexerException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Locale;

import static java.lang.Math.max;

/**
 * Exception when totalizing fails to make token or read the expected content.
 */
public class PrettyException extends SerializerException {
    private final static long serialVersionUID = 1513434504497049610L;

    private int    lineNo;
    private int    linePos;
    private CharSequence line;
    private String file;
    private int    length;

    public PrettyException(LexerException e, String format, Object... args) {
        super(e, format, args);
        if (e.getLine() != null) {
            setLine(e.getLine());
            setLineNo(e.getLineNo());
            setLinePos(e.getLinePos());
            setLength(e.getLength());
        }
    }

    public PrettyException(String format, Object... args) {
        super(format, args);
    }

    public PrettyException(Throwable cause, String format, Object... args) {
        super(cause, format, args);
    }

    public PrettyException(PrettyToken token, String format, Object... args) {
        super(format, args);
        setLinePos(token.linePos());
        setLineNo(token.lineNo());
        setLength(token.length());
        setLine(token.line());
    }

    /**
     * @return The 1-indexed line number of the fault.
     */
    public int getLineNo() {
        return lineNo;
    }

    /**
     * @return The 1-indexed position on the given line.
     */
    public int getLinePos() {
        return linePos;
    }

    /**
     * @return The number of u16 chars representing the fault.
     */
    public int getLength() {
        return length;
    }

    /**
     * @return The whole line of the fault, not including line feed.
     */
    @Nullable
    public String getLine() {
        return line == null ? null : line.toString();
    }

    /**
     * @return The file that contains the fault.
     */
    @Nullable
    public String getFile() {
        return file;
    }

    public PrettyException setLineNo(int lineNo) {
        this.lineNo = lineNo;
        return this;
    }

    public PrettyException setLinePos(int linePos) {
        this.linePos = linePos;
        return this;
    }

    public PrettyException setLength(int len) {
        this.length = len;
        return this;
    }

    public PrettyException setLine(CharSequence line) {
        this.line = line;
        return this;
    }

    public PrettyException setFile(String file) {
        this.file = file;
        return this;
    }

    @Override
    public PrettyException initCause(Throwable cause) {
        return (PrettyException) super.initCause(cause);
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "\n" + displayString();
    }

    @Nonnull
    @Override
    public String displayString() {
        if (lineNo > 0) {
            String fileSpec = "";
            if (file != null) {
                fileSpec = " in " + file;
            }
            if (line != null) {
                if (length > 1) {
                    return String.format(Locale.US,
                                         "Error%s on line %d, row %d-%d: %s%n" +
                                         "%s%n" +
                                         "%s%s",
                                         fileSpec,
                                         lineNo,
                                         linePos,
                                         linePos + length - 1,
                                         getMessage(),
                                         line,
                                         Strings.times("-", linePos - 1),
                                         Strings.times("^", max(1, length)));
                }
                return String.format(Locale.US,
                                     "Error%s on line %d, row %d: %s%n" +
                                     "%s%n" +
                                     "%s^",
                                     fileSpec,
                                     lineNo,
                                     linePos,
                                     getMessage(),
                                     line,
                                     Strings.times("-", linePos - 1));
            } else {
                return String.format(Locale.US,
                                     "Error%s on line %d, row %d: %s",
                                     fileSpec,
                                     getLineNo(),
                                     getLinePos(),
                                     getMessage());
            }
        } else if (file != null) {
            return String.format(Locale.US, "Error in %s: %s", file, getMessage());
        } else {
            return String.format(Locale.US, "Error: %s", getMessage());
        }
    }
}