package net.sf.openrocket.util;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class TextUtil {
private static final char[] HEX = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/**
* Return the byte array for the string (in US-ASCII charset).
*
* This function is implemented because Froyo (Android API 8) does not support
* String.getBytes(Charset)
*/
public static byte[] asciiBytes(String string) {
ByteBuffer encoded = Charset.forName("US-ASCII").encode(string);
return encoded.array();
}
/**
* Return the bytes formatted as a hexadecimal string. The length of the
* string will be twice the number of bytes, with no spacing between the bytes
* and lowercase letters utilized.
*
* @param bytes the bytes to convert.
* @return the bytes in hexadecimal notation.
*/
public static final String hexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(HEX[(b >>> 4) & 0xF]);
sb.append(HEX[b & 0xF]);
}
return sb.toString();
}
/**
* Return a string of the double value with suitable precision for storage.
* The string is the shortest representation of the value including at least
* 5 digits of precision.
*
* @param d the value to present.
* @return a representation with suitable precision.
*/
public static final String doubleToString(double d) {
// Check for special cases
if (MathUtil.equals(d, 0))
return "0";
if (Double.isNaN(d))
return "NaN";
if (Double.isInfinite(d)) {
if (d < 0)
return "-Inf";
else
return "Inf";
}
final String sign = (d < 0) ? "-" : "";
double abs = Math.abs(d);
// Small and large values always in exponential notation
if (abs < 0.001 || abs >= 100000000) {
return sign + exponentialFormat(abs);
}
// Check whether decimal or exponential notation is shorter
String exp = exponentialFormat(abs);
String dec = decimalFormat(abs);
if (dec.length() <= exp.length())
return sign + dec;
else
return sign + exp;
}
/*
* value must be positive and not zero!
*/
private static String exponentialFormat(double value) {
int exp;
exp = 0;
while (value < 1.0) {
value *= 10;
exp--;
}
while (value >= 10.0) {
value /= 10;
exp++;
}
return shortDecimal(value, 4) + "e" + exp;
}
/*
* value must be positive and not zero!
*/
private static String decimalFormat(double value) {
if (value >= 10000)
return "" + (int) (value + 0.5);
int decimals = 1;
double v = value;
while (v < 1000) {
v *= 10;
decimals++;
}
return shortDecimal(value, decimals);
}
/*
* value must be positive!
*/
private static String shortDecimal(double value, int decimals) {
// Calculate rounding and limit values (rounding slightly smaller)
int rounding = 1;
double limit = 0.5;
for (int i = 0; i < decimals; i++) {
rounding *= 10;
limit /= 10;
}
// Round value
value = (Math.rint(value * rounding) + 0.1) / rounding;
int whole = (int) value;
value -= whole;
if (value < limit)
return "" + whole;
limit *= 10;
StringBuilder sb = new StringBuilder();
sb.append("" + whole);
sb.append('.');
for (int i = 0; i < decimals; i++) {
value *= 10;
whole = (int) value;
value -= whole;
sb.append((char) ('0' + whole));
if (value < limit)
return sb.toString();
limit *= 10;
}
return sb.toString();
}
/**
* Escape a string as XML or HTML. Encodes the following characters:
* <ul>
* <li>less than, greater than
* <li>quotation mark, apostrophe
* <li>ampersand
* <li>all control characters except newline, carriage return and tab
* </ul>
*
* The result is both valid XML and HTML 2.0. The majority of characters are left unchanged.
*/
public static String escapeXML(String s) {
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '&') {
sb.append("&");
} else if (c == '<') {
sb.append("<");
} else if (c == '>') {
sb.append(">");
} else if (c == '"') {
sb.append(""");
} else if (((c < 32) && (c != '\t') && (c != '\n') && (c != '\r')) || (c == '\'') || (c == 127)) {
// ' is not used since it's not standard HTML, use numerical escape instead
sb.append("").append((int) c).append(';');
} else {
sb.append(c);
}
}
return sb.toString();
}
}