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}