1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
package tim.prune.data;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
/**
* Abstract class to offer general number manipulation functions
*/
public abstract class NumberUtils
{
/** Locale-specific number formatter */
private static final NumberFormat LOCAL_FORMAT = NumberFormat.getNumberInstance();
/** UK-specific number formatter object to avoid lots of instantiations */
private static final NumberFormat UK_FORMAT = NumberFormat.getNumberInstance(Locale.UK);
// Select the UK locale for this formatter so that decimal point is always used (not comma)
static {
if (UK_FORMAT instanceof DecimalFormat) ((DecimalFormat) UK_FORMAT).applyPattern("0.000");
}
/**
* Format the given number in UK format (decimal point) to the given number of decimal places
* @param inNumber double number to format
* @param inDecimalPlaces number of decimal places
*/
public static String formatNumberUk(double inNumber, int inDecimalPlaces)
{
UK_FORMAT.setMaximumFractionDigits(inDecimalPlaces);
UK_FORMAT.setMinimumFractionDigits(inDecimalPlaces);
UK_FORMAT.setRoundingMode(RoundingMode.HALF_UP);
return UK_FORMAT.format(inNumber);
}
/**
* Format the given number in the local format to the given number of decimal places
* @param inNumber double number to format
* @param inDecimalPlaces number of decimal places
*/
public static String formatNumberLocal(double inNumber, int inDecimalPlaces)
{
final int numDecimals = (inDecimalPlaces < 0 ? 3 : inDecimalPlaces);
LOCAL_FORMAT.setMaximumFractionDigits(numDecimals);
LOCAL_FORMAT.setMinimumFractionDigits(numDecimals);
LOCAL_FORMAT.setRoundingMode(RoundingMode.HALF_UP);
return LOCAL_FORMAT.format(inNumber);
}
/**
* @return the number of decimal places in the given string
* or -1 if the string doesn't represent a number
*/
public static int numberOfDecimalPlaces(String inString)
{
String value = (inString == null ? "" : inString.trim());
int cPos = value.length() - 1;
int digits = 0;
boolean hasAnyDigits = false;
while (cPos >= 0)
{
char c = value.charAt(cPos);
if (!Character.isDigit(c))
{
if (c == '.' || c == ',') {
return (hasAnyDigits || digits == 0) ? digits : -1;
}
return hasAnyDigits ? 0 : -1;
}
hasAnyDigits = true;
cPos--;
digits++;
}
return hasAnyDigits ? 0 : -1;
}
/**
* Format the given number in the local format to match the number of decimal places
* @param inNumber double number to format
* @param inPattern example string showing decimals
*/
public static String formatNumberLocalToMatch(double inNumber, String inPattern) {
return formatNumberLocal(inNumber, numberOfDecimalPlaces(inPattern));
}
/**
* @param inValue string value, can be null or empty
* @return value extracted using either java formatting or locale formatting
*/
static Double parseDoubleUsingLocale(String inValue)
{
final String trimmedValue = (inValue == null ? "" : inValue.trim());
if (trimmedValue.isEmpty()) {
return null;
}
// We have some contents, so try parsing using the regular java formatting (decimal dot)
try {
return Double.parseDouble(trimmedValue);
}
catch (NumberFormatException ignored) {
}
// That threw an exception, so try with the system's locale instead (for example, using decimal comma)
if (isLocalDecimal(inValue))
{
try
{
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
return nf.parse(trimmedValue).doubleValue();
}
catch (ParseException ignored) {
}
}
// Neither worked
return null;
}
/** @return true if the given string looks like a local decimal value */
private static boolean isLocalDecimal(String inValue)
{
if (inValue == null || inValue.isEmpty()) {
return false;
}
String allowedChars = "-0123456789";
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
if (nf instanceof DecimalFormat) {
allowedChars += ((DecimalFormat) nf).getDecimalFormatSymbols().getDecimalSeparator();
}
for (int i=0; i<inValue.length(); i++)
{
if (allowedChars.indexOf(inValue.charAt(i)) < 0) {
return false; // another character found
}
}
// only contains allowed characters
return true;
}
/** @return String to use between numbers, depending on the decimal character */
public static String getValueSeparator()
{
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
if (nf instanceof DecimalFormat)
{
char decimalChar = ((DecimalFormat) nf).getDecimalFormatSymbols().getDecimalSeparator();
if (decimalChar == ',') {
return "; ";
}
}
return ", ";
}
public static int getIntOrZero(String inString)
{
try {
return Integer.parseInt(inString);
}
catch (NumberFormatException ignored) {
return 0;
}
}
public static double getDoubleOrZero(String inString)
{
try {
return Double.parseDouble(inString);
}
catch (NumberFormatException | NullPointerException ignored) {
return 0.0;
}
}
}
|