001/*******************************************************************************
002 * Copyright (c) 2015, 2016 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.Reader;
026import java.util.Arrays;
027import java.util.Objects;
028
029/**
030 * This class serves as the entry point to the minimal-json API.
031 * <p>
032 * To <strong>parse</strong> a given JSON input, use the <code>parse()</code> methods like in this example:
033 * </p>
034 * 
035 * <pre>
036 * JsonObject object = Json.parse(string).asObject();
037 * </pre>
038 * <p>
039 * To <strong>create</strong> a JSON data structure to be serialized, use the methods <code>value()</code>,
040 * <code>array()</code>, and <code>object()</code>. For example, the following snippet will produce the JSON string
041 * <em>{"foo": 23, "bar": true}</em>:
042 * </p>
043 * 
044 * <pre>
045 * String string = Json.object().add("foo", 23).add("bar", true).toString();
046 * </pre>
047 * <p>
048 * To create a JSON array from a given Java array, you can use one of the <code>array()</code> methods with varargs
049 * parameters:
050 * </p>
051 * 
052 * <pre>
053 * String[] names = ...
054 * JsonArray array = Json.array(names);
055 * </pre>
056 */
057public final class Json {
058
059  // String constants
060  private static final String VALUES_IS_NULL = "values is null";
061  private static final String STRING_IS_NULL = "string is null";
062  private static final String READER_IS_NULL = "reader is null";
063  private static final String INFINITE_AND_NAN = "Infinite and NaN values not permitted in JSON";
064
065  private Json() {
066    // not meant to be instantiated
067  }
068
069  /**
070   * Represents the JSON literal <code>null</code>.
071   */
072  public static final JsonValue NULL = new JsonLiteral("null");
073
074  /**
075   * Represents the JSON literal <code>true</code>.
076   */
077  public static final JsonValue TRUE = new JsonLiteral("true");
078
079  /**
080   * Represents the JSON literal <code>false</code>.
081   */
082  public static final JsonValue FALSE = new JsonLiteral("false");
083
084  /**
085   * Returns a JsonValue instance that represents the given <code>int</code> value.
086   *
087   * @param value
088   *          the value to get a JSON representation for
089   * @return a JSON value that represents the given value
090   */
091  public static JsonValue value(int value) {
092    return new JsonNumber(Integer.toString(value, 10));
093  }
094
095  /**
096   * Returns a JsonValue instance that represents the given <code>long</code> value.
097   *
098   * @param value
099   *          the value to get a JSON representation for
100   * @return a JSON value that represents the given value
101   */
102  public static JsonValue value(long value) {
103    return new JsonNumber(Long.toString(value, 10));
104  }
105
106  /**
107   * Returns a JsonValue instance that represents the given <code>float</code> value.
108   *
109   * @param value
110   *          the value to get a JSON representation for
111   * @return a JSON value that represents the given value
112   */
113  public static JsonValue value(float value) {
114    if (Float.isInfinite(value) || Float.isNaN(value)) {
115      throw new IllegalArgumentException(INFINITE_AND_NAN);
116    }
117    return new JsonNumber(cutOffPointZero(Float.toString(value)));
118  }
119
120  /**
121   * Returns a JsonValue instance that represents the given <code>double</code> value.
122   *
123   * @param value
124   *          the value to get a JSON representation for
125   * @return a JSON value that represents the given value
126   */
127  public static JsonValue value(double value) {
128    if (Double.isInfinite(value) || Double.isNaN(value)) {
129      throw new IllegalArgumentException(INFINITE_AND_NAN);
130    }
131    return new JsonNumber(cutOffPointZero(Double.toString(value)));
132  }
133
134  /**
135   * Returns a JsonValue instance that represents the given string.
136   *
137   * @param string
138   *          the string to get a JSON representation for
139   * @return a JSON value that represents the given string
140   */
141  public static JsonValue value(String string) {
142    return string == null ? NULL : new JsonString(string);
143  }
144
145  /**
146   * Returns a JsonValue instance that represents the given <code>boolean</code> value.
147   *
148   * @param value
149   *          the value to get a JSON representation for
150   * @return a JSON value that represents the given value
151   */
152  public static JsonValue value(boolean value) {
153    return value ? TRUE : FALSE;
154  }
155
156  /**
157   * Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the constructor.
158   *
159   * @return a new empty JSON array
160   */
161  public static JsonArray array() {
162    return new JsonArray();
163  }
164
165  /**
166   * Creates a new JsonArray that contains the JSON representations of the given <code>int</code> values.
167   *
168   * @param values
169   *          the values to be included in the new JSON array
170   * @return a new JSON array that contains the given values
171   */
172  public static JsonArray array(int... values) {
173    Objects.requireNonNull(values, VALUES_IS_NULL);
174    JsonArray array = new JsonArray();
175    Arrays.stream(values).forEach(array::add);
176    return array;
177  }
178
179  /**
180   * Creates a new JsonArray that contains the JSON representations of the given <code>long</code> values.
181   *
182   * @param values
183   *          the values to be included in the new JSON array
184   * @return a new JSON array that contains the given values
185   */
186  public static JsonArray array(long... values) {
187    Objects.requireNonNull(values, VALUES_IS_NULL);
188    JsonArray array = new JsonArray();
189    Arrays.stream(values).forEach(array::add);
190    return array;
191  }
192
193  /**
194   * Creates a new JsonArray that contains the JSON representations of the given <code>float</code> values.
195   *
196   * @param values
197   *          the values to be included in the new JSON array
198   * @return a new JSON array that contains the given values
199   */
200  public static JsonArray array(float... values) {
201    Objects.requireNonNull(values, VALUES_IS_NULL);
202    JsonArray array = new JsonArray();
203    for (float value : values) {
204      array.add(value);
205    }
206    return array;
207  }
208
209  /**
210   * Creates a new JsonArray that contains the JSON representations of the given <code>double</code> values.
211   *
212   * @param values
213   *          the values to be included in the new JSON array
214   * @return a new JSON array that contains the given values
215   */
216  public static JsonArray array(double... values) {
217    Objects.requireNonNull(values, VALUES_IS_NULL);
218    JsonArray array = new JsonArray();
219    Arrays.stream(values).forEach(array::add);
220    return array;
221  }
222
223  /**
224   * Creates a new JsonArray that contains the JSON representations of the given <code>boolean</code> values.
225   *
226   * @param values
227   *          the values to be included in the new JSON array
228   * @return a new JSON array that contains the given values
229   */
230  public static JsonArray array(boolean... values) {
231    Objects.requireNonNull(values, VALUES_IS_NULL);
232    JsonArray array = new JsonArray();
233    for (boolean value : values) {
234      array.add(value);
235    }
236    return array;
237  }
238
239  /**
240   * Creates a new JsonArray that contains the JSON representations of the given strings.
241   *
242   * @param strings
243   *          the strings to be included in the new JSON array
244   * @return a new JSON array that contains the given strings
245   */
246  public static JsonArray array(String... strings) {
247    Objects.requireNonNull(strings, VALUES_IS_NULL);
248    JsonArray array = new JsonArray();
249    Arrays.stream(strings).forEach(array::add);
250    return array;
251  }
252
253  /**
254   * Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the constructor.
255   *
256   * @return a new empty JSON object
257   */
258  public static JsonObject object() {
259    return new JsonObject();
260  }
261
262  /**
263   * Parses the given input string as JSON. The input must contain a valid JSON value, optionally padded with
264   * whitespace.
265   *
266   * @param string
267   *          the input string, must be valid JSON
268   * @return a value that represents the parsed JSON
269   * @throws ParseException
270   *           if the input is not valid JSON
271   */
272  public static JsonValue parse(String string) {
273    Objects.requireNonNull(string, STRING_IS_NULL);
274    DefaultHandler handler = new DefaultHandler();
275    new JsonParser(handler).parse(string);
276    return handler.getValue();
277  }
278
279  /**
280   * Reads the entire input from the given reader and parses it as JSON. The input must contain a
281   * valid JSON value, optionally padded with whitespace.
282   * <p>
283   * Characters are read in chunks into an input buffer. Hence, wrapping a reader in an additional
284   * <code>BufferedReader</code> likely won't improve reading performance.
285   * </p>
286   *
287   * @param reader
288   *          the reader to read the JSON value from
289   * @return a value that represents the parsed JSON
290   * @throws IOException
291   *           if an I/O error occurs in the reader
292   * @throws ParseException
293   *           if the input is not valid JSON
294   */
295  public static JsonValue parse(Reader reader) throws IOException {
296    Objects.requireNonNull(reader, READER_IS_NULL);
297    DefaultHandler handler = new DefaultHandler();
298    new JsonParser(handler).parse(reader);
299    return handler.getValue();
300  }
301
302  private static String cutOffPointZero(String string) {
303    if (string.endsWith(".0")) {
304      return string.substring(0, string.length() - 2);
305    }
306    return string;
307  }
308
309  static class DefaultHandler extends JsonHandler<JsonArray, JsonObject> {
310
311    protected JsonValue value;
312
313    @Override
314    public JsonArray startArray() {
315      return new JsonArray();
316    }
317
318    @Override
319    public JsonObject startObject() {
320      return new JsonObject();
321    }
322
323    @Override
324    public void endNull() {
325      value = NULL;
326    }
327
328    @Override
329    public void endBoolean(boolean bool) {
330      value = bool ? TRUE : FALSE;
331    }
332
333    @Override
334    public void endString(String string) {
335      value = new JsonString(string);
336    }
337
338    @Override
339    public void endNumber(String string) {
340      value = new JsonNumber(string);
341    }
342
343    @Override
344    public void endArray(JsonArray array) {
345      value = array;
346    }
347
348    @Override
349    public void endObject(JsonObject object) {
350      value = object;
351    }
352
353    @Override
354    public void endArrayValue(JsonArray array) {
355      array.add(value);
356    }
357
358    @Override
359    public void endObjectValue(JsonObject object, String name) {
360      object.add(name, value);
361    }
362
363    JsonValue getValue() {
364      return value;
365    }
366
367  }
368
369}