1 /*
  2  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package hat.tools.json;
 27 
 28 
 29 import hat.tools.json.impl.JsonNumberImpl;
 30 
 31 import java.math.BigDecimal;
 32 import java.math.BigInteger;
 33 
 34 /**
 35  * The interface that represents JSON number, an arbitrary-precision
 36  * number represented in base 10 using decimal digits.
 37  * <p>
 38  * A {@code JsonNumber} can be produced by {@link Json#parse(String)}.
 39  * Alternatively, {@link #of(double)} and its overloads can be used to obtain
 40  * a {@code JsonNumber} from a {@code Number}.
 41  * When a JSON number is parsed, a {@code JsonNumber} object is created
 42  * as long as the parsed value adheres to the JSON number
 43  * <a href="https://datatracker.ietf.org/doc/html/rfc8259#section-6">
 44  * syntax</a>. The value of the {@code JsonNumber}
 45  * can be retrieved from {@link #toString()} as the string representation
 46  * from which the JSON number is originally parsed, with
 47  * {@link #toNumber()} as a {@code Number} instance, or with
 48  * {@link #toBigDecimal()}.
 49  *
 50  * @spec https://datatracker.ietf.org/doc/html/rfc8259#section-6 RFC 8259:
 51  *      The JavaScript Object Notation (JSON) Data Interchange Format - Numbers
 52  * @since 99
 53  */
 54 public non-sealed interface JsonNumber extends JsonValue {
 55 
 56     /**
 57      * {@return the {@code Number} parsed or translated from the
 58      * {@link #toString string representation} of this {@code JsonNumber}}
 59      * <p>
 60      * This method operates on the string representation and depending on that
 61      * representation computes and returns an instance of {@code Long}, {@code BigInteger},
 62      * {@code Double}, or {@code BigDecimal}.
 63      * <p>
 64      * If the string representation is the decimal string representation of
 65      * a {@code long} value, parsable by {@link Long#parseLong(String)},
 66      * then that {@code long} value is returned in its boxed form as {@code Long}.
 67      * Otherwise, if the string representation is the decimal string representation of a
 68      * {@code BigInteger}, translatable by {@link BigInteger#BigInteger(String)},
 69      * then that {@code BigInteger} is returned.
 70      * Otherwise, if the string representation is the decimal string representation of
 71      * a {@code double} value, parsable by {@link Double#parseDouble(String)},
 72      * and the {@code double} value is not {@link Double#isInfinite() infinite}, then that
 73      * {@code double} value is returned in its boxed form as {@code Double}.
 74      * Otherwise, and in all other cases, the string representation is the decimal string
 75      * representation of a {@code BigDecimal}, translatable by
 76      * {@link BigDecimal#BigDecimal(String)}, and that {@code BigDecimal} is
 77      * returned.
 78      * <p>
 79      * The computation may not preserve all information in the string representation.
 80      * In all of the above cases one or more leading zero digits are not preserved.
 81      * In the third case, returning {@code Double}, decimal to binary conversion may lose
 82      * decimal precision, and will not preserve one or more trailing zero digits in the fraction
 83      * part.
 84      *
 85      * @apiNote
 86      * Pattern matching can be used to match against {@code Long},
 87      * {@code Double}, {@code BigInteger}, or {@code BigDecimal} reference
 88      * types. For example:
 89      * {@snippet lang=java:
 90      * switch(jsonNumber.toNumber()) {
 91      *     case Long l -> { ... }
 92      *     case Double d -> { ... }
 93      *     case BigInteger bi -> { ... }
 94      *     case BigDecimal bd -> { ... }
 95      *     default -> { } // should not happen
 96      * }
 97      *}
 98      * @throws NumberFormatException if the {@code Number} cannot be parsed or translated from the string representation
 99      * @see #toBigDecimal()
100      * @see #toString()
101      */
102     Number toNumber();
103 
104     /**
105      * {@return the {@code BigDecimal} translated from the
106      * {@link #toString string representation} of this {@code JsonNumber}}
107      * <p>
108      * The string representation is the decimal string representation of a
109      * {@code BigDecimal}, translatable by {@link BigDecimal#BigDecimal(String)},
110      * and that {@code BigDecimal} is returned.
111      * <p>
112      * The translation may not preserve all information in the string representation.
113      * The sign is not preserved for the decimal string representation {@code -0.0}. One or more
114      * leading zero digits are not preserved.
115      *
116      * @throws NumberFormatException if the {@code BigDecimal} cannot be translated from the string representation
117      */
118     BigDecimal toBigDecimal();
119 
120     /**
121      * Creates a JSON number whose string representation is the
122      * decimal string representation of the given {@code double} value,
123      * produced by applying the value to {@link Double#toString(double)}.
124      *
125      * @param num the given {@code double} value.
126      * @return a JSON number created from a {@code double} value
127      * @throws IllegalArgumentException if the given {@code double} value
128      * is {@link Double#isNaN() NaN} or is {@link Double#isInfinite() infinite}.
129      */
130     static JsonNumber of(double num) {
131         // non-integral types
132         return new JsonNumberImpl(num);
133     }
134 
135     /**
136      * Creates a JSON number whose string representation is the
137      * decimal string representation of the given {@code long} value,
138      * produced by applying the value to {@link Long#toString(long)}.
139      *
140      * @param num the given {@code long} value.
141      * @return a JSON number created from a {@code long} value
142      */
143     static JsonNumber of(long num) {
144         // integral types
145         return new JsonNumberImpl(num);
146     }
147 
148     /**
149      * Creates a JSON number whose string representation is the
150      * string representation of the given {@code BigInteger} value.
151      *
152      * @param num the given {@code BigInteger} value.
153      * @return a JSON number created from a {@code BigInteger} value
154      */
155     static JsonNumber of(BigInteger num) {
156         return new JsonNumberImpl(num);
157     }
158 
159     /**
160      * Creates a JSON number whose string representation is the
161      * string representation of the given {@code BigDecimal} value.
162      *
163      * @param num the given {@code BigDecimal} value.
164      * @return a JSON number created from a {@code BigDecimal} value
165      */
166     static JsonNumber of(BigDecimal num) {
167         return new JsonNumberImpl(num);
168     }
169 
170     /**
171      * {@return the decimal string representation of this {@code JsonNumber}}
172      *
173      * If this {@code JsonNumber} is created by parsing a JSON number in a JSON document,
174      * it preserves the string representation in the document, regardless of its
175      * precision or range. For example, a JSON number like
176      * {@code 3.141592653589793238462643383279} in the JSON document will be
177      * returned exactly as it appears.
178      * If this {@code JsonNumber} is created via one of the factory methods,
179      * such as {@link JsonNumber#of(double)}, then the string representation is
180      * specified by the factory method.
181      */
182     @Override
183     String toString();
184 
185     /**
186      * {@return true if the given {@code obj} is equal to this {@code JsonNumber}}
187      * The comparison is based on the string representation of this {@code JsonNumber},
188      * ignoring the case.
189      *
190      * @see #toString()
191      */
192     @Override
193     boolean equals(Object obj);
194 
195     /**
196      * {@return the hash code value of this {@code JsonNumber}} The returned hash code
197      * is calculated based on the string representation of this {@code JsonNumber},
198      * ignoring the case.
199      *
200      * @see #toString()
201      */
202     @Override
203     int hashCode();
204 }