com.evolveum.midpoint.util.histogram.Histogram.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.util.histogram.Histogram.java

Source

/*
 * Copyright (c) 2010-2017 Evolveum
 *
 * 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 com.evolveum.midpoint.util.histogram;

import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;

/**
 * @author mederly
 */
public class Histogram<T> {

    private static final int ZEROS_BEFORE_SKIP = 5;
    private static final int ZEROS_AFTER_SKIP = 5;

    private final int step;
    private final int maxLength;

    public Histogram(int step, int maxLength) {
        this.step = step;
        this.maxLength = maxLength;
    }

    private final ArrayList<HistogramEntry<T>> entries = new ArrayList<>();

    private long minValue, maxValue, totalValue = 0;
    private T minItem, maxItem;
    private int items = 0;

    public void register(T item, long value) {
        if (items == 0) {
            minValue = maxValue = value;
            minItem = maxItem = item;
        } else {
            if (value < minValue) {
                minValue = value;
                minItem = item;
            }
            if (value > maxValue) {
                maxValue = value;
                maxItem = item;
            }
        }
        totalValue += value;
        items++;

        long bucketLong = value / step;
        int bucket = bucketLong < maxLength - 1 ? (int) bucketLong : maxLength - 1;
        if (entries.size() <= bucket) {
            entries.ensureCapacity(bucket);
            while (entries.size() <= bucket) {
                entries.add(new HistogramEntry<>());
            }
        }
        entries.get(bucket).record(item, value);
    }

    public int getStep() {
        return step;
    }

    public int getMaxLength() {
        return maxLength;
    }

    public ArrayList<HistogramEntry<T>> getEntries() {
        return entries;
    }

    @SuppressWarnings("unused")
    public long getMinValue() {
        return minValue;
    }

    @SuppressWarnings("unused")
    public long getMaxValue() {
        return maxValue;
    }

    @SuppressWarnings("unused")
    public long getTotalValue() {
        return totalValue;
    }

    public int getItems() {
        return items;
    }

    public String dump(int columns) {
        StringBuilder sb = new StringBuilder();
        sb.append("Count: ").append(items).append("\n");
        if (items == 0) {
            return sb.toString();
        }
        sb.append("Min: ").append(minValue).append(" (").append(minItem).append(")\n");
        sb.append("Max: ").append(maxValue).append(" (").append(maxItem).append(")\n");
        sb.append("Avg: ").append(totalValue / items).append("\n");
        sb.append("\nHistogram:\n\n");
        int maxIntervalLength = Math.max(getIntervalString(entries.size()).length(), 10);
        String rowFormatString = "%" + maxIntervalLength + "s : %6s : %s : %s\n";
        //noinspection ConstantConditions
        int maxCount = entries.stream().mapToInt(e -> e.getItemsCount()).max().getAsInt();
        sb.append(String.format(rowFormatString, "Interval", "Items", column(0, maxCount, columns),
                "Representative"));
        int dividerLength = maxIntervalLength + columns + 40;
        sb.append(StringUtils.repeat("-", dividerLength)).append("\n");
        for (int i = 0; i < entries.size(); i++) {
            int skipUntil = computeSkipUntil(i);
            if (skipUntil >= 0) {
                sb.append(StringUtils.repeat(" ~ ", dividerLength / 6 - 3));
                sb.append(skipUntil - i).append(" lines skipped");
                sb.append(StringUtils.repeat(" ~ ", dividerLength / 6 - 3)).append("\n");
                i = skipUntil - 1;
            } else {
                HistogramEntry<T> entry = entries.get(i);
                sb.append(
                        String.format(rowFormatString, getIntervalString(i), String.valueOf(entry.getItemsCount()),
                                column(entry.getItemsCount(), maxCount, columns), getRepresentative(entry)));
            }
        }
        sb.append("\n");
        return sb.toString();
    }

    private int computeSkipUntil(int i) {
        if (i < ZEROS_BEFORE_SKIP) {
            return -1;
        }
        int firstNonZero = zerosTo(i - ZEROS_BEFORE_SKIP);
        if (firstNonZero - ZEROS_AFTER_SKIP <= i + 1) { // so that at least 2 lines are skipped
            return -1;
        }
        return firstNonZero - ZEROS_AFTER_SKIP;
    }

    private int zerosTo(int i) {
        while (i < entries.size() && entries.get(i).getItemsCount() == 0) {
            i++;
        }
        return i;
    }

    private Object column(int count, int maxCount, int columns) {
        int bars = (int) ((double) columns * (double) count / (double) maxCount);
        if (count > 0 && bars == 0) {
            bars = 1;
        }
        return StringUtils.repeat("#", bars) + StringUtils.repeat(" ", columns - bars);
    }

    private String getRepresentative(HistogramEntry<T> entry) {
        if (entry.getRepresentativeItem() == null) {
            return "";
        }
        return entry.getRepresentativeItem() + " (" + entry.getRepresentativeItemValue() + ")";
    }

    private String getIntervalString(int i) {
        if (i == entries.size() - 1) {
            return String.format("[%d-%d]", getLower(i), maxValue);
        } else {
            return String.format("[%d-%d]", getLower(i), getUpper(i));
        }
    }

    private long getLower(int bucket) {
        return bucket * step;
    }

    private long getUpper(int bucket) {
        return getLower(bucket + 1) - 1;
    }
}