public static String asciiBar(final long value,
                                  final long minValueInc,
                                  final long maxValueInc,
                                  final int maxChars) {

        final long range = maxValueInc - minValueInc;
        final int barRange = maxChars * 8;
        final double scaleFactor = barRange / (double) range;

        final long scaledValue = (long) ((value - minValueInc) * scaleFactor);

        final String bar;
        if (scaledValue > barRange) {
            bar = Strings.repeat(BLOCK_8_8THS_WIDE, maxChars);
        } else {
            final int fullBlockCount = (int) (scaledValue / 8);
            final int remainder = (int) (scaledValue % 8);

            LOGGER.trace("value {}, scaleFactor {}, scaledValue {}, fullBlockCount {}, remainder {}",
                    value, scaleFactor, scaledValue, fullBlockCount, remainder);

            if (remainder == 0) {
                bar = Strings.repeat(BLOCK_8_8THS_WIDE, fullBlockCount);
            } else {
                bar = Strings.repeat(BLOCK_8_8THS_WIDE, fullBlockCount) + BLOCKS_ARR[remainder - 1];
            }
        }
        return bar;
    }
Attempts to determine the structure of the table from the public getters
of the collection items. The column names are derived from the method name,
e.g. getFirstNameLength() becomes "First Name Length".
Sub-classes of {@link Number} are right aligned.

@param sortColumns If true columns will be in order of column name, else they will
                   be in no particular order.
@return A {@link String} containing the markdown style table.
stroom-util/src/main/java/stroom/util/logging/AsciiTable.java