/*******************************************************************************
*
* Copyright 2010 Alexandru Craciun, and individual contributors as indicated
* by the @authors tag.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
******************************************************************************/
package org.netxilia.api.impl.value;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.Currency;
import java.util.Locale;
import org.netxilia.api.value.IGenericValue;
import org.netxilia.api.value.IGenericValueParser;
import org.netxilia.api.value.NumberValue;
import org.springframework.beans.factory.InitializingBean;
/**
* Taken from Stripes number converter
*
* @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
*
*/
public class NumberParser implements IGenericValueParser, InitializingBean {
private Locale locale;
private NumberFormat[] formats;
private String currencySymbol;
private String localeId;
/** Returns the Locale set on the object using setLocale(). */
public Locale getLocale() {
return locale;
}
public String getLocaleId() {
return localeId;
}
public void setLocaleId(String localeId) {
this.localeId = localeId;
}
/**
* Fetches one or more NumberFormat instances that can be used to parse numbers for the current locale. The default
* implementation returns two instances, one regular NumberFormat and a currency instance of NumberFormat.
*
* @return one or more NumberFormats to use in parsing numbers
*/
protected NumberFormat[] getNumberFormats() {
return new NumberFormat[] { NumberFormat.getInstance(this.locale) };
}
@Override
public IGenericValue parse(String text) {
String input = preprocess(text);
ParsePosition pp = new ParsePosition(0);
for (NumberFormat format : this.formats) {
pp.setIndex(0);
Number number = format.parse(input, pp);
if (number != null && input.length() == pp.getIndex()) {
return new NumberValue(number.doubleValue());
}
}
return null;
}
/**
* Pre-processes the String to give the NumberFormats a better shot at parsing the input. The default implementation
* trims the String for whitespace and then looks to see if the number is surrounded by parentheses, e.g. (800), and
* if so removes the parentheses and prepends a minus sign. Lastly it will remove the currency symbol from the
* String so that we don't have to use too many NumberFormats!
*
* @param input
* the String as input by the user
* @return the result of preprocessing the String
*/
protected String preprocess(String input) {
// Step 1: trim whitespace
String output = input.trim();
// Step 2: remove the currency symbol
// The casts are to make sure we don't call replace(String regex, String replacement)
output = output.replace(currencySymbol, "");
// Step 3: replace parentheses with negation
if (output.startsWith("(") && output.endsWith(")")) {
output = "-" + output.substring(1, output.length() - 1);
}
return output;
}
@Override
public void afterPropertiesSet() throws Exception {
this.locale = localeId != null ? new Locale(localeId) : Locale.getDefault();
this.formats = getNumberFormats();
// Use the appropriate currency symbol if our locale has a country, otherwise try the dollar sign!
if (locale.getCountry() != null && !"".equals(locale.getCountry())) {
this.currencySymbol = Currency.getInstance(locale).getSymbol(locale);
} else {
this.currencySymbol = "$";
}
}
}