1 /*
2 * Copyright (C) 2008 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.google.gson;
18
19 import com.google.gson.internal.$Gson$Preconditions;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22
23 import com.google.gson.internal.LazilyParsedNumber;
24
25 /**
26 * A class representing a Json primitive value. A primitive value
27 * is either a String, a Java primitive, or a Java primitive
28 * wrapper type.
29 *
30 * @author Inderjeet Singh
31 * @author Joel Leitch
32 */
33 public final class JsonPrimitive extends JsonElement {
34
35 private final Object value;
36
37 /**
38 * Create a primitive containing a boolean value.
39 *
40 * @param bool the value to create the primitive with.
41 */
42 public JsonPrimitive(Boolean bool) {
43 value = $Gson$Preconditions.checkNotNull(bool);
44 }
45
46 /**
47 * Create a primitive containing a {@link Number}.
48 *
49 * @param number the value to create the primitive with.
50 */
51 public JsonPrimitive(Number number) {
52 value = $Gson$Preconditions.checkNotNull(number);
53 }
54
55 /**
56 * Create a primitive containing a String value.
57 *
58 * @param string the value to create the primitive with.
59 */
60 public JsonPrimitive(String string) {
61 value = $Gson$Preconditions.checkNotNull(string);
62 }
63
64 /**
65 * Create a primitive containing a character. The character is turned into a one character String
66 * since Json only supports String.
67 *
68 * @param c the value to create the primitive with.
69 */
70 public JsonPrimitive(Character c) {
71 // convert characters to strings since in JSON, characters are represented as a single
72 // character string
73 value = $Gson$Preconditions.checkNotNull(c).toString();
74 }
75
76 /**
77 * Returns the same value as primitives are immutable.
78 * @since 2.8.2
79 */
80 @Override
81 public JsonPrimitive deepCopy() {
82 return this;
83 }
84
85 /**
86 * Check whether this primitive contains a boolean value.
87 *
88 * @return true if this primitive contains a boolean value, false otherwise.
89 */
90 public boolean isBoolean() {
91 return value instanceof Boolean;
92 }
93
94 /**
95 * convenience method to get this element as a boolean value.
96 *
97 * @return get this element as a primitive boolean value.
98 */
99 @Override
100 public boolean getAsBoolean() {
101 if (isBoolean()) {
102 return ((Boolean) value).booleanValue();
103 }
104 // Check to see if the value as a String is "true" in any case.
105 return Boolean.parseBoolean(getAsString());
106 }
107
108 /**
109 * Check whether this primitive contains a Number.
110 *
111 * @return true if this primitive contains a Number, false otherwise.
112 */
113 public boolean isNumber() {
114 return value instanceof Number;
115 }
116
117 /**
118 * convenience method to get this element as a Number.
119 *
120 * @return get this element as a Number.
121 * @throws NumberFormatException if the value contained is not a valid Number.
122 */
123 @Override
124 public Number getAsNumber() {
125 return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value;
126 }
127
128 /**
129 * Check whether this primitive contains a String value.
130 *
131 * @return true if this primitive contains a String value, false otherwise.
132 */
133 public boolean isString() {
134 return value instanceof String;
135 }
136
137 /**
138 * convenience method to get this element as a String.
139 *
140 * @return get this element as a String.
141 */
142 @Override
143 public String getAsString() {
144 if (isNumber()) {
145 return getAsNumber().toString();
146 } else if (isBoolean()) {
147 return ((Boolean) value).toString();
148 } else {
149 return (String) value;
150 }
151 }
152
153 /**
154 * convenience method to get this element as a primitive double.
155 *
156 * @return get this element as a primitive double.
157 * @throws NumberFormatException if the value contained is not a valid double.
158 */
159 @Override
160 public double getAsDouble() {
161 return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
162 }
163
164 /**
165 * convenience method to get this element as a {@link BigDecimal}.
166 *
167 * @return get this element as a {@link BigDecimal}.
168 * @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}.
169 */
170 @Override
171 public BigDecimal getAsBigDecimal() {
172 return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
173 }
174
175 /**
176 * convenience method to get this element as a {@link BigInteger}.
177 *
178 * @return get this element as a {@link BigInteger}.
179 * @throws NumberFormatException if the value contained is not a valid {@link BigInteger}.
180 */
181 @Override
182 public BigInteger getAsBigInteger() {
183 return value instanceof BigInteger ?
184 (BigInteger) value : new BigInteger(value.toString());
185 }
186
187 /**
188 * convenience method to get this element as a float.
189 *
190 * @return get this element as a float.
191 * @throws NumberFormatException if the value contained is not a valid float.
192 */
193 @Override
194 public float getAsFloat() {
195 return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
196 }
197
198 /**
199 * convenience method to get this element as a primitive long.
200 *
201 * @return get this element as a primitive long.
202 * @throws NumberFormatException if the value contained is not a valid long.
203 */
204 @Override
205 public long getAsLong() {
206 return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
207 }
208
209 /**
210 * convenience method to get this element as a primitive short.
211 *
212 * @return get this element as a primitive short.
213 * @throws NumberFormatException if the value contained is not a valid short value.
214 */
215 @Override
216 public short getAsShort() {
217 return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
218 }
219
220 /**
221 * convenience method to get this element as a primitive integer.
222 *
223 * @return get this element as a primitive integer.
224 * @throws NumberFormatException if the value contained is not a valid integer.
225 */
226 @Override
227 public int getAsInt() {
228 return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
229 }
230
231 @Override
232 public byte getAsByte() {
233 return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
234 }
235
236 @Override
237 public char getAsCharacter() {
238 return getAsString().charAt(0);
239 }
240
241 @Override
242 public int hashCode() {
243 if (value == null) {
244 return 31;
245 }
246 // Using recommended hashing algorithm from Effective Java for longs and doubles
247 if (isIntegral(this)) {
248 long value = getAsNumber().longValue();
249 return (int) (value ^ (value >>> 32));
250 }
251 if (value instanceof Number) {
252 long value = Double.doubleToLongBits(getAsNumber().doubleValue());
253 return (int) (value ^ (value >>> 32));
254 }
255 return value.hashCode();
256 }
257
258 @Override
259 public boolean equals(Object obj) {
260 if (this == obj) {
261 return true;
262 }
263 if (obj == null || getClass() != obj.getClass()) {
264 return false;
265 }
266 JsonPrimitive other = (JsonPrimitive)obj;
267 if (value == null) {
268 return other.value == null;
269 }
270 if (isIntegral(this) && isIntegral(other)) {
271 return getAsNumber().longValue() == other.getAsNumber().longValue();
272 }
273 if (value instanceof Number && other.value instanceof Number) {
274 double a = getAsNumber().doubleValue();
275 // Java standard types other than double return true for two NaN. So, need
276 // special handling for double.
277 double b = other.getAsNumber().doubleValue();
278 return a == b || (Double.isNaN(a) && Double.isNaN(b));
279 }
280 return value.equals(other.value);
281 }
282
283 /**
284 * Returns true if the specified number is an integral type
285 * (Long, Integer, Short, Byte, BigInteger)
286 */
287 private static boolean isIntegral(JsonPrimitive primitive) {
288 if (primitive.value instanceof Number) {
289 Number number = (Number) primitive.value;
290 return number instanceof BigInteger || number instanceof Long || number instanceof Integer
291 || number instanceof Short || number instanceof Byte;
292 }
293 return false;
294 }
295 }
296