001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.woody.datatype.convertor;
018:
019: import java.util.Locale;
020: import java.text.DecimalFormat;
021: import java.text.NumberFormat;
022: import java.text.ParseException;
023: import java.math.BigDecimal;
024: import java.math.BigInteger;
025:
026: /**
027: * A Convertor for {@link BigDecimal}s backed by the
028: * {@link java.text.DecimalFormat DecimalFormat} class.
029: *
030: * <p>It can be configured to use one of these variants: integer,
031: * number, currency or percent.
032: *
033: * <p>Alternatively, a <strong>formatting pattern</strong> can be used. This can either be a locale-dependent
034: * or locale-independent formatting pattern. When looking up a formatting pattern, a mechansim
035: * similar to resource bundle lookup is used. Suppose the locale is nl-BE, then first a formatting
036: * pattern for nl-BE will be sought, then one for nl, and if that is not
037: * found, finally the locale-independent formatting pattern will be used.
038: *
039: * <p>Note: the earlier statement about the fact that this class uses java.text.DecimalFormat
040: * is not entirely correct. In fact, it uses a small wrapper class that will either delegate to
041: * java.text.DecimalFormat or com.ibm.icu.text.DecimalFormat. The com.ibm version will automatically
042: * be used if it is present on the classpath, otherwise the java.text version will be used.
043: *
044: * @version CVS $Id: FormattingDecimalConvertor.java 503643 2007-02-05 11:27:11Z cziegeler $
045: */
046: public class FormattingDecimalConvertor implements Convertor {
047: private int variant;
048: /** Locale-specific formatting patterns. */
049: private LocaleMap localizedPatterns;
050: /** Non-locale specific formatting pattern. */
051: private String nonLocalizedPattern;
052:
053: public static final int INTEGER = 0;
054: public static final int NUMBER = 1;
055: public static final int CURRENCY = 2;
056: public static final int PERCENT = 3;
057:
058: public FormattingDecimalConvertor() {
059: this .variant = getDefaultVariant();
060: this .localizedPatterns = new LocaleMap();
061: }
062:
063: protected int getDefaultVariant() {
064: return NUMBER;
065: }
066:
067: public Object convertFromString(String value, Locale locale,
068: Convertor.FormatCache formatCache) {
069: DecimalFormat decimalFormat = getDecimalFormat(locale,
070: formatCache);
071: Number decimalValue;
072: try {
073: decimalValue = decimalFormat.parse(value);
074: } catch (ParseException e) {
075: return null;
076: }
077:
078: if (decimalValue instanceof BigDecimal) {
079: // no need for conversion
080: } else if (decimalValue instanceof Integer) {
081: decimalValue = new BigDecimal(decimalValue.intValue());
082: } else if (decimalValue instanceof Long) {
083: decimalValue = new BigDecimal(decimalValue.longValue());
084: } else if (decimalValue instanceof Double) {
085: decimalValue = new BigDecimal(decimalValue.doubleValue());
086: } else if (decimalValue instanceof BigInteger) {
087: decimalValue = new BigDecimal((BigInteger) decimalValue);
088: } else {
089: return null;
090: }
091:
092: return decimalValue;
093: }
094:
095: public String convertToString(Object value, Locale locale,
096: Convertor.FormatCache formatCache) {
097: DecimalFormat decimalFormat = getDecimalFormat(locale,
098: formatCache);
099: return decimalFormat.format(value);
100: }
101:
102: protected final DecimalFormat getDecimalFormat(Locale locale,
103: Convertor.FormatCache formatCache) {
104: DecimalFormat decimalFormat = null;
105: if (formatCache != null)
106: decimalFormat = (DecimalFormat) formatCache.get();
107: if (decimalFormat == null) {
108: decimalFormat = getDecimalFormat(locale);
109: if (formatCache != null)
110: formatCache.store(decimalFormat);
111: }
112: return decimalFormat;
113: }
114:
115: private DecimalFormat getDecimalFormat(Locale locale) {
116: DecimalFormat decimalFormat = null;
117:
118: switch (variant) {
119: case INTEGER:
120: decimalFormat = (DecimalFormat) NumberFormat
121: .getNumberInstance(locale);
122: decimalFormat.setMaximumFractionDigits(0);
123: decimalFormat.setDecimalSeparatorAlwaysShown(false);
124: decimalFormat.setParseIntegerOnly(true);
125: break;
126: case NUMBER:
127: decimalFormat = (DecimalFormat) NumberFormat
128: .getNumberInstance(locale);
129: break;
130: case CURRENCY:
131: decimalFormat = (DecimalFormat) NumberFormat
132: .getCurrencyInstance(locale);
133: break;
134: case PERCENT:
135: decimalFormat = (DecimalFormat) NumberFormat
136: .getPercentInstance(locale);
137: break;
138: }
139:
140: String pattern = (String) localizedPatterns.get(locale);
141:
142: if (pattern != null) {
143: decimalFormat.applyPattern(pattern);
144: } else if (nonLocalizedPattern != null) {
145: decimalFormat.applyPattern(nonLocalizedPattern);
146: }
147: return decimalFormat;
148: }
149:
150: public void setVariant(int variant) {
151: if (variant != INTEGER && variant != NUMBER
152: && variant != CURRENCY && variant != PERCENT)
153: throw new IllegalArgumentException(
154: "Invalid value for variant parameter.");
155: this .variant = variant;
156: }
157:
158: public void addFormattingPattern(Locale locale, String pattern) {
159: localizedPatterns.put(locale, pattern);
160: }
161:
162: public void setNonLocalizedPattern(String pattern) {
163: this .nonLocalizedPattern = pattern;
164: }
165:
166: public Class getTypeClass() {
167: return java.math.BigDecimal.class;
168: }
169: }
|