org.powerassert.synthetic.ExpressionRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.powerassert.synthetic.ExpressionRenderer.java

Source

/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed 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 org.powerassert.synthetic;

import java.lang.reflect.Array;
import java.util.*;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class ExpressionRenderer {
    boolean showTypes;

    public ExpressionRenderer(boolean showTypes) {
        this.showTypes = showTypes;
    }

    public String render(RecordedExpression<?> recordedExpr) {
        int offset = 0;
        for (char c : recordedExpr.getText().toCharArray()) {
            if (Character.isWhitespace(c))
                offset++;
            else
                break;
        }

        String intro = recordedExpr.getText().trim().replaceAll(";$", ""); // strip trailing semicolons
        Map<Integer, Integer> strippedWhitespace = strippedWhitespaceUpToColumn(intro);
        intro = intro.replaceAll("\n\\s+", " ");

        List<String> lines = new ArrayList<>();

        for (RecordedValue recordedValue : filterAndSortByAnchor(recordedExpr.getValues())) {
            int offsetAnchor = recordedValue.getAnchor() - offset;
            placeValue(lines, recordedValue.getValue(), offsetAnchor - strippedWhitespace.get(offsetAnchor));
        }

        lines.add(0, intro);

        String rendered = "";
        for (String line : lines) {
            rendered += line + "\n";
        }
        return rendered;
    }

    /**
     * Help count how much newline and margin whitespace we have stripped up to a particular index so we know
     * how to offset anchors
     */
    Map<Integer, Integer> strippedWhitespaceUpToColumn(String intro) {
        Map<Integer, Integer> stripped = new TreeMap<>();
        boolean countingWhitespace = false;
        int col = 0;
        int count = 0;
        for (char c : intro.toCharArray()) {
            if (c == '\n' && !countingWhitespace) {
                // we don't count the first newline, as this will be replaced with a space
                // (yielding the same column width in terms of characters)
                countingWhitespace = true;
            } else if (countingWhitespace) {
                if (Character.isWhitespace(c))
                    count++;
                else
                    countingWhitespace = false;
            }
            stripped.put(col, count);
            col++;
        }
        return stripped;
    }

    private Iterable<RecordedValue> filterAndSortByAnchor(List<RecordedValue> recordedValues) {
        Map<Integer, RecordedValue> map = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer n1, Integer n2) {
                return n2.compareTo(n1);
            }
        });

        for (RecordedValue value : recordedValues) {
            if (!map.containsKey(value.getAnchor()))
                map.put(value.getAnchor(), value);
        }

        return map.values();
    }

    private void placeValue(List<String> lines, Object value, int col) {
        String str = renderValue(value);

        if (lines.isEmpty())
            lines.add("");

        // ensure that there is at least one row of pipes between the expression and values
        lines.set(0, placeString(lines.get(0), "|", col));

        for (int i = 1; i < lines.size(); i++) {
            String line = lines.get(i);
            if (fits(line, str, col)) {
                lines.set(i, placeString(line, str, col));
                return;
            }
            lines.set(i, placeString(line, "|", col));
        }

        String newLine = placeString("", str, col);
        lines.add(newLine);
    }

    private String renderValue(Object value) {
        String str;
        if (value == null)
            str = "null";
        else if (value.getClass().isArray()) {
            // recursive string join
            str = "[";
            for (Object o : toArray(value)) {
                str += renderValue(o) + ", ";
            }
            str = (str.length() > 1 ? str.substring(0, str.length() - 2) : "") + "]";
        } else {
            try {
                if (value.getClass().getMethod("toString").getDeclaringClass() == Object.class) {
                    str = ToStringBuilder.reflectionToString(value, ToStringStyle.SHORT_PREFIX_STYLE);
                } else
                    str = value.toString();
            } catch (NoSuchMethodException e) {
                str = value.toString();
            }
        }

        return showTypes && value != null ? str + " (" + value.getClass().getName() + ")" : str;
    }

    private String placeString(String line, String str, int anchor) {
        int diff = anchor - line.length();
        for (int i = 0; i < diff; i++) {
            line += ' ';
        }
        String prefix = line.substring(0, anchor);
        String suffix = anchor + str.length() > line.length() ? "" : line.substring(anchor + str.length());
        return prefix + str + suffix;
    }

    private Boolean fits(String line, String str, int anchor) {
        if (str.length() > line.length())
            return false;

        for (char c : line.substring(anchor, anchor + str.length() + 1).toCharArray()) {
            if (!Character.isWhitespace(c))
                return false;
        }
        return true;
    }

    private Object[] toArray(Object array) {
        if (array.getClass().getComponentType().isPrimitive()) {
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < Array.getLength(array); i++) {
                list.add(Array.get(array, i));
            }
            return list.toArray();
        } else {
            return (Object[]) array;
        }
    }
}