001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyrighted [year] [name of copyright owner]".
013 *
014 * Copyright © 2010–2011 ApexIdentity Inc. All rights reserved.
015 * Portions Copyrighted 2011-2017 ForgeRock AS.
016 */
017
018package org.forgerock.json;
019
020import java.util.AbstractMap;
021import java.util.AbstractSet;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.LinkedHashMap;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030import java.util.Set;
031
032import org.forgerock.util.Function;
033import org.forgerock.util.RangeSet;
034
035/**
036 * Represents a value in a JSON object model structure. JSON values are
037 * represented with standard Java objects: {@link String}, {@link Number},
038 * {@link Map}, {@link List}, {@link Boolean} and {@code null}.
039 */
040public class JsonValue implements Cloneable, Iterable<JsonValue> {
041
042    /**
043     * Returns a mutable JSON array containing the provided objects. This method
044     * is provided as a convenience method for constructing JSON arrays. Example
045     * usage:
046     *
047     * <pre>
048     * JsonValue value = json(array(1, 2, 3));
049     * </pre>
050     *
051     * @param objects
052     *            The array elements.
053     * @return A JSON array.
054     */
055    public static List<Object> array(final Object... objects) {
056        List<Object> array = new ArrayList<>(objects.length);
057        for (Object o : objects) {
058            array.add(unwrap(o));
059        }
060        return array;
061    }
062
063    /**
064     * Returns a JSON field for inclusion in a JSON object using
065     * {@link #object(java.util.Map.Entry...) object} only if its value is not {@code null}.
066     * Example usage:
067     *
068     * <pre>
069     * JsonValue value = json(object(fieldIfNotNull(&quot;uid&quot;, getUid()));
070     * </pre>
071     * <p>
072     * Note: This feature depends on the {@link #object(java.util.Map.Entry...)} method that
073     * checks if the entry is not {@code null} before including it into the map.
074     * </p>
075     *
076     * @param key
077     *            The JSON field name.
078     * @param value
079     *            The JSON field value (may be {@code null}).
080     * @return The JSON field for inclusion in a JSON object or {@code null}.
081     * @see #object(java.util.Map.Entry...)
082     */
083    public static Map.Entry<String, Object> fieldIfNotNull(final String key, final Object value) {
084        return value != null ? field(key, value) : null;
085    }
086
087    /**
088     * Returns a JSON field for inclusion in a JSON object using
089     * {@link #object(java.util.Map.Entry...) object}. This method is provided
090     * as a convenience method for constructing JSON objects. Example usage:
091     *
092     * <pre>
093     * JsonValue value = json(object(field(&quot;uid&quot;, &quot;bjensen&quot;), field(&quot;age&quot;, 30)));
094     * </pre>
095     *
096     * @param key
097     *            The JSON field name.
098     * @param value
099     *            The JSON field value.
100     * @return The JSON field for inclusion in a JSON object.
101     */
102    public static Map.Entry<String, Object> field(final String key, final Object value) {
103        return new AbstractMap.SimpleImmutableEntry<>(key, unwrap(value));
104    }
105
106    /**
107     * Returns a JSON value whose content is the provided object. This method is
108     * provided as a convenience method for constructing JSON objects, instead
109     * of using {@link #JsonValue(Object)}. Example usage:
110     *
111     * <pre>
112     * JsonValue value =
113     *         json(object(field(&quot;uid&quot;, &quot;bjensen&quot;),
114     *                     field(&quot;roles&quot;, array(&quot;sales&quot;, &quot;marketing&quot;))));
115     * </pre>
116     *
117     * @param object
118     *            the Java object representing the JSON value.
119     * @return The JSON value.
120     */
121    public static JsonValue json(final Object object) {
122        return new JsonValue(unwrap(object));
123    }
124
125    /**
126     * Returns a JSON object comprised of the provided JSON
127     * {@link #field(String, Object) fields}. This method is provided as a
128     * convenience method for constructing JSON objects. Example usage:
129     *
130     * <pre>
131     * JsonValue value = json(object(field(&quot;uid&quot;, &quot;bjensen&quot;), field(&quot;age&quot;, 30)));
132     * </pre>
133     *
134     * @param fields
135     *            The list of {@link #field(String, Object) fields} to include
136     *            in the JSON object. {@code null} elements are allowed, but are
137     *            not included in the returned map (this makes it easier to
138     *            include optional elements).
139     * @return The JSON object.
140     */
141    @SafeVarargs
142    public static Map<String, Object> object(final Map.Entry<String, Object>... fields) {
143        final Map<String, Object> object = object(fields.length);
144        for (final Map.Entry<String, Object> field : fields) {
145            if (field != null) {
146                object.put(field.getKey(), unwrap(field.getValue()));
147            }
148        }
149        return object;
150    }
151
152    /**
153     * Produces an empty JSON object pre-allocated for {@code size}
154     * {@link #field(String, Object) fields}. This method is provided as a
155     * convenience method for constructing JSON objects. Example usage:
156     *
157     * <pre>
158     * JsonValue value = json(object(20));
159     * for (Map.Entry&lt;String, Object&gt; entry : someMap.entrySet()) {
160     *     value.put(entry.getKey(), entry.getValue());
161     * }
162     * </pre>
163     *
164     * @param size
165     *            The size of the JSON object to allocate.
166     * @return The [empty] JSON object.
167     */
168    public static Map<String, Object> object(int size) {
169        return new LinkedHashMap<>(size);
170    }
171
172    /**
173     * Returns the key as an list index value. If the string does not represent
174     * a valid list index value, then {@code -1} is returned.
175     *
176     * @param key
177     *            the key to be converted into an list index value.
178     * @return the converted index value, or {@code -1} if invalid.
179     */
180    public static int toIndex(final String key) {
181        if (key == null || key.isEmpty()) {
182            return -1;
183        }
184
185        // verify that every character is a digit (this also prevents negative values)
186        int result = 0;
187
188        for (int i = 0; i < key.length(); ++i) {
189            final char c = key.charAt(i);
190            if (c < '0' || c > '9') {
191                return -1;
192            }
193            result = result * 10 + (c - '0');
194        }
195        return result;
196    }
197
198    /**
199     * Unwrap the object if it is a JsonValue - used when combining JsonValues so we
200     * do not get nested JsonValue wrappers.
201     *
202     * @param object the object to unwrap.
203     * @return the unwrapped object.
204     */
205    private static Object unwrap(final Object object) {
206        return object instanceof JsonValue
207                ? ((JsonValue) object).getObject()
208                : object;
209    }
210
211    /** The Java object representing this JSON value. */
212    private Object object;
213
214    /** The pointer to the value within a JSON structure. */
215    private JsonPointer pointer;
216
217    /**
218     * Constructs a JSON value object with a given object. This constructor will
219     * automatically unwrap {@link JsonValue} objects.
220     *
221     * @param object
222     *            the Java object representing the JSON value.
223     */
224    public JsonValue(final Object object) {
225        this(object, null);
226    }
227
228    /**
229     * Constructs a JSON value object with a given object and pointer. This
230     * constructor will automatically unwrap {@link JsonValue} objects.
231     *
232     * @param object
233     *            the Java object representing the JSON value.
234     * @param pointer
235     *            the pointer to the value in a JSON structure.
236     */
237    public JsonValue(final Object object, final JsonPointer pointer) {
238        this.object = object;
239        this.pointer = pointer;
240        if (object instanceof JsonValue) {
241            final JsonValue jv = (JsonValue) object;
242            this.object = jv.object;
243            if (pointer == null) {
244                this.pointer = jv.pointer;
245            }
246        }
247        if (this.pointer == null) {
248            this.pointer = new JsonPointer();
249        }
250    }
251
252    /**
253     * Adds the specified value to the list. Adding a value to a list shifts any
254     * existing elements at or above the specified index to the right by one.
255     *
256     * @param index
257     *            the {@code List} index of the value to add.
258     * @param object
259     *            the java object to add.
260     * @return this JSON value.
261     * @throws JsonValueException
262     *             if this JSON value is not a {@code List} or index is out of
263     *             range.
264     */
265    public JsonValue add(final int index, final Object object) {
266        final List<Object> list = required().asList();
267        if (index < 0 || index > list.size()) {
268            throw new JsonValueException(this, "List index out of range: " + index);
269        }
270        list.add(index, unwrap(object));
271        return this;
272    }
273
274    /**
275     * Adds the value identified by the specified pointer, relative to this
276     * value as root. If doing so would require the creation of a new object or
277     * list, a {@code JsonValueException} will be thrown.
278     * <p>
279     * NOTE: values may be added to a list using the reserved JSON pointer token
280     * "-". For example, the pointer "/a/b/-" will add a new element to the list
281     * referenced by "/a/b".
282     *
283     * @param pointer
284     *            identifies the child value to add.
285     * @param object
286     *            the Java object value to add.
287     * @return this JSON value.
288     * @throws JsonValueException
289     *             if the specified pointer is invalid.
290     */
291    public JsonValue add(final JsonPointer pointer, final Object object) {
292        navigateToParentOf(pointer).required().addToken(pointer.leaf(), object);
293        return this;
294    }
295
296    /**
297     * Adds the specified value to the end of the list. This method is
298     * equivalent to the following code:
299     *
300     * <pre>
301     * add(size(), object);
302     * </pre>
303     *
304     * @param object
305     *            the java object to add.
306     * @return this JSON value.
307     * @throws JsonValueException
308     *             if this JSON value is not a {@code List}.
309     */
310    public JsonValue add(final Object object) {
311        if (isList()) {
312            return add(size(), object);
313        }
314        throw new JsonValueException(this, "Expecting a List");
315    }
316
317    /**
318     * Adds the specified value.
319     * <p>
320     * If adding to a list value, the specified key must be parseable as an
321     * unsigned base-10 integer and be less than or equal to the list size.
322     * Adding a value to a list shifts any existing elements at or above the
323     * specified index to the right by one.
324     *
325     * @param key
326     *            the {@code Map} key or {@code List} index to add.
327     * @param object
328     *            the Java object to add.
329     * @return this JSON value.
330     * @throws JsonValueException
331     *             if not a {@code Map} or {@code List}, the {@code Map} key
332     *             already exists, or the {@code List} index is out of range.
333     */
334    public JsonValue add(final String key, final Object object) {
335        if (isMap()) {
336            final Map<String, Object> map = asMap();
337            if (map.containsKey(key)) {
338                throw new JsonValueException(this, "Map key " + key + " already exists");
339            }
340            map.put(key, unwrap(object));
341        } else if (isList()) {
342            add(toIndex(key), object);
343        } else {
344            throw new JsonValueException(this, "Expecting a Map or List");
345        }
346        return this;
347    }
348
349    /**
350     * Adds the value identified by the specified pointer, relative to this
351     * value as root. Missing parent objects or lists will be created on demand.
352     * <p>
353     * NOTE: values may be added to a list using the reserved JSON pointer token
354     * "-". For example, the pointer "/a/b/-" will add a new element to the list
355     * referenced by "/a/b".
356     *
357     * @param pointer
358     *            identifies the child value to add.
359     * @param object
360     *            the Java object value to add.
361     * @return this JSON value.
362     * @throws JsonValueException
363     *             if the specified pointer is invalid.
364     */
365    public JsonValue addPermissive(final JsonPointer pointer, final Object object) {
366        navigateToParentOfPermissive(pointer).addToken(pointer.leaf(), object);
367        return this;
368    }
369
370    /**
371     * Returns the JSON value as a {@link Boolean} object. If the value is
372     * {@code null}, this method returns {@code null}.
373     *
374     * @return the boolean value.
375     * @throws JsonValueException
376     *             if the JSON value is not a boolean type.
377     */
378    public Boolean asBoolean() {
379        return (object == null ? null : (Boolean) (expect(Boolean.class).object));
380    }
381
382    /**
383     * Returns the JSON value as a {@link Double} object. This may involve
384     * rounding. If the JSON value is {@code null}, this method returns
385     * {@code null}.
386     *
387     * @return the double-precision floating point value.
388     * @throws JsonValueException
389     *             if the JSON value is not a number.
390     */
391    public Double asDouble() {
392        return (object == null ? null : Double.valueOf(asNumber().doubleValue()));
393    }
394
395    /**
396     * Returns the JSON value as an {@link Integer} object. This may involve
397     * rounding or truncation. If the JSON value is {@code null}, this method
398     * returns {@code null}.
399     *
400     * @return the integer value.
401     * @throws JsonValueException
402     *             if the JSON value is not a number.
403     */
404    public Integer asInteger() {
405        return (object == null ? null : Integer.valueOf(asNumber().intValue()));
406    }
407
408    /**
409     * Returns the JSON value as a {@link Collection} object. If the JSON value is
410     * {@code null}, this method returns {@code null}.
411     *
412     * @return the collection value, or {@code null} if no value.
413     * @throws JsonValueException
414     *             if the JSON value is not a {@code Collection}.
415     */
416    public Collection<Object> asCollection() {
417        return asCollection(Object.class);
418    }
419
420    /**
421     * Returns the JSON value as a {@link List} object. If the JSON value is
422     * {@code null}, this method returns {@code null}.
423     * The returned {@link List} is <b>not</b> a copy : any interaction with it
424     * will affect the {@link JsonValue}.
425     *
426     * @return the list value, or {@code null} if no value.
427     * @throws JsonValueException
428     *             if the JSON value is not a {@code List}.
429     */
430    public List<Object> asList() {
431        return asList(Object.class);
432    }
433
434    /**
435     * Returns the JSON value as a {@link Collection} containing objects of the
436     * specified type. If the value is {@code null}, this method returns
437     * {@code null}. If any of the elements of the collection are not {@code null} and
438     * not of the specified type, {@code JsonValueException} is thrown.
439     * The returned {@link Collection} is <b>not</b> a copy : any interaction with it
440     * will affect the {@link JsonValue}.
441     *
442     * @param <E>
443     *            the type of elements in this collection
444     * @param type
445     *            the type of object that all elements are expected to be.
446     * @return the collection value, or {@code null} if no value.
447     * @throws JsonValueException
448     *             if the JSON value is not a {@code Collection} or contains an
449     *             unexpected type.
450     * @throws NullPointerException
451     *             if {@code type} is {@code null}.
452     */
453    @SuppressWarnings("unchecked")
454    public <E> Collection<E> asCollection(final Class<E> type) {
455        if (object != null) {
456            expect(Collection.class);
457            if (type != Object.class) {
458                final Collection<Object> coll = (Collection<Object>) this.object;
459                for (final Object element : coll) {
460                    if (element != null && !type.isInstance(element)) {
461                        throw new JsonValueException(this, "Expecting a Collection of " + type.getName()
462                                + " elements");
463                    }
464                }
465            }
466        }
467        return (Collection<E>) object;
468    }
469
470    /**
471     * Returns the JSON value as a {@link List} containing objects of the
472     * specified type. If the value is {@code null}, this method returns
473     * {@code null}. If any of the elements of the list are not {@code null} and
474     * not of the specified type, {@code JsonValueException} is thrown.
475     * The returned {@link List} is <b>not</b> a copy : any interaction with it
476     * will affect the {@link JsonValue}.
477     *
478     * @param <E>
479     *            the type of elements in this list
480     * @param type
481     *            the type of object that all elements are expected to be.
482     * @return the list value, or {@code null} if no value.
483     * @throws JsonValueException
484     *             if the JSON value is not a {@code List} or contains an unexpected type.
485     * @throws NullPointerException
486     *             if {@code type} is {@code null}.
487     */
488    @SuppressWarnings("unchecked")
489    public <E> List<E> asList(final Class<E> type) {
490        if (object != null) {
491            expect(List.class);
492            if (type != Object.class) {
493                final List<Object> list = (List<Object>) this.object;
494                for (final Object element : list) {
495                    if (element != null && !type.isInstance(element)) {
496                        throw new JsonValueException(this, "Expecting a List of " + type.getName()
497                                + " elements");
498                    }
499                }
500            }
501        }
502        return (List<E>) object;
503    }
504
505    /**
506     * Returns the JSON value as an object whose type
507     * (and value) is specified by a transformation function. It is up to to the
508     * transformation function to transform/enforce source types of the elements
509     * in the Json source element and to decide what to do depending on the kind
510     * of {@link JsonValue} : if it is null, a {@link String}, a {@link List},
511     * or {@link Map}. If the type-transformation cannot occur,
512     * the exception specified by the transformation function is thrown.
513     *
514     * @param <V>
515     *            the type of element
516     * @param <E>
517     *            the type of exception thrown by the transformation function
518     * @param transformFunction
519     *            a {@link Function} to transform the JsonValue element to the desired type
520     * @return the value, or {@code null} if no value.
521     * @throws E
522     *             if the JsonValue element cannot be transformed
523     * @throws NullPointerException
524     *             if {@code transformFunction} is {@code null}.
525     */
526    public <V, E extends Exception> V as(final Function<JsonValue, V, E> transformFunction) throws E {
527        return transformFunction.apply(this);
528    }
529
530    /**
531     * Returns the JSON value as a {@link Long} object. This may involve
532     * rounding or truncation. If the JSON value is {@code null}, this method
533     * returns {@code null}.
534     *
535     * @return the long integer value.
536     * @throws JsonValueException
537     *             if the JSON value is not a number.
538     */
539    public Long asLong() {
540        return (object == null ? null : Long.valueOf(asNumber().longValue()));
541    }
542
543    /**
544     * Returns the JSON value as a {@code Map} object. If the JSON value is
545     * {@code null}, this method returns {@code null}.
546     *
547     * @return the map value, or {@code null} if no value.
548     * @throws JsonValueException
549     *             if the JSON value is not a {@code Map}.
550     */
551    @SuppressWarnings("unchecked")
552    public Map<String, Object> asMap() {
553        return (object == null ? null : (Map<String, Object>) (expect(Map.class).object));
554    }
555
556    /**
557     * Returns the JSON value as a {@link Map} containing objects of the
558     * specified type. If the value is {@code null}, this method returns
559     * {@code null}. If any of the values of the map are not {@code null} and
560     * not of the specified type, {@code JsonValueException} is thrown.
561     *
562     * @param <V>
563     *            the type of values in this map
564     * @param type
565     *            the type of object that all values are expected to be.
566     * @return the map value, or {@code null} if no value.
567     * @throws JsonValueException
568     *             if the JSON value is not a {@code Map} or contains an
569     *             unexpected type.
570     * @throws NullPointerException
571     *             if {@code type} is {@code null}.
572     */
573    @SuppressWarnings("unchecked")
574    public <V> Map<String, V> asMap(final Class<V> type) {
575        if (object != null) {
576            expect(Map.class);
577            if (type != Object.class) {
578                final Map<String, Object> map = (Map<String, Object>) this.object;
579                for (final Object element : map.values()) {
580                    if (element != null && !type.isInstance(element)) {
581                        throw new JsonValueException(this, "Expecting a Map of " + type.getName()
582                                + " elements");
583                    }
584                }
585            }
586        }
587        return (Map<String, V>) object;
588    }
589    /**
590     * Returns the JSON value as a {@link Map} containing a collection of
591     * objects of the specified type. If the value is {@code null}, this method
592     * returns {@code null}. If any of the values of the map are not {@code null} and
593     * not of the specified type, {@code JsonValueException} is thrown.
594     *
595     * @param <E>
596     *            the type of elements in the collection
597     * @param elementType
598     *            the type of object that all collection elements are
599     *            expected to be.
600     * @return the map value, or {@code null} if no value.
601     * @throws JsonValueException
602     *             if the JSON value is not a {@code Map} or contains an
603     *             unexpected type.
604     * @throws NullPointerException
605     *             if {@code type} is {@code null}.
606     */
607    @SuppressWarnings("unchecked")
608    public <E> Map<String, List<E>> asMapOfList(final Class<E> elementType) {
609        if (object != null) {
610            expect(Map.class);
611            if (elementType != Object.class) {
612                final Map<String, Object> map = (Map<String, Object>) this.object;
613                for (final Object value : map.values()) {
614                    if (value != null && !(value instanceof List)) {
615                        throw new JsonValueException(this, "Expecting a Map of List values");
616                    }
617                    final List<?> list = (List<?>) value;
618                    for (final Object element : list) {
619                        if (element != null && !elementType.isInstance(element)) {
620                            throw new JsonValueException(this, "Expecting a Map of Lists with "
621                                    + elementType.getName() + " elements");
622                        }
623                    }
624                }
625            }
626        }
627        return (Map<String, List<E>>) object;
628    }
629
630    /**
631     * Returns the JSON value as a {@code Number} object. If the JSON value is
632     * {@code null}, this method returns {@code null}.
633     *
634     * @return the numeric value.
635     * @throws JsonValueException
636     *             if the JSON value is not a number.
637     */
638    public Number asNumber() {
639        return (object == null ? null : (Number) (expect(Number.class).object));
640    }
641
642    /**
643     * Returns the JSON value as a {@code String} object. If the JSON value is
644     * {@code null}, this method returns {@code null}.
645     *
646     * @return the string value.
647     * @throws JsonValueException
648     *             if the JSON value is not a string.
649     */
650    public String asString() {
651        return (object == null ? null : (String) (expect(String.class).object));
652    }
653
654    /**
655     * Removes all child values from this JSON value, if it has any.
656     */
657    public void clear() {
658        if (isMap()) {
659            asMap().clear();
660        } else if (isCollection()) {
661            asCollection().clear();
662        }
663    }
664
665    /**
666     * Returns a shallow copy of this JSON value. If this JSON value contains a
667     * {@code Map} or a {@code List} object, the returned JSON
668     * value will contain a shallow copy of the original contained object.
669     * <p>
670     * The new value's members can be modified without affecting the original
671     * value. Modifying the member's members will almost certainly affect the
672     * original value. To avoid this, use the {@link #copy} method to return a
673     * deep copy of the JSON value.
674     * <p>
675     * This method does not traverse the value's members, nor will it apply any
676     * transformations.
677     *
678     * @return a shallow copy of this JSON value.
679     */
680    @Override
681    public JsonValue clone() {
682        final JsonValue result = new JsonValue(this.object, this.pointer);
683        if (isMap()) {
684            result.object = new LinkedHashMap<>(this.asMap());
685        } else if (isList()) {
686            result.object = new ArrayList<>(this.asList());
687        }
688        return result;
689    }
690
691    /**
692     * Returns {@code true} this JSON value contains an item with the specified
693     * value.
694     *
695     * @param object
696     *            the object to seek within this JSON value.
697     * @return {@code true} if this value contains the specified member value.
698     */
699    public boolean contains(final Object object) {
700        boolean result = false;
701        if (isMap()) {
702            result = asMap().containsValue(object);
703        } else if (isCollection()) {
704            result = asCollection().contains(object);
705        }
706        return result;
707    }
708
709    /**
710     * Returns a deep copy of this JSON value.
711     * <p>
712     * Note: This method is recursive, and currently has no ability to detect or
713     * correct for structures containing cyclic references. Processing such a
714     * structure will result in a {@link StackOverflowError} being thrown.
715     *
716     * @return a deep copy of this JSON value.
717     */
718    public JsonValue copy() {
719        // TODO: track original values to resolve cyclic references
720        final JsonValue result = new JsonValue(object, pointer); // start with shallow copy
721        if (this.isMap()) {
722            final Map<String, Object> map = object(size());
723            for (final String key : keys()) {
724                map.put(key, this.get(key).copy().getObject()); // recursion
725            }
726            result.object = map;
727        } else if (isList()) {
728            final ArrayList<Object> list = new ArrayList<>(size());
729            for (final JsonValue element : this) {
730                list.add(element.copy().getObject()); // recursion
731            }
732            result.object = list;
733        }
734        return result;
735    }
736
737    /**
738     * Defaults the JSON value to the specified value if it is currently
739     * {@code null}.
740     *
741     * @param object
742     *            the object to default to.
743     * @return this JSON value or a new JSON value containing the default value.
744     */
745    public JsonValue defaultTo(final Object object) {
746        return (this.object != null ? this : new JsonValue(object, this.pointer));
747    }
748
749    /**
750     * Called to enforce that the JSON value is of a particular type. A value of
751     * {@code null} is allowed.
752     *
753     * @param type
754     *            the class that the underlying value must have.
755     * @return this JSON value.
756     * @throws JsonValueException
757     *             if the value is not the specified type.
758     */
759    public JsonValue expect(final Class<?> type) {
760        if (object != null && !type.isInstance(object)) {
761            throw new JsonValueException(this, "Expecting a " + type.getName());
762        }
763        return this;
764    }
765
766    /**
767     * Returns the specified child value. If this JSON value is not a
768     * {@link List} or if no such child exists, then a JSON value containing a
769     * {@code null} is returned.
770     *
771     * @param index
772     *            index of child element value to return.
773     * @return the child value, or a JSON value containing {@code null}.
774     * @throws JsonValueException
775     *             if index is negative.
776     */
777    public JsonValue get(final int index) {
778        Object result = null;
779        if (index < 0) {
780            throw new JsonValueException(this, "List index out of range: " + index);
781        }
782        if (isList() && index >= 0) {
783            final List<Object> list = asList();
784            if (index < list.size()) {
785                result = list.get(index);
786            }
787        }
788        return new JsonValue(result, pointer.child(index));
789    }
790
791    /**
792     * Returns the specified child value with a pointer, relative to this value
793     * as root. If the specified child value does not exist, then {@code null}
794     * is returned.
795     *
796     * @param pointer
797     *            the JSON pointer identifying the child value to return.
798     * @return the child value, or {@code null} if no such value exists.
799     */
800    public JsonValue get(final JsonPointer pointer) {
801        JsonValue result = this;
802        for (final String token : pointer) {
803            final JsonValue member = result.get(token);
804            if (member.isNull() && !result.isDefined(token)) {
805                return null; // undefined value yields null, not a JSON value containing null
806            }
807            result = member;
808        }
809        return result;
810    }
811
812    /**
813     * Returns the specified item value. If no such member value exists, then a
814     * JSON value containing {@code null} is returned.
815     *
816     * @param key
817     *            the {@code Map} key or {@code List} index identifying the item
818     *            to return.
819     * @return a JSON value containing the value or {@code null}.
820     */
821    public JsonValue get(final String key) {
822        Object result = null;
823        if (isMap()) {
824            result = asMap().get(key);
825        } else if (isList()) {
826            final List<Object> list = asList();
827            final int index = toIndex(key);
828            if (index >= 0 && index < list.size()) {
829                result = list.get(index);
830            }
831        }
832        return new JsonValue(result, pointer.child(key));
833    }
834
835    /**
836     * Returns the raw Java object representing this JSON value.
837     *
838     * @return the raw Java object representing this JSON value.
839     */
840    public Object getObject() {
841        return object;
842    }
843
844    /**
845     * Returns the pointer of the JSON value in its JSON structure.
846     *
847     * @return the pointer of the JSON value in its JSON structure.
848     */
849    public JsonPointer getPointer() {
850        return pointer;
851    }
852
853    /**
854     * Returns {@code true} if the JSON value is a {@link Boolean}.
855     *
856     * @return {@code true} if the JSON value is a {@link Boolean}.
857     */
858    public boolean isBoolean() {
859        return (object != null && object instanceof Boolean);
860    }
861
862    /**
863     * Returns {@code true} if this JSON value contains the specified item.
864     *
865     * @param key
866     *            the {@code Map} key or {@code List} index of the item to seek.
867     * @return {@code true} if this JSON value contains the specified member.
868     * @throws NullPointerException
869     *             if {@code key} is {@code null}.
870     */
871    public boolean isDefined(final String key) {
872        boolean result = false;
873        if (isMap()) {
874            result = asMap().containsKey(key);
875        } else if (isList()) {
876            final int index = toIndex(key);
877            result = (index >= 0 && index < asList().size());
878        }
879        return result;
880    }
881
882    /**
883     * Returns {@code true} if the JSON value is a {@link Collection}.
884     *
885     * @return {@code true} if the JSON value is a {@link Collection}.
886     */
887    public boolean isCollection() {
888        return (object instanceof Collection);
889    }
890
891    /**
892     * Returns {@code true} if the JSON value is a {@link List}.
893     *
894     * @return {@code true} if the JSON value is a {@link List}.
895     */
896    public boolean isList() {
897        return (object instanceof List);
898    }
899
900    /**
901     * Returns {@code true} if the JSON value is a {@link Map}.
902     *
903     * @return {@code true} if the JSON value is a {@link Map}.
904     */
905    public boolean isMap() {
906        return (object instanceof Map);
907    }
908
909    /**
910     * Returns {@code true} if the value is {@code null}.
911     *
912     * @return {@code true} if the value is {@code null}.
913     */
914    public boolean isNull() {
915        return (object == null);
916    }
917
918    /**
919     * Returns {@code true} if the value is not {@code null}.
920     *
921     * @return {@code true} if the value is not {@code null}.
922     */
923    public boolean isNotNull() {
924        return !isNull();
925    }
926
927    /**
928     * Returns {@code true} if the JSON value is a {@link Number}.
929     *
930     * @return {@code true} if the JSON value is a {@link Number}.
931     */
932    public boolean isNumber() {
933        return (object != null && object instanceof Number);
934    }
935
936    /**
937     * Returns {@code true} if the JSON value is a {@link String}.
938     *
939     * @return {@code true} if the JSON value is a {@link String}.
940     */
941    public boolean isString() {
942        return (object != null && object instanceof String);
943    }
944
945    /**
946     * Returns an iterator over the child values that this JSON value contains.
947     * If this value is a {@link Map}, then the order of the
948     * resulting child values is undefined. Calling the {@link Iterator#remove()}
949     * method of the returned iterator will throw a {@link UnsupportedOperationException}.
950     *
951     * @return an iterator over the child values that this JSON value contains.
952     */
953    @Override
954    public Iterator<JsonValue> iterator() {
955        if (isList()) { // optimize for list
956            return new Iterator<JsonValue>() {
957                int cursor = 0;
958                Iterator<Object> i = asList().iterator();
959
960                @Override
961                public boolean hasNext() {
962                    return i.hasNext();
963                }
964
965                @Override
966                public JsonValue next() {
967                    final Object element = i.next();
968                    return new JsonValue(element, pointer.child(cursor++));
969                }
970
971                @Override
972                public void remove() {
973                    throw new UnsupportedOperationException();
974                }
975            };
976        } else {
977            return new Iterator<JsonValue>() {
978                Iterator<String> i = keys().iterator();
979
980                @Override
981                public boolean hasNext() {
982                    return i.hasNext();
983                }
984
985                @Override
986                public JsonValue next() {
987                    return get(i.next());
988                }
989
990                @Override
991                public void remove() {
992                    throw new UnsupportedOperationException();
993                }
994            };
995        }
996    }
997
998    /**
999     * Returns the set of keys for this JSON value's child values. If this value
1000     * is a {@code Map}, then the order of the resulting keys is the same as the
1001     * underlying Map implementation. If there are no child values, this method
1002     * returns an empty set.
1003     *
1004     * @return the set of keys for this JSON value's child values.
1005     */
1006    public Set<String> keys() {
1007        if (isMap()) {
1008            return asMap().keySet();
1009        } else if (isList()) {
1010            return new AbstractSet<String>() {
1011                final RangeSet range = new RangeSet(JsonValue.this.size()); // 0 through size-1 inclusive
1012
1013                @Override
1014                public boolean contains(final Object o) {
1015                    boolean result = false;
1016                    if (o instanceof String) {
1017                        try {
1018                            result = range.contains(Integer.valueOf((String) o));
1019                        } catch (final NumberFormatException nfe) {
1020                            // ignore; yields false
1021                        }
1022                    }
1023                    return result;
1024                }
1025
1026                @Override
1027                public Iterator<String> iterator() {
1028                    return new Iterator<String>() {
1029                        Iterator<Integer> i = range.iterator();
1030
1031                        @Override
1032                        public boolean hasNext() {
1033                            return i.hasNext();
1034                        }
1035
1036                        @Override
1037                        public String next() {
1038                            return i.next().toString();
1039                        }
1040
1041                        @Override
1042                        public void remove() {
1043                            throw new UnsupportedOperationException();
1044                        }
1045                    };
1046                }
1047
1048                @Override
1049                public int size() {
1050                    return range.size();
1051                }
1052            };
1053        } else {
1054            return Collections.emptySet();
1055        }
1056    }
1057
1058    /**
1059     * Sets the value of the specified child list element.
1060     *
1061     * @param index
1062     *            the {@code List} index identifying the child value to set.
1063     * @param object
1064     *            the Java value to assign to the list element.
1065     * @return this JSON value.
1066     * @throws JsonValueException
1067     *             if this JSON value is not a {@code List} or index is out of
1068     *             range.
1069     */
1070    public JsonValue put(final int index, final Object object) {
1071        final List<Object> list = required().asList();
1072        if (index < 0 || index > list.size()) {
1073            throw new JsonValueException(this, "List index out of range: " + index);
1074        } else if (index == list.size()) { // appending to end of list
1075            list.add(unwrap(object));
1076        } else { // replacing existing element
1077            list.set(index, unwrap(object));
1078        }
1079        return this;
1080    }
1081
1082    /**
1083     * Sets the value identified by the specified pointer, relative to this
1084     * value as root. If doing so would require the creation of a new object or
1085     * list, a {@code JsonValueException} will be thrown.
1086     * <p>
1087     * NOTE: values may be added to a list using the reserved JSON pointer token
1088     * "-". For example, the pointer "/a/b/-" will add a new element to the list
1089     * referenced by "/a/b".
1090     *
1091     * @param pointer
1092     *            identifies the child value to set.
1093     * @param object
1094     *            the Java object value to set.
1095     * @return this JSON value.
1096     * @throws JsonValueException
1097     *             if the specified pointer is invalid.
1098     */
1099    public JsonValue put(final JsonPointer pointer, final Object object) {
1100        navigateToParentOf(pointer).required().putToken(pointer.leaf(), object);
1101        return this;
1102    }
1103
1104    /**
1105     * Sets the value of the specified member.  
1106     * <p>
1107     * If setting a list element, the specified key must be parseable as an
1108     * unsigned base-10 integer and be less than or equal to the size of the
1109     * list.
1110     *
1111     * @param key
1112     *            the {@code Map} key or {@code List} index identifying the
1113     *            child value to set.
1114     * @param object
1115     *            the object value to assign to the member.
1116     * @return this JSON value.
1117     * @throws JsonValueException
1118     *             if this JSON value is not a {@code Map} or {@code List}.
1119     * @throws NullPointerException
1120     *             if {@code key} is {@code null}.
1121     */
1122    public JsonValue put(final String key, final Object object) {
1123        if (key == null) {
1124            throw new NullPointerException();
1125        } else if (isMap()) {
1126            asMap().put(key, unwrap(object));
1127        } else if (isList()) {
1128            put(toIndex(key), object);
1129        } else {
1130            throw new JsonValueException(this, "Expecting a Map or List");
1131        }
1132        return this;
1133    }
1134
1135    /**
1136     * Sets the value identified by the specified pointer, relative to this
1137     * value as root. Missing parent objects or lists will be created on demand.
1138     * <p>
1139     * NOTE: values may be added to a list using the reserved JSON pointer token
1140     * "-". For example, the pointer "/a/b/-" will add a new element to the list
1141     * referenced by "/a/b".
1142     *
1143     * @param pointer
1144     *            identifies the child value to set.
1145     * @param object
1146     *            the Java object value to set.
1147     * @return this JSON value.
1148     * @throws JsonValueException
1149     *             if the specified pointer is invalid.
1150     */
1151    public JsonValue putPermissive(final JsonPointer pointer, final Object object) {
1152        navigateToParentOfPermissive(pointer).putToken(pointer.leaf(), object);
1153        return this;
1154    }
1155
1156    /**
1157     * Removes the specified child value, shifting any subsequent elements to
1158     * the left. If the JSON value is not a {@code List}, calling this method
1159     * has no effect.
1160     *
1161     * @param index
1162     *            the {@code List} index identifying the child value to remove.
1163     */
1164    public void remove(final int index) {
1165        if (index >= 0 && isList()) {
1166            final List<Object> list = asList();
1167            if (index < list.size()) {
1168                list.remove(index);
1169            }
1170        }
1171    }
1172
1173    /**
1174     * Removes the specified child value with a pointer, relative to this value
1175     * as root. If the specified child value is not defined, calling this method
1176     * has no effect.
1177     *
1178     * @param pointer
1179     *            the JSON pointer identifying the child value to remove.
1180     */
1181    public void remove(final JsonPointer pointer) {
1182        navigateToParentOf(pointer).remove(pointer.leaf());
1183    }
1184
1185    /**
1186     * Removes the specified child value. If the specified child value is not
1187     * defined, calling this method has no effect.
1188     *
1189     * @param key
1190     *            the {@code Map} key or {@code List} index identifying the
1191     *            child value to remove.
1192     */
1193    public void remove(final String key) {
1194        if (isMap()) {
1195            asMap().remove(key);
1196        } else if (isList()) {
1197            remove(toIndex(key));
1198        }
1199    }
1200
1201    /**
1202     * Throws a {@code JsonValueException} if the JSON value is {@code null}.
1203     *
1204     * @return this JSON value.
1205     * @throws JsonValueException
1206     *             if the JSON value is {@code null}.
1207     */
1208    public JsonValue required() {
1209        if (object == null) {
1210            throw new JsonValueException(this, "Expecting a value");
1211        }
1212        return this;
1213    }
1214
1215    /**
1216     * Sets the Java object representing this JSON value.
1217     * <p>
1218     * This method will automatically unwrap {@link JsonValue} objects.
1219     *
1220     * @param object
1221     *            the object to set.
1222     */
1223    public void setObject(final Object object) {
1224        this.object = object;
1225        if (object instanceof JsonValue) {
1226            final JsonValue jv = (JsonValue) object;
1227            this.object = jv.object;
1228        }
1229    }
1230
1231    /**
1232     * Returns the number of values that this JSON value contains.
1233     *
1234     * @return the number of values that this JSON value contains.
1235     */
1236    public int size() {
1237        if (isMap()) {
1238            return asMap().size();
1239        } else if (isCollection()) {
1240            return asCollection().size();
1241        } else {
1242            return 0;
1243        }
1244    }
1245
1246    /**
1247     * Returns a string representation of the JSON value. The result
1248     * resembles—but is not guaranteed to conform to—JSON syntax. This method
1249     * does not apply transformations to the value's children.
1250     *
1251     * @return a string representation of the JSON value.
1252     */
1253    @SuppressWarnings("unchecked")
1254    @Override
1255    public String toString() {
1256        final StringBuilder sb = new StringBuilder();
1257        if (isNull()) {
1258            sb.append("null");
1259        } else if (isMap()) {
1260            sb.append("{ ");
1261            final Map<Object, Object> map = (Map<Object, Object>) object;
1262            for (final Iterator<Object> i = map.keySet().iterator(); i.hasNext();) {
1263                final Object key = i.next();
1264                sb.append('"');
1265                appendEscapedString(sb, key.toString());
1266                sb.append("\": ");
1267                sb.append(new JsonValue(map.get(key)).toString()); // recursion
1268                if (i.hasNext()) {
1269                    sb.append(", ");
1270                }
1271            }
1272            sb.append(" }");
1273        } else if (isCollection()) {
1274            sb.append("[ ");
1275            for (final Iterator<Object> i = ((Collection<Object>) object).iterator(); i.hasNext();) {
1276                sb.append(new JsonValue(i.next()).toString()); // recursion
1277                if (i.hasNext()) {
1278                    sb.append(", ");
1279                }
1280            }
1281            sb.append(" ]");
1282        } else if (isString()) {
1283            sb.append('"');
1284            appendEscapedString(sb, object.toString());
1285            sb.append('"');
1286        } else {
1287            sb.append(object.toString());
1288        }
1289        return sb.toString();
1290    }
1291
1292    /**
1293     * As per json.org a string is any Unicode character except " or \ or
1294     * control characters. Special characters will be escaped using a \ as
1295     * follows:
1296     * <ul>
1297     * <li> {@literal \ "} - double quote
1298     * <li> {@literal \ \} - back slash
1299     * <li> {@literal \ b} - backspace
1300     * <li> {@literal \ f} - form feed
1301     * <li> {@literal \ n} - new line
1302     * <li> {@literal \ r} - carriage return
1303     * <li> {@literal \ t} - tab
1304     * <li> {@literal \ u xxxx} - other control characters.
1305     * </ul>
1306     */
1307    private static void appendEscapedString(final StringBuilder sb, final String s) {
1308        final int size = s.length();
1309        for (int i = 0; i < size; i++) {
1310            final char c = s.charAt(i);
1311            switch (c) {
1312            // Escape characters which must be escaped.
1313            case '"':
1314                sb.append("\\\"");
1315                break;
1316            case '\\':
1317                sb.append("\\\\");
1318                break;
1319            // Escape common controls to the C equivalent to make them easier to read.
1320            case '\b':
1321                sb.append("\\b");
1322                break;
1323            case '\f':
1324                sb.append("\\f");
1325                break;
1326            case '\n':
1327                sb.append("\\n");
1328                break;
1329            case '\r':
1330                sb.append("\\r");
1331                break;
1332            case '\t':
1333                sb.append("\\t");
1334                break;
1335            default:
1336                if (Character.isISOControl(c)) {
1337                    final String hex = Integer.toHexString(c).toUpperCase(Locale.ENGLISH);
1338                    final int hexPadding = 4 - hex.length();
1339                    sb.append("\\u");
1340                    for (int j = 0; j < hexPadding; j++) {
1341                        sb.append('0');
1342                    }
1343                    sb.append(hex);
1344                } else {
1345                    sb.append(c);
1346                }
1347            }
1348        }
1349    }
1350
1351    private void addToken(final String token, final Object object) {
1352        if (isEndOfListToken(token) && isList()) {
1353            add(object);
1354        } else {
1355            add(token, object);
1356        }
1357    }
1358
1359    private boolean isEndOfListToken(final String token) {
1360        return token.equals("-");
1361    }
1362
1363    private boolean isIndexToken(final String token) {
1364        if (token.isEmpty()) {
1365            return false;
1366        } else {
1367            for (int i = 0; i < token.length(); i++) {
1368                final char c = token.charAt(i);
1369                if (!Character.isDigit(c)) {
1370                    return false;
1371                }
1372            }
1373            return true;
1374        }
1375    }
1376
1377    private JsonValue navigateToParentOf(final JsonPointer pointer) {
1378        JsonValue jv = this;
1379        final int size = pointer.size();
1380        for (int n = 0; n < size - 1; n++) {
1381            jv = jv.get(pointer.get(n));
1382            if (jv.isNull()) {
1383                break;
1384            }
1385        }
1386        return jv;
1387    }
1388
1389    private JsonValue navigateToParentOfPermissive(final JsonPointer pointer) {
1390        JsonValue jv = this;
1391        final int size = pointer.size();
1392        for (int n = 0; n < size - 1; n++) {
1393            final String token = pointer.get(n);
1394            final JsonValue next = jv.get(token);
1395            if (next.isNotNull()) {
1396                jv = next;
1397            } else if (isIndexToken(token)) {
1398                throw new JsonValueException(this, "Expecting a value");
1399            } else {
1400                // Create the field based on the type of the next token.
1401                final String nextToken = pointer.get(n + 1);
1402                if (isEndOfListToken(nextToken)) {
1403                    jv.add(token, new ArrayList<>());
1404                    jv = jv.get(token);
1405                } else if (isIndexToken(nextToken)) {
1406                    throw new JsonValueException(this, "Expecting a value");
1407                } else {
1408                    jv.putPermissive(new JsonPointer(token), new LinkedHashMap<>());
1409                    jv = jv.get(token);
1410                }
1411            }
1412        }
1413        return jv;
1414    }
1415
1416    private void putToken(final String token, final Object object) {
1417        if (isEndOfListToken(token) && isList()) {
1418            add(object);
1419        } else {
1420            put(token, object);
1421        }
1422    }
1423
1424    /**
1425     * Performs a deep comparison of this JSON value with another JSON value, and returns whether the two objects
1426     * are identical.  Fails fast in that a {@code false} is returned as soon as a difference is detected.
1427     * <p>
1428     *     <b>Note:</b> Only values recognisable as JSON primitives ({@link Map}, {@link List}, {@link Number},
1429     *     {@link Boolean}, {@link String} and {@code null}) are supported.
1430     * </p>
1431     * @param other another value.
1432     * @return whether the two objects are equal.
1433     * @throws NullPointerException if {@code other} is {@code null}.
1434     * @throws IllegalArgumentException if this or the {@code other} value contains non-JSON primitive values.
1435     */
1436    public boolean isEqualTo(JsonValue other) {
1437        return JsonPatch.isEqual(this, other);
1438    }
1439
1440    /**
1441     * Performs a deep comparison of this JSON vlaue with another JSON value, and produces a
1442     * JSON Patch value, which contains the operations necessary to modify the current value
1443     * to arrive at the {@code target} value.
1444     *
1445     * @param target the intended target value.
1446     * @return the resulting JSON Patch value.
1447     * @throws NullPointerException if either of {@code original} or {@code target} are {@code null}.
1448     */
1449    public JsonValue diff(JsonValue target) {
1450        return JsonPatch.diff(this, target);
1451    }
1452
1453    /**
1454     * Applies a set of modifications in a JSON patch value to the current object, resulting
1455     * in the intended target value. In the event of a failure, this method does not revert
1456     * any modifications applied up to the point of failure.
1457     *
1458     * @param patch the JSON Patch value, specifying the modifications to apply to the original value.
1459     * @throws JsonValueException if application of the patch failed.
1460     */
1461    public void patch(JsonValue patch) {
1462        JsonPatch.patch(this, patch);
1463    }
1464}