Package gherkin.formatter

Source Code of gherkin.formatter.MatchResultPair

package gherkin.formatter;

import gherkin.formatter.model.Background;
import gherkin.formatter.model.BasicStatement;
import gherkin.formatter.model.CellResult;
import gherkin.formatter.model.Comment;
import gherkin.formatter.model.DescribedStatement;
import gherkin.formatter.model.DocString;
import gherkin.formatter.model.Examples;
import gherkin.formatter.model.Feature;
import gherkin.formatter.model.Match;
import gherkin.formatter.model.Result;
import gherkin.formatter.model.Row;
import gherkin.formatter.model.Scenario;
import gherkin.formatter.model.ScenarioOutline;
import gherkin.formatter.model.Step;
import gherkin.formatter.model.Tag;
import gherkin.formatter.model.TagStatement;
import gherkin.util.Mapper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import static gherkin.util.FixJava.join;
import static gherkin.util.FixJava.map;

/**
* This class pretty prints feature files like they were in the source, only
* prettier. That is, with consistent indentation. This class is also a {@link Reporter},
* which means it can be used to print execution results - highlighting arguments,
* printing source information and exception information.
*/
public class PrettyFormatter implements Reporter, Formatter {
    private final StepPrinter stepPrinter = new StepPrinter();
    private final NiceAppendable out;
    private final boolean executing;

    private String uri;
    private Mapper<Tag, String> tagNameMapper = new Mapper<Tag, String>() {
        @Override
        public String map(Tag tag) {
            return tag.getName();
        }
    };
    private Formats formats;
    private Match match;
    private int[][] cellLengths;
    private int[] maxLengths;
    private int rowIndex;
    private List<? extends Row> rows;
    private Integer rowHeight = null;
    private boolean rowsAbove = false;

    private List<Step> steps = new ArrayList<Step>();
    private List<Integer> indentations = new ArrayList<Integer>();
    private List<MatchResultPair> matchesAndResults = new ArrayList<MatchResultPair>();
    private DescribedStatement statement;

    public PrettyFormatter(Appendable out, boolean monochrome, boolean executing) {
        this.out = new NiceAppendable(out);
        this.executing = executing;
        setMonochrome(monochrome);
    }

    public void setMonochrome(boolean monochrome) {
        if (monochrome) {
            formats = new MonochromeFormats();
        } else {
            formats = new AnsiFormats();
        }
    }

    @Override
    public void uri(String uri) {
        this.uri = uri;
    }

    @Override
    public void feature(Feature feature) {
        printComments(feature.getComments(), "");
        printTags(feature.getTags(), "");
        out.println(feature.getKeyword() + ": " + feature.getName());
        printDescription(feature.getDescription(), "  ", false);
    }

    @Override
    public void background(Background background) {
        replay();
        statement = background;
    }

    @Override
    public void scenario(Scenario scenario) {
        replay();
        statement = scenario;
    }

    @Override
    public void scenarioOutline(ScenarioOutline scenarioOutline) {
        replay();
        statement = scenarioOutline;
    }

    @Override
    public void startOfScenarioLifeCycle(Scenario scenario) {
        // NoOp
    }

    @Override
    public void endOfScenarioLifeCycle(Scenario scenario) {
        // NoOp
    }

    private void replay() {
        addAnyOrphanMatch();
        printStatement();
        printSteps();
    }

    private void printSteps() {
        while (!steps.isEmpty()) {
            if (matchesAndResults.isEmpty()) {
                printStep("skipped", Collections.<Argument>emptyList(), null);
            } else {
                MatchResultPair matchAndResult = matchesAndResults.remove(0);
                printStep(matchAndResult.getResultStatus(), matchAndResult.getMatchArguments(),
                        matchAndResult.getMatchLocation());
                if (matchAndResult.hasResultErrorMessage()) {
                   printError(matchAndResult.result);
                }
            }
        }
    }

    private void printStatement() {
        if (statement == null) {
            return;
        }
        calculateLocationIndentations();
        out.println();
        printComments(statement.getComments(), "  ");
        if (statement instanceof TagStatement) {
            printTags(((TagStatement) statement).getTags(), "  ");
        }
        StringBuilder buffer = new StringBuilder("  ");
        buffer.append(statement.getKeyword());
        buffer.append(": ");
        buffer.append(statement.getName());
        String location = executing ? uri + ":" + statement.getLine() : null;
        buffer.append(indentedLocation(location));
        out.println(buffer);
        printDescription(statement.getDescription(), "    ", true);
        statement = null;
    }

    private String indentedLocation(String location) {
        StringBuilder sb = new StringBuilder();
        int indentation = indentations.isEmpty() ? 0 : indentations.remove(0);
        if (location == null) {
            return "";
        }
        for (int i = 0; i < indentation; i++) {
            sb.append(' ');
        }
        sb.append(' ');
        sb.append(getFormat("comment").text("# " + location));
        return sb.toString();
    }

    @Override
    public void examples(Examples examples) {
        replay();
        out.println();
        printComments(examples.getComments(), "    ");
        printTags(examples.getTags(), "    ");
        out.println("    " + examples.getKeyword() + ": " + examples.getName());
        printDescription(examples.getDescription(), "      ", true);
        table(examples.getRows());
    }

    @Override
    public void step(Step step) {
        steps.add(step);
    }

    @Override
    public void match(Match match) {
        addAnyOrphanMatch();
        this.match = match;
    }

    private void addAnyOrphanMatch() {
        if (this.match != null) {
            matchesAndResults.add(new MatchResultPair(this.match, null));
        }
    }

    @Override
    public void embedding(String mimeType, byte[] data) {
        // Do nothing
    }

    @Override
    public void write(String text) {
        out.println(getFormat("output").text(text));
    }

    @Override
    public void result(Result result) {
        matchesAndResults.add(new MatchResultPair(match, result));
        match = null;
    }

    @Override
    public void before(Match match, Result result) {
        printHookFailure(match, result, true);
    }

    @Override
    public void after(Match match, Result result) {
        printHookFailure(match, result, false);
    }

    private void printHookFailure(Match match, Result result, boolean isBefore) {
        if (result.getStatus().equals(Result.FAILED)) {
            Format format = getFormat(result.getStatus());

            StringBuffer context = new StringBuffer("Failure in ");
            if (isBefore) {
                context.append("before");
            } else {
                context.append("after");
            }
            context.append(" hook:");

            out.println(format.text(context.toString()) + format.text(match.getLocation()));
            out.println(format.text("Message: ") + format.text(result.getErrorMessage()));

            if (result.getError() != null) {
                printError(result);
            }
        }

    }

    private void printStep(String status, List<Argument> arguments, String location) {
        Step step = steps.remove(0);
        Format textFormat = getFormat(status);
        Format argFormat = getArgFormat(status);

        printComments(step.getComments(), "    ");

        StringBuilder buffer = new StringBuilder("    ");
        buffer.append(textFormat.text(step.getKeyword()));
        stepPrinter.writeStep(new NiceAppendable(buffer), textFormat, argFormat, step.getName(), arguments);
        buffer.append(indentedLocation(location));

        out.println(buffer);
        if (step.getRows() != null) {
            table(step.getRows());
        } else if (step.getDocString() != null) {
            docString(step.getDocString());
        }
    }

    private Format getFormat(String key) {
        return formats.get(key);
    }

    private Format getArgFormat(String key) {
        return formats.get(key + "_arg");
    }

    public void table(List<? extends Row> rows) {
        prepareTable(rows);
        if (!executing) {
            for (Row row : rows) {
                row(row.createResults("skipped"));
                nextRow();
            }
        }
    }

    private void prepareTable(List<? extends Row> rows) {
        this.rows = rows;

        // find the largest row
        int columnCount = 0;
        for (Row row : rows) {
            if (columnCount < row.getCells().size()) {
                columnCount = row.getCells().size();
            }
        }

        cellLengths = new int[rows.size()][columnCount];
        maxLengths = new int[columnCount];
        for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
            Row row = rows.get(rowIndex);
            final List<String> cells = row.getCells();
            for (int colIndex = 0; colIndex < columnCount; colIndex++) {
                final String cell = getCellSafely(cells, colIndex);
                final int length = escapeCell(cell).length();
                cellLengths[rowIndex][colIndex] = length;
                maxLengths[colIndex] = Math.max(maxLengths[colIndex], length);
            }
        }
        rowIndex = 0;
    }

    private String getCellSafely(final List<String> cells, final int colIndex) {
        return (colIndex < cells.size()) ? cells.get(colIndex) : "";
    }

    public void row(List<CellResult> cellResults) {
        StringBuilder buffer = new StringBuilder();
        Row row = rows.get(rowIndex);
        if (rowsAbove) {
            buffer.append(formats.up(rowHeight));
        } else {
            rowsAbove = true;
        }
        rowHeight = 1;

        for (Comment comment : row.getComments()) {
            buffer.append("      ");
            buffer.append(comment.getValue());
            buffer.append("\n");
            rowHeight++;
        }
        switch (row.getDiffType()) {
            case NONE:
                buffer.append("      | ");
                break;
            case DELETE:
                buffer.append("    ").append(formats.get("skipped").text("-")).append(" | ");
                break;
            case INSERT:
                buffer.append("    ").append(formats.get("comment").text("+")).append(" | ");
                break;
        }
        for (int colIndex = 0; colIndex < maxLengths.length; colIndex++) {
            String cellText = escapeCell(getCellSafely(row.getCells(), colIndex));
            String status = null;
            switch (row.getDiffType()) {
                case NONE:
                    status = cellResults.size() < colIndex ? cellResults.get(colIndex).getStatus() : "skipped";
                    break;
                case DELETE:
                    status = "skipped";
                    break;
                case INSERT:
                    status = "comment";
                    break;
            }
            Format format = formats.get(status);
            buffer.append(format.text(cellText));
            int padding = maxLengths[colIndex] - cellLengths[rowIndex][colIndex];
            padSpace(buffer, padding);
            if (colIndex < maxLengths.length - 1) {
                buffer.append(" | ");
            } else {
                buffer.append(" |");
            }
        }
        out.println(buffer);
        rowHeight++;
        Set<Result> seenResults = new HashSet<Result>();
        for (CellResult cellResult : cellResults) {
            for (Result result : cellResult.getResults()) {
                if (result.getErrorMessage() != null && !seenResults.contains(result)) {
                    printError(result);
                    rowHeight += result.getErrorMessage().split("\n").length;
                    seenResults.add(result);
                }
            }
        }
    }

    private void printError(Result result) {
        Format failed = formats.get("failed");
        out.println(indent(failed.text(result.getErrorMessage()), "      "));
    }

    public void nextRow() {
        rowIndex++;
        rowsAbove = false;
    }

    @Override
    public void syntaxError(String state, String event, List<String> legalEvents, String uri, Integer line) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void done() {
        // We're *not* closing the stream here.
        // https://github.com/cucumber/gherkin/issues/151
        // https://github.com/cucumber/cucumber-jvm/issues/96
    }

    @Override
    public void close() {
        out.close();
    }

    private String escapeCell(String cell) {
        return cell.replaceAll("\\\\(?!\\|)", "\\\\\\\\").replaceAll("\\n", "\\\\n").replaceAll("\\|", "\\\\|");
    }

    public void docString(DocString docString) {
        out.println("      \"\"\"");
        out.println(escapeTripleQuotes(indent(docString.getValue(), "      ")));
        out.println("      \"\"\"");
    }

    public void eof() {
        replay();
    }

    private void calculateLocationIndentations() {
        int[] lineWidths = new int[steps.size() + 1];
        int i = 0;

        List<BasicStatement> statements = new ArrayList<BasicStatement>();
        statements.add(statement);
        statements.addAll(steps);
        int maxLineWidth = 0;
        for (BasicStatement statement : statements) {
            int stepWidth = statement.getKeyword().length() + statement.getName().length();
            lineWidths[i++] = stepWidth;
            maxLineWidth = Math.max(maxLineWidth, stepWidth);
        }
        for (int lineWidth : lineWidths) {
            indentations.add(maxLineWidth - lineWidth);
        }
    }

    private void padSpace(StringBuilder buffer, int indent) {
        for (int i = 0; i < indent; i++) {
            buffer.append(" ");
        }
    }

    private void printComments(List<Comment> comments, String indent) {
        for (Comment comment : comments) {
            out.println(indent + comment.getValue());
        }
    }

    private void printTags(List<Tag> tags, String indent) {
        if (tags.isEmpty()) return;
        out.println(indent + join(map(tags, tagNameMapper), " "));
    }

    private void printDescription(String description, String indentation, boolean newline) {
        if (!"".equals(description)) {
            out.println(indent(description, indentation));
            if (newline) out.println();
        }
    }

    private static final Pattern START = Pattern.compile("^", Pattern.MULTILINE);

    private static String indent(String s, String indentation) {
        return START.matcher(s).replaceAll(indentation);
    }

    private static final Pattern TRIPLE_QUOTES = Pattern.compile("\"\"\"", Pattern.MULTILINE);
    private static final String ESCAPED_TRIPLE_QUOTES = "\\\\\"\\\\\"\\\\\"";

    private static String escapeTripleQuotes(String s) {
        return TRIPLE_QUOTES.matcher(s).replaceAll(ESCAPED_TRIPLE_QUOTES);
    }
}

class MatchResultPair {
    public final Match match;
    public final Result result;

    public MatchResultPair(Match match, Result result) {
        this.match = match;
        this.result = result;
    }

    public List<Argument> getMatchArguments() {
        if (match != null) {
            return match.getArguments();
        }
        return Collections.<Argument>emptyList();
    }

    public String getMatchLocation() {
        if (match != null) {
            return match.getLocation();
        }
        return null;
    }

    public String getResultStatus() {
        if (result != null) {
            return result.getStatus();
        }
        return "skipped";
    }

    public boolean hasResultErrorMessage() {
        return result != null && result.getErrorMessage() != null;
    }
}
TOP

Related Classes of gherkin.formatter.MatchResultPair

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.