All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jknack.handlebars.internal.HbsErrorReporter Maven / Gradle / Ivy

The newest version!
/*
 * Handlebars.java: https://github.com/jknack/handlebars.java
 * Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
 * Copyright (c) 2012 Edgar Espina
 */
package com.github.jknack.handlebars.internal;

import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.Validate.notNull;

import java.util.BitSet;

import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonToken;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.IntStream;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.Interval;
import org.apache.commons.lang3.StringUtils;

import com.github.jknack.handlebars.HandlebarsError;
import com.github.jknack.handlebars.HandlebarsException;

/**
 * The Handlebars error reporter.
 *
 * @author edgar.espina
 * @since 0.10.0
 */
public class HbsErrorReporter implements ANTLRErrorListener {

  /** A file's name. */
  private String filename;

  /**
   * Creates a new {@link HbsErrorReporter}.
   *
   * @param filename The file's name. Required.
   */
  public HbsErrorReporter(final String filename) {
    this.filename = notNull(filename, "A filename is required.");
  }

  @Override
  public void syntaxError(
      final Recognizer recognizer,
      final Object offendingSymbol,
      final int line,
      final int charPositionInLine,
      final String msg,
      final RecognitionException e) {
    int column = Math.max(1, charPositionInLine);
    CommonToken offendingToken = (CommonToken) offendingSymbol;
    StringBuilder message = new StringBuilder();
    message.append(filename).append(":").append(line).append(":").append(column).append(": ");
    String stacktrace = "";
    int reasonStart = message.length();
    if (offendingToken == null) {
      String[] parts = StringUtils.split(msg, "\n");
      message.append(parts[0]);
      stacktrace = "\n" + join(parts, "\n", 1, parts.length);
    } else {
      message.append("found: '").append(offendingToken.getText()).append("', ");
      message.append("expected: '").append(msg).append("'");
    }
    String reason = message.substring(reasonStart);
    message.append("\n");
    int evidenceStat = message.length();
    String[] lines = lines(recognizer);
    underline(message, lines, line, column);
    String prevLine = lineAt(lines, line > lines.length ? lines.length : line - 2);
    String nextLine = lineAt(lines, line);
    String evidence = prevLine + "\n" + message.substring(evidenceStat) + "\n" + nextLine;
    message.append(stacktrace);
    HandlebarsError error =
        new HandlebarsError(
            filename, line, column, reason.replace("", "EOF"), evidence, message.toString());
    throw new HandlebarsException(error);
  }

  /**
   * Get a line at the specified number (if possible).
   *
   * @param lines The lines.
   * @param number The line number to extract.
   * @return The line or an empty string.
   */
  private String lineAt(final String[] lines, final int number) {
    if (number >= 0 && number < lines.length) {
      return lines[number];
    }
    return "";
  }

  /**
   * Build an underline mark and make the error message pretty.
   *
   * @param message The message.
   * @param lines The source lines.
   * @param line The offending's line.
   * @param charPositionInLine The offenfing's column.
   */
  private void underline(
      final StringBuilder message,
      final String[] lines,
      final int line,
      final int charPositionInLine) {
    String errorLine = lines[Math.min(line - 1, lines.length - 1)];
    message.append(errorLine).append("\n");
    for (int i = 0; i < charPositionInLine; i++) {
      message.append(" ");
    }
    message.append("^");
  }

  /**
   * Extract lines.
   *
   * @param recognizer A lexer/parser.
   * @return Source lines
   */
  private String[] lines(final Recognizer recognizer) {
    IntStream stream = recognizer.getInputStream();
    if (stream instanceof CommonTokenStream) {
      stream = ((CommonTokenStream) stream).getTokenSource().getInputStream();
    }
    final String input;
    if (stream instanceof CharStream) {
      input = ((CharStream) stream).getText(new Interval(0, stream.size()));
    } else {
      input = stream.toString();
    }
    String[] lines = input.split("\n");
    return lines;
  }

  @Override
  public void reportAmbiguity(
      final Parser recognizer,
      final DFA dfa,
      final int startIndex,
      final int stopIndex,
      final boolean exact,
      final BitSet ambigAlts,
      final ATNConfigSet configs) {}

  @Override
  public void reportAttemptingFullContext(
      final Parser recognizer,
      final DFA dfa,
      final int startIndex,
      final int stopIndex,
      final BitSet conflictingAlts,
      final ATNConfigSet configs) {}

  @Override
  public void reportContextSensitivity(
      final Parser recognizer,
      final DFA dfa,
      final int startIndex,
      final int stopIndex,
      final int prediction,
      final ATNConfigSet configs) {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy