/******************************************************************************* * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text or * such license is available at www.eclipse.org. ******************************************************************************/ package org.eclipse.buckminster.core.helpers; import java.util.Date; import java.util.Locale; import java.util.SortedMap; import java.util.TreeMap; import java.util.Map.Entry; public class ProgressStatistics { public interface AmountConverter { String convert(long amount); } static class FileSizeConverter implements AmountConverter { @Override public String convert(long amount) { if (amount < 1024) return String.format(Locale.US, "%dB", Long.valueOf(amount)); //$NON-NLS-1$ else if (amount < 1024 * 1024) return String.format(Locale.US, "%.2fkB", Double.valueOf(((double) amount) / 1024)); //$NON-NLS-1$ else return String.format(Locale.US, "%.2fMB", Double.valueOf(((double) amount) / (1024 * 1024))); //$NON-NLS-1$ } } static class TrivialConverter implements AmountConverter { @Override public String convert(long amount) { return "" + amount; //$NON-NLS-1$ } } public static final AmountConverter TRIVIAL_CONVERTER = new TrivialConverter(); public static final AmountConverter FILESIZE_CONVERTER = new FileSizeConverter(); public static final int DEFAULT_REPORT_INTERVAL = 1000; public static final int DEFAULT_RECENT_SPEED_INTERVAL = 5000; public static final int DEFULAT_RECENT_SPEED_RESOLUTION = 1000; private long current; private long total; private Date startTime; private AmountConverter converter; private Date lastReportTime; private int reportInterval; private int recentSpeedInterval; private int recentSpeedResolution; private SortedMap<Long, Long> recentSpeedMap; private long recentSpeedMapKey; public ProgressStatistics() { current = 0; total = -1; startTime = new Date(); lastReportTime = null; reportInterval = DEFAULT_REPORT_INTERVAL; recentSpeedInterval = DEFAULT_RECENT_SPEED_INTERVAL; recentSpeedResolution = DEFULAT_RECENT_SPEED_RESOLUTION; converter = TRIVIAL_CONVERTER; recentSpeedMap = new TreeMap<Long, Long>(); recentSpeedMapKey = 0L; } public ProgressStatistics(long total) { this(); this.total = total; } public long getAverageSpeed() { long dur = getDuration(); if (dur >= 1000) return current / (dur / 1000); return 0L; } public long getDuration() { return (new Date()).getTime() - startTime.getTime(); } public double getPercentage() { if (total > 0) return ((double) current) / ((double) total); return 0.0; } synchronized public long getRecentSpeed() { removeObsoleteRecentSpeedData(getDuration() / recentSpeedResolution); long dur = 0L; long amount = 0L; SortedMap<Long, Long> relevantData = recentSpeedMap.headMap(Long.valueOf(recentSpeedMapKey)); for (Entry<Long, Long> entry : relevantData.entrySet()) { dur += recentSpeedResolution; amount += entry.getValue().longValue(); } if (dur >= 1000) return amount / (dur / 1000); return 0L; } public int getRecentSpeedInterval() { return recentSpeedInterval; } public int getRecentSpeedResolution() { return recentSpeedResolution; } public int getReportInterval() { return reportInterval; } public void increase(long inc) { registerRecentSpeed(getDuration() / recentSpeedResolution, inc); current += inc; } public String report() { return converter.convert(current) + (total != -1 ? " of " + converter.convert(total) //$NON-NLS-1$ : "") + " at " + converter.convert(getRecentSpeed()) + "/s"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } public void setConverter(AmountConverter converter) { if (converter == null) converter = TRIVIAL_CONVERTER; this.converter = converter; } public void setRecentSpeedInterval(int recentSpeedInterval) { if (recentSpeedInterval <= 0) recentSpeedInterval = DEFAULT_RECENT_SPEED_INTERVAL; this.recentSpeedInterval = recentSpeedInterval; } public void setRecentSpeedResolution(int recentSpeedResolution) { if (recentSpeedResolution <= 0) recentSpeedResolution = DEFULAT_RECENT_SPEED_RESOLUTION; this.recentSpeedResolution = recentSpeedResolution; } public void setReportInterval(int reportInterval) { this.reportInterval = reportInterval; } public boolean shouldReport() { Date now = new Date(); if (lastReportTime == null || now.getTime() - lastReportTime.getTime() >= reportInterval) { lastReportTime = now; return true; } return false; } @Override public String toString() { return report(); } synchronized private void registerRecentSpeed(long key, long inc) { Long keyL = Long.valueOf(key); Long currentValueL = recentSpeedMap.get(keyL); long currentValue = 0L; if (currentValueL != null) currentValue = currentValueL.longValue(); recentSpeedMap.put(keyL, Long.valueOf(inc + currentValue)); if (recentSpeedMapKey != key) { recentSpeedMapKey = key; removeObsoleteRecentSpeedData(key); } } synchronized private void removeObsoleteRecentSpeedData(long lastKey) { long threshold = lastKey - recentSpeedInterval / recentSpeedResolution; recentSpeedMap.headMap(Long.valueOf(threshold)).clear(); } }