001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.value;
007:
008: import java.math.BigDecimal;
009: import java.sql.PreparedStatement;
010: import java.sql.SQLException;
011:
012: import org.h2.constant.ErrorCode;
013: import org.h2.constant.SysProperties;
014: import org.h2.message.Message;
015: import org.h2.util.MathUtils;
016:
017: /**
018: * Implementation of the DECIMAL data type.
019: */
020: public class ValueDecimal extends Value {
021: // TODO doc: document differences for BigDecimal 1.5 <> 1.4
022: private final BigDecimal value;
023: private String valueString;
024: private int precision;
025:
026: private static final BigDecimal DEC_ZERO = new BigDecimal("0");
027: private static final BigDecimal DEC_ONE = new BigDecimal("1");
028: private static final ValueDecimal ZERO = new ValueDecimal(DEC_ZERO);
029: private static final ValueDecimal ONE = new ValueDecimal(DEC_ONE);
030:
031: public static final int DEFAULT_PRECISION = 65535;
032: public static final int DEFAULT_SCALE = 32767;
033: public static final int DEFAULT_DISPLAY_SIZE = 65535;
034: private static final int DIVIDE_SCALE_ADD = 25;
035:
036: private ValueDecimal(BigDecimal value) {
037: if (value == null) {
038: throw new IllegalArgumentException();
039: } else if (!SysProperties.ALLOW_BIG_DECIMAL_EXTENSIONS
040: && !value.getClass().equals(BigDecimal.class)) {
041: SQLException e = Message.getSQLException(
042: ErrorCode.INVALID_CLASS_2, new String[] {
043: BigDecimal.class.getName(),
044: value.getClass().getName() });
045: throw Message.convertToInternal(e);
046: }
047: this .value = value;
048: }
049:
050: public Value add(Value v) {
051: ValueDecimal dec = (ValueDecimal) v;
052: return ValueDecimal.get(value.add(dec.value));
053: }
054:
055: public Value subtract(Value v) {
056: ValueDecimal dec = (ValueDecimal) v;
057: return ValueDecimal.get(value.subtract(dec.value));
058: }
059:
060: public Value negate() {
061: return ValueDecimal.get(value.negate());
062: }
063:
064: public Value multiply(Value v) {
065: ValueDecimal dec = (ValueDecimal) v;
066: return ValueDecimal.get(value.multiply(dec.value));
067: }
068:
069: public Value divide(Value v) throws SQLException {
070: ValueDecimal dec = (ValueDecimal) v;
071: // TODO value: divide decimal: rounding?
072: if (dec.value.signum() == 0) {
073: throw Message.getSQLException(ErrorCode.DIVISION_BY_ZERO_1,
074: getSQL());
075: }
076: BigDecimal bd = value.divide(dec.value, value.scale()
077: + DIVIDE_SCALE_ADD, BigDecimal.ROUND_HALF_DOWN);
078: if (bd.signum() == 0) {
079: bd = DEC_ZERO;
080: } else if (bd.scale() > 0) {
081: if (!bd.unscaledValue().testBit(0)) {
082: String s = bd.toString();
083: int i = s.length() - 1;
084: while (i >= 0 && s.charAt(i) == '0') {
085: i--;
086: }
087: if (i < s.length() - 1) {
088: s = s.substring(0, i + 1);
089: bd = new BigDecimal(s);
090: }
091: }
092: }
093: return ValueDecimal.get(bd);
094: }
095:
096: public String getSQL() {
097: return getString();
098: }
099:
100: public int getType() {
101: return Value.DECIMAL;
102: }
103:
104: protected int compareSecure(Value o, CompareMode mode) {
105: ValueDecimal v = (ValueDecimal) o;
106: int c = value.compareTo(v.value);
107: return c == 0 ? 0 : (c < 0 ? -1 : 1);
108: }
109:
110: public int getSignum() {
111: return value.signum();
112: }
113:
114: public BigDecimal getBigDecimal() {
115: return value;
116: }
117:
118: public String getString() {
119: if (valueString == null) {
120: valueString = value.toString();
121: }
122: return valueString;
123: }
124:
125: public long getPrecision() {
126: if (precision == 0) {
127: precision = value.unscaledValue().abs().toString().length();
128: }
129: return precision;
130: }
131:
132: public boolean checkPrecision(long precision) {
133: if (precision == DEFAULT_PRECISION) {
134: return true;
135: }
136: return getPrecision() <= precision;
137: }
138:
139: public int getScale() {
140: return value.scale();
141: }
142:
143: public int hashCode() {
144: return value.hashCode();
145: }
146:
147: public Object getObject() {
148: return value;
149: }
150:
151: public void set(PreparedStatement prep, int parameterIndex)
152: throws SQLException {
153: prep.setBigDecimal(parameterIndex, value);
154: }
155:
156: public Value convertScale(boolean onlyToSmallerScale,
157: int targetScale) throws SQLException {
158: if (value.scale() == targetScale) {
159: return this ;
160: }
161: if (onlyToSmallerScale || targetScale >= DEFAULT_SCALE) {
162: if (value.scale() < targetScale) {
163: return this ;
164: }
165: }
166: BigDecimal bd = MathUtils.setScale(value, targetScale);
167: return ValueDecimal.get(bd);
168: }
169:
170: public Value convertPrecision(long precision) throws SQLException {
171: if (getPrecision() <= precision) {
172: return this ;
173: }
174: throw Message.getSQLException(
175: ErrorCode.VALUE_TOO_LARGE_FOR_PRECISION_1, ""
176: + precision);
177: }
178:
179: public static ValueDecimal get(BigDecimal dec) {
180: if (DEC_ZERO.equals(dec)) {
181: return ZERO;
182: } else if (DEC_ONE.equals(dec)) {
183: return ONE;
184: }
185: // TODO value optimization: find a way to read size of BigDecimal,
186: // check max cache size
187: return (ValueDecimal) Value.cache(new ValueDecimal(dec));
188: }
189:
190: public int getDisplaySize() {
191: return MathUtils.convertLongToInt(getPrecision() + 2); // - .
192: }
193:
194: public boolean equals(Object other) {
195: return other instanceof ValueDecimal
196: && value.equals(((ValueDecimal) other).value);
197: }
198:
199: public int getMemory() {
200: return getString().length() * 3 + 120;
201: }
202:
203: }
|