KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.SQLDecimal
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 org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.services.io.ArrayInputStream;
27
28 import org.apache.derby.iapi.services.sanity.SanityManager;
29
30 import org.apache.derby.iapi.services.io.StoredFormatIds;
31 import org.apache.derby.iapi.services.io.Storable;
32
33 import org.apache.derby.iapi.error.StandardException;
34
35 import org.apache.derby.iapi.services.cache.ClassSize;
36 import org.apache.derby.iapi.services.info.JVMInfo;
37
38 import java.math.BigDecimal JavaDoc;
39 import java.math.BigInteger JavaDoc;
40 import java.lang.Math JavaDoc;
41 import java.lang.reflect.Method JavaDoc;
42 import java.lang.reflect.InvocationTargetException JavaDoc;
43 import java.io.ObjectOutput JavaDoc;
44 import java.io.ObjectInput JavaDoc;
45 import java.io.IOException JavaDoc;
46
47 import java.sql.ResultSet JavaDoc;
48 import java.sql.PreparedStatement JavaDoc;
49 import java.sql.SQLException JavaDoc;
50 import java.sql.Types JavaDoc;
51
52 /**
53  * SQLDecimal satisfies the DataValueDescriptor
54  * interfaces (i.e., OrderableDataType). It implements a numeric/decimal column,
55  * e.g. for * storing a column value; it can be specified
56  * when constructed to not allow nulls. Nullability cannot be changed
57  * after construction, as it affects the storage size and mechanism.
58  * <p>
59  * Because OrderableDataType is a subtype of DataType,
60  * SQLDecimal can play a role in either a DataType/Row
61  * or a OrderableDataType/Row, interchangeably.
62  * <p>
63  * We assume the store has a flag for nullness of the value,
64  * and simply return a 0-length array for the stored form
65  * when the value is null.
66  *
67  * @author jamie
68  */

69 public final class SQLDecimal extends NumberDataType implements VariableSizeDataValue
70 {
71     private static final BigDecimal JavaDoc ZERO = BigDecimal.valueOf(0L);
72     private static final BigDecimal JavaDoc ONE = BigDecimal.valueOf(1L);
73     static final BigDecimal JavaDoc MAXLONG_PLUS_ONE = BigDecimal.valueOf(Long.MAX_VALUE).add(ONE);
74     static final BigDecimal JavaDoc MINLONG_MINUS_ONE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(ONE);
75
76
77
78     /**
79      * object state. Note that scale and precision are
80      * always determined dynamically from value when
81      * it is not null.
82
83        The field value can be null without the data value being null.
84        In this case the value is stored in rawData and rawScale. This
85        is to allow the minimal amount of work to read a SQLDecimal from disk.
86        Creating the BigDecimal is expensive as it requires allocating
87        three objects, the last two are a waste in the case the row does
88        not qualify or the row will be written out by the sorter before being
89        returned to the application.
90         <P>
91         This means that this field must be accessed for read indirectly through
92         the getBigDecimal() method, and when setting it the rawData field must
93         be set to null.
94
95      */

96     private BigDecimal JavaDoc value;
97
98     /**
99         See comments for value
100     */

101     private byte[] rawData;
102
103     /**
104         See comments for value
105     */

106     private int rawScale;
107
108     private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( SQLDecimal.class);
109     private static final int BIG_DECIMAL_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( BigDecimal JavaDoc.class);
110
111     public int estimateMemoryUsage()
112     {
113         int sz = BASE_MEMORY_USAGE;
114         if( null != value)
115             sz += BIG_DECIMAL_MEMORY_USAGE + (value.unscaledValue().bitLength() + 8)/8;
116         if( null != rawData)
117             sz += rawData.length;
118         return sz;
119     }
120
121
122     ////////////////////////////////////////////////////////////////////
123
//
124
// CLASS INTERFACE
125
//
126
////////////////////////////////////////////////////////////////////
127
/** no-arg constructor, required by Formattable */
128     public SQLDecimal()
129     {
130     }
131
132     public SQLDecimal(BigDecimal JavaDoc val)
133     {
134         value = val;
135     }
136
137     public SQLDecimal(BigDecimal JavaDoc val, int nprecision, int scale)
138             throws StandardException
139     {
140         
141         value = val;
142         if ((value != null) && (scale >= 0))
143         {
144             value = value.setScale(scale,
145                             BigDecimal.ROUND_DOWN);
146         }
147     }
148
149     public SQLDecimal(String JavaDoc val)
150     {
151         value = new BigDecimal JavaDoc(val);
152     }
153
154     /*
155      * DataValueDescriptor interface
156      * (mostly implemented in DataType)
157      *
158      */

159
160
161     /**
162      * @exception StandardException thrown on failure to convert
163      */

164     public int getInt() throws StandardException
165     {
166         if (isNull())
167             return 0;
168
169         try {
170             long lv = getLong();
171
172             if ((lv >= Integer.MIN_VALUE) && (lv <= Integer.MAX_VALUE))
173                 return (int) lv;
174
175         } catch (StandardException se) {
176         }
177
178         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
179     }
180
181     /**
182      * @exception StandardException thrown on failure to convert
183      */

184     public byte getByte() throws StandardException
185     {
186         if (isNull())
187             return (byte)0;
188
189         try {
190             long lv = getLong();
191
192             if ((lv >= Byte.MIN_VALUE) && (lv <= Byte.MAX_VALUE))
193                 return (byte) lv;
194
195         } catch (StandardException se) {
196         }
197
198         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TINYINT");
199     }
200
201     /**
202      * @exception StandardException thrown on failure to convert
203      */

204     public short getShort() throws StandardException
205     {
206         if (isNull())
207             return (short)0;
208
209         try {
210             long lv = getLong();
211
212             if ((lv >= Short.MIN_VALUE) && (lv <= Short.MAX_VALUE))
213                 return (short) lv;
214
215         } catch (StandardException se) {
216         }
217
218         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "SMALLINT");
219     }
220
221     /**
222      * @exception StandardException thrown on failure to convert
223      */

224     public long getLong() throws StandardException
225     {
226         BigDecimal JavaDoc localValue = getBigDecimal();
227         if (localValue == null)
228             return (long)0;
229
230         // Valid range for long is
231
// greater than Long.MIN_VALUE - 1
232
// *and*
233
// less than Long.MAX_VALUE + 1
234
//
235
// This ensures that DECIMAL values with an integral value
236
// equal to the Long.MIN/MAX_VALUE round correctly to those values.
237
// e.g. 9223372036854775807.1 converts to 9223372036854775807
238
// this matches DB2 UDB behaviour
239

240         if ( (localValue.compareTo(SQLDecimal.MINLONG_MINUS_ONE) == 1)
241             && (localValue.compareTo(SQLDecimal.MAXLONG_PLUS_ONE) == -1)) {
242
243             return localValue.longValue();
244         }
245
246         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "BIGINT");
247     }
248
249     /**
250      * @exception StandardException thrown on failure to convert
251      */

252     public float getFloat() throws StandardException
253     {
254         BigDecimal JavaDoc localValue = getBigDecimal();
255         if (localValue == null)
256             return (float)0;
257
258         // If the BigDecimal is out of range for the float
259
// then positive or negative infinity is returned.
260
float value = NumberDataType.normalizeREAL(localValue.floatValue());
261
262         return value;
263     }
264
265     /**
266      *
267      * If we have a value that is greater than the maximum double,
268      * exception is thrown. Otherwise, ok. If the value is less
269      * than can be represented by a double, ti will get set to
270      * the smallest double value.
271      *
272      * @exception StandardException thrown on failure to convert
273      */

274     public double getDouble() throws StandardException
275     {
276         BigDecimal JavaDoc localValue = getBigDecimal();
277         if (localValue == null)
278             return (double)0;
279
280         // If the BigDecimal is out of range for double
281
// then positive or negative infinity is returned.
282
double value = NumberDataType.normalizeDOUBLE(localValue.doubleValue());
283         return value;
284     }
285
286     private BigDecimal JavaDoc getBigDecimal()
287     {
288         if ((value == null) && (rawData != null))
289         {
290             value = new BigDecimal JavaDoc(new BigInteger JavaDoc(rawData), rawScale);
291         }
292
293         return value;
294     }
295     
296     /**
297      * DECIMAL implementation. Convert to a BigDecimal using getObject
298      * which will return a BigDecimal
299      */

300     public int typeToBigDecimal()
301     {
302         return java.sql.Types.DECIMAL;
303     }
304
305     // 0 or null is false, all else is true
306
public boolean getBoolean()
307     {
308
309         BigDecimal JavaDoc localValue = getBigDecimal();
310         if (localValue == null)
311             return false;
312
313         return localValue.compareTo(ZERO) != 0;
314     }
315
316     public String JavaDoc getString()
317     {
318         BigDecimal JavaDoc localValue = getBigDecimal();
319         if (localValue == null)
320             return null;
321         else if (JVMInfo.JDK_ID < JVMInfo.J2SE_15)
322             return localValue.toString();
323         else
324         {
325             // use reflection so we can still compile using JDK1.4
326
// if we are prepared to require 1.5 to compile then this can be a direct call
327
try {
328                 return (String JavaDoc) toPlainString.invoke(localValue, null);
329             } catch (IllegalAccessException JavaDoc e) {
330                 // can't happen based on the JDK spec
331
throw new IllegalAccessError JavaDoc("toPlainString");
332             } catch (InvocationTargetException JavaDoc e) {
333                 Throwable JavaDoc t = e.getTargetException();
334                 if (t instanceof RuntimeException JavaDoc) {
335                     throw (RuntimeException JavaDoc) t;
336                 } else if (t instanceof Error JavaDoc) {
337                     throw (Error JavaDoc) t;
338                 } else {
339                     // can't happen
340
throw new IncompatibleClassChangeError JavaDoc("toPlainString");
341                 }
342             }
343         }
344     }
345
346     private static final Method JavaDoc toPlainString;
347     private static final Method JavaDoc bdPrecision;
348     static {
349         Method JavaDoc m;
350         try {
351             m = BigDecimal JavaDoc.class.getMethod("toPlainString", null);
352         } catch (NoSuchMethodException JavaDoc e) {
353             m = null;
354         }
355         toPlainString = m;
356         try {
357             m = BigDecimal JavaDoc.class.getMethod("precision", null);
358         } catch (NoSuchMethodException JavaDoc e) {
359             m = null;
360         }
361         bdPrecision = m;
362     }
363
364     public Object JavaDoc getObject()
365     {
366         /*
367         ** BigDecimal is immutable
368         */

369         return getBigDecimal();
370     }
371
372     /**
373      * Set the value from a correctly typed BigDecimal object.
374      * @throws StandardException
375      */

376     void setObject(Object JavaDoc theValue) throws StandardException
377     {
378         setValue((BigDecimal JavaDoc) theValue);
379     }
380     
381     protected void setFrom(DataValueDescriptor theValue) throws StandardException {
382
383         setCoreValue(SQLDecimal.getBigDecimal(theValue));
384     }
385
386     public int getLength()
387     {
388         return getDecimalValuePrecision();
389     }
390
391     // this is for DataType's error generator
392
public String JavaDoc getTypeName()
393     {
394         return TypeId.DECIMAL_NAME;
395     }
396
397     /*
398      * Storable interface, implies Externalizable, TypedFormat
399      */

400
401     /**
402         Return my format identifier.
403
404         @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
405     */

406     public int getTypeFormatId()
407     {
408         return StoredFormatIds.SQL_DECIMAL_ID;
409     }
410
411     /*
412      * see if the decimal value is null.
413      */

414     /** @see Storable#isNull */
415     public boolean isNull()
416     {
417         return (value == null) && (rawData == null);
418     }
419
420     /**
421      * Distill the BigDecimal to a byte array and
422      * write out: <UL>
423      * <LI> scale (zero or positive) as a byte </LI>
424      * <LI> length of byte array as a byte</LI>
425      * <LI> the byte array </LI> </UL>
426      *
427      */

428     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc
429     {
430         // never called when value is null
431
if (SanityManager.DEBUG)
432             SanityManager.ASSERT(! isNull());
433
434         int scale;
435         byte[] byteArray;
436
437         if (value != null) {
438             scale = value.scale();
439             
440             // J2SE 5.0 introduced negative scale value for BigDecimals.
441
// In previouse Java releases a negative scale was not allowed
442
// (threw an exception on setScale and the constructor that took
443
// a scale).
444
//
445
// Thus the Derby format for DECIMAL implictly assumed a
446
// positive or zero scale value, and thus now must explicitly
447
// be positive. This is to allow databases created under J2SE 5.0
448
// to continue to be supported under JDK 1.3/JDK 1.4, ie. to continue
449
// the platform independence, independent of OS/cpu and JVM.
450
//
451
// If the scale is negative set the scale to be zero, this results
452
// in an unchanged value with a new scale. A BigDecimal with a
453
// negative scale by definition is a whole number.
454
// e.g. 1000 can be represented by:
455
// a BigDecimal with scale -3 (unscaled value of 1)
456
// or a BigDecimal with scale 0 (unscaled value of 1000)
457

458             if (scale < 0) {
459                 scale = 0;
460                 value = value.setScale(0);
461             }
462
463             BigInteger JavaDoc bi = value.unscaledValue();
464             byteArray = bi.toByteArray();
465         } else {
466             scale = rawScale;
467             byteArray = rawData;
468         }
469         
470         if (SanityManager.DEBUG)
471         {
472             if (scale < 0)
473                 SanityManager.THROWASSERT("DECIMAL scale at writeExternal is negative "
474                     + scale + " value " + toString());
475         }
476
477         out.writeByte(scale);
478         out.writeByte(byteArray.length);
479         out.write(byteArray);
480     }
481
482     /**
483      * Note the use of rawData: we reuse the array if the
484      * incoming array is the same length or smaller than
485      * the array length.
486      *
487      * @see java.io.Externalizable#readExternal
488      */

489     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc
490     {
491         // clear the previous value to ensure that the
492
// rawData value will be used
493
value = null;
494
495         rawScale = in.readUnsignedByte();
496         int size = in.readUnsignedByte();
497
498         /*
499         ** Allocate a new array if the data to read
500         ** is larger than the existing array, or if
501         ** we don't have an array yet.
502
503         Need to use readFully below and NOT just read because read does not
504         guarantee getting size bytes back, whereas readFully does (unless EOF).
505         */

506         if ((rawData == null) || size != rawData.length)
507         {
508             rawData = new byte[size];
509         }
510         in.readFully(rawData);
511
512     }
513     public void readExternalFromArray(ArrayInputStream in) throws IOException JavaDoc
514     {
515         // clear the previous value to ensure that the
516
// rawData value will be used
517
value = null;
518
519         rawScale = in.readUnsignedByte();
520         int size = in.readUnsignedByte();
521
522         /*
523         ** Allocate a new array if the data to read
524         ** is larger than the existing array, or if
525         ** we don't have an array yet.
526
527         Need to use readFully below and NOT just read because read does not
528         guarantee getting size bytes back, whereas readFully does (unless EOF).
529         */

530         if ((rawData == null) || size != rawData.length)
531         {
532             rawData = new byte[size];
533         }
534         in.readFully(rawData);
535     }
536
537     /**
538      * @see Storable#restoreToNull
539      *
540      */

541     public void restoreToNull()
542     {
543         value = null;
544         rawData = null;
545     }
546
547
548     /** @exception StandardException Thrown on error */
549     protected int typeCompare(DataValueDescriptor arg) throws StandardException
550     {
551         BigDecimal JavaDoc otherValue = SQLDecimal.getBigDecimal(arg);
552         return getBigDecimal().compareTo(otherValue);
553     }
554
555     /*
556      * DataValueDescriptor interface
557      */

558
559     /**
560      * <B> WARNING </B> clone is a shallow copy
561      * @see DataValueDescriptor#getClone
562      */

563     public DataValueDescriptor getClone()
564     {
565         return new SQLDecimal(getBigDecimal());
566     }
567
568     /**
569      * @see DataValueDescriptor#getNewNull
570      */

571     public DataValueDescriptor getNewNull()
572     {
573         return new SQLDecimal();
574     }
575
576     /**
577      * @see DataValueDescriptor#setValueFromResultSet
578      *
579      * @exception SQLException Thrown on error
580      */

581     public void setValueFromResultSet(ResultSet JavaDoc resultSet, int colNumber,
582                                       boolean isNullable)
583         throws SQLException JavaDoc
584     {
585             value = resultSet.getBigDecimal(colNumber);
586             rawData = null;
587     }
588     /**
589         Set the value into a PreparedStatement.
590
591         @exception SQLException Error setting value in PreparedStatement
592     */

593     public final void setInto(PreparedStatement JavaDoc ps, int position) throws SQLException JavaDoc {
594
595         if (isNull()) {
596             ps.setNull(position, java.sql.Types.DECIMAL);
597             return;
598         }
599
600         ps.setBigDecimal(position, getBigDecimal());
601     }
602     
603     /**
604      *
605      * <B> WARNING </B> there is no checking to make sure
606      * that theValue doesn't exceed the precision/scale of
607      * the current SQLDecimal. It is just assumed that the
608      * SQLDecimal is supposed to take the precision/scale of
609      * the BigDecimalized String.
610      *
611      * @exception StandardException throws NumberFormatException
612      * when the String format is not recognized.
613      */

614     public void setValue(String JavaDoc theValue) throws StandardException
615     {
616         rawData = null;
617
618         if (theValue == null)
619         {
620             value = null;
621         }
622         else
623         {
624             try
625             {
626                 theValue = theValue.trim();
627                 value = new BigDecimal JavaDoc(theValue);
628                 rawData = null;
629             } catch (NumberFormatException JavaDoc nfe)
630             {
631                 throw invalidFormat();
632             }
633         }
634     }
635
636     /**
637      * @see NumberDataValue#setValue
638      *
639      * @exception StandardException Thrown on error
640      */

641     public void setValue(double theValue) throws StandardException
642     {
643         setCoreValue(NumberDataType.normalizeDOUBLE(theValue));
644     }
645
646     /**
647      * @see NumberDataValue#setValue
648      *
649      */

650     public void setValue(float theValue)
651         throws StandardException
652     {
653         setCoreValue((double)NumberDataType.normalizeREAL(theValue));
654     }
655
656     /**
657      * @see NumberDataValue#setValue
658      *
659      */

660     public void setValue(long theValue)
661     {
662         value = BigDecimal.valueOf(theValue);
663         rawData = null;
664     }
665
666     /**
667      * @see NumberDataValue#setValue
668      *
669      */

670     public void setValue(int theValue)
671     {
672         setValue((long)theValue);
673     }
674
675     /**
676         Only to be called when the application sets a value using BigDecimal
677         through setBigDecimal calls.
678     */

679     public void setBigDecimal(Number JavaDoc theValue) throws StandardException
680     {
681         setCoreValue((BigDecimal JavaDoc) theValue);
682     }
683
684     /**
685         Called when setting a DECIMAL value internally or from
686         through a procedure or function.
687         Handles long in addition to BigDecimal to handle
688         identity being stored as a long but returned as a DECIMAL.
689     */

690     public void setValue(Number JavaDoc theValue) throws StandardException
691     {
692         if (SanityManager.ASSERT)
693         {
694             if (theValue != null &&
695                 !(theValue instanceof java.math.BigDecimal JavaDoc) &&
696                 !(theValue instanceof java.lang.Long JavaDoc))
697                 SanityManager.THROWASSERT("SQLDecimal.setValue(Number) passed a " + theValue.getClass());
698         }
699
700         if (theValue instanceof BigDecimal JavaDoc || theValue == null)
701             setCoreValue((BigDecimal JavaDoc) theValue);
702         else
703             setValue(theValue.longValue());
704     }
705
706     /**
707      * @see NumberDataValue#setValue
708      *
709      */

710     public void setValue(boolean theValue)
711     {
712         setCoreValue(theValue ? ONE : ZERO);
713     }
714
715     /*
716      * DataValueDescriptor interface
717      */

718
719     /** @see DataValueDescriptor#typePrecedence */
720     public int typePrecedence()
721     {
722         return TypeId.DECIMAL_PRECEDENCE;
723     }
724     // END DataValueDescriptor interface
725

726     private void setCoreValue(BigDecimal JavaDoc theValue)
727     {
728         value = theValue;
729         rawData = null;
730     }
731
732     private void setCoreValue(double theValue) {
733         value = new BigDecimal JavaDoc(Double.toString(theValue));
734         rawData = null;
735     }
736
737     /**
738      * Normalization method - this method may be called when putting
739      * a value into a SQLDecimal, for example, when inserting into a SQLDecimal
740      * column. See NormalizeResultSet in execution.
741      * <p>
742      * Note that truncation is allowed on the decimal portion
743      * of a numeric only.
744      *
745      * @param desiredType The type to normalize the source column to
746      * @param source The value to normalize
747      *
748      * @throws StandardException Thrown for null into
749      * non-nullable column, and for
750      * truncation error
751      */

752     public void normalize(
753                 DataTypeDescriptor desiredType,
754                 DataValueDescriptor source)
755                         throws StandardException
756     {
757         int desiredScale = desiredType.getScale();
758         int desiredPrecision = desiredType.getPrecision();
759
760         setFrom(source);
761         setWidth(desiredPrecision, desiredScale, true);
762     }
763
764
765     /*
766     ** SQL Operators
767     */

768
769
770     /**
771      * This method implements the + operator for DECIMAL.
772      *
773      * @param addend1 One of the addends
774      * @param addend2 The other addend
775      * @param result The result of a previous call to this method, null
776      * if not called yet
777      *
778      * @return A SQLDecimal containing the result of the addition
779      *
780      * @exception StandardException Thrown on error
781      */

782
783     public NumberDataValue plus(NumberDataValue addend1,
784                             NumberDataValue addend2,
785                             NumberDataValue result)
786                 throws StandardException
787     {
788         if (result == null)
789         {
790             result = new SQLDecimal();
791         }
792
793         if (addend1.isNull() || addend2.isNull())
794         {
795             result.setToNull();
796             return result;
797         }
798
799         result.setBigDecimal(SQLDecimal.getBigDecimal(addend1).add(SQLDecimal.getBigDecimal(addend2)));
800         return result;
801     }
802
803     /**
804      * This method implements the - operator for "decimal - decimal".
805      *
806      * @param left The value to be subtracted from
807      * @param right The value to be subtracted
808      * @param result The result of a previous call to this method, null
809      * if not called yet
810      *
811      * @return A SQLDecimal containing the result of the subtraction
812      *
813      * @exception StandardException Thrown on error
814      */

815
816     public NumberDataValue minus(NumberDataValue left,
817                             NumberDataValue right,
818                             NumberDataValue result)
819                 throws StandardException
820     {
821         if (result == null)
822         {
823             result = new SQLDecimal();
824         }
825
826         if (left.isNull() || right.isNull())
827         {
828             result.setToNull();
829             return result;
830         }
831
832         result.setBigDecimal(SQLDecimal.getBigDecimal(left).subtract(SQLDecimal.getBigDecimal(right)));
833         return result;
834     }
835
836     /**
837      * This method implements the * operator for "double * double".
838      *
839      * @param left The first value to be multiplied
840      * @param right The second value to be multiplied
841      * @param result The result of a previous call to this method, null
842      * if not called yet
843      *
844      * @return A SQLDecimal containing the result of the multiplication
845      *
846      * @exception StandardException Thrown on error
847      */

848
849     public NumberDataValue times(NumberDataValue left,
850                             NumberDataValue right,
851                             NumberDataValue result)
852                 throws StandardException
853     {
854         if (result == null)
855         {
856             result = new SQLDecimal();
857         }
858
859         if (left.isNull() || right.isNull())
860         {
861             result.setToNull();
862             return result;
863         }
864
865         result.setBigDecimal(SQLDecimal.getBigDecimal(left).multiply(SQLDecimal.getBigDecimal(right)));
866         return result;
867     }
868
869     /**
870      * This method implements the / operator for BigDecimal/BigDecimal
871      *
872      * @param dividend The numerator
873      * @param divisor The denominator
874      * @param result The result of a previous call to this method, null
875      * if not called yet
876      *
877      * @return A SQLDecimal containing the result of the division
878      *
879      * @exception StandardException Thrown on error
880      */

881
882     public NumberDataValue divide(NumberDataValue dividend,
883                              NumberDataValue divisor,
884                              NumberDataValue result)
885                 throws StandardException
886     {
887         return divide(dividend, divisor, result, -1);
888     }
889
890     /**
891      * This method implements the / operator for BigDecimal/BigDecimal
892      *
893      * @param dividend The numerator
894      * @param divisor The denominator
895      * @param result The result of a previous call to this method, null
896      * if not called yet
897      * @param scale The result scale, if < 0, calculate the scale according
898      * to the actual values' sizes
899      *
900      * @return A SQLDecimal containing the result of the division
901      *
902      * @exception StandardException Thrown on error
903      */

904
905     public NumberDataValue divide(NumberDataValue dividend,
906                              NumberDataValue divisor,
907                              NumberDataValue result,
908                              int scale)
909                 throws StandardException
910     {
911         if (result == null)
912         {
913             result = new SQLDecimal();
914         }
915
916         if (dividend.isNull() || divisor.isNull())
917         {
918             result.setToNull();
919             return result;
920         }
921
922         BigDecimal JavaDoc divisorBigDecimal = SQLDecimal.getBigDecimal(divisor);
923
924         if (divisorBigDecimal.compareTo(ZERO) == 0)
925         {
926             throw StandardException.newException(SQLState.LANG_DIVIDE_BY_ZERO);
927         }
928         BigDecimal JavaDoc dividendBigDecimal = SQLDecimal.getBigDecimal(dividend);
929         
930         /*
931         ** Set the result scale to be either the passed in scale, whcih was
932         ** calculated at bind time to be max(ls+rp-rs+1, 4), where ls,rp,rs
933         ** are static data types' sizes, which are predictable and stable
934         ** (for the whole result set column, eg.); otherwise dynamically
935         ** calculates the scale according to actual values. Beetle 3901
936         */

937         result.setBigDecimal(dividendBigDecimal.divide(
938                                     divisorBigDecimal,
939                                     scale > -1 ? scale :
940                                     Math.max((dividendBigDecimal.scale() +
941                                             SQLDecimal.getWholeDigits(divisorBigDecimal) +
942                                             1),
943                                         NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE),
944                                     BigDecimal.ROUND_DOWN));
945         
946         return result;
947     }
948
949     /**
950      * This method implements the unary minus operator for double.
951      *
952      * @param result The result of a previous call to this method, null
953      * if not called yet
954      *
955      * @return A SQLDecimal containing the result of the division
956      *
957      * @exception StandardException Thrown on error
958      */

959
960     public NumberDataValue minus(NumberDataValue result)
961                                     throws StandardException
962     {
963         if (result == null)
964         {
965             result = new SQLDecimal();
966         }
967
968         if (this.isNull())
969         {
970             result.setToNull();
971             return result;
972         }
973
974         result.setBigDecimal(getBigDecimal().negate());
975         return result;
976     }
977
978     /**
979      * This method implements the isNegative method.
980      *
981      * @return A boolean. If this.value is negative, return true.
982      * For positive values or null, return false.
983      */

984
985     protected boolean isNegative()
986     {
987         return !isNull() && (getBigDecimal().compareTo(ZERO) == -1);
988     }
989     
990     /*
991      * String display of value
992      */

993     public String JavaDoc toString()
994     {
995         if (isNull())
996             return "NULL";
997         else
998             return getString();
999     }
1000
1001    /*
1002     * Hash code
1003     */

1004    public int hashCode()
1005    {
1006        long longVal;
1007        BigDecimal JavaDoc localValue = getBigDecimal();
1008
1009        double doubleVal = (localValue != null) ? localValue.doubleValue() : 0;
1010
1011        if (Double.isInfinite(doubleVal))
1012        {
1013            /*
1014             ** This loses the fractional part, but it probably doesn't
1015             ** matter for numbers that are big enough to overflow a double -
1016             ** it's probably rare for numbers this big to be different only in
1017             ** their fractional parts.
1018             */

1019            longVal = localValue.longValue();
1020        }
1021        else
1022        {
1023            longVal = (long) doubleVal;
1024            if (longVal != doubleVal)
1025            {
1026                longVal = Double.doubleToLongBits(doubleVal);
1027            }
1028        }
1029
1030        return (int) (longVal ^ (longVal >> 32));
1031    }
1032
1033    ///////////////////////////////////////////////////////////////////////////////
1034
//
1035
// VariableSizeDataValue interface
1036
//
1037
///////////////////////////////////////////////////////////////////////////////
1038

1039    /**
1040     * Set the precision/scale of the to the desired values.
1041     * Used when CASTing. Ideally we'd recycle normalize(), but
1042     * the use is different.
1043     *
1044     * @param desiredPrecision the desired precision -- IGNORE_PREICISION
1045     * if it is to be ignored.
1046     * @param desiredScale the desired scale
1047     * @param errorOnTrunc throw error on truncation (ignored --
1048     * always thrown if we truncate the non-decimal part of
1049     * the value)
1050     *
1051     * @exception StandardException Thrown on non-zero truncation
1052     * if errorOnTrunc is true
1053     */

1054    public void setWidth(int desiredPrecision,
1055            int desiredScale,
1056            boolean errorOnTrunc)
1057            throws StandardException
1058    {
1059        if (isNull())
1060            return;
1061            
1062        if (desiredPrecision != IGNORE_PRECISION &&
1063            ((desiredPrecision - desiredScale) < SQLDecimal.getWholeDigits(getBigDecimal())))
1064        {
1065            throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
1066                                    ("DECIMAL/NUMERIC("+desiredPrecision+","+desiredScale+")"));
1067        }
1068        value = value.setScale(desiredScale, BigDecimal.ROUND_DOWN);
1069        rawData = null;
1070    }
1071
1072    /**
1073     * Return the SQL scale of this value, number of digits after the
1074     * decimal point, or zero for a whole number. This does not match the
1075     * return from BigDecimal.scale() since in J2SE 5.0 onwards that can return
1076     * negative scales.
1077     */

1078    public int getDecimalValuePrecision()
1079    {
1080        if (isNull())
1081            return 0;
1082            
1083        BigDecimal JavaDoc localValue = getBigDecimal();
1084
1085        return SQLDecimal.getWholeDigits(localValue) + getDecimalValueScale();
1086    }
1087
1088    /**
1089     * Return the SQL scale of this value, number of digits after the
1090     * decimal point, or zero for a whole number. This does not match the
1091     * return from BigDecimal.scale() since in J2SE 5.0 onwards that can return
1092     * negative scales.
1093     */

1094    public int getDecimalValueScale()
1095    {
1096        if (isNull())
1097            return 0;
1098        
1099        if (value == null)
1100            return rawScale;
1101    
1102        int scale = value.scale();
1103        if (scale >= 0)
1104            return scale;
1105        
1106        // BigDecimal scale is negative, so number must have no fractional
1107
// part as its value is the unscaled value * 10^-scale
1108
return 0;
1109    }
1110    
1111    /**
1112     * Get a BigDecimal representing the value of a DataValueDescriptor
1113     * @param value Non-null value to be converted
1114     * @return BigDecimal value
1115     * @throws StandardException Invalid conversion or out of range.
1116     */

1117    public static BigDecimal JavaDoc getBigDecimal(DataValueDescriptor value) throws StandardException
1118    {
1119        if (SanityManager.DEBUG)
1120        {
1121            if (value.isNull())
1122                SanityManager.THROWASSERT("NULL value passed to SQLDecimal.getBigDecimal");
1123        }
1124        
1125        switch (value.typeToBigDecimal())
1126        {
1127        case Types.DECIMAL:
1128            return (BigDecimal JavaDoc) value.getObject();
1129        case Types.CHAR:
1130            try {
1131                return new BigDecimal JavaDoc(value.getString().trim());
1132            } catch (NumberFormatException JavaDoc nfe) {
1133                throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, "java.math.BigDecimal");
1134            }
1135        case Types.BIGINT:
1136            return BigDecimal.valueOf(value.getLong());
1137        default:
1138            if (SanityManager.DEBUG)
1139                SanityManager.THROWASSERT("invalid return from " + value.getClass() + ".typeToBigDecimal() " + value.typeToBigDecimal());
1140            return null;
1141        }
1142    }
1143
1144    /**
1145     * Calculate the number of digits to the left of the decimal point
1146     * of the passed in value.
1147     * @param decimalValue Value to get whole digits from, never null.
1148     * @return number of whole digits.
1149     */

1150    private static int getWholeDigits(BigDecimal JavaDoc decimalValue)
1151    {
1152        /**
1153         * if ONE > abs(value) then the number of whole digits is 0
1154         */

1155        decimalValue = decimalValue.abs();
1156        if (ONE.compareTo(decimalValue) == 1)
1157        {
1158            return 0;
1159        }
1160        
1161        if (JVMInfo.JDK_ID >= JVMInfo.J2SE_15)
1162        {
1163            // use reflection so we can still compile using JDK1.4
1164
// if we are prepared to require 1.5 to compile then this can be a
1165
// direct call
1166
try {
1167                // precision is the number of digits in the unscaled value,
1168
// subtracting the scale (positive or negative) will give the
1169
// number of whole digits.
1170
int precision = ((Integer JavaDoc) bdPrecision.invoke(decimalValue,
1171                        null)).intValue();
1172                return precision - decimalValue.scale();
1173            } catch (IllegalAccessException JavaDoc e) {
1174                // can't happen based on the JDK spec
1175
throw new IllegalAccessError JavaDoc("precision");
1176            } catch (InvocationTargetException JavaDoc e) {
1177                Throwable JavaDoc t = e.getTargetException();
1178                if (t instanceof RuntimeException JavaDoc) {
1179                    throw (RuntimeException JavaDoc) t;
1180                } else if (t instanceof Error JavaDoc) {
1181                    throw (Error JavaDoc) t;
1182                } else {
1183                    // can't happen
1184
throw new IncompatibleClassChangeError JavaDoc("precision");
1185                }
1186            }
1187            
1188        }
1189   
1190        String JavaDoc s = decimalValue.toString();
1191        return (decimalValue.scale() == 0) ? s.length() : s.indexOf('.');
1192    }
1193}
1194
Popular Tags