001/*******************************************************************************
002 * Copyright (c) 2013, 2017 EclipseSource.
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a copy
005 * of this software and associated documentation files (the "Software"), to deal
006 * in the Software without restriction, including without limitation the rights
007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
008 * copies of the Software, and to permit persons to whom the Software is
009 * furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in all
012 * copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
020 * SOFTWARE.
021 ******************************************************************************/
022package com.restfb.json;
023
024import java.io.IOException;
025import java.io.Serializable;
026import java.io.StringWriter;
027import java.io.Writer;
028import java.util.Objects;
029
030/**
031 * Represents a JSON value. This can be a JSON <strong>object</strong>, an <strong> array</strong>, a
032 * <strong>number</strong>, a <strong>string</strong>, or one of the literals
033 * <strong>true</strong>, <strong>false</strong>, and <strong>null</strong>.
034 * <p>
035 * The literals <strong>true</strong>, <strong>false</strong>, and <strong>null</strong> are
036 * represented by the constants {@link Json#TRUE}, {@link Json#FALSE}, and {@link Json#NULL}.
037 * </p>
038 * <p>
039 * JSON <strong>objects</strong> and <strong>arrays</strong> are represented by the subtypes
040 * {@link JsonObject} and {@link JsonArray}. Instances of these types can be created using the public constructors of these classes.
041 * </p>
042 * <p>
043 * Instances that represent JSON <strong>numbers</strong>, <strong>strings</strong> and
044 * <strong>boolean</strong> values can be created using the static factory methods
045 * {@link Json#value(String)}, {@link Json#value(long)}, {@link Json#value(double)}, etc.
046 * </p>
047 * <p>
048 * In order to find out whether an instance of this class is of a certain type, the methods
049 * {@link #isObject()}, {@link #isArray()}, {@link #isString()}, {@link #isNumber()} etc. can be used.
050 * </p>
051 * <p>
052 * If the type of a JSON value is known, the methods {@link #asObject()}, {@link #asArray()}, {@link #asString()},
053 * {@link #asInt()}, etc. can be used to get this value directly in the appropriate target type.
054 * </p>
055 * <p>
056 * This class is <strong>not supposed to be extended</strong> by clients.
057 * </p>
058 */
059@SuppressWarnings("serial") // use default serial UID
060public abstract class JsonValue implements Serializable {
061
062  // String constants
063  private static final String NOT_A_NUMBER = "Not a number: ";
064  private static final String NOT_A_STRING = "Not a string: ";
065  private static final String NOT_A_BOOLEAN = "Not a boolean: ";
066  // String constants
067  protected static final String STRING_IS_NULL = "string is null";
068  protected static final String OBJECT_IS_NULL = "object is null";
069  protected static final String NAME_IS_NULL = "name is null";
070  protected static final String VALUE_IS_NULL = "value is null";
071  protected static final String ARRAY_IS_NULL = "array is null";
072
073  JsonValue() {
074    // prevent subclasses outside of this package
075  }
076
077  /**
078   * Detects whether this value represents a JSON object. If this is the case, this value is an instance of
079   * {@link JsonObject}.
080   *
081   * @return <code>true</code> if this value is an instance of JsonObject
082   */
083  public boolean isObject() {
084    return false;
085  }
086
087  /**
088   * Detects whether this value represents a JSON array. If this is the case, this value is an instance of
089   * {@link JsonArray}.
090   *
091   * @return <code>true</code> if this value is an instance of JsonArray
092   */
093  public boolean isArray() {
094    return false;
095  }
096
097  /**
098   * Detects whether this value represents a JSON number.
099   *
100   * @return <code>true</code> if this value represents a JSON number
101   */
102  public boolean isNumber() {
103    return false;
104  }
105
106  /**
107   * Detects whether this value represents a JSON string.
108   *
109   * @return <code>true</code> if this value represents a JSON string
110   */
111  public boolean isString() {
112    return false;
113  }
114
115  /**
116   * Detects whether this value represents a boolean value.
117   *
118   * @return <code>true</code> if this value represents either the JSON literal <code>true</code> or <code>false</code>
119   */
120  public boolean isBoolean() {
121    return false;
122  }
123
124  /**
125   * Detects whether this value represents the JSON literal <code>true</code>.
126   *
127   * @return <code>true</code> if this value represents the JSON literal <code>true</code>
128   */
129  public boolean isTrue() {
130    return false;
131  }
132
133  /**
134   * Detects whether this value represents the JSON literal <code>false</code>.
135   *
136   * @return <code>true</code> if this value represents the JSON literal <code>false</code>
137   */
138  public boolean isFalse() {
139    return false;
140  }
141
142  /**
143   * Detects whether this value represents the JSON literal <code>null</code>.
144   *
145   * @return <code>true</code> if this value represents the JSON literal <code>null</code>
146   */
147  public boolean isNull() {
148    return false;
149  }
150
151  /**
152   * Returns this JSON value as {@link JsonObject}, assuming that this value represents a JSON object. If this is not
153   * the case, an exception is thrown.
154   *
155   * @return a JSONObject for this value
156   * @throws UnsupportedOperationException
157   *           if this value is not a JSON object
158   */
159  public JsonObject asObject() {
160    throw new UnsupportedOperationException("Not an object: " + toString());
161  }
162
163  /**
164   * Returns this JSON value as {@link JsonArray}, assuming that this value represents a JSON array. If this is not the
165   * case, an exception is thrown.
166   *
167   * @return a JSONArray for this value
168   * @throws UnsupportedOperationException
169   *           if this value is not a JSON array
170   */
171  public JsonArray asArray() {
172    throw new UnsupportedOperationException("Not an array: " + toString());
173  }
174
175  /**
176   * Returns this JSON value as an <code>int</code> value, assuming that this value represents a JSON number that can be
177   * interpreted as Java <code>int</code>. If this is not the case, an exception is thrown.
178   * <p>
179   * To be interpreted as Java <code>int</code>, the JSON number must neither contain an exponent nor a fraction part.
180   * Moreover, the number must be in the <code>Integer</code> range.
181   * </p>
182   *
183   * @return this value as <code>int</code>
184   * @throws UnsupportedOperationException
185   *           if this value is not a JSON number
186   * @throws NumberFormatException
187   *           if this JSON number can not be interpreted as <code>int</code> value
188   */
189  public int asInt() {
190    throw new UnsupportedOperationException(NOT_A_NUMBER + toString());
191  }
192
193  /**
194   * Returns this JSON value as a <code>long</code> value, assuming that this value represents a JSON number that can be
195   * interpreted as Java <code>long</code>. If this is not the case, an exception is thrown.
196   * <p>
197   * To be interpreted as Java <code>long</code>, the JSON number must neither contain an exponent nor a fraction part.
198   * Moreover, the number must be in the <code>Long</code> range.
199   * </p>
200   *
201   * @return this value as <code>long</code>
202   * @throws UnsupportedOperationException
203   *           if this value is not a JSON number
204   * @throws NumberFormatException
205   *           if this JSON number can not be interpreted as <code>long</code> value
206   */
207  public long asLong() {
208    throw new UnsupportedOperationException(NOT_A_NUMBER + toString());
209  }
210
211  /**
212   * Returns this JSON value as a <code>float</code> value, assuming that this value represents a JSON number. If this
213   * is not the case, an exception is thrown.
214   * <p>
215   * If the JSON number is out of the <code>Float</code> range, {@link Float#POSITIVE_INFINITY} or
216   * {@link Float#NEGATIVE_INFINITY} is returned.
217   * </p>
218   *
219   * @return this value as <code>float</code>
220   * @throws UnsupportedOperationException
221   *           if this value is not a JSON number
222   */
223  public float asFloat() {
224    throw new UnsupportedOperationException(NOT_A_NUMBER + toString());
225  }
226
227  /**
228   * Returns this JSON value as a <code>double</code> value, assuming that this value represents a JSON number. If this
229   * is not the case, an exception is thrown.
230   * <p>
231   * If the JSON number is out of the <code>Double</code> range, {@link Double#POSITIVE_INFINITY} or
232   * {@link Double#NEGATIVE_INFINITY} is returned.
233   * </p>
234   *
235   * @return this value as <code>double</code>
236   * @throws UnsupportedOperationException
237   *           if this value is not a JSON number
238   */
239  public double asDouble() {
240    throw new UnsupportedOperationException(NOT_A_NUMBER + toString());
241  }
242
243  /**
244   * Returns this JSON value as String, assuming that this value represents a JSON string. If this is not the case, an
245   * exception is thrown.
246   *
247   * @return the string represented by this value
248   * @throws UnsupportedOperationException
249   *           if this value is not a JSON string
250   */
251  public String asString() {
252    throw new UnsupportedOperationException(NOT_A_STRING + toString());
253  }
254
255  /**
256   * Returns this JSON value as a <code>boolean</code> value, assuming that this value is either <code>true</code> or
257   * <code>false</code>. If this is not the case, an exception is thrown.
258   *
259   * @return this value as <code>boolean</code>
260   * @throws UnsupportedOperationException
261   *           if this value is neither <code>true</code> or <code>false</code>
262   */
263  public boolean asBoolean() {
264    throw new UnsupportedOperationException(NOT_A_BOOLEAN + toString());
265  }
266
267  /**
268   * Writes the JSON representation of this value to the given writer in its minimal form, without any additional
269   * whitespace.
270   * <p>
271   * Writing performance can be improved by using a {@link java.io.BufferedWriter BufferedWriter}.
272   * </p>
273   *
274   * @param writer
275   *          the writer to write this value to
276   * @throws IOException
277   *           if an I/O error occurs in the writer
278   */
279  public void writeTo(Writer writer) throws IOException {
280    writeTo(writer, WriterConfig.MINIMAL);
281  }
282
283  /**
284   * Writes the JSON representation of this value to the given writer using the given formatting.
285   * <p>
286   * Writing performance can be improved by using a {@link java.io.BufferedWriter BufferedWriter}.
287   * </p>
288   *
289   * @param writer
290   *          the writer to write this value to
291   * @param config
292   *          a configuration that controls the formatting or <code>null</code> for the minimal form
293   * @throws IOException
294   *           if an I/O error occurs in the writer
295   */
296  public void writeTo(Writer writer, WriterConfig config) throws IOException {
297    Objects.requireNonNull(writer, "writer is null");
298    Objects.requireNonNull(config, "config is null");
299    WritingBuffer buffer = new WritingBuffer(writer, 128);
300    write(config.createWriter(buffer));
301    buffer.flush();
302  }
303
304  /**
305   * Returns the JSON string for this value in its minimal form, without any additional whitespace.
306   * The result is guaranteed to be a valid input for the method {@link Json#parse(String)} and to
307   * create a value that is <em>equal</em> to this object.
308   *
309   * @return a JSON string that represents this value
310   */
311  @Override
312  public String toString() {
313    return toString(WriterConfig.UNICODE);
314  }
315
316  /**
317   * Returns the JSON string for this value using the given formatting.
318   *
319   * @param config
320   *          a configuration that controls the formatting or <code>null</code> for the minimal form
321   * @return a JSON string that represents this value
322   */
323  public String toString(WriterConfig config) {
324    StringWriter writer = new StringWriter();
325    try {
326      writeTo(writer, config);
327    } catch (IOException exception) {
328      // StringWriter does not throw IOExceptions
329      throw new RuntimeException(exception);
330    }
331    return writer.toString();
332  }
333
334  /**
335   * Indicates whether some other object is "equal to" this one according to the contract specified in
336   * {@link Object#equals(Object)}.
337   * <p>
338   * Two JsonValues are considered equal if and only if they represent the same JSON text. As a consequence, two given
339   * JsonObjects may be different even though they contain the same set of names with the same values, but in a
340   * different order.
341   * </p>
342   *
343   * @param object
344   *          the reference object with which to compare
345   * @return true if this object is the same as the object argument; false otherwise
346   */
347  @Override
348  public boolean equals(Object object) {
349    return super.equals(object);
350  }
351
352  @Override
353  public int hashCode() {
354    return super.hashCode();
355  }
356
357  abstract void write(JsonWriter writer) throws IOException;
358
359}