/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.eas.client.forms.components.rt; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.Format; import java.text.NumberFormat; import java.text.ParseException; import javax.swing.text.NumberFormatter; /** * Number formatter descandant class intended to solve "eaten decimal separator" and some other problems. * This problem occurs with allowsInvalid flag setted to false. * The problem is: when you type in a control string like this: "412,", it transforms into "412". * Thus, you are unable to type integer part and than fraction part of a number digit by digit. * In this situation user is forced to type all digits from both parts of a number and than choose * where decimal separator is to be placed. It's ugly and terrible for any user. * There are two other approachs to solve this problem. The first is to set mask like this: "#.0##". * Second is to call setDecimalSeparatorAlwaysShown(true) on the decimal format. * These approachs lead to decimal separator be allways shown. It's strongly undesireable! * I whant the control to input all digits, i've type in it, if they are all valid and in the order of my input! * So, this class allows such behavior to be applied. * @author mg */ public class AdaptiveDecimalFormatter extends NumberFormatter { private static DummyMinusValue DUMMY_MINUS = new DummyMinusValue(); public AdaptiveDecimalFormatter() { super(); } public AdaptiveDecimalFormatter(NumberFormat aFormat) { super(aFormat); } @Override public Object stringToValue(String aText) throws ParseException { if (aText == null || aText.isEmpty()) { return null; } else { Format f = getFormat(); if (f instanceof DecimalFormat) { DecimalFormat df = (DecimalFormat) f; if (getFormattedTextField().hasFocus()) { DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); String userString = prepareText(aText, dfs); if (!userString.isEmpty()) { int sepIndex = userString.indexOf(dfs.getDecimalSeparator()); if (sepIndex == -1) { sepIndex = userString.indexOf(dfs.getMonetaryDecimalSeparator()); } int intCount = 0; int fracCount = 0; if (sepIndex != -1) { intCount = sepIndex; fracCount = userString.length() - (sepIndex + 1); int realIntCount = ajustIntPart(intCount, userString, sepIndex); df.setDecimalSeparatorAlwaysShown(true); df.setMaximumIntegerDigits(Math.max(realIntCount, df.getMaximumIntegerDigits())); df.setMinimumIntegerDigits(realIntCount); df.setMaximumFractionDigits(Math.max(fracCount, df.getMaximumFractionDigits())); df.setMinimumFractionDigits(fracCount); } else { df.setDecimalSeparatorAlwaysShown(false); String userMeaningString = userString.replaceFirst("0+", ""); if (userString.isEmpty()) { df.setMinimumIntegerDigits(0); } else { if (userMeaningString.length() < userString.length()) { df.setMinimumIntegerDigits(userMeaningString.length() + 1); } else { df.setMinimumIntegerDigits(0); } } df.setMinimumFractionDigits(0); } } else { if (aText.length() == 1 && aText.toCharArray()[0] == dfs.getMinusSign()) { return DUMMY_MINUS; } } } else { df.setDecimalSeparatorAlwaysShown(false); df.setMinimumIntegerDigits(1); df.setMinimumFractionDigits(0); } } return super.stringToValue(aText); } } protected int ajustIntPart(int intCount, String userString, int sepIndex) { int realIntCount = intCount; String userIntString = userString.substring(0, sepIndex); String userIntMeaningString = userIntString.replaceFirst("0+", ""); if (userIntString.isEmpty()) { realIntCount = intCount; } else { if (userIntMeaningString.length() < userIntString.length()) { realIntCount = userIntMeaningString.length() + 1; } else { realIntCount = intCount; } } return realIntCount; } protected String prepareText(String aText, DecimalFormatSymbols dfs) { String userString = aText.replaceAll(dfs.getCurrencySymbol(), ""); userString = userString.replaceAll(dfs.getInternationalCurrencySymbol(), ""); userString = userString.replaceAll(dfs.getExponentSeparator(), ""); userString = userString.replaceAll(dfs.getInfinity(), ""); userString = userString.replaceAll(dfs.getNaN(), ""); userString = userString.replaceAll(new String(new char[]{dfs.getGroupingSeparator()}), ""); userString = userString.replaceAll(new String(new char[]{dfs.getMinusSign()}), ""); userString = userString.replaceAll(new String(new char[]{dfs.getPerMill()}), ""); userString = userString.replaceAll(new String(new char[]{dfs.getPercent()}), ""); return userString; } @Override public String valueToString(Object aValue) throws ParseException { if (aValue == null) { return ""; } else { if (aValue == DUMMY_MINUS) { return "-"; } else { return super.valueToString(aValue); } } } }