package org.andork.format; import java.text.CharacterIterator; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.function.IntSupplier; import java.util.function.ToDoubleFunction; import org.andork.func.CharSupplier; public class DoubleParser implements ToDoubleFunction<CharacterIterator> { private static CharSupplier createDefaultDecimalSeparator() { DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(); final char separator = format.getDecimalFormatSymbols().getDecimalSeparator(); return () -> separator; } private static CharSupplier createDefaultGroupingSeparator() { DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(); final char separator = format.getDecimalFormatSymbols().getGroupingSeparator(); return () -> separator; } private static IntSupplier createDefaultGroupSize() { DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(); final int groupSize = format.getGroupingSize(); return () -> groupSize; } CharSupplier groupingSeparator; CharSupplier decimalSeparator; IntSupplier groupSize; IntSupplier lastGroupSize; public DoubleParser() { this(createDefaultGroupingSeparator(), createDefaultDecimalSeparator(), createDefaultGroupSize(), createDefaultGroupSize()); } public DoubleParser(CharSupplier groupingSeparator, CharSupplier decimalSeparator, IntSupplier groupSize, IntSupplier lastGroupSize) { super(); this.groupingSeparator = groupingSeparator; this.decimalSeparator = decimalSeparator; this.groupSize = groupSize; this.lastGroupSize = lastGroupSize; } @Override public double applyAsDouble(CharacterIterator i) { char groupingSeparator = this.groupingSeparator.getAsChar(); char decimalSeparator = this.decimalSeparator.getAsChar(); int groupSize = this.groupSize.getAsInt(); int lastGroupSize = this.lastGroupSize.getAsInt(); while (i.current() != CharacterIterator.DONE && Character.isWhitespace(i.current())) { i.next(); } char c = i.current(); double result; boolean grouped = false; int currentGroupSize = 0; if (Character.isDigit(c)) { result = 0.0; while (c != CharacterIterator.DONE) { if (Character.isDigit(c)) { result = 10.0 * result + (c - '0'); c = i.next(); currentGroupSize++; } else if (c == groupingSeparator) { if (currentGroupSize > groupSize || grouped && currentGroupSize != groupSize) { throw new IllegalArgumentException("Invalid digit grouping"); } grouped = true; currentGroupSize = 0; c = i.next(); } else { break; } } } else { result = Double.NaN; if (c != decimalSeparator) { return result; } } if (grouped && currentGroupSize != lastGroupSize) { throw new IllegalArgumentException("Invalid digit grouping"); } if (c == decimalSeparator) { c = i.next(); if (!Character.isDigit(c)) { return result; } else if (Double.isNaN(result)) { result = 0.0; } double divisor = 1.0; while (c != CharacterIterator.DONE) { if (Character.isDigit(c)) { result = 10.0 * result + (c - '0'); c = i.next(); divisor *= 10.0; } else { break; } } result /= divisor; } return result; } }