KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.iapi.types.BinaryDecimal
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.io.IOException JavaDoc;
25 import java.io.ObjectInput JavaDoc;
26 import java.io.ObjectOutput JavaDoc;
27 import java.sql.ResultSet JavaDoc;
28 import java.sql.SQLException JavaDoc;
29 import java.sql.Types JavaDoc;
30
31 import org.apache.derby.iapi.error.StandardException;
32 import org.apache.derby.iapi.reference.SQLState;
33 import org.apache.derby.iapi.services.io.ArrayInputStream;
34 import org.apache.derby.iapi.services.io.StoredFormatIds;
35 import org.apache.derby.iapi.services.sanity.SanityManager;
36
37 /**
38  * SQL DECIMAL using raw data. Provides the basis for the
39  * CDCDecimal implementation.
40  * <P>
41  * The on-disk format must match the SQLDecimal format so that
42  * databases are portable across J2ME and J2SE environments.
43  * <P>
44  * The format of the byte array is defined by the return of the
45  * java.math.BigInteger.toByteArray:, extracted here.
46  *
47  * Returns a byte array containing the two's-complement representation of this BigInteger.
48  * The byte array will be in big-endian byte-order: the most significant byte is in the zeroth element.
49  *
50  * This is the format for DECIMAL even if BigINteger is not available, e.g. OSGi ee.minimum.
51  */

52
53 abstract class BinaryDecimal extends NumberDataType
54     implements VariableSizeDataValue
55 {
56     /**
57      * An unscaled value of 1 in two's complement
58      */

59     private static final byte[] ONE_2C = {(byte) 0x01};
60     
61     /**
62      * The unscaled value as a binary two's complement array.
63     */

64     protected byte[] data2c;
65
66     /**
67      * The SQL scale, zero or positive, of the value
68      */

69     protected int sqlScale;
70     
71     
72     BinaryDecimal() {
73     }
74     
75     /*
76     ** Methods about the DECIMAL type itself.
77     */

78
79     /**
80      * DECIMAL implementation.
81      * Use DECIMAL to indicate to self that another
82      * passed in value is an instance of this type.
83      */

84     public final int typeToBigDecimal()
85     {
86         return java.sql.Types.DECIMAL;
87     }
88
89     /** @see DataValueDescriptor#typePrecedence */
90     public final int typePrecedence()
91     {
92         return TypeId.DECIMAL_PRECEDENCE;
93     }
94     
95     /* Return DECIMAL as the type name.
96      * @see org.apache.derby.iapi.types.DataValueDescriptor#getTypeName()
97      */

98     public final String JavaDoc getTypeName() {
99         return TypeId.DECIMAL_NAME;
100     }
101
102     /**
103      * Return my format identifier.
104      *
105      * @see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
106      */

107     public final int getTypeFormatId() {
108         return StoredFormatIds.SQL_DECIMAL_ID;
109     }
110     
111     /*
112     ** NULL handling.
113     */

114
115     /**
116      * see if the decimal value is null.
117      */

118     public boolean isNull()
119     {
120         return data2c == null;
121     }
122
123     public void restoreToNull()
124     {
125         data2c = null;
126     }
127
128     /* Check the leftmost bit, if set the value is negative.
129      * NULL values return false.
130      * @see org.apache.derby.iapi.types.NumberDataType#isNegative()
131      */

132     protected boolean isNegative() {
133         return !isNull() && ((data2c[0] & 0x80) != 0);
134     }
135     
136     
137     /*
138     ** Methods to convert values into this DECIMAL
139     */

140     
141     /**
142      * Set the value from a long.
143      */

144     public void setValue(long theValue)
145     {
146         byte[] rd = data2c;
147         if (rd == null || rd.length < 8)
148             rd = new byte[8];
149         
150         rd[0] = (byte)(theValue >>> 56);
151         rd[1] = (byte)(theValue >>> 48);
152         rd[2] = (byte)(theValue >>> 40);
153         rd[3] = (byte)(theValue >>> 32);
154         rd[4] = (byte)(theValue >>> 24);
155         rd[5] = (byte)(theValue >>> 16);
156         rd[6] = (byte)(theValue >>> 8);
157         rd[7] = (byte) theValue;
158         
159         if (SanityManager.DEBUG)
160         {
161             data2c = rd;
162             sqlScale = 0;
163             try {
164             if (theValue != getLong())
165                 SanityManager.THROWASSERT("BinaryDecimal invalid long conversion before reduce in "
166                         + theValue + " out " + getLong());
167             }
168             catch (StandardException se)
169             {
170                 SanityManager.THROWASSERT(se.toString());
171             }
172         }
173         
174         data2c = BinaryDecimal.reduceBytes2c(rd, 0, 8);
175         sqlScale = 0;
176         
177         if (SanityManager.DEBUG)
178         {
179             try {
180             if (theValue != getLong())
181                 SanityManager.THROWASSERT("BinaryDecimal invalid long conversion after reduce in "
182                         + theValue + " out " + getLong());
183             }
184             catch (StandardException se)
185             {
186                 SanityManager.THROWASSERT(se.toString());
187             }
188         }
189     }
190
191     /**
192      * Set the value from an int, just copy 'byte-by-byte'
193      * from the int to a four byte array. Then reduce.
194      * @see NumberDataValue#setValue
195      */

196     public final void setValue(int theValue)
197     {
198         byte[] rd = data2c;
199         if (rd == null || rd.length < 4)
200             rd = new byte[4];
201         
202         rd[0] = (byte)(theValue >>> 24);
203         rd[1] = (byte)(theValue >>> 16);
204         rd[2] = (byte)(theValue >>> 8);
205         rd[3] = (byte) theValue;
206             
207         data2c = BinaryDecimal.reduceBytes2c(rd, 0, 4);
208         sqlScale = 0;
209     }
210     
211     /**
212      * Set the value from a boolean
213      */

214     public void setValue(boolean theValue)
215     {
216         int intValue = theValue ? 1 : 0;
217         setValue(intValue);
218     }
219     
220     /**
221      * Convert from a double, normalize and then convert as a String.
222      *
223      * @exception StandardException Thrown on error
224      */

225     public final void setValue(double theValue) throws StandardException
226     {
227         setCoreValue(NumberDataType.normalizeDOUBLE(theValue));
228     }
229
230     /**
231      * Convert from a float, normalize and then convert as a String.
232      *
233      */

234     public final void setValue(float theValue)
235         throws StandardException
236     {
237         setCoreValue((double)NumberDataType.normalizeREAL(theValue));
238     }
239     
240     private void setCoreValue(double theValue) throws StandardException {
241         setValue(Double.toString(theValue));
242     }
243     
244     /**
245     Called when setting a DECIMAL value internally or from
246     through a procedure or function.
247     Handles long in addition to BigDecimal to handle
248     identity being stored as a long but returned as a DECIMAL.
249     */

250     public void setValue(Number JavaDoc theValue) throws StandardException
251     {
252         if (SanityManager.ASSERT)
253         {
254             if (theValue != null &&
255                 !(theValue instanceof java.lang.Long JavaDoc))
256                 SanityManager.THROWASSERT("BinaryDecimal.setValue(Number) passed a " + theValue.getClass());
257         }
258     
259         if (theValue == null)
260             setToNull();
261         else
262             setValue(theValue.longValue());
263     }
264     
265     /**
266      * Set this DECIMAL value from another DataValueDescriptor
267      */

268     protected void setFrom(DataValueDescriptor dvd) throws StandardException
269     {
270     
271         switch (dvd.typeToBigDecimal())
272         {
273             case Types.CHAR:
274             case Types.DECIMAL: // TODO : direct copy
275

276                 setValue(dvd.getString());
277                 break;
278             case Types.BIGINT:
279                 setValue(dvd.getLong());
280                 break;
281             default:
282                 super.setFrom(dvd);
283         }
284     }
285     /*
286     ** Methods to get a value from this DECIMAL
287     */

288
289     /**
290      * Return a int from this value.
291      *
292      * @exception StandardException
293      * this value is out of range for an int
294      */

295     public final int getInt() throws StandardException
296     {
297         if (isNull())
298             return 0;
299
300         try {
301             long lv = getLong();
302
303             if ((lv >= Integer.MIN_VALUE) && (lv <= Integer.MAX_VALUE))
304                 return (int) lv;
305
306         } catch (StandardException se) {
307             // out of range error but with incorrect messgae (BIGINT)
308
// fall through to correct message
309
}
310
311         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
312     }
313
314     /**
315      * Return a byte from this value.
316      *
317      * @exception StandardException
318      * this value is out of range for a short
319      */

320     public final byte getByte() throws StandardException {
321         if (isNull())
322             return (byte) 0;
323
324         try {
325             long lv = getLong();
326
327             if ((lv >= Byte.MIN_VALUE) && (lv <= Byte.MAX_VALUE))
328                 return (byte) lv;
329
330         } catch (StandardException se) {
331             // out of range error but with incorrect messgae (BIGINT)
332
// fall through to correct message
333
}
334
335         throw StandardException.newException(
336                 SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TINYINT");
337     }
338
339     /**
340      * Return a short from this value.
341      * @exception StandardException this value is out of range for a short
342      */

343     public final short getShort() throws StandardException
344     {
345         if (isNull())
346             return (short)0;
347
348         try {
349             long lv = getLong();
350
351             if ((lv >= Short.MIN_VALUE) && (lv <= Short.MAX_VALUE))
352                 return (short) lv;
353
354         } catch (StandardException se) {
355             // out of range error but with incorrect messgae (BIGINT)
356
// fall through to correct message
357
}
358
359         throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "SMALLINT");
360     }
361     
362     
363     /*
364     ** DECIMAL arithmetic methods.
365     */

366     
367     /**
368      * This method implements the + operator for DECIMAL.
369      *
370      * @param addend1 One of the addends
371      * @param addend2 The other addend
372      * @param result The result of a previous call to this method, null
373      * if not called yet
374      *
375      * @return A SQLDecimal containing the result of the addition
376      *
377      * @exception StandardException Thrown on error
378      */

379
380     public final NumberDataValue plus(NumberDataValue addend1,
381                             NumberDataValue addend2,
382                             NumberDataValue result)
383                 throws StandardException
384     {
385         if (result == null)
386         {
387             result = (NumberDataValue) getNewNull();
388         }
389
390         if (addend1.isNull() || addend2.isNull())
391         {
392             result.setToNull();
393             return result;
394         }
395
396         return plusNN(addend1, addend2, result);
397     }
398     
399     /* (non-Javadoc)
400      * @see org.apache.derby.iapi.types.NumberDataValue#times(org.apache.derby.iapi.types.NumberDataValue, org.apache.derby.iapi.types.NumberDataValue, org.apache.derby.iapi.types.NumberDataValue)
401      */

402     public final NumberDataValue times(NumberDataValue left, NumberDataValue right, NumberDataValue result)
403     throws StandardException
404     {
405         if (result == null)
406         {
407             result = (NumberDataValue) getNewNull();
408         }
409
410         if (left.isNull() || right.isNull())
411         {
412             result.setToNull();
413             return result;
414         }
415         return timesNN(left, right, result);
416     }
417     public NumberDataValue divide(NumberDataValue dividend,
418              NumberDataValue divisor,
419              NumberDataValue result)
420 throws StandardException
421 {
422 return divide(dividend, divisor, result, -1);
423 }
424
425     /**
426      * This method implements the / operator for BigDecimal/BigDecimal
427      *
428      * @param dividend The numerator
429      * @param divisor The denominator
430      * @param result The result of a previous call to this method, null
431      * if not called yet
432      * @param scale The result scale, if < 0, calculate the scale according
433      * to the actual values' sizes
434      *
435      * @return A SQLDecimal containing the result of the division
436      *
437      * @exception StandardException Thrown on error
438      */

439
440     public final NumberDataValue divide(NumberDataValue dividend,
441                              NumberDataValue divisor,
442                              NumberDataValue result,
443                              int scale)
444                 throws StandardException
445     {
446         if (result == null)
447         {
448             result = (NumberDataValue) getNewNull();
449         }
450
451         if (dividend.isNull() || divisor.isNull())
452         {
453             result.setToNull();
454             return result;
455         }
456         
457         return divideNN(dividend, divisor, result, scale);
458     }
459     public final NumberDataValue minus(NumberDataValue left, NumberDataValue right, NumberDataValue result)
460     throws StandardException
461     {
462         if (result == null)
463         {
464             result = (NumberDataValue) getNewNull();
465         }
466
467         if (left.isNull() || right.isNull())
468         {
469             result.setToNull();
470             return result;
471         }
472         
473         return minusNN(left, right, result);
474     }
475     
476     /**
477      * Implement subtraction using addition and negation of the right value.
478      */

479     public NumberDataValue minusNN(NumberDataValue left, NumberDataValue right, NumberDataValue result)
480         throws StandardException
481     {
482         // Requires plusNN() correctly handles that its right argument and
483
// result can be references to the same object.
484
return plusNN(left, right.minus(result), result);
485     }
486         
487     /*
488     ** Abstract methods for handling non-null arithmetic.
489     ** Eventually move these methods into NumberDataType
490     ** and directly compile to them when arguments cannot
491     ** be null. A performance optimization.
492     */

493     
494     
495     /**
496      * Multiple two non-nullable values using DECIMAL arithmetic.
497      */

498     public abstract NumberDataValue timesNN(NumberDataValue left,
499             NumberDataValue right, NumberDataValue result)
500             throws StandardException;
501
502     /**
503      * Add two non-nullable values using DECIMAL arithmetic.
504      * For subclasses of BinaryDecimal, any implementation
505      * must handle the result and addend2 (right) being references
506      * to the same object.
507      */

508     public abstract NumberDataValue plusNN(NumberDataValue addend1,
509             NumberDataValue addend2, NumberDataValue result)
510             throws StandardException;
511
512     /**
513      * Divide two non-nullable values using DECIMAL arithmetic.
514      */

515     public abstract NumberDataValue divideNN(NumberDataValue dividend,
516             NumberDataValue divisor, NumberDataValue result, int scale)
517             throws StandardException;
518     
519     /*
520     ** Methods that act directly on twos complement byte arrays.
521     */

522
523     /**
524      * Compress the passed in byte array so that leading
525      * 0x00 and 0xff are removed when possible.
526      * E.g.
527      * 0x00000453 ->>> 0x0453
528      * 0xfffffff2 ->>> 0xf2
529      * 0xff192312 ->>> 0xff192312 (unchanged)
530      * 0xffff8039 ->>> 0x8039
531      * data2c is set to the compressed value.
532      * @param dataLength Valid length of data in data2c.
533      */

534     private static byte[] reduceBytes2c(byte[] rd, int offset, int dataLength)
535     {
536         // look for leading zeros, if the value
537
// is dataLength bytes long then look
538
// at up to the first (dataLength - 1) bytes
539
// to see if leading 0x00 can be removed.
540

541         int leading;
542         for (leading = 0; leading < (dataLength - 1); leading++)
543         {
544             if (rd[offset + leading] != (byte) 0)
545                 break;
546             
547             // if the hi bit of the next byte is set
548
// then we cannot strip this 0x00 otherwise
549
// the number will turn negative.
550
if ((rd[offset + leading + 1] & 0x80) != 0)
551                 break;
552         }
553
554         if (leading == 0)
555         {
556             // now a similar trick with 0xff, but a slight
557
// complication.
558
for (; leading < (dataLength - 1); leading++)
559             {
560                 // Need to check the highest byte of the
561
// would-be remaining significant byte is
562
// set to indicate this is still a negative number
563

564                 if ((rd[offset + leading] == (byte) 0xff) && ((rd[offset + leading+1] & (byte) 0x80) != 0))
565                     continue;
566                 break;
567             }
568         }
569         
570         if ((leading != 0) || (rd.length != dataLength))
571         {
572             byte[] reduced = new byte[dataLength - leading];
573             System.arraycopy(rd, offset + leading, reduced, 0, reduced.length);
574             return reduced;
575         }
576         
577         return rd;
578     }
579
580     /**
581      * Return the SQL scale of this value, number of digits after the
582      * decimal point, or zero for a whole number.
583      */

584     public int getDecimalValueScale()
585     {
586         if (isNull())
587             return 0;
588         
589         return sqlScale;
590     }
591     
592     /*
593     ** I/O handling
594     */

595
596     /**
597      * Distill the Decimal to a byte array and
598      * Write out: <UL>
599      * <LI> scale (unsigned byte) </LI>
600      * <LI> length of byte array </LI>
601      * <LI> the byte array </LI> </UL>
602      *
603      */

604     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc
605     {
606         // never called when value is null
607
if (SanityManager.DEBUG)
608             SanityManager.ASSERT(! isNull());
609
610         out.writeByte(sqlScale);
611         out.writeByte(data2c.length);
612         out.write(data2c);
613     }
614     
615     /**
616      * Note the use of data2c: we reuse the array if the
617      * incoming array is the same length or smaller than
618      * the array length.
619      *
620      * @see java.io.Externalizable#readExternal
621      */

622     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc
623     {
624         sqlScale = in.readUnsignedByte();
625         int size = in.readUnsignedByte();
626
627         /*
628         ** Allocate a new array if the data to read
629         ** is larger than the existing array, or if
630         ** we don't have an array yet.
631
632         Need to use readFully below and NOT just read because read does not
633         guarantee getting size bytes back, whereas readFully does (unless EOF).
634         */

635         if ((data2c == null) || size != data2c.length)
636         {
637             data2c = new byte[size];
638         }
639         in.readFully(data2c);
640
641     }
642     public void readExternalFromArray(ArrayInputStream in) throws IOException JavaDoc
643     {
644         sqlScale = in.readUnsignedByte();
645         int size = in.readUnsignedByte();
646
647         /*
648         ** Allocate a new array if the data to read
649         ** is larger than the existing array, or if
650         ** we don't have an array yet.
651
652         Need to use readFully below and NOT just read because read does not
653         guarantee getting size bytes back, whereas readFully does (unless EOF).
654         */

655         if ((data2c == null) || size != data2c.length)
656         {
657             data2c = new byte[size];
658         }
659         in.readFully(data2c);
660     }
661
662
663
664     public final int getLength()
665     {
666         return getDecimalValuePrecision();
667     }
668
669     /* (non-Javadoc)
670      * @see org.apache.derby.iapi.types.DataValueDescriptor#getClone()
671      */

672     public DataValueDescriptor getClone() {
673         BinaryDecimal dvd = (BinaryDecimal) getNewNull();
674         
675         if (this.data2c != null)
676         {
677             dvd.data2c = new byte[data2c.length];
678             System.arraycopy(data2c, 0, dvd.data2c, 0, data2c.length);
679             dvd.sqlScale = sqlScale;
680         }
681         
682         return dvd;
683     }
684
685     /* (non-Javadoc)
686      * @see org.apache.derby.iapi.types.DataValueDescriptor#setValueFromResultSet(java.sql.ResultSet, int, boolean)
687      */

688     public void setValueFromResultSet(ResultSet JavaDoc resultSet, int colNumber, boolean isNullable) throws StandardException, SQLException JavaDoc {
689         // TODO Auto-generated method stub
690
throw StandardException.newException(SQLState.NOT_IMPLEMENTED);
691         
692     }
693
694     /* (non-Javadoc)
695      * @see org.apache.derby.iapi.types.DataValueDescriptor#estimateMemoryUsage()
696      */

697     public int estimateMemoryUsage() {
698         // TODO Auto-generated method stub
699
return 0;
700     }
701     
702     public int hashCode()
703     {
704         if (isNull())
705             return 0;
706
707         try {
708             return (int) getLong();
709         } catch (StandardException se)
710         {
711             return 0;
712         }
713     }
714 }
715
Popular Tags