001 
002 /*
003  *  Descripter 1.0 - Java Script Engines
004  *  Copyright (C) 2010-2015  Jianjun Liu (J.J.Liu)
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package org.descripter.js.api.core;
021 
022 import java.math.RoundingMode;
023 import java.text.DecimalFormat;
024 
025 import org.descripter.js.api.Function;
026 
027 /**
028  * <p>Emulates JavaScript Number objects.</p>
029  * 
030  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.descripter.org" target="_blank">http://www.descripter.org</a>
031  * @since Descripter 1.0
032  */
033 public class CNumber extends CObject
034 {
035     private final Number value;
036 
037     /**
038      * <p>Constructs a {@link CObject} context of this type.</p>
039      * @param constructor The constructor {@link Function} object.
040      * @param value The initial value
041      * @since Descripter 1.0
042      */
043     public CNumber(Function<?> constructor, Number value) {
044         super(constructor);
045         this.value = value;
046     }
047 
048     /**
049      * <p>Returns a string representation of the current object.</p>
050      * @return The string representation of the current object
051      * @since Descripter 1.0
052      */
053     @Override
054     public final String toString() {
055         return value.toString();
056     }
057 
058     /**
059      * <p>Returns the primitive value associated with this object, if there is one. </p>
060      * @return The primitive value associated with this object, if there is one, or this object itself.
061      * @since Descripter 1.0
062      */
063     @Override
064     public final Number valueOf() {
065         return value;
066     }
067 
068     /**
069      * <p>Converts the current number to a string using exponential notation with the 
070      * specified number of digits after the decimal place.</p>
071      * @param digits The number of digits that appears after the decimal point. This may be a 
072      * value between 0 and 20, inclusive, and implementations may optionally support a larger 
073      * range of values. If this argument is undefined, as many digits as necessary are used.
074      * @return A string representation of the current number, in exponential notation, 
075      * with one digit before the decimal place and <tt>digits</tt> digits after the 
076      * decimal place. The fractional part of the number is rounded, or padded with zeros, 
077      * as necessary, so that it has the specified length.
078      * @throws RuntimeException JavaScript throws a <tt>RangeError</tt> if 
079      * <tt>digits</tt> is too small or too large.
080      * @throws RuntimeException JavaScript throws a <tt>TypeError</tt> if this method 
081      * is invoked on an instance that is not a number.
082      * @see #toFixed(Object)
083      * @see #toLocaleString()
084      * @see #toPrecision()
085      * @see #toPrecision(int)
086      * @see #toString()
087      * @since Descripter 1.0
088      */
089     public final String toExponential(Object digits) {
090         DecimalFormat df = new DecimalFormat("0.0E0");
091         df.setDecimalSeparatorAlwaysShown(false);
092         df.setMaximumFractionDigits(intValue(digits));
093         return df.format(value);
094     }
095 
096     /**
097      * <p>Converts the current number to a string that contains a specified number of 
098      * digits after the decimal place.</p>
099      * @param digits The number of digits to appear after the decimal point; this may be a 
100      * value between 0 and 20, inclusive, and implementations may optionally support a 
101      * larger range of values. If this argument is undefined, it is treated as 0.
102      * @return A string representation of the current number that does not use exponential 
103      * notation and has exactly <tt>digits</tt> digits after the decimal place. The number 
104      * is rounded if necessary, and the fractional part is padded with zeros if necessary so 
105      * that it has the specified length. If the current number is greater than 1e+21, this 
106      * method simply calls {@link #toString()} and returns a string in exponential 
107      * notation.
108      * @throws RuntimeException JavaScript throws a <tt>RangeError</tt> if 
109      * <tt>digits</tt> is too small or too large.
110      * @throws RuntimeException JavaScript throws a <tt>TypeError</tt> if this method 
111      * is invoked on an instance that is not a number.
112      * @see #toExponential(Object)
113      * @see #toLocaleString()
114      * @see #toPrecision()
115      * @see #toPrecision(int)
116      * @see #toString()
117      * @since Descripter 1.0
118      */
119     public final String toFixed(Object digits) {
120         DecimalFormat df = new DecimalFormat("0.0");
121         df.setDecimalSeparatorAlwaysShown(false);
122         df.setMaximumFractionDigits(intValue(digits));
123         df.setRoundingMode(RoundingMode.DOWN);
124         return df.format(value);
125     }
126 
127     /**
128      * <p>Converts the current number to a string.</p>
129      * <p>This method simply calls {@link #toString()} to convert the number to a base-10 
130      * value.</p>
131      * @return A string representation of the current number. The number is rounded or 
132      * padded with zeros as necessary.
133      * @throws RuntimeException JavaScript throws a <tt>TypeError</tt> if this method 
134      * is invoked on an instance that is not a number.
135      * @see #toExponential(Object)
136      * @see #toFixed(Object)
137      * @see #toLocaleString()
138      * @see #toPrecision(int)
139      * @see #toString()
140      * @since Descripter 1.0
141      */
142     public final String toPrecision() {
143         return toString(value);
144     }
145 
146     /**
147      * <p>Converts the current number to a string using the specified number of significant 
148      * digits. Uses exponential or fixed-point notation depending on the size of the number 
149      * and the number of significant digits specified.</p>
150      * @param precision The number of significant digits to appear in the returned string. 
151      * This may be a value between 1 and 21, inclusive. Implementations are allowed to 
152      * optionally support larger and smaller values of precision. If this argument is 
153      * undefined, the {@link #toString()} method is used instead to convert the number to 
154      * a base-10 value.
155      * @return A string representation of the current number that contains 
156      * <tt>precision</tt> significant digits. If <tt>precision</tt> is large 
157      * enough to include all the digits of the integer part of the number, the returned 
158      * string uses fixed-point notation. Otherwise, exponential notation is used with one 
159      * digit before the decimal place and <tt>precision - 1</tt> digits after the 
160      * decimal place. The number is rounded or padded with zeros as necessary.
161      * @throws RuntimeException JavaScript throws a <tt>RangeError</tt> if 
162      * <tt>digits</tt> is too small or too large.
163      * @throws RuntimeException JavaScript throws a <tt>TypeError</tt> if this method 
164      * is invoked on an instance that is not a number.
165      * @see #toExponential(Object)
166      * @see #toFixed(Object)
167      * @see #toLocaleString()
168      * @see #toPrecision()
169      * @see #toString()
170      * @since Descripter 1.0
171      */
172     public final String toPrecision(int precision) {
173         DecimalFormat df = new DecimalFormat("0.0E0");
174         df.setMinimumIntegerDigits(precision);
175         df.setMaximumFractionDigits(0);
176         df.setDecimalSeparatorAlwaysShown(false);
177         double d = Double.parseDouble(df.format(value));
178         if (value instanceof Double || value instanceof Float) {
179             return Double.toString(d);
180         } else {
181             return Long.toString((long)d);
182         }
183     }
184 }