]>
Commit | Line | Data |
---|---|---|
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 java.math.BigDecimal; | |
20 | import java.math.BigInteger; | |
21 | ||
22 | import com.google.gson.internal.$Gson$Preconditions; | |
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 static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class, | |
36 | float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class, | |
37 | Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class }; | |
38 | ||
39 | private Object value; | |
40 | ||
41 | /** | |
42 | * Create a primitive containing a boolean value. | |
43 | * | |
44 | * @param bool the value to create the primitive with. | |
45 | */ | |
46 | public JsonPrimitive(Boolean bool) { | |
47 | setValue(bool); | |
48 | } | |
49 | ||
50 | /** | |
51 | * Create a primitive containing a {@link Number}. | |
52 | * | |
53 | * @param number the value to create the primitive with. | |
54 | */ | |
55 | public JsonPrimitive(Number number) { | |
56 | setValue(number); | |
57 | } | |
58 | ||
59 | /** | |
60 | * Create a primitive containing a String value. | |
61 | * | |
62 | * @param string the value to create the primitive with. | |
63 | */ | |
64 | public JsonPrimitive(String string) { | |
65 | setValue(string); | |
66 | } | |
67 | ||
68 | /** | |
69 | * Create a primitive containing a character. The character is turned into a one character String | |
70 | * since Json only supports String. | |
71 | * | |
72 | * @param c the value to create the primitive with. | |
73 | */ | |
74 | public JsonPrimitive(Character c) { | |
75 | setValue(c); | |
76 | } | |
77 | ||
78 | /** | |
79 | * Create a primitive using the specified Object. It must be an instance of {@link Number}, a | |
80 | * Java primitive type, or a String. | |
81 | * | |
82 | * @param primitive the value to create the primitive with. | |
83 | */ | |
84 | JsonPrimitive(Object primitive) { | |
85 | setValue(primitive); | |
86 | } | |
87 | ||
88 | @Override | |
89 | JsonPrimitive deepCopy() { | |
90 | return this; | |
91 | } | |
92 | ||
93 | void setValue(Object primitive) { | |
94 | if (primitive instanceof Character) { | |
95 | // convert characters to strings since in JSON, characters are represented as a single | |
96 | // character string | |
97 | char c = ((Character) primitive).charValue(); | |
98 | this.value = String.valueOf(c); | |
99 | } else { | |
100 | $Gson$Preconditions.checkArgument(primitive instanceof Number | |
101 | || isPrimitiveOrString(primitive)); | |
102 | this.value = primitive; | |
103 | } | |
104 | } | |
105 | ||
106 | /** | |
107 | * Check whether this primitive contains a boolean value. | |
108 | * | |
109 | * @return true if this primitive contains a boolean value, false otherwise. | |
110 | */ | |
111 | public boolean isBoolean() { | |
112 | return value instanceof Boolean; | |
113 | } | |
114 | ||
115 | /** | |
116 | * convenience method to get this element as a {@link Boolean}. | |
117 | * | |
118 | * @return get this element as a {@link Boolean}. | |
119 | */ | |
120 | @Override | |
121 | Boolean getAsBooleanWrapper() { | |
122 | return (Boolean) value; | |
123 | } | |
124 | ||
125 | /** | |
126 | * convenience method to get this element as a boolean value. | |
127 | * | |
128 | * @return get this element as a primitive boolean value. | |
129 | */ | |
130 | @Override | |
131 | public boolean getAsBoolean() { | |
132 | if (isBoolean()) { | |
133 | return getAsBooleanWrapper().booleanValue(); | |
134 | } else { | |
135 | // Check to see if the value as a String is "true" in any case. | |
136 | return Boolean.parseBoolean(getAsString()); | |
137 | } | |
138 | } | |
139 | ||
140 | /** | |
141 | * Check whether this primitive contains a Number. | |
142 | * | |
143 | * @return true if this primitive contains a Number, false otherwise. | |
144 | */ | |
145 | public boolean isNumber() { | |
146 | return value instanceof Number; | |
147 | } | |
148 | ||
149 | /** | |
150 | * convenience method to get this element as a Number. | |
151 | * | |
152 | * @return get this element as a Number. | |
153 | * @throws NumberFormatException if the value contained is not a valid Number. | |
154 | */ | |
155 | @Override | |
156 | public Number getAsNumber() { | |
157 | return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value; | |
158 | } | |
159 | ||
160 | /** | |
161 | * Check whether this primitive contains a String value. | |
162 | * | |
163 | * @return true if this primitive contains a String value, false otherwise. | |
164 | */ | |
165 | public boolean isString() { | |
166 | return value instanceof String; | |
167 | } | |
168 | ||
169 | /** | |
170 | * convenience method to get this element as a String. | |
171 | * | |
172 | * @return get this element as a String. | |
173 | */ | |
174 | @Override | |
175 | public String getAsString() { | |
176 | if (isNumber()) { | |
177 | return getAsNumber().toString(); | |
178 | } else if (isBoolean()) { | |
179 | return getAsBooleanWrapper().toString(); | |
180 | } else { | |
181 | return (String) value; | |
182 | } | |
183 | } | |
184 | ||
185 | /** | |
186 | * convenience method to get this element as a primitive double. | |
187 | * | |
188 | * @return get this element as a primitive double. | |
189 | * @throws NumberFormatException if the value contained is not a valid double. | |
190 | */ | |
191 | @Override | |
192 | public double getAsDouble() { | |
193 | return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString()); | |
194 | } | |
195 | ||
196 | /** | |
197 | * convenience method to get this element as a {@link BigDecimal}. | |
198 | * | |
199 | * @return get this element as a {@link BigDecimal}. | |
200 | * @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}. | |
201 | */ | |
202 | @Override | |
203 | public BigDecimal getAsBigDecimal() { | |
204 | return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString()); | |
205 | } | |
206 | ||
207 | /** | |
208 | * convenience method to get this element as a {@link BigInteger}. | |
209 | * | |
210 | * @return get this element as a {@link BigInteger}. | |
211 | * @throws NumberFormatException if the value contained is not a valid {@link BigInteger}. | |
212 | */ | |
213 | @Override | |
214 | public BigInteger getAsBigInteger() { | |
215 | return value instanceof BigInteger ? | |
216 | (BigInteger) value : new BigInteger(value.toString()); | |
217 | } | |
218 | ||
219 | /** | |
220 | * convenience method to get this element as a float. | |
221 | * | |
222 | * @return get this element as a float. | |
223 | * @throws NumberFormatException if the value contained is not a valid float. | |
224 | */ | |
225 | @Override | |
226 | public float getAsFloat() { | |
227 | return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString()); | |
228 | } | |
229 | ||
230 | /** | |
231 | * convenience method to get this element as a primitive long. | |
232 | * | |
233 | * @return get this element as a primitive long. | |
234 | * @throws NumberFormatException if the value contained is not a valid long. | |
235 | */ | |
236 | @Override | |
237 | public long getAsLong() { | |
238 | return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString()); | |
239 | } | |
240 | ||
241 | /** | |
242 | * convenience method to get this element as a primitive short. | |
243 | * | |
244 | * @return get this element as a primitive short. | |
245 | * @throws NumberFormatException if the value contained is not a valid short value. | |
246 | */ | |
247 | @Override | |
248 | public short getAsShort() { | |
249 | return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString()); | |
250 | } | |
251 | ||
252 | /** | |
253 | * convenience method to get this element as a primitive integer. | |
254 | * | |
255 | * @return get this element as a primitive integer. | |
256 | * @throws NumberFormatException if the value contained is not a valid integer. | |
257 | */ | |
258 | @Override | |
259 | public int getAsInt() { | |
260 | return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString()); | |
261 | } | |
262 | ||
263 | @Override | |
264 | public byte getAsByte() { | |
265 | return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString()); | |
266 | } | |
267 | ||
268 | @Override | |
269 | public char getAsCharacter() { | |
270 | return getAsString().charAt(0); | |
271 | } | |
272 | ||
273 | private static boolean isPrimitiveOrString(Object target) { | |
274 | if (target instanceof String) { | |
275 | return true; | |
276 | } | |
277 | ||
278 | Class<?> classOfPrimitive = target.getClass(); | |
279 | for (Class<?> standardPrimitive : PRIMITIVE_TYPES) { | |
280 | if (standardPrimitive.isAssignableFrom(classOfPrimitive)) { | |
281 | return true; | |
282 | } | |
283 | } | |
284 | return false; | |
285 | } | |
286 | ||
287 | @Override | |
288 | public int hashCode() { | |
289 | if (value == null) { | |
290 | return 31; | |
291 | } | |
292 | // Using recommended hashing algorithm from Effective Java for longs and doubles | |
293 | if (isIntegral(this)) { | |
294 | long value = getAsNumber().longValue(); | |
295 | return (int) (value ^ (value >>> 32)); | |
296 | } | |
297 | if (value instanceof Number) { | |
298 | long value = Double.doubleToLongBits(getAsNumber().doubleValue()); | |
299 | return (int) (value ^ (value >>> 32)); | |
300 | } | |
301 | return value.hashCode(); | |
302 | } | |
303 | ||
304 | @Override | |
305 | public boolean equals(Object obj) { | |
306 | if (this == obj) { | |
307 | return true; | |
308 | } | |
309 | if (obj == null || getClass() != obj.getClass()) { | |
310 | return false; | |
311 | } | |
312 | JsonPrimitive other = (JsonPrimitive)obj; | |
313 | if (value == null) { | |
314 | return other.value == null; | |
315 | } | |
316 | if (isIntegral(this) && isIntegral(other)) { | |
317 | return getAsNumber().longValue() == other.getAsNumber().longValue(); | |
318 | } | |
319 | if (value instanceof Number && other.value instanceof Number) { | |
320 | double a = getAsNumber().doubleValue(); | |
321 | // Java standard types other than double return true for two NaN. So, need | |
322 | // special handling for double. | |
323 | double b = other.getAsNumber().doubleValue(); | |
324 | return a == b || (Double.isNaN(a) && Double.isNaN(b)); | |
325 | } | |
326 | return value.equals(other.value); | |
327 | } | |
328 | ||
329 | /** | |
330 | * Returns true if the specified number is an integral type | |
331 | * (Long, Integer, Short, Byte, BigInteger) | |
332 | */ | |
333 | private static boolean isIntegral(JsonPrimitive primitive) { | |
334 | if (primitive.value instanceof Number) { | |
335 | Number number = (Number) primitive.value; | |
336 | return number instanceof BigInteger || number instanceof Long || number instanceof Integer | |
337 | || number instanceof Short || number instanceof Byte; | |
338 | } | |
339 | return false; | |
340 | } | |
341 | } |