001: /* Decimalbox.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Jul 5, 2007 5:38:01 PM, Created by henrichen
010: }}IS_NOTE
011:
012: Copyright (C) 2007 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019:
020: package org.zkoss.mil;
021:
022: import java.math.BigDecimal;
023: import java.text.DecimalFormatSymbols;
024:
025: import org.zkoss.lang.JVMs;
026: import org.zkoss.math.RoundingModes;
027: import org.zkoss.mil.impl.InputElement;
028: import org.zkoss.mil.mesg.MMil;
029: import org.zkoss.util.Locales;
030: import org.zkoss.zk.ui.WrongValueException;
031:
032: /**
033: * Decimal input box.
034: * @author henrichen
035: *
036: */
037: public class Decimalbox extends InputElement {
038: private static final long serialVersionUID = 200707051754L;
039: /** The rounding mode. */
040: private int _rounding = BigDecimal.ROUND_HALF_EVEN;
041: public static final int AUTO = -1000000000;
042: private int _scale = AUTO;
043:
044: public Decimalbox() {
045: setMaxlength(11);
046: }
047:
048: public Decimalbox(BigDecimal value) throws WrongValueException {
049: this ();
050: setValue(value);
051: }
052:
053: /** Sets the rounding mode.
054: * Note: You cannot change the rounding mode unless you are
055: * using Java 6 or later.
056: *
057: * @param mode the rounding mode. Allowed value:
058: * {@link BigDecimal#ROUND_CEILING}, {@link BigDecimal#ROUND_DOWN},
059: * {@link BigDecimal#ROUND_FLOOR}, {@link BigDecimal#ROUND_HALF_DOWN},
060: * {@link BigDecimal#ROUND_HALF_UP}, {@link BigDecimal#ROUND_HALF_EVEN},
061: * {@link BigDecimal#ROUND_UNNECESSARY} and {@link BigDecimal#ROUND_UP}
062: *
063: * @exception UnsupportedOperationException if Java 5 or below
064: */
065: public void setRoundingMode(int mode) {
066: if (_rounding != mode) {
067: if (!JVMs.isJava6())
068: throw new UnsupportedOperationException(
069: "Java 6 or above is required");
070: _rounding = mode;
071: }
072: }
073:
074: /** Sets the rounding mode by the name.
075: * Note: You cannot change the rounding mode unless you are
076: * using Java 6 or later.
077: *
078: * @param name the rounding mode's name. Allowed value:
079: <dl>
080: <dt>CEILING</dt>
081: <dd>Rounding mode to round towards positive infinity.</dd>
082: <dt>DOWN</dt>
083: <dd>Rounding mode to round towards zero.</dd>
084: <dt>FLOOR</dt>
085: <dd>Rounding mode to round towards negative infinity.</dd>
086: <dt>HALF_DOWN</dt>
087: <dd>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.</dd>
088: <dt>HALF_EVEN</dt>
089: <dd>Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.</dd>
090: <dt>HALF_UP</dt>
091: <dd>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.</dd>
092: <dt>UNNECESSARY</dt>
093: <dd>Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.</dd>
094: <dt>UP</dt>
095: <dd>Rounding mode to round away from zero.</dd>
096: </dl>
097: * @exception UnsupportedOperationException if Java 5 or below
098: * @see RoundingModes
099: */
100: public void setRoundingMode(String name) {
101: setRoundingMode(RoundingModes.valueOf(name));
102: }
103:
104: /** Returns the rounding mode.
105: * <p>Default: {@link BigDecimal#ROUND_HALF_EVEN}.
106: */
107: public int getRoundingMode() {
108: return _rounding;
109: }
110:
111: /** Returns the value (in BigDecimal), might be null unless
112: * a constraint stops it.
113: * @exception WrongValueException if user entered a wrong value
114: */
115: public BigDecimal getValue() throws WrongValueException {
116: return (BigDecimal) getTargetValue();
117: }
118:
119: /** Returns the value in double. If null, zero is returned.
120: */
121: public double doubleValue() throws WrongValueException {
122: final BigDecimal val = getValue();
123: return val != null ? val.doubleValue() : 0.0;
124: }
125:
126: /** Returns the value in integer. If null, zero is returned.
127: */
128: public int intValue() throws WrongValueException {
129: final BigDecimal val = getValue();
130: return val != null ? val.intValue() : 0;
131: }
132:
133: /** Returns the value in long. If null, zero is returned.
134: */
135: public long longValue() throws WrongValueException {
136: final BigDecimal val = getValue();
137: return val != null ? val.longValue() : 0;
138: }
139:
140: /** Returns the value in short. If null, zero is returned.
141: */
142: public short shortValue() throws WrongValueException {
143: final BigDecimal val = getValue();
144: return val != null ? val.shortValue() : 0;
145: }
146:
147: /** Sets the value (in BigDecimal).
148: * @exception WrongValueException if value is wrong
149: */
150: public void setValue(BigDecimal value) throws WrongValueException {
151: setRawValue(value);
152: }
153:
154: /** Returns the scale for the decimal number storing in this component,
155: * or {@link #AUTO} if the scale is decided automatically (based on
156: * what user has entered).
157: *
158: * <p>Default: {@link #AUTO}.
159: */
160: public int getScale() {
161: return _scale;
162: }
163:
164: /** Returns the scale for the decimal number storing in this component,
165: * or {@link #AUTO} if the scale is decided automatically (based on
166: * what user has entered).
167: *
168: * <p>Default: {@link #AUTO}.
169: */
170: public void setScale(int scale) {
171: _scale = scale;
172: }
173:
174: //-- super --//
175: protected Object coerceFromString(String value)
176: throws WrongValueException {
177: final Object[] vals = toNumberOnly(value);
178: final String val = (String) vals[0];
179: if (val == null || val.length() == 0)
180: return null;
181:
182: try {
183: BigDecimal v = new BigDecimal(val);
184: if (_scale != AUTO)
185: v = v.setScale(_scale, getRoundingMode());
186:
187: int divscale = vals[1] != null ? ((Integer) vals[1])
188: .intValue() : 0;
189: if (divscale > 0) {
190: final BigDecimal ten = new BigDecimal(10);
191: do {
192: v = v.divide(ten, _scale == AUTO ? v.scale() + 1
193: : _scale, getRoundingMode());
194: } while (--divscale > 0);
195: }
196: return v;
197: } catch (NumberFormatException ex) {
198: throw new WrongValueException(this , MMil.NUMBER_REQUIRED,
199: value);
200: }
201: }
202:
203: protected String coerceToString(Object value) {
204: return value == null ? "" : value.toString();
205: }
206:
207: /* (non-Javadoc)
208: * @see org.zkoss.mil.impl.InputElement#getInternalType()
209: */
210: protected int getInternalType() {
211: return DECIMAL;
212: }
213:
214: /** Filters out non digit characters, such comma and whitespace,
215: * from the specified value.
216: * It is designed to let user enter data in more free style.
217: * They may or may not enter data in the specified format.
218: *
219: * @return a two element array. The first element is the string to
220: * parse with, say, Double.parseDouble. The second element is
221: * an integer to indicate how many digits the result shall be scaled.
222: * For example, if the second element is 2. Then, the result shall be
223: * divided with 10 ^ 2.
224: */
225: protected Object[] toNumberOnly(String val) {
226: if (val == null)
227: return new Object[] { null, null };
228:
229: final DecimalFormatSymbols symbols = new DecimalFormatSymbols(
230: Locales.getCurrent());
231: final char GROUPING = symbols.getGroupingSeparator(), DECIMAL = symbols
232: .getDecimalSeparator(), PERCENT = symbols.getPercent(), PER_MILL = symbols
233: .getPerMill(), //1/1000
234: //not support yet: INFINITY = symbols.getInfinity(), NAN = symbols.getNaN(),
235: MINUS = symbols.getMinusSign();
236: StringBuffer sb = null;
237: int divscale = 0; //the second element
238: boolean minus = false;
239: for (int j = 0, len = val.length(); j < len; ++j) {
240: final char cc = val.charAt(j);
241:
242: boolean ignore = false;
243: //We handle percent and (nnn) specially
244: if (cc == PERCENT) {
245: divscale += 2;
246: ignore = true;
247: } else if (cc == PER_MILL) {
248: divscale += 3;
249: ignore = true;
250: } else if (cc == '(') {
251: minus = true;
252: ignore = true;
253: }
254:
255: //We don't add if cc shall be ignored (not alphanum but in fmt)
256: if (!ignore)
257: ignore = (cc < '0' || cc > '9')
258: && cc != DECIMAL
259: && cc != MINUS
260: && cc != '+'
261: && (Character.isWhitespace(cc)
262: || cc == GROUPING || cc == ')');
263: if (ignore) {
264: if (sb == null)
265: sb = new StringBuffer(len).append(val.substring(0,
266: j));
267: } else {
268: final char c2 = cc == MINUS ? '-' : cc == DECIMAL ? '.'
269: : cc;
270: if (cc != c2) {
271: if (sb == null)
272: sb = new StringBuffer(len).append(val
273: .substring(0, j));
274: sb.append(c2);
275: } else if (sb != null) {
276: sb.append(c2);
277: }
278: }
279: }
280: if (minus) {
281: if (sb == null)
282: sb = new StringBuffer(val.length() + 1).append(val);
283: if (sb.length() > 0) {
284: if (sb.charAt(0) == '-') {
285: sb.deleteCharAt(0);
286: } else {
287: sb.insert(0, '-');
288: }
289: }
290: }
291: return new Object[] { (sb != null ? sb.toString() : val),
292: new Integer(divscale) };
293: }
294: }
|