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.impl;
27
28
29 import hat.tools.json.JsonNumber;
30
31 import java.math.BigDecimal;
32 import java.math.BigInteger;
33 import java.util.Locale;
34
35 /**
36 * JsonNumber implementation class
37 */
38 public final class JsonNumberImpl implements JsonNumber {
39
40 private final char[] doc;
41 private final int startOffset;
42 private final int endOffset;
43 private Number theNumber;
44
45 public JsonNumberImpl(Number num) {
46 if (num == null ||
47 num instanceof Double d && (d.isNaN() || d.isInfinite())) {
48 throw new IllegalArgumentException("Not a valid JSON number");
49 }
50 theNumber = num;
51 // unused
52 startOffset = -1;
53 endOffset = -1;
54 doc = null;
55 }
56
57 public JsonNumberImpl(char[] doc, int start, int end) {
58 this.doc = doc;
59 startOffset = start;
60 endOffset = end;
61 }
62
63 @Override
64 public Number toNumber() {
65 var n = theNumber;
66 if (n == null) {
67 n = theNumber = computeNumber();
68 }
69 return n;
70 }
71
72 private Number computeNumber() {
73 var str = toString();
74 // Check if integral (Java literal format)
75 boolean integerOnly = true;
76 for (int index = 0; index < str.length(); index++) {
77 char c = str.charAt(index);
78 if (c == '.' || c == 'e' || c == 'E') {
79 integerOnly = false;
80 break;
81 }
82 }
83 if (integerOnly) {
84 try {
85 return Long.parseLong(str);
86 } catch (NumberFormatException _) {
87 return new BigInteger(str);
88 }
89 } else {
90 var db = Double.parseDouble(str);
91 if (Double.isInfinite(db)) {
92 return toBigDecimal();
93 } else {
94 return db;
95 }
96 }
97 }
98
99 @Override
100 public BigDecimal toBigDecimal() {
101 return new BigDecimal(toString());
102 }
103
104 @Override
105 public String toString() {
106 return new String(doc, startOffset, endOffset - startOffset);
107 }
108
109 @Override
110 public boolean equals(Object o) {
111 return o instanceof JsonNumber ojn &&
112 toString().compareToIgnoreCase(ojn.toString()) == 0;
113 }
114
115 @Override
116 public int hashCode() {
117 return toString().toLowerCase(Locale.ROOT).hashCode();
118 }
119 }