clean up API, better metrics
parent
6731ad986b
commit
a19f09ab1f
@ -0,0 +1,436 @@
|
|||||||
|
package org.xbib.elx.common;
|
||||||
|
|
||||||
|
import org.xbib.time.pretty.PrettyTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taken from org,apache.commons.lang.DurationFormatUtils of Apache commons-lang.
|
||||||
|
*/
|
||||||
|
public class FormatUtil {
|
||||||
|
|
||||||
|
private static final PrettyTime pretty = new PrettyTime();
|
||||||
|
|
||||||
|
private static final String EMPTY = "";
|
||||||
|
private static final String YEAR = "y";
|
||||||
|
private static final String MONTH = "M";
|
||||||
|
private static final String DAY = "d";
|
||||||
|
private static final String HOUR = "H";
|
||||||
|
private static final String MINUTE = "m";
|
||||||
|
private static final String SECOND = "s";
|
||||||
|
private static final String MILLISECOND = "S";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of milliseconds in a standard second.
|
||||||
|
*/
|
||||||
|
private static final long MILLIS_PER_SECOND = 1000;
|
||||||
|
/**
|
||||||
|
* Number of milliseconds in a standard minute.
|
||||||
|
*/
|
||||||
|
private static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
|
||||||
|
/**
|
||||||
|
* Number of milliseconds in a standard hour.
|
||||||
|
*/
|
||||||
|
private static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
|
||||||
|
/**
|
||||||
|
* Number of milliseconds in a standard day.
|
||||||
|
*/
|
||||||
|
private static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
|
||||||
|
|
||||||
|
private static final String[] BYTES = {
|
||||||
|
" B", " kB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"
|
||||||
|
};
|
||||||
|
private static final String[] BYTES_PER_SECOND = {
|
||||||
|
" B/s", " kB/s", " MB/s", " GB/s", " TB/s", " PB/s", " EB/s", " ZB/s", " YB/s"
|
||||||
|
};
|
||||||
|
private static final String[] DOCS_PER_SECOND = {
|
||||||
|
" dps", " kdps", " Mdps", " Gdps", " Tdps", " Pdps", " Edps", " Zdps", " Ydps"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format byte size (file size as example) into a string,
|
||||||
|
* with two digits after dot and actual measure (MB, GB or other).
|
||||||
|
*
|
||||||
|
* @param size value to format
|
||||||
|
* @return formatted string in bytes, kB, MB or other.
|
||||||
|
*/
|
||||||
|
public static String formatSize(long size) {
|
||||||
|
return format(size, BYTES, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatSize(double size) {
|
||||||
|
return format(size, BYTES, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format speed values (copy speed as example) into a string
|
||||||
|
* with two digits after dot and actual measure (MB/s, GB/s or other).
|
||||||
|
*
|
||||||
|
* @param speed value to format
|
||||||
|
* @return formatted string in bytes/s, kB/s, MB/s or other.
|
||||||
|
*/
|
||||||
|
public static String formatSpeed(long speed) {
|
||||||
|
return format(speed, BYTES_PER_SECOND, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatSpeed(double speed) {
|
||||||
|
return format(speed, BYTES_PER_SECOND, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatDocumentSpeed(long speed) {
|
||||||
|
return format(speed, DOCS_PER_SECOND, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatDocumentSpeed(double speed) {
|
||||||
|
return format(speed, DOCS_PER_SECOND, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format any value without string appending.
|
||||||
|
*
|
||||||
|
* @param size value to format
|
||||||
|
* @param measureUnits array of strings to use as measurement units. Use BYTES_PER_SECOND as example.
|
||||||
|
* @param measureQuantity quantiry, required to step into next unit. Like 1024 for bytes,
|
||||||
|
* 1000 for meters or 100 for century.
|
||||||
|
* @return formatted size with measure unit
|
||||||
|
*/
|
||||||
|
private static String format(long size, String[] measureUnits, int measureQuantity) {
|
||||||
|
if (size <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (size < measureQuantity) {
|
||||||
|
return size + measureUnits[0];
|
||||||
|
}
|
||||||
|
int i = 1;
|
||||||
|
double d = size;
|
||||||
|
while ((d = d / measureQuantity) > (measureQuantity - 1)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
long l = (long) (d * 100);
|
||||||
|
d = (double) l / 100;
|
||||||
|
if (i < measureUnits.length) {
|
||||||
|
return d + measureUnits[i];
|
||||||
|
}
|
||||||
|
return String.valueOf(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(double value, String[] measureUnits, int measureQuantity) {
|
||||||
|
double d = value;
|
||||||
|
if (d <= 0.0d) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (d < measureQuantity) {
|
||||||
|
return d + measureUnits[0];
|
||||||
|
}
|
||||||
|
int i = 1;
|
||||||
|
while ((d = d / measureQuantity) > (measureQuantity - 1)) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
long l = (long) (d * 100);
|
||||||
|
d = (double) l / 100;
|
||||||
|
if (i < measureUnits.length) {
|
||||||
|
return d + measureUnits[i];
|
||||||
|
}
|
||||||
|
return String.valueOf(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatMillis(long millis) {
|
||||||
|
return pretty.format(pretty.calculateDuration(millis));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatDurationWords(long value, boolean suppressLeadingZeroElements,
|
||||||
|
boolean suppressTrailingZeroElements) {
|
||||||
|
// This method is generally replacable by the format method, but
|
||||||
|
// there are a series of tweaks and special cases that require
|
||||||
|
// trickery to replicate.
|
||||||
|
String duration = formatDuration(value, "d' days 'H' hours 'm' minutes 's' seconds'");
|
||||||
|
if (suppressLeadingZeroElements) {
|
||||||
|
// this is a temporary marker on the front. Like ^ in regexp.
|
||||||
|
duration = " " + duration;
|
||||||
|
String tmp = replaceOnce(duration, " 0 days", "");
|
||||||
|
if (tmp.length() != duration.length()) {
|
||||||
|
duration = tmp;
|
||||||
|
tmp = replaceOnce(duration, " 0 hours", "");
|
||||||
|
if (tmp.length() != duration.length()) {
|
||||||
|
duration = tmp;
|
||||||
|
tmp = replaceOnce(duration, " 0 minutes", "");
|
||||||
|
if (tmp.length() != duration.length()) {
|
||||||
|
duration = replaceOnce(tmp, " 0 seconds", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (duration.length() != 0) {
|
||||||
|
// strip the space off again
|
||||||
|
duration = duration.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (suppressTrailingZeroElements) {
|
||||||
|
String tmp = replaceOnce(duration, " 0 seconds", "");
|
||||||
|
if (tmp != null && tmp.length() != duration.length()) {
|
||||||
|
duration = tmp;
|
||||||
|
tmp = replaceOnce(duration, " 0 minutes", "");
|
||||||
|
if (tmp.length() != duration.length()) {
|
||||||
|
duration = tmp;
|
||||||
|
tmp = replaceOnce(duration, " 0 hours", "");
|
||||||
|
if (tmp.length() != duration.length()) {
|
||||||
|
duration = replaceOnce(tmp, " 0 days", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
duration = " " + duration;
|
||||||
|
duration = replaceOnce(duration, " 1 seconds", " 1 second");
|
||||||
|
duration = replaceOnce(duration, " 1 minutes", " 1 minute");
|
||||||
|
duration = replaceOnce(duration, " 1 hours", " 1 hour");
|
||||||
|
duration = replaceOnce(duration, " 1 days", " 1 day");
|
||||||
|
return duration != null ? duration.trim() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatDuration(long millis, String format) {
|
||||||
|
long durationMillis = millis;
|
||||||
|
List<Token> tokens = lexx(format);
|
||||||
|
int days = 0;
|
||||||
|
int hours = 0;
|
||||||
|
int minutes = 0;
|
||||||
|
int seconds = 0;
|
||||||
|
int milliseconds = 0;
|
||||||
|
|
||||||
|
if (Token.containsTokenWithValue(tokens, DAY)) {
|
||||||
|
days = (int) (durationMillis / MILLIS_PER_DAY);
|
||||||
|
durationMillis -= days * MILLIS_PER_DAY;
|
||||||
|
}
|
||||||
|
if (Token.containsTokenWithValue(tokens, HOUR)) {
|
||||||
|
hours = (int) (durationMillis / MILLIS_PER_HOUR);
|
||||||
|
durationMillis -= hours * MILLIS_PER_HOUR;
|
||||||
|
}
|
||||||
|
if (Token.containsTokenWithValue(tokens, MINUTE)) {
|
||||||
|
minutes = (int) (durationMillis / MILLIS_PER_MINUTE);
|
||||||
|
durationMillis -= minutes * MILLIS_PER_MINUTE;
|
||||||
|
}
|
||||||
|
if (Token.containsTokenWithValue(tokens, SECOND)) {
|
||||||
|
seconds = (int) (durationMillis / MILLIS_PER_SECOND);
|
||||||
|
durationMillis -= seconds * MILLIS_PER_SECOND;
|
||||||
|
}
|
||||||
|
if (Token.containsTokenWithValue(tokens, MILLISECOND)) {
|
||||||
|
milliseconds = (int) durationMillis;
|
||||||
|
}
|
||||||
|
return format(tokens, days, hours, minutes, seconds, milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The internal method to do the formatting.</p>
|
||||||
|
*
|
||||||
|
* @param tokens the tokens
|
||||||
|
* @param days the number of days
|
||||||
|
* @param hours the number of hours
|
||||||
|
* @param minutes the number of minutes
|
||||||
|
* @param seconds the number of seconds
|
||||||
|
* @param millis the number of millis
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
private static String format(List<Token> tokens,
|
||||||
|
int days, int hours, int minutes, int seconds, int millis) {
|
||||||
|
int milliseconds = millis;
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
boolean lastOutputSeconds = false;
|
||||||
|
for (Token token : tokens) {
|
||||||
|
Object value = token.getValue();
|
||||||
|
if (value instanceof StringBuilder) {
|
||||||
|
buffer.append(value);
|
||||||
|
} else {
|
||||||
|
if (DAY.equals(value)) {
|
||||||
|
buffer.append(days);
|
||||||
|
lastOutputSeconds = false;
|
||||||
|
} else if (HOUR.equals(value)) {
|
||||||
|
buffer.append(hours);
|
||||||
|
lastOutputSeconds = false;
|
||||||
|
} else if (MINUTE.equals(value)) {
|
||||||
|
buffer.append(minutes);
|
||||||
|
lastOutputSeconds = false;
|
||||||
|
} else if (SECOND.equals(value)) {
|
||||||
|
buffer.append(seconds);
|
||||||
|
lastOutputSeconds = true;
|
||||||
|
} else if (MILLISECOND.equals(value)) {
|
||||||
|
if (lastOutputSeconds) {
|
||||||
|
milliseconds += 1000;
|
||||||
|
String str = Integer.toString(milliseconds);
|
||||||
|
buffer.append(str.substring(1));
|
||||||
|
} else {
|
||||||
|
buffer.append(milliseconds);
|
||||||
|
}
|
||||||
|
lastOutputSeconds = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a classic date format string into Tokens.
|
||||||
|
*
|
||||||
|
* @param format to parse
|
||||||
|
* @return array of Token
|
||||||
|
*/
|
||||||
|
private static List<Token> lexx(String format) {
|
||||||
|
char[] array = format.toCharArray();
|
||||||
|
List<Token> list = new ArrayList<>(array.length);
|
||||||
|
boolean inLiteral = false;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Token previous = null;
|
||||||
|
for (char ch : array) {
|
||||||
|
if (inLiteral && ch != '\'') {
|
||||||
|
sb.append(ch);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Object value = null;
|
||||||
|
switch (ch) {
|
||||||
|
case '\'':
|
||||||
|
if (inLiteral) {
|
||||||
|
sb = new StringBuilder();
|
||||||
|
inLiteral = false;
|
||||||
|
} else {
|
||||||
|
sb = new StringBuilder();
|
||||||
|
list.add(new Token(sb));
|
||||||
|
inLiteral = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
value = YEAR;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
value = MONTH;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
value = DAY;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
value = HOUR;
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
value = MINUTE;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
value = SECOND;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
value = MILLISECOND;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (sb.length() == 0) {
|
||||||
|
sb = new StringBuilder();
|
||||||
|
list.add(new Token(sb));
|
||||||
|
}
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
if (previous != null && value.equals(previous.getValue())) {
|
||||||
|
previous.increment();
|
||||||
|
} else {
|
||||||
|
Token token = new Token(value);
|
||||||
|
list.add(token);
|
||||||
|
previous = token;
|
||||||
|
}
|
||||||
|
sb.setLength(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String replaceOnce(String text, String searchString, String replacement) {
|
||||||
|
return replace(text, searchString, replacement, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String replace(String text, String searchString, String replacement, int maxvalue) {
|
||||||
|
int max = maxvalue;
|
||||||
|
if (isNullOrEmpty(text) || isNullOrEmpty(searchString) || replacement == null || max == 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
int start = 0;
|
||||||
|
int end = text.indexOf(searchString, start);
|
||||||
|
if (end == -1) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
int replLength = searchString.length();
|
||||||
|
int increase = replacement.length() - replLength;
|
||||||
|
increase = Math.max(increase, 0);
|
||||||
|
increase *= (max < 0 ? 16 : (Math.min(max, 64)));
|
||||||
|
StringBuilder buf = new StringBuilder(text.length() + increase);
|
||||||
|
while (end != -1) {
|
||||||
|
buf.append(text, start, end).append(replacement);
|
||||||
|
start = end + replLength;
|
||||||
|
if (--max == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end = text.indexOf(searchString, start);
|
||||||
|
}
|
||||||
|
buf.append(text.substring(start));
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNullOrEmpty(String target) {
|
||||||
|
return target == null || EMPTY.equals(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element that is parsed from the format pattern.
|
||||||
|
*/
|
||||||
|
private static class Token {
|
||||||
|
|
||||||
|
private final Object value;
|
||||||
|
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
Token(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
this.count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean containsTokenWithValue(List<Token> tokens, Object value) {
|
||||||
|
for (Token token : tokens) {
|
||||||
|
if (token.getValue().equals(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void increment() {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof Token) {
|
||||||
|
Token tok = (Token) obj;
|
||||||
|
if (this.value.getClass() != tok.value.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.count != tok.count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.value instanceof StringBuilder) {
|
||||||
|
return this.value.toString().equals(tok.value.toString());
|
||||||
|
} else if (this.value instanceof Number) {
|
||||||
|
return this.value.equals(tok.value);
|
||||||
|
} else {
|
||||||
|
return this.value == tok.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value + " (" + count + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue