KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > iapi > types > BigIntegerDecimal


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.BigIntegerDecimal
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.iapi.types;
23
24 import java.math.BigInteger JavaDoc;
25 import java.sql.Types JavaDoc;
26
27 import org.apache.derby.iapi.error.StandardException;
28 import org.apache.derby.iapi.reference.SQLState;
29 import org.apache.derby.iapi.services.sanity.SanityManager;
30
31 /**
32  * DECIMAL support using the immutable java.math.BigInteger to perform arithmetic
33  * and conversions. Extends BinaryDecimal to use the base
34  * support of that class. J2ME/CDC/Foundation includes BigInteger.
35  *
36  * A BigInteger is used in calculations etc. to represent the integral unscaled value.
37  * It is simply created from new BigInteger(data2c). No additional instance fields
38  * are used by this class, a possible enhancement would be to keep the BigInteger around
39  * but would require calls from the parent class to reset state etc.
40  */

41
42 public final class BigIntegerDecimal extends BinaryDecimal
43 {
44     private static final BigInteger JavaDoc TEN = BigInteger.valueOf(10L);
45     private static final BigInteger JavaDoc MAXLONG_PLUS_ONE = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
46     private static final BigInteger JavaDoc MINLONG_MINUS_ONE = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE);
47     
48     public BigIntegerDecimal() {
49     }
50
51     public DataValueDescriptor getNewNull()
52     {
53         return new BigIntegerDecimal();
54     }
55     
56     public long getLong() throws StandardException
57     {
58         if (isNull())
59             return 0L;
60         
61         BigInteger JavaDoc bi = new BigInteger JavaDoc(data2c);
62         
63         // If at any time we see that the value to be scaled down
64
// is within the range for a long, then we are guaranteed
65
// that the scaled down value is within the range for long.
66
boolean rangeOk = false;
67         if ((bi.compareTo(BigIntegerDecimal.MAXLONG_PLUS_ONE) < 0)
68             && (bi.compareTo(BigIntegerDecimal.MINLONG_MINUS_ONE) > 0))
69             rangeOk = true;
70             
71         for (int i = 0; i < sqlScale; i++)
72         {
73             bi = bi.divide(BigIntegerDecimal.TEN);
74             if (rangeOk)
75                 continue;
76             
77             if ((bi.compareTo(BigIntegerDecimal.MAXLONG_PLUS_ONE) < 0)
78                     && (bi.compareTo(BigIntegerDecimal.MINLONG_MINUS_ONE) > 0))
79                     rangeOk = true;
80             }
81         
82         // TODO Range checking
83
if (!rangeOk)
84             throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "BIGINT");
85         
86         return bi.longValue();
87     }
88
89     public float getFloat() throws StandardException
90     {
91         if (isNull())
92             return 0.0f;
93         return NumberDataType.normalizeREAL(Float.parseFloat(getString()));
94     }
95     public double getDouble() throws StandardException
96     {
97         if (isNull())
98             return 0.0;
99         return NumberDataType.normalizeDOUBLE(Double.parseDouble(getString()));
100     }
101     
102     // 0 or null is false, all else is true
103
public boolean getBoolean()
104     {
105         if (isNull())
106             return false;
107         
108         BigInteger JavaDoc bi = new BigInteger JavaDoc(data2c);
109         return bi.compareTo(java.math.BigInteger.ZERO) != 0;
110     }
111
112     /**
113      * Set the value from a String, the format is
114      * nnnn
115      *
116      * Scale always set to zero.
117      */

118     public void setValue(String JavaDoc theValue) throws StandardException
119     {
120         if (theValue == null)
121         {
122             restoreToNull();
123             return;
124         }
125         
126         theValue = theValue.trim();
127         
128         int dot = theValue.indexOf('.');
129         int ePosition = theValue.indexOf('e');
130         if (ePosition == -1)
131             ePosition = theValue.indexOf('E');
132         
133         
134         int scale = 0;
135         try
136         {
137             // handle the exponent
138
if (ePosition != -1)
139             {
140                 if (dot > ePosition)
141                     throw invalidFormat();
142                 
143                 // Integer.parseInt does not handle a + sign in
144
// front of the number, while the format for the
145
// exponent allows it. Need to strip it off.
146

147                 int expOffset = ePosition + 1;
148
149                 if (expOffset >= theValue.length())
150                     throw invalidFormat();
151                 
152                 if (theValue.charAt(expOffset) == '+')
153                 {
154                     // strip the plus but must ensure the next character
155
// is not a - sign. Any other invalid sign will be handled
156
// by Integer.parseInt.
157
expOffset++;
158                     if (expOffset >= theValue.length())
159                         throw invalidFormat();
160                     if (theValue.charAt(expOffset) == '-')
161                         throw invalidFormat();
162                 }
163                 
164                 String JavaDoc exponent = theValue.substring(expOffset);
165                 
166                 scale = -1 * Integer.parseInt(exponent);
167                 theValue = theValue.substring(0, ePosition);
168             }
169             
170             if (dot != -1)
171             {
172                 // remove the dot from the string
173
String JavaDoc leading = theValue.substring(0, dot);
174                 
175                 scale += (theValue.length() - (dot + 1));
176                 
177                 theValue = leading.concat(theValue.substring(dot + 1, theValue.length()));
178             }
179             
180             if (scale < 0)
181             {
182                 for (int i = scale; i < 0; i++)
183                     theValue = theValue.concat("0");
184                 scale = 0;
185             }
186             
187             BigInteger JavaDoc bi = new BigInteger JavaDoc(theValue);
188             data2c = bi.toByteArray();
189             sqlScale = scale;
190             
191         } catch (NumberFormatException JavaDoc nfe)
192         {
193             throw invalidFormat();
194         }
195     }
196     
197     
198     /* (non-Javadoc)
199      * @see org.apache.derby.iapi.types.DataValueDescriptor#getString()
200      */

201     public String JavaDoc getString() {
202         
203         if (isNull())
204             return null;
205         
206         String JavaDoc unscaled = new BigInteger JavaDoc(data2c).toString();
207                 
208         if (sqlScale == 0)
209             return unscaled;
210
211         boolean isNegative = this.isNegative();
212         
213         if (sqlScale >= (unscaled.length() - (isNegative ? 1 : 0)))
214         {
215             if (isNegative)
216                 unscaled = unscaled.substring(1);
217
218             String JavaDoc val = isNegative ? "-0." : "0.";
219             for (int i = sqlScale - unscaled.length(); i > 0; i--)
220                 val = val.concat("0");
221             return val.concat(unscaled);
222         }
223         
224         String JavaDoc val = unscaled.substring(0, unscaled.length() - sqlScale);
225         val = val.concat(".");
226         return val.concat(unscaled.substring(unscaled.length() - sqlScale, unscaled.length()));
227     }
228     
229     /**
230      * Return the SQL precision of this value.
231      */

232     public int getDecimalValuePrecision()
233     {
234         if (isNull())
235             return 0;
236         
237         BigInteger JavaDoc bi = new BigInteger JavaDoc(data2c);
238         
239         if (BigInteger.ZERO.equals(bi))
240             return 0;
241         
242         int precision = bi.toString().length();
243         
244         if (this.isNegative())
245             precision--;
246         
247         if (precision < sqlScale)
248             return sqlScale;
249         
250         return precision;
251     }
252     
253
254     /**
255      * Compare two non-null NumberDataValues using DECIMAL arithmetic.
256      */

257     protected int typeCompare(DataValueDescriptor arg) throws StandardException {
258         
259         BigIntegerDecimal obid = getBID(arg);
260         
261         // need to align scales to perform comparisions
262
int tscale = getDecimalValueScale();
263         int oscale = obid.getDecimalValueScale();
264     
265         BigInteger JavaDoc tbi = new BigInteger JavaDoc(data2c);
266         BigInteger JavaDoc obi = new BigInteger JavaDoc(obid.data2c);
267         
268         if (tscale < oscale)
269             tbi = BigIntegerDecimal.rescale(tbi, oscale - tscale);
270         else if (oscale < tscale)
271             obi = BigIntegerDecimal.rescale(obi, tscale - oscale);
272     
273         return tbi.compareTo(obi);
274     }
275
276     /**
277      * Add two non-null NumberDataValues using DECIMAL arithmetic.
278      * Uses add() to perform the calculation.
279      */

280     public NumberDataValue plusNN(NumberDataValue left, NumberDataValue right, NumberDataValue result)
281     throws StandardException {
282         
283         BinaryDecimal resultBid = (BinaryDecimal) result;
284         if (resultBid == null)
285             resultBid = new BigIntegerDecimal();
286                 
287         BigIntegerDecimal lbid = getBID(left);
288         BigIntegerDecimal rbid = getBID(right);
289         
290         // need to align scales to perform plus
291
int lscale = lbid.getDecimalValueScale();
292         int rscale = rbid.getDecimalValueScale();
293         
294         BigInteger JavaDoc bi1 = new BigInteger JavaDoc(lbid.data2c);
295         BigInteger JavaDoc bi2 = new BigInteger JavaDoc(rbid.data2c);
296         
297         int tscale = lscale;
298         if (lscale < rscale)
299         {
300             bi1 = BigIntegerDecimal.rescale(bi1, rscale - lscale);
301             tscale = rscale;
302         }
303         else if (rscale < lscale)
304         {
305             bi2 = BigIntegerDecimal.rescale(bi2, lscale - rscale);
306         }
307             
308         
309
310         bi1 = bi1.add(bi2);
311         
312         resultBid.data2c = bi1.toByteArray();
313         resultBid.sqlScale = tscale;
314         
315         return resultBid;
316     }
317
318     /**
319      * Negate the number.
320      * @see org.apache.derby.iapi.types.NumberDataValue#minus(org.apache.derby.iapi.types.NumberDataValue)
321      */

322     public NumberDataValue minus(NumberDataValue result) throws StandardException {
323         
324         if (result == null)
325             result = (NumberDataValue) getNewNull();
326
327         if (isNull())
328             result.setToNull();
329         else
330         {
331             BinaryDecimal rbd = (BinaryDecimal) result;
332             
333             BigInteger JavaDoc bi = new BigInteger JavaDoc(data2c);
334             // scale remains unchanged.
335
rbd.data2c = bi.negate().toByteArray();
336             rbd.sqlScale = sqlScale;
337         
338         }
339             
340         return result;
341     }
342
343
344     /**
345      * Multiple two non-null NumberDataValues using DECIMAL arithmetic.
346      * Uses BigInteger.multipy() to perform the calculation.
347      * Simply multiply the unscaled values and add the scales, proof:
348      
349      <code>
350      left * right
351      = (left_unscaled * 10^-left_scale) * (right_unscaled * 10^-right_scale)
352      = (left_unscaled * 10^-left_scale) * (right_unscaled * 10^-right_scale)
353      = (left_unscaled * right_unscaled) * 10^-(left_scale + right_scale)
354      </code>
355      */

356     public NumberDataValue timesNN(NumberDataValue left, NumberDataValue right, NumberDataValue result)
357     throws StandardException
358     {
359         BigIntegerDecimal resultBid = (BigIntegerDecimal) result;
360         if (resultBid == null)
361             resultBid = new BigIntegerDecimal();
362                 
363         BigIntegerDecimal lbid = getBID(left);
364         BigIntegerDecimal rbid = getBID(right);
365
366         BigInteger JavaDoc lbi = new BigInteger JavaDoc(lbid.data2c);
367         BigInteger JavaDoc rbi = new BigInteger JavaDoc(rbid.data2c);
368         
369         rbi = lbi.multiply(rbi);
370         resultBid.data2c = rbi.toByteArray();
371         resultBid.sqlScale = lbid.getDecimalValueScale() + rbid.getDecimalValueScale();
372         
373         return resultBid;
374     }
375
376     /**
377      * Divide two non-null NumberDataValues using DECIMAL arithmetic.
378      * Uses divide() to perform the calculation.
379      * Simply multiply the unscaled values and subtract the scales, proof:
380      
381      <code>
382         left / right
383         = (left_unscaled * 10^-left_scale) / (right_unscaled * 10^-right_scale)
384         = (left_unscaled / right_unscaled) * (10^-left_scale / 10^-right_scale)
385         = (left_unscaled / right_unscaled) * (10^-(left_scale-right_scale))
386     </code>
387      */

388     public NumberDataValue divideNN(NumberDataValue left, NumberDataValue right,
389             NumberDataValue result, int scale)
390     throws StandardException
391     {
392         BinaryDecimal resultBid = (BinaryDecimal) result;
393         if (resultBid == null)
394             resultBid = new BigIntegerDecimal();
395                 
396         BigIntegerDecimal lbid = getBID(left);
397         BigIntegerDecimal rbid = getBID(right);
398
399         BigInteger JavaDoc lbi = new BigInteger JavaDoc(lbid.data2c);
400         BigInteger JavaDoc rbi = new BigInteger JavaDoc(rbid.data2c);
401         
402         if (BigInteger.ZERO.equals(rbi))
403             throw StandardException.newException(SQLState.LANG_DIVIDE_BY_ZERO);
404                 
405         int lscale = lbid.getDecimalValueScale();
406         int rscale = rbid.getDecimalValueScale();
407                 
408         if (scale >= 0)
409         {
410             if (lscale < (scale + rscale)) {
411                 lbi = BigIntegerDecimal.rescale(lbi, scale + rscale - lscale);
412                 lscale = scale + rscale;
413             }
414         }
415         
416         rbi = lbi.divide(rbi);
417         resultBid.sqlScale = lscale - rscale;
418         
419         if (resultBid.sqlScale < 0)
420         {
421             rbi = BigIntegerDecimal.rescale(rbi, -resultBid.sqlScale);
422             resultBid.sqlScale = 0;
423         }
424             
425         resultBid.data2c = rbi.toByteArray();
426         
427         return resultBid;
428     }
429
430     public void normalize(
431             DataTypeDescriptor desiredType,
432             DataValueDescriptor source)
433                     throws StandardException
434     {
435         int desiredScale = desiredType.getScale();
436         int desiredPrecision = desiredType.getPrecision();
437
438         setFrom(source);
439         setWidth(desiredPrecision, desiredScale, true);
440     }
441     
442     /* (non-Javadoc)
443      * @see org.apache.derby.iapi.types.VariableSizeDataValue#setWidth(int, int, boolean)
444      */

445     public void setWidth(int desiredPrecision, int desiredScale,
446             boolean errorOnTrunc) throws StandardException
447     {
448         if (isNull())
449             return;
450             
451         int deltaScale = desiredScale - sqlScale;
452         if (desiredPrecision != IGNORE_PRECISION)
453         {
454             int currentPrecision = getDecimalValuePrecision();
455     
456             int futurePrecision = currentPrecision + deltaScale;
457             
458             if (futurePrecision > desiredPrecision)
459                 throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
460                         ("DECIMAL/NUMERIC("+desiredPrecision+","+desiredScale+")"));
461         }
462         
463         if (deltaScale == 0)
464             return;
465         
466         BigInteger JavaDoc bi = new BigInteger JavaDoc(data2c);
467         
468         bi = BigIntegerDecimal.rescale(bi, deltaScale);
469
470         data2c = bi.toByteArray();
471         sqlScale = desiredScale;
472     }
473     
474     /**
475      * Obtain a BinaryDecimal that represents the passed in value.
476      */

477     private BigIntegerDecimal getBID(DataValueDescriptor value)
478     throws StandardException
479      {
480         switch (value.typeToBigDecimal()) {
481         case Types.DECIMAL:
482             return (BigIntegerDecimal) value;
483         case Types.CHAR: {
484             BigIntegerDecimal bid = new BigIntegerDecimal();
485             bid.setValue(value.getString());
486             return bid;
487         }
488
489         case Types.BIGINT: {
490             BigIntegerDecimal bid = new BigIntegerDecimal();
491             bid.setValue(value.getLong());
492             return bid;
493         }
494         default:
495             if (SanityManager.DEBUG)
496                 SanityManager.THROWASSERT("invalid return from "
497                         + value.getClass() + ".typeToBigDecimal() "
498                         + value.typeToBigDecimal());
499             return null;
500         }
501     }
502     /**
503      * Rescale a BigInteger, a positive delta means the scale is increased, zero
504      * no change and negative decrease of scale. It is up to the caller to
505      * manage the actual scale of the value, e.g. don't allow the scale to go
506      * negative.
507      *
508      * @param bi
509      * value to be rescaled
510      * @param deltaScale
511      * change of scale
512      * @return rescaled value
513      */

514     private static BigInteger JavaDoc rescale(BigInteger JavaDoc bi, int deltaScale) {
515         if (deltaScale == 0)
516             return bi;
517         if (deltaScale > 0) {
518             // scale increasing, e.g. 10.23 to 10.2300
519
for (int i = 0; i < deltaScale; i++)
520                 bi = bi.multiply(BigIntegerDecimal.TEN);
521
522         } else if (deltaScale < 0) {
523             // scale decreasing, e.g. 10.2345 to 10.23
524
for (int i = deltaScale; i < 0; i++)
525                 bi = bi.divide(BigIntegerDecimal.TEN);
526         }
527         return bi;
528     }
529     /*
530      * String display of value
531      */

532     public String JavaDoc toString()
533     {
534         if (isNull())
535             return "NULL";
536         else
537             return getString();
538     }
539 }
540
Popular Tags