001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * ConvertToNumberExpression.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.function;
030:
031: import java.lang.reflect.Method;
032: import java.math.BigDecimal;
033: import java.text.DecimalFormat;
034: import java.text.DecimalFormatSymbols;
035: import java.text.ParseException;
036: import java.util.Locale;
037:
038: import org.jfree.report.DataRow;
039: import org.jfree.util.ObjectUtilities;
040:
041: /**
042: * Parses a string into a number using the given decimal-format.
043: *
044: * @author Thomas Morgner
045: * @see java.text.DecimalFormat
046: */
047: public class ConvertToNumberExpression extends AbstractExpression {
048: /**
049: * The default pattern if no other format-string was given. This parses decimal integer numbers with the
050: * highest precision. This pattern was the default for JDK 1.4 and below, but changed in JDK 1.5. We stick
051: * with the original pattern here.
052: */
053: private static final String DECIMALFORMAT_DEFAULT_PATTERN = "#,###.###################################################"
054: + "#########################################################"
055: + "#########################################################"
056: + "#########################################################"
057: + "#########################################################"
058: + "#########################################################"
059: + "####";
060:
061: /**
062: * A constant for the numeric value zero.
063: */
064: private static final BigDecimal ZERO = new BigDecimal(0);
065:
066: /**
067: * The name of the data-row column from where to read the string that should be parsed.
068: */
069: private String field;
070: /**
071: * The date-format that is used for the parsing.
072: */
073: private String format;
074: /**
075: * The locale. If undefined, the report's locale is used.
076: */
077: private Locale locale;
078: /**
079: * A variable that caches the last locale object used.
080: */
081: private Locale lastLocale;
082: /**
083: * A variable that caches the last number-format object used.
084: */
085: private DecimalFormat decimalFormat;
086:
087: /**
088: * Default Constructor.
089: */
090: public ConvertToNumberExpression() {
091: }
092:
093: /**
094: * Returns the name of the data-row column from where to read the string that should be parsed.
095: *
096: * @return the field name.
097: */
098: public String getField() {
099: return field;
100: }
101:
102: /**
103: * Defines the name of the data-row column from where to read the string that should be parsed.
104: *
105: * @param field the name of the field.
106: */
107: public void setField(final String field) {
108: this .field = field;
109: }
110:
111: /**
112: * Returns the SimpleDateFormat pattern that is used for the parsing.
113: *
114: * @return the pattern string.
115: * @see java.text.SimpleDateFormat
116: */
117: public String getFormat() {
118: return format;
119: }
120:
121: /**
122: * Defines the SimpleDateFormat pattern that is used for the parsing.
123: *
124: * @param format the pattern string.
125: * @see java.text.SimpleDateFormat
126: */
127: public void setFormat(final String format) {
128: this .format = format;
129: this .lastLocale = null;
130: this .decimalFormat = null;
131: }
132:
133: /**
134: * Returns the locale that is used for the parsing.
135: *
136: * @return the locale.
137: */
138: public Locale getLocale() {
139: return locale;
140: }
141:
142: /**
143: * Defines the locale that is used for the parsing.
144: *
145: * @param locale the locale.
146: */
147: public void setLocale(final Locale locale) {
148: this .locale = locale;
149: this .lastLocale = null;
150: this .decimalFormat = null;
151: }
152:
153: /**
154: * Parses the value read from the column specified by the given field-name and tries to parse it into a Number
155: * using the given DecimalFormat-pattern.
156: *
157: * @return the value of the function.
158: */
159: public Object getValue() {
160: final DataRow dataRow = getDataRow();
161: // get the row directly as a Number
162: final Object o = dataRow.get(field);
163: // check if that thing is a Number
164: if (o instanceof Number) {
165: return o;
166: }
167:
168: // get a string and convert
169: final String formatString = getFormat();
170: try {
171: Locale localeUsed = locale;
172: if (localeUsed == null) {
173: localeUsed = getResourceBundleFactory().getLocale();
174: }
175:
176: if (decimalFormat == null
177: || ObjectUtilities.equal(lastLocale, localeUsed) == false) {
178: final String effectiveFormatString;
179: if (formatString == null || formatString.length() == 0) {
180: // this is a workaround for a bug in JDK 1.5
181: effectiveFormatString = DECIMALFORMAT_DEFAULT_PATTERN;
182: } else {
183: effectiveFormatString = formatString;
184: }
185: lastLocale = localeUsed;
186: decimalFormat = new DecimalFormat(effectiveFormatString);
187: activateBigDecimalMode(decimalFormat);
188: decimalFormat
189: .setDecimalFormatSymbols(new DecimalFormatSymbols(
190: localeUsed));
191: }
192:
193: return decimalFormat.parse(String.valueOf(o));
194: } catch (ParseException e) {
195: return ZERO;
196: }
197: }
198:
199: /**
200: * Uses reflection to enable the use of BigDecimals in the parsing. This produces better results, but only
201: * works on JDK 1.4 or higher.
202: *
203: * @param format the format object that should be patched.
204: */
205: private void activateBigDecimalMode(final DecimalFormat format) {
206: if (ObjectUtilities.isJDK14()) {
207: try {
208: final Method method = DecimalFormat.class.getMethod(
209: "setParseBigDecimal",
210: new Class[] { Boolean.TYPE });
211: method.invoke(format, new Object[] { Boolean.TRUE });
212: } catch (Exception e) {
213: // ignore it, as it will always fail on JDK 1.4 or lower ..
214: }
215: }
216: }
217:
218: }
|