summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java')
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java732
1 files changed, 476 insertions, 256 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
index 4601cce9..2599a3a5 100644
--- a/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
+++ b/src/de/lmu/ifi/dbs/elki/utilities/FormatUtil.java
@@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.utilities;
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
- Copyright (C) 2013
+ Copyright (C) 2014
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Formatter;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -56,6 +57,11 @@ public final class FormatUtil {
public static final NumberFormat NF2 = NumberFormat.getInstance(Locale.US);
/**
+ * Number Formatter (3 digits) for output purposes.
+ */
+ public static final NumberFormat NF3 = NumberFormat.getInstance(Locale.US);
+
+ /**
* Number Formatter (4 digits) for output purposes.
*/
public static final NumberFormat NF4 = NumberFormat.getInstance(Locale.US);
@@ -80,6 +86,9 @@ public final class FormatUtil {
NF2.setMinimumFractionDigits(2);
NF2.setMaximumFractionDigits(2);
NF2.setGroupingUsed(false);
+ NF3.setMinimumFractionDigits(3);
+ NF3.setMaximumFractionDigits(3);
+ NF3.setGroupingUsed(false);
NF4.setMinimumFractionDigits(4);
NF4.setMaximumFractionDigits(4);
NF4.setGroupingUsed(false);
@@ -122,39 +131,34 @@ public final class FormatUtil {
private static final int[] TIME_UNIT_DIGITS = new int[] { 3, 2, 2, 2, 2 };
/**
- * Formats the double d with the specified fraction digits.
+ * Initialize a number format with ELKI standard options (US locale, no
+ * grouping).
*
- * @param d the double array to be formatted
- * @param digits the number of fraction digits
- * @return a String representing the double d
- */
- public static String format(final double d, int digits) {
+ * @param digits Number of digits to use
+ * @return Number format
+ */
+ public static NumberFormat makeNumberFormat(int digits) {
+ // Prefer predefined number formats where applicable.
+ // TODO: cache others, too?
+ switch(digits){
+ case 0:
+ return NF0;
+ case 2:
+ return NF2;
+ case 3:
+ return NF3;
+ case 4:
+ return NF4;
+ case 6:
+ return NF6;
+ case 8:
+ return NF8;
+ }
final NumberFormat nf = NumberFormat.getInstance(Locale.US);
nf.setMaximumFractionDigits(digits);
nf.setMinimumFractionDigits(digits);
nf.setGroupingUsed(false);
- return nf.format(d);
- }
-
- /**
- * Formats the double d with the specified number format.
- *
- * @param d the double array to be formatted
- * @param nf the number format to be used for formatting
- * @return a String representing the double d
- */
- public static String format(final double d, NumberFormat nf) {
- return nf.format(d);
- }
-
- /**
- * Formats the double d with 2 fraction digits.
- *
- * @param d the double to be formatted
- * @return a String representing the double d
- */
- public static String format(final double d) {
- return format(d, 2);
+ return nf;
}
/**
@@ -166,50 +170,37 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[] d, String sep) {
- StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < d.length; i++) {
- if(i > 0) {
- buffer.append(sep).append(d[i]);
- }
- else {
- buffer.append(d[i]);
- }
+ if(d.length == 0) {
+ return "";
}
- return buffer.toString();
+ return formatTo(new StringBuilder(), d, sep).toString();
}
/**
- * Formats the double array d with the specified separator and the specified
- * fraction digits.
+ * Formats the double array d with the specified number format.
*
* @param d the double array to be formatted
- * @param sep the separator between the single values of the double array,
- * e.g. ','
- * @param digits the number of fraction digits
+ * @param nf the number format to be used for formatting
* @return a String representing the double array d
*/
- public static String format(double[] d, String sep, int digits) {
- StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
- buffer.append(format(d[i], digits)).append(sep);
- }
- else {
- buffer.append(format(d[i], digits));
- }
- }
- return buffer.toString();
+ public static String format(double[] d, NumberFormat nf) {
+ return format(d, " ", nf);
}
/**
* Formats the double array d with the specified number format.
*
* @param d the double array to be formatted
+ * @param sep the separator between the single values of the double array,
+ * e.g. ','
* @param nf the number format to be used for formatting
* @return a String representing the double array d
*/
- public static String format(double[] d, NumberFormat nf) {
- return format(d, " ", nf);
+ public static String format(double[] d, String sep, NumberFormat nf) {
+ if(d.length == 0) {
+ return "";
+ }
+ return formatTo(new StringBuilder(), d, sep, nf).toString();
}
/**
@@ -218,42 +209,49 @@ public final class FormatUtil {
* @param d the double array to be formatted
* @param sep the separator between the single values of the double array,
* e.g. ','
- * @param nf the number format to be used for formatting
* @return a String representing the double array d
*/
- public static String format(double[] d, String sep, NumberFormat nf) {
- StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
- buffer.append(format(d[i], nf)).append(sep);
- }
- else {
- buffer.append(format(d[i], nf));
- }
+ public static StringBuilder formatTo(StringBuilder a, double[] d, String sep) {
+ if(d.length == 0) {
+ return a;
}
- return buffer.toString();
+ a.append(d[0]);
+ for(int i = 1; i < d.length; i++) {
+ a.append(sep);
+ a.append(d[i]);
+ }
+ return a;
}
/**
- * Formats the double array d with ',' as separator and 2 fraction digits.
+ * Formats the double array d with the specified number format.
*
* @param d the double array to be formatted
+ * @param sep the separator between the single values of the double array,
+ * e.g. ','
+ * @param nf the number format to be used for formatting
* @return a String representing the double array d
*/
- public static String format(double[] d) {
- return format(d, ", ", 2);
+ public static StringBuilder formatTo(StringBuilder a, double[] d, String sep, NumberFormat nf) {
+ if(d.length == 0) {
+ return a;
+ }
+ a.append(nf.format(d[0]));
+ for(int i = 1; i < d.length; i++) {
+ a.append(sep);
+ a.append(nf.format(d[i]));
+ }
+ return a;
}
/**
- * Formats the double array d with ', ' as separator and with the specified
- * fraction digits.
+ * Formats the double array d with ', ' as separator and 2 fraction digits.
*
* @param d the double array to be formatted
- * @param digits the number of fraction digits
* @return a String representing the double array d
*/
- public static String format(double[] d, int digits) {
- return format(d, ", ", digits);
+ public static String format(double[] d) {
+ return formatTo(new StringBuilder(), d, ", ").toString();
}
/**
@@ -263,11 +261,7 @@ public final class FormatUtil {
* @return a String representing the double array d
*/
public static String format(double[][] d) {
- StringBuilder buffer = new StringBuilder();
- for(double[] array : d) {
- buffer.append(format(array, ", ", 2)).append('\n');
- }
- return buffer.toString();
+ return format(d, "\n", ", ", NF2);
}
/**
@@ -277,110 +271,40 @@ public final class FormatUtil {
* @param d the double array to be formatted
* @param sep1 the first separator of the outer array
* @param sep2 the second separator of the inner array
- * @param digits the number of fraction digits
+ * @param nf the number format to use
* @return a String representing the double array d
*/
- public static String format(double[][] d, String sep1, String sep2, int digits) {
- StringBuilder buffer = new StringBuilder();
-
- for(int i = 0; i < d.length; i++) {
- if(i < d.length - 1) {
- buffer.append(format(d[i], sep2, digits)).append(sep1);
- }
- else {
- buffer.append(format(d[i], sep2, digits));
- }
+ public static String format(double[][] d, String sep1, String sep2, NumberFormat nf) {
+ if(d.length == 0) {
+ return "";
}
-
- return buffer.toString();
- }
-
- /**
- * Formats the Double array f with the specified separator and the specified
- * fraction digits.
- *
- * @param f the Double array to be formatted
- * @param sep the separator between the single values of the Double array,
- * e.g. ','
- * @param digits the number of fraction digits
- * @return a String representing the Double array f
- */
- public static String format(Double[] f, String sep, int digits) {
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
- buffer.append(format(f[i].doubleValue(), digits)).append(sep);
- }
- else {
- buffer.append(format(f[i].doubleValue(), digits));
- }
+ formatTo(buffer, d[0], sep2, nf);
+ for(int i = 1; i < d.length; i++) {
+ buffer.append(sep1);
+ formatTo(buffer, d[i], sep2, nf);
}
return buffer.toString();
}
/**
- * Formats the Double array f with ',' as separator and 2 fraction digits.
- *
- * @param f the Double array to be formatted
- * @return a String representing the Double array f
- */
- public static String format(Double[] f) {
- return format(f, ", ", 2);
- }
-
- /**
- * Formats the Double array f with the specified separator and the specified
- * fraction digits.
+ * Formats the double array d with the specified number format.
*
- * @param f the Double array to be formatted
- * @param sep the separator between the single values of the Double array,
+ * @param d the double array to be formatted
+ * @param sep the separator between the single values of the double array,
* e.g. ','
- * @param nf the number format
- * @return a String representing the Double array f
+ * @param nf the number format to be used for formatting
+ * @return a String representing the double array d
*/
- public static String format(Double[] f, String sep, NumberFormat nf) {
- StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
- buffer.append(format(f[i].doubleValue(), nf)).append(sep);
- }
- else {
- buffer.append(format(f[i].doubleValue(), nf));
- }
+ public static String format(float[] d, String sep, NumberFormat nf) {
+ if(d.length == 0) {
+ return "";
}
- return buffer.toString();
- }
-
- /**
- * Formats the Double array f with ',' as separator and 2 fraction digits.
- *
- * @param f the Double array to be formatted
- * @param nf the Number format
- * @return a String representing the Double array f
- */
- public static String format(Double[] f, NumberFormat nf) {
- return format(f, ", ", nf);
- }
-
- /**
- * Formats the float array f with the specified separator and the specified
- * fraction digits.
- *
- * @param f the float array to be formatted
- * @param sep the separator between the single values of the float array, e.g.
- * ','
- * @param digits the number of fraction digits
- * @return a String representing the float array f
- */
- public static String format(float[] f, String sep, int digits) {
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < f.length; i++) {
- if(i < f.length - 1) {
- buffer.append(format(f[i], digits)).append(sep);
- }
- else {
- buffer.append(format(f[i], digits));
- }
+ buffer.append(nf.format((double) d[0]));
+ for(int i = 1; i < d.length; i++) {
+ buffer.append(sep);
+ buffer.append(nf.format((double) d[i]));
}
return buffer.toString();
}
@@ -392,7 +316,7 @@ public final class FormatUtil {
* @return a String representing the float array f
*/
public static String format(float[] f) {
- return format(f, ", ", 2);
+ return format(f, ", ", NF2);
}
/**
@@ -404,14 +328,14 @@ public final class FormatUtil {
* @return a String representing the int array a
*/
public static String format(int[] a, String sep) {
+ if(a.length == 0) {
+ return "";
+ }
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
- buffer.append(a[i]).append(sep);
- }
- else {
- buffer.append(a[i]);
- }
+ buffer.append(a[0]);
+ for(int i = 1; i < a.length; i++) {
+ buffer.append(sep);
+ buffer.append(a[i]);
}
return buffer.toString();
}
@@ -427,51 +351,20 @@ public final class FormatUtil {
}
/**
- * Formats the Integer array a for printing purposes.
- *
- * @param a the Integer array to be formatted
- * @param sep the separator between the single values of the float array, e.g.
- * ','
- * @return a String representing the Integer array a
- */
- public static String format(Integer[] a, String sep) {
- StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
- buffer.append(a[i]).append(sep);
- }
- else {
- buffer.append(a[i]);
- }
- }
- return buffer.toString();
- }
-
- /**
- * Formats the Integer array a for printing purposes.
- *
- * @param a the Integer array to be formatted
- * @return a String representing the Integer array a
- */
- public static String format(Integer[] a) {
- return format(a, ", ");
- }
-
- /**
* Formats the long array a for printing purposes.
*
* @param a the long array to be formatted
* @return a String representing the long array a
*/
public static String format(long[] a) {
+ if(a.length == 0) {
+ return "";
+ }
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
- buffer.append(a[i]).append(", ");
- }
- else {
- buffer.append(a[i]);
- }
+ buffer.append(a[0]);
+ for(int i = 1; i < a.length; i++) {
+ buffer.append(", ");
+ buffer.append(a[i]);
}
return buffer.toString();
}
@@ -483,14 +376,14 @@ public final class FormatUtil {
* @return a String representing the byte array a
*/
public static String format(byte[] a) {
+ if(a.length == 0) {
+ return "";
+ }
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < a.length; i++) {
- if(i < a.length - 1) {
- buffer.append(a[i]).append(", ");
- }
- else {
- buffer.append(a[i]);
- }
+ buffer.append(a[0]);
+ for(int i = 1; i < a.length; i++) {
+ buffer.append(", ");
+ buffer.append(a[i]);
}
return buffer.toString();
}
@@ -504,14 +397,14 @@ public final class FormatUtil {
* @return a String representing the boolean array b
*/
public static String format(boolean[] b, final String sep) {
+ if(b.length == 0) {
+ return "";
+ }
StringBuilder buffer = new StringBuilder();
- for(int i = 0; i < b.length; i++) {
- if(i < b.length - 1) {
- buffer.append(format(b[i])).append(sep);
- }
- else {
- buffer.append(format(b[i]));
- }
+ buffer.append(format(b[0]));
+ for(int i = 1; i < b.length; i++) {
+ buffer.append(sep);
+ buffer.append(format(b[i]));
}
return buffer.toString();
}
@@ -523,10 +416,7 @@ public final class FormatUtil {
* @return a String representing of the boolean b
*/
public static String format(final boolean b) {
- if(b) {
- return "1";
- }
- return "0";
+ return b ? "1" : "0";
}
/**
@@ -539,19 +429,11 @@ public final class FormatUtil {
*/
public static String format(BitSet bitSet, int dim, String sep) {
StringBuilder msg = new StringBuilder();
-
- for(int d = 0; d < dim; d++) {
- if(d > 0) {
- msg.append(sep);
- }
- if(bitSet.get(d)) {
- msg.append('1');
- }
- else {
- msg.append('0');
- }
+ msg.append(bitSet.get(0) ? '1' : '0');
+ for(int d = 1; d < dim; d++) {
+ msg.append(sep);
+ msg.append(bitSet.get(d) ? '1' : '0');
}
-
return msg.toString();
}
@@ -583,13 +465,11 @@ public final class FormatUtil {
return d.iterator().next();
}
StringBuilder buffer = new StringBuilder();
- boolean first = true;
- for(String str : d) {
- if(!first) {
- buffer.append(sep);
- }
- buffer.append(str);
- first = false;
+ Iterator<String> it = d.iterator();
+ buffer.append(it.next());
+ while(it.hasNext()) {
+ buffer.append(sep);
+ buffer.append(it.next());
}
return buffer.toString();
}
@@ -745,7 +625,11 @@ public final class FormatUtil {
* given NumberFormat
*/
public static String format(Vector m, NumberFormat nf) {
- return "[" + FormatUtil.format(m.getArrayRef(), nf) + "]";
+ StringBuilder buf = new StringBuilder();
+ buf.append('[');
+ formatTo(buf, m.getArrayRef(), ", ", nf);
+ buf.append(']');
+ return buf.toString();
}
/**
@@ -754,7 +638,7 @@ public final class FormatUtil {
* @return String representation of this Vector
*/
public static String format(Vector m) {
- return format(m, FormatUtil.NF);
+ return format(m.getArrayRef());
}
/**
@@ -1025,6 +909,18 @@ public final class FormatUtil {
/**
* Preallocated exceptions.
*/
+ private static final NumberFormatException EMPTY_STRING = new NumberFormatException("Parser called on an empty string.") {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+ };
+
+ /**
+ * Preallocated exceptions.
+ */
private static final NumberFormatException EXPONENT_OVERFLOW = new NumberFormatException("Precision overflow for double exponent.") {
private static final long serialVersionUID = 1L;
@@ -1111,6 +1007,9 @@ public final class FormatUtil {
* @return Double value
*/
public static double parseDouble(final CharSequence str, final int start, final int end) {
+ if(start >= end) {
+ throw EMPTY_STRING;
+ }
// Current position and character.
int pos = start;
char cur = str.charAt(pos);
@@ -1165,7 +1064,7 @@ public final class FormatUtil {
// Reads exponent.
int exp = 0;
- if((pos < end) && ((cur == 'E') || (cur == 'e'))) {
+ if((pos + 1 < end) && ((cur == 'E') || (cur == 'e'))) {
cur = str.charAt(++pos);
final boolean isNegativeExp = (cur == '-');
if((isNegativeExp || (cur == '+')) && (++pos < end)) {
@@ -1211,6 +1110,122 @@ public final class FormatUtil {
}
/**
+ * Parse a double from a character sequence.
+ *
+ * In contrast to Javas {@link Double#parseDouble}, this will <em>not</em>
+ * create an object and thus is expected to put less load on the garbage
+ * collector. It will accept some more spellings of NaN and infinity, thus
+ * removing the need for checking for these independently.
+ *
+ * @param str String
+ * @param start Begin
+ * @param end End
+ * @return Double value
+ */
+ public static double parseDouble(final byte[] str, final int start, final int end) {
+ if(start >= end) {
+ throw EMPTY_STRING;
+ }
+ // Current position and character.
+ int pos = start;
+ byte cur = str[pos];
+
+ // Match for NaN spellings
+ if(matchNaN(str, cur, pos, end)) {
+ return Double.NaN;
+ }
+ // Match sign
+ boolean isNegative = (cur == '-');
+ // Carefully consume the - character, update c and i:
+ if((isNegative || (cur == '+')) && (++pos < end)) {
+ cur = str[pos];
+ }
+ if(matchInf(str, cur, pos, end)) {
+ return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+
+ // Begin parsing real numbers!
+ if(((cur < '0') || (cur > '9')) && (cur != '.')) {
+ throw NOT_A_NUMBER;
+ }
+
+ // Parse digits into a long, remember offset of decimal point.
+ long decimal = 0;
+ int decimalPoint = -1;
+ while(true) {
+ final int digit = cur - '0';
+ if((digit >= 0) && (digit <= 9)) {
+ final long tmp = (decimal << 3) + (decimal << 1) + digit;
+ if((decimal > MAX_LONG_OVERFLOW) || (tmp < decimal)) {
+ throw PRECISION_OVERFLOW;
+ }
+ decimal = tmp;
+ }
+ else if((cur == '.') && (decimalPoint < 0)) {
+ decimalPoint = pos;
+ }
+ else { // No more digits, or a second dot.
+ break;
+ }
+ if(++pos < end) {
+ cur = str[pos];
+ }
+ else {
+ break;
+ }
+ }
+ // We need the offset from the back for adjusting the exponent:
+ // Note that we need the current value of i!
+ decimalPoint = (decimalPoint >= 0) ? pos - decimalPoint - 1 : 0;
+
+ // Reads exponent.
+ int exp = 0;
+ if((pos + 1 < end) && ((cur == 'E') || (cur == 'e'))) {
+ cur = str[++pos];
+ final boolean isNegativeExp = (cur == '-');
+ if((isNegativeExp || (cur == '+')) && (++pos < end)) {
+ cur = str[pos];
+ }
+ if((cur < '0') || (cur > '9')) { // At least one digit required.
+ throw INVALID_EXPONENT;
+ }
+ while(true) {
+ final int digit = cur - '0';
+ if((digit >= 0) && (digit < 10)) {
+ final int tmp = (exp << 3) + (exp << 1) + digit;
+ // Actually, double can only handle Double.MAX_EXPONENT? How about
+ // subnormal?
+ if((exp > MAX_INT_OVERFLOW) || (tmp < exp)) {
+ throw EXPONENT_OVERFLOW;
+ }
+ exp = tmp;
+ }
+ else {
+ break;
+ }
+ if(++pos < end) {
+ cur = str[pos];
+ }
+ else {
+ break;
+ }
+ }
+ if(isNegativeExp) {
+ exp = -exp;
+ }
+ }
+ // Adjust exponent by the offset of the dot in our long.
+ if(decimalPoint >= 0) {
+ exp = exp - decimalPoint;
+ }
+ if(pos != end) {
+ throw TRAILING_CHARACTERS;
+ }
+
+ return BitsUtil.lpow10(isNegative ? -decimal : decimal, exp);
+ }
+
+ /**
* Match "NaN" in a number of different capitalizations.
*
* @param str String to match
@@ -1243,6 +1258,38 @@ public final class FormatUtil {
}
/**
+ * Match "NaN" in a number of different capitalizations.
+ *
+ * @param str String to match
+ * @param firstchar First character
+ * @param start Interval begin
+ * @param end Interval end
+ * @return {@code true} when NaN was recognized.
+ */
+ private static boolean matchNaN(byte[] str, byte firstchar, int start, int end) {
+ final int len = end - start;
+ if(len < 2 || len > 3) {
+ return false;
+ }
+ if(firstchar != 'N' && firstchar != 'n') {
+ return false;
+ }
+ final byte c1 = str[start + 1];
+ if(c1 != 'a' && c1 != 'A') {
+ return false;
+ }
+ // Accept just "NA", too:
+ if(len == 2) {
+ return true;
+ }
+ final byte c2 = str[start + 2];
+ if(c2 != 'N' && c2 != 'n') {
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Maximum long that we can process without overflowing.
*/
private static final long MAX_LONG_OVERFLOW = Long.MAX_VALUE / 10;
@@ -1297,6 +1344,40 @@ public final class FormatUtil {
}
/**
+ * Match "inf", "infinity" in a number of different capitalizations.
+ *
+ * @param str String to match
+ * @param firstchar First character
+ * @param start Interval begin
+ * @param end Interval end
+ * @return {@code true} when infinity was recognized.
+ */
+ private static boolean matchInf(byte[] str, byte firstchar, int start, int end) {
+ final int len = end - start;
+ // The wonders of unicode. This is more than one byte on UTF-8
+ if(len == 1 && firstchar == '∞') {
+ return true;
+ }
+ if(len != 3 && len != INFINITY_LENGTH) {
+ return false;
+ }
+ // Test beginning: "inf"
+ if(firstchar != 'I' && firstchar != 'i') {
+ return false;
+ }
+ for(int i = 1, j = INFINITY_LENGTH + 1; i < INFINITY_LENGTH; i++, j++) {
+ final byte c = str[start + i];
+ if(c != INFINITY_PATTERN[i] && c != INFINITY_PATTERN[j]) {
+ return false;
+ }
+ if(i == 2 && len == 3) {
+ return true;
+ }
+ }
+ return true;
+ }
+
+ /**
* Parse a long integer from a character sequence.
*
* @param str String
@@ -1348,4 +1429,143 @@ public final class FormatUtil {
return isNegative ? -decimal : decimal;
}
+
+ /**
+ * Format a boolean value as string "true" or "false".
+ *
+ * @param b Boolean to Format
+ * @param buf Buffer to append to
+ * @return Same buffer
+ */
+ public static StringBuilder format(boolean b, StringBuilder buf) {
+ return buf.append(b ? "true" : "false");
+ }
+
+ /**
+ * Format a boolean value as string "1" or "0".
+ *
+ * @param b Boolean to Format
+ * @param buf Buffer to append to
+ * @return Same buffer
+ */
+ public static StringBuilder formatBit(boolean b, StringBuilder buf) {
+ return buf.append(b ? '1' : '0');
+ }
+
+ /**
+ * Format an integer value as decimal.
+ *
+ * @param i Integer value to format.
+ * @param buf Buffer to append to
+ * @return Same buffer
+ */
+ public static StringBuilder format(int i, StringBuilder buf) {
+ // Int seems to be well optimized
+ return buf.append(i);
+ }
+
+ /**
+ * Format a long value as decimal.
+ *
+ * @param i Long value to format.
+ * @param buf Buffer to append to
+ * @return Same buffer
+ */
+ public static StringBuilder format(long i, StringBuilder buf) {
+ // Long seems to be well optimized
+ return buf.append(i);
+ }
+
+ /**
+ * Buffer for zero padding.
+ */
+ private static final char[] ZEROPADDING = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' };
+
+ /**
+ * Buffer for whitespace padding.
+ */
+ private static final char[] SPACEPADDING = new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
+
+ /**
+ * Append zeros to a buffer.
+ *
+ * @param buf Buffer to append to
+ * @param zeros Number of zeros to append.
+ * @return Buffer
+ */
+ public static StringBuilder appendZeros(StringBuilder buf, int zeros) {
+ for(int i = zeros; i > 0; i -= ZEROPADDING.length) {
+ buf.append(ZEROPADDING, 0, i < ZEROPADDING.length ? i : ZEROPADDING.length);
+ }
+ return buf;
+ }
+
+ /**
+ * Append whitespace to a buffer.
+ *
+ * @param buf Buffer to append to
+ * @param spaces Number of spaces to append.
+ * @return Buffer
+ */
+ public static StringBuilder appendSpace(StringBuilder buf, int spaces) {
+ for(int i = spaces; i > 0; i -= SPACEPADDING.length) {
+ buf.append(SPACEPADDING, 0, i < SPACEPADDING.length ? i : SPACEPADDING.length);
+ }
+ return buf;
+ }
+
+ /**
+ * Compute the number of characters needed to represent the integer x.
+ *
+ * Reimplementation of {@link Long#stringSize}, but public and without loop.
+ *
+ * @param x Integer value
+ * @return Number of digits needed
+ */
+ public static int stringSize(int x) {
+ if(x < 0) {
+ // Avoid overflow on extreme negative
+ return (x == Integer.MIN_VALUE) ? 11 : stringSize(-x) + 1;
+ }
+ // This is almost a binary search - 10 cases is not a power of two, and we
+ // assume that the smaller values are more frequent.
+ return //
+ (x < 10000) ? // 1-4 vs. 5-10
+ /**/(x < 100) ? // 1-2 vs. 3-4
+ /* */((x < 10) ? 1 : 2) : //
+ /* */((x < 1000) ? 3 : 4) : //
+ /**/(x < 1000000) ? // 5-6 vs. 7-10
+ /* */((x < 100000) ? 5 : 6) : // 5-6
+ /* */(x < 100000000) ? // 7-8 vs. 9-10
+ /* */((x < 10000000) ? 7 : 8) : // 7-8
+ /* */((x < 1000000000) ? 9 : 10) // 9-10
+ ;
+ }
+
+ /**
+ * Compute the number of characters needed to represent the integer x.
+ *
+ * Reimplementation of {@link Long#stringSize}, but public and without loop.
+ *
+ * @param x Integer value
+ * @return Number of digits needed
+ */
+ public static int stringSize(long x) {
+ if(x < 0) {
+ // Avoid overflow on extreme negative
+ return (x == Long.MIN_VALUE) ? 20 : stringSize(-x) + 1;
+ }
+ // This is almost a binary search.
+ return (x <= Integer.MAX_VALUE) ? stringSize((int) x) : //
+ (x < 10000000000000L) ? // 10-13 vs. 14-19
+ /**/(x < 100000000000L) ? // 10-11 vs. 12-13
+ /* */((x < 10000000000L) ? 10 : 11) : //
+ /* */((x < 1000000000000L) ? 12 : 13) : //
+ /**/(x < 1000000000000000L) ? // 14-15 vs. 16-19
+ /* */((x < 100000000000000L) ? 14 : 15) : // 14-15
+ /* */(x < 100000000000000000L) ? // 16-17 vs. 18-19
+ /* */((x < 10000000000000000L) ? 16 : 17) : // 16-17
+ /* */((x < 1000000000000000000L) ? 18 : 19) // 18-19
+ ;
+ }
}