/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.tdb.store; import java.math.BigDecimal; import java.math.BigInteger; import org.apache.jena.atlas.lib.BitsLong ; public class DecimalNode { //private static Logger log = LoggerFactory.getLogger(DecimalNode.class) ; BigDecimal decimal = null ; // signed 8 bits of scale, signed 48 bits of value. // Decimal precision is 47 bits (it's signed) or around 14 places. // Not finance industry accuracy nor XSD (18 places minimum) but still useful. static final int SCALE_LEN = 8 ; static final int VALUE_LEN = 48 ; static final int ENC_LEN = 48+SCALE_LEN ; static final long MAX_VALUE = (1L<< (VALUE_LEN-1) )-1 ; static final long MIN_VALUE = -(1L<< (VALUE_LEN-1) ) ; static final int MAX_SCALE = (1<< (SCALE_LEN-1) )-1 ; static final int MIN_SCALE = -(1<< (SCALE_LEN-1) ) ; static final BigInteger MAX_I = BigInteger.valueOf(MAX_VALUE) ; static final BigInteger MIN_I = BigInteger.valueOf(MIN_VALUE) ; // Bits counts static private int SCALE_LO = 56-SCALE_LEN ; static private int SCALE_HI = 56 ; // Exclusive index static private int VALUE_LO = 0 ; static private int VALUE_HI = VALUE_LO+VALUE_LEN ; private int scale ; private long value ; public static DecimalNode valueOf(BigDecimal decimal) { int scale = decimal.scale() ; BigInteger bigInt = decimal.unscaledValue() ; if ( bigInt.compareTo(MAX_I) > 0 || bigInt.compareTo(MIN_I) < 0 ) // This check makes sure that bigInt.longValue() is safe return null ; return valueOf(bigInt.longValue(), scale) ; } public static DecimalNode valueOf(long binValue, int scale) { if ( scale < MIN_SCALE || scale > MAX_SCALE ) { //log.warn("Scale out of range: ("+binValue+","+scale+")") ; return null ; } if ( binValue < MIN_VALUE || binValue > MAX_VALUE ) { //log.warn("Value out of range: ("+binValue+","+scale+")") ; return null ; } return new DecimalNode(binValue, scale) ; } private DecimalNode(long value, int scale) { this.scale = scale ; this.value = value ; } public long pack() { return pack(value, scale) ; } public static long pack(long value, int scale) { // pack : DECIMAL , sign, scale, value long v = BitsLong.pack(0, NodeId.DECIMAL, ENC_LEN, Long.SIZE) ; v = BitsLong.pack(v, scale, SCALE_LO, SCALE_HI) ; v = BitsLong.pack(v, value, VALUE_LO, VALUE_HI) ; // No need to do something about negative numbers return v ; } public static DecimalNode unpack(long v) { //assert BitsLong.unpack(v, 56, 64) == NodeId.DECIMAL ; int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI) ; long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI) ; return new DecimalNode(value, scale) ; } public static BigDecimal unpackAsBigDecimal(long v) { // Can I say tuples-in-java? Or "Multiple return values"? //assert BitsLong.unpack(v, 56, 64) == NodeId.DECIMAL ; int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI) ; long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI) ; // Sign extend value. if ( BitsLong.isSet(value, VALUE_HI-1) ) value = value | -1L<<(VALUE_HI) ; return BigDecimal.valueOf(value, scale) ; } public BigDecimal get() { if ( decimal == null ) decimal = BigDecimal.valueOf(value, scale) ; return decimal ; } @Override public String toString() { return get().toPlainString() ; } public int getScale() { return scale ; } public long getValue() { return value ; } }