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