001/* 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil. Licensed under the Apache 003 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the 004 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 005 * Unless required by applicable law or agreed to in writing, software distributed under the License 006 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 007 * or implied. See the License for the specific language governing permissions and limitations under 008 * the License. 009 */ 010package org.javamoney.moneta.spi; 011 012import java.math.BigDecimal; 013import java.math.BigInteger; 014import java.util.Objects; 015import java.util.concurrent.atomic.AtomicLong; 016 017import javax.money.MonetaryContext; 018import javax.money.NumberValue; 019 020import org.javamoney.moneta.Money; 021 022/** 023 * Default implementation of {@link NumberValue} based on {@link BigDecimal}. 024 * 025 * @author Anatole Tresch 026 * @author Werner Keil 027 */ 028public class DefaultNumberValue extends NumberValue { 029 030 /** 031 * serialVersionUID. 032 */ 033 private static final long serialVersionUID = 1L; 034 /** The numeric value. */ 035 private final Number number; 036 037 /** 038 * The value 1, with a scale of 0.<br> 039 * Backed by {@link BigDecimal#ONE} 040 * 041 * @since 0.8 042 */ 043 public static final NumberValue ONE = new DefaultNumberValue(BigDecimal.ONE); 044 045 public DefaultNumberValue(Number number) { 046 Objects.requireNonNull(number, "Number required"); 047 this.number = number; 048 } 049 050 /** 051 * Creates a new instance of {@link NumberValue}, using the given number. 052 * 053 * @param number 054 * The numeric part, not null. 055 * @return A new instance of {@link NumberValue}. 056 */ 057 public static NumberValue of(Number number) { 058 return new DefaultNumberValue(number); 059 } 060 061 /* 062 * (non-Javadoc) 063 * @see javax.money.NumberValue#getNumberType() 064 */ 065 @Override 066 public Class<?> getNumberType() { 067 return this.number.getClass(); 068 } 069 070 /* 071 * (non-Javadoc) 072 * @see javax.money.NumberValue#getPrecision() 073 */ 074 @Override 075 public int getPrecision() { 076 return numberValue(BigDecimal.class).precision(); 077 } 078 079 /* 080 * (non-Javadoc) 081 * @see javax.money.NumberValue#getScale() 082 */ 083 @Override 084 public int getScale() { 085 return numberValue(BigDecimal.class).scale(); 086 } 087 088 /* 089 * (non-Javadoc) 090 * @see javax.money.NumberBinding#getIntValue() 091 */ 092 @Override 093 public int intValue() { 094 return this.number.intValue(); 095 } 096 097 /* 098 * (non-Javadoc) 099 * @see javax.money.NumberBinding#getIntValueExact() 100 */ 101 @Override 102 public int intValueExact() { 103 return getBigDecimal(number).intValueExact(); 104 } 105 106 /* 107 * (non-Javadoc) 108 * @see javax.money.NumberBinding#getLongValue() 109 */ 110 @Override 111 public long longValue() { 112 return this.number.longValue(); 113 } 114 115 /* 116 * (non-Javadoc) 117 * @see javax.money.NumberBinding#getLongValueExact() 118 */ 119 @Override 120 public long longValueExact() { 121 return getBigDecimal(number).longValueExact(); 122 } 123 124 /* 125 * (non-Javadoc) 126 * @see javax.money.NumberBinding#getFloatValue() 127 */ 128 @Override 129 public float floatValue() { 130 return this.number.floatValue(); 131 } 132 133 /* 134 * (non-Javadoc) 135 * @see javax.money.NumberBinding#getDoubleValue() 136 */ 137 @Override 138 public double doubleValue() { 139 return this.number.doubleValue(); 140 } 141 142 /* 143 * (non-Javadoc) 144 * @see javax.money.NumberBinding#getDoubleValueExact() 145 */ 146 @Override 147 public double doubleValueExact() { 148 double d = this.number.doubleValue(); 149 if (d == Double.NEGATIVE_INFINITY || d == Double.POSITIVE_INFINITY) { 150 throw new ArithmeticException("Unable to convert to double: " 151 + this.number); 152 } 153 return d; 154 } 155 156 /* 157 * (non-Javadoc) 158 * @see javax.money.NumberBinding#getNumberValue(java.lang.Class) 159 */ 160 @SuppressWarnings("unchecked") 161 @Override 162 public <T extends Number> T numberValue(Class<T> numberType) { 163 if (BigDecimal.class == numberType) { 164 return (T) getBigDecimal(number); 165 } 166 else if (BigInteger.class == numberType) { 167 return (T) getBigDecimal(number).toBigInteger(); 168 } 169 else if (Double.class == numberType) { 170 return (T) Double.valueOf(this.number.doubleValue()); 171 } 172 else if (Float.class == numberType) { 173 return (T) Float.valueOf(this.number.floatValue()); 174 } 175 else if (Long.class == numberType) { 176 return (T) Long.valueOf(this.number.longValue()); 177 } 178 else if (Integer.class == numberType) { 179 return (T) Integer.valueOf(this.number.intValue()); 180 } 181 else if (Short.class == numberType) { 182 return (T) Short.valueOf(this.number.shortValue()); 183 } 184 else if (Byte.class == numberType) { 185 return (T) Byte.valueOf(this.number.byteValue()); 186 } 187 throw new IllegalArgumentException("Unsupported numeric type: " 188 + numberType); 189 } 190 191 /* 192 * (non-Javadoc) 193 * @see javax.money.NumberValue#numberValueExact(java.lang.Class) 194 */ 195 @SuppressWarnings("unchecked") 196 @Override 197 public <T extends Number> T numberValueExact(Class<T> numberType) { 198 if (BigDecimal.class == numberType) { 199 return (T) getBigDecimal(number); 200 } 201 else if (BigInteger.class == numberType) { 202 return (T) getBigDecimal(number).toBigIntegerExact(); 203 } 204 else if (Double.class == numberType) { 205 double d = this.number.doubleValue(); 206 if (d == Double.NEGATIVE_INFINITY || d == Double.POSITIVE_INFINITY) { 207 throw new ArithmeticException( 208 "Value not exact mappable to double: " + this.number); 209 } 210 return (T) Double.valueOf(d); 211 } 212 else if (Float.class == numberType) { 213 float f = this.number.floatValue(); 214 if (f == Float.NEGATIVE_INFINITY || f == Float.POSITIVE_INFINITY) { 215 throw new ArithmeticException( 216 "Value not exact mappable to float: " + this.number); 217 } 218 return (T) Float.valueOf(f); 219 } 220 else if (Long.class == numberType) { 221 return (T) Long.valueOf(getBigDecimal(number).longValueExact()); 222 } 223 else if (Integer.class == numberType) { 224 return (T) Integer.valueOf(getBigDecimal(number).intValueExact()); 225 } 226 else if (Short.class == numberType) { 227 return (T) Short.valueOf(getBigDecimal(number).shortValueExact()); 228 } 229 else if (Byte.class == numberType) { 230 return (T) Short.valueOf(getBigDecimal(number).byteValueExact()); 231 } 232 throw new IllegalArgumentException("Unsupported numeric type: " 233 + numberType); 234 } 235 236 /* 237 * (non-Javadoc) 238 * @see java.lang.Object#toString() 239 */ 240 @Override 241 public String toString() { 242 return String.valueOf(number); 243 } 244 245 /** 246 * Creates a {@link BigDecimal} from the given {@link Number} doing the valid conversion 247 * depending the type given. 248 * 249 * @param num 250 * the number type 251 * @return the corresponding {@link BigDecimal} 252 */ 253 protected static BigDecimal getBigDecimal(Number num) { 254 // try fast equality check first (delegates to identity!) 255 if (BigDecimal.class.equals(num.getClass())) { 256 return (BigDecimal) num; 257 } 258 if (Long.class.equals(num.getClass()) 259 || Integer.class.equals(num.getClass()) 260 || Short.class.equals(num.getClass()) 261 || Byte.class.equals(num.getClass()) 262 || AtomicLong.class.equals(num.getClass())) { 263 return BigDecimal.valueOf(num.longValue()); 264 } 265 if (Float.class.equals(num.getClass()) 266 || Double.class.equals(num.getClass())) { 267 return new BigDecimal(num.toString()); 268 } 269 // try instance of (slower) 270 if (num instanceof BigDecimal) { 271 return (BigDecimal) num; 272 } 273 if (num instanceof BigInteger) { 274 return new BigDecimal((BigInteger) num); 275 } 276 try { 277 // Avoid imprecise conversion to double value if at all possible 278 return new BigDecimal(num.toString()); 279 } catch (NumberFormatException e) { 280 } 281 return BigDecimal.valueOf(num.doubleValue()); 282 } 283 284}