com.github.jsonj.JsonArray.java Source code

Java tutorial

Introduction

Here is the source code for com.github.jsonj.JsonArray.java

Source

/**
 * Copyright (c) 2011, Jilles van Gurp
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.github.jsonj;

import static com.github.jsonj.tools.JsonBuilder.fromObject;
import static com.github.jsonj.tools.JsonBuilder.primitive;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import org.apache.commons.lang.StringUtils;

import com.github.jsonj.exceptions.JsonTypeMismatchException;
import com.github.jsonj.tools.JsonBuilder;
import com.github.jsonj.tools.JsonSerializer;

/**
 * Representation of json arrays.
 */
public class JsonArray extends ArrayList<JsonElement> implements JsonElement {
    private static final long serialVersionUID = -1269731858619421388L;
    private boolean immutable = false;

    public JsonArray() {
        super();
    }

    public JsonArray(Collection<?> existing) {
        super();
        for (Object o : existing) {
            add(fromObject(o));
        }
    }

    public JsonArray(Stream<Object> s) {
        super();
        s.forEach(o -> this.addObject(o));
    }

    /**
     * Allows you to add any kind of object.
     * @param o the value; will be passed through fromObject()
     * @return true if object was added
     */
    public boolean addObject(Object o) {
        return this.add(fromObject(o));
    }

    /**
     * Variant of add that takes a string instead of a JsonElement. The inherited add only supports JsonElement.
     * @param s string
     * @return true if element was added successfully
     */
    public boolean add(final String s) {
        return add(primitive(s));
    }

    /**
     * Variant of add that adds one or more strings.
     * @param strings values
     */
    public void add(final String... strings) {
        for (String s : strings) {
            add(primitive(s));
        }
    }

    /**
     * Variant of add that adds one or more numbers (float/int).
     * @param numbers values
     */
    public void add(final Number... numbers) {
        for (Number n : numbers) {
            add(primitive(n));
        }
    }

    /**
     * Variant of add that adds one or more booleans.
     * @param booleans values
     */
    public void add(final Boolean... booleans) {
        for (Boolean b : booleans) {
            add(primitive(b));
        }
    }

    /**
     * Variant of add that adds one or more JsonElements.
     * @param elements elements
     */
    public void add(final JsonElement... elements) {
        for (JsonElement element : elements) {
            add(element);
        }
    }

    /**
     * Variant of add that adds one or more JsonBuilders. This means you don't have to call get() on the builder when adding object builders.
     * @param elements builders
     */
    public void add(JsonBuilder... elements) {
        for (JsonBuilder element : elements) {
            add(element.get());
        }
    }

    @Override
    public boolean add(JsonElement e) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.add(e);
    }

    @Override
    public void add(int index, JsonElement element) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        super.add(index, element);
    }

    @Override
    public boolean addAll(@SuppressWarnings("rawtypes") Collection c) {
        for (Object element : c) {
            if (element instanceof JsonElement) {
                add((JsonElement) element);
            } else {
                add(primitive(element));
            }
        }
        return c.size() != 0;
    }

    /**
     * Convenient method providing a few alternate ways of extracting elements
     * from a JsonArray.
     *
     * @param label label
     * @return the first element in the array matching the label or the n-th
     *         element if the label is an integer and the element an object or
     *         an array.
     */
    public JsonElement get(final String label) {
        int i = 0;
        try {
            for (JsonElement e : this) {
                if (e.isPrimitive() && e.asPrimitive().asString().equals(label)) {
                    return e;
                } else if ((e.isObject() || e.isArray()) && Integer.valueOf(label).equals(i)) {
                    return e;
                }
                i++;
            }
        } catch (NumberFormatException e) {
            // fail gracefully
            return null;
        }
        // the element was not found
        return null;
    }

    public JsonElement first() {
        return get(0);
    }

    public JsonElement last() {
        return get(size() - 1);
    }

    /**
     * Variant of contains that checks if the array contains something that can be extracted with JsonElement get(final String label).
     * @param label label
     * @return true if the array contains the element
     */
    public boolean contains(final String label) {
        return get(label) != null;
    }

    @Override
    public JsonType type() {
        return JsonType.array;
    }

    @Override
    public JsonObject asObject() {
        throw new JsonTypeMismatchException("not an object");
    }

    @Override
    public JsonArray asArray() {
        return this;
    }

    @Override
    public JsonSet asSet() {
        JsonSet set = JsonBuilder.set();
        set.addAll(this);
        return set;
    }

    public double[] asDoubleArray() {
        double[] result = new double[size()];
        int i = 0;
        for (JsonElement e : this) {
            result[i++] = e.asPrimitive().asDouble();
        }
        return result;
    }

    public int[] asIntArray() {
        int[] result = new int[size()];
        int i = 0;
        for (JsonElement e : this) {
            result[i++] = e.asPrimitive().asInt();
        }
        return result;
    }

    public String[] asStringArray() {
        String[] result = new String[size()];
        int i = 0;
        for (JsonElement e : this) {
            result[i++] = e.asPrimitive().asString();
        }
        return result;
    }

    @Override
    public JsonPrimitive asPrimitive() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public float asFloat() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public double asDouble() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public int asInt() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public long asLong() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public boolean asBoolean() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public String asString() {
        throw new JsonTypeMismatchException("not a primitive");
    }

    @Override
    public boolean isObject() {
        return false;
    }

    @Override
    public boolean isArray() {
        return true;
    }

    @Override
    public boolean isPrimitive() {
        return false;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == null) {
            return false;
        }
        if (!(o instanceof JsonArray)) {
            return false;
        }
        JsonArray array = (JsonArray) o;
        if (size() != array.size()) {
            return false;
        }
        for (int i = 0; i < size(); i++) {
            JsonElement e1 = get(i);
            JsonElement e2 = array.get(i);
            if (!e1.equals(e2)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        int code = 7;
        for (JsonElement e : this) {
            code += e.hashCode();
        }
        return code;
    }

    @Override
    public Object clone() {
        return deepClone();
    }

    @SuppressWarnings("unchecked")
    @Override
    public JsonArray deepClone() {
        JsonArray array = new JsonArray();
        for (JsonElement jsonElement : this) {
            JsonElement e = jsonElement.deepClone();
            array.add(e);
        }
        return array;
    }

    @Override
    public JsonArray immutableClone() {
        JsonArray array = new JsonArray();
        for (JsonElement jsonElement : this) {
            JsonElement e = jsonElement.immutableClone();
            array.add(e);
        }
        array.immutable = true;
        return array;
    }

    public boolean isNotEmpty() {
        return !isEmpty();
    }

    @Override
    public boolean isEmpty() {
        boolean empty = true;
        if (size() > 0) {
            for (JsonElement element : this) {
                empty = empty && element.isEmpty();
                if (!empty) {
                    return false;
                }
            }
        }
        return empty;
    }

    @Override
    public boolean remove(Object o) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }

        if (o instanceof JsonElement) {
            return super.remove(o);
        } else {
            // try remove it as a primitive.
            return super.remove(primitive(o));
        }
    }

    @Override
    public void removeEmpty() {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }

        Iterator<JsonElement> iterator = iterator();
        while (iterator.hasNext()) {
            JsonElement jsonElement = iterator.next();
            if (jsonElement.isEmpty()) {
                iterator.remove();
            } else {
                jsonElement.removeEmpty();
            }
        }
    }

    @Override
    public String toString() {
        return JsonSerializer.serialize(this, false);
    }

    @Override
    public void serialize(Writer w) throws IOException {
        w.append(JsonSerializer.OPEN_BRACKET);
        Iterator<JsonElement> it = iterator();
        while (it.hasNext()) {
            JsonElement jsonElement = it.next();
            jsonElement.serialize(w);
            if (it.hasNext()) {
                w.append(JsonSerializer.COMMA);
            }
        }
        w.append(JsonSerializer.CLOSE_BRACKET);
    }

    @Override
    public String prettyPrint() {
        return JsonSerializer.serialize(this, true);
    }

    @Override
    public boolean isNumber() {
        return false;
    }

    @Override
    public boolean isBoolean() {
        return false;
    }

    @Override
    public boolean isNull() {
        return false;
    }

    @Override
    public boolean isString() {
        return false;
    }

    /**
     * Replaces the first matching element.
     * @param e1 original
     * @param e2 replacement
     * @return true if the element was replaced.
     */
    public boolean replace(JsonElement e1, JsonElement e2) {
        int index = indexOf(e1);
        if (index >= 0) {
            set(index, e2);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Replaces the element.
     * @param e1 original
     * @param e2 replacement
     * @return true if element was replaced
     */
    public boolean replace(Object e1, Object e2) {
        return replace(fromObject(e1), fromObject(e2));
    }

    /**
     * Convenient replace method that allows you to replace an object based on field equality for a specified field.
     * Useful if you have an id field in your objects. Note, the array may contain non objects as well or objects
     * without the specified field. Those elements won't be replaced of course.
     *
     * @param e1 object you want replaced; must have a value at the specified path
     * @param e2 replacement
     * @param path path
     * @return true if something was replaced.
     */
    public boolean replaceObject(JsonObject e1, JsonObject e2, String... path) {
        JsonElement compareElement = e1.get(path);
        if (compareElement == null) {
            throw new IllegalArgumentException(
                    "specified path may not be null in object " + StringUtils.join(path));
        }
        int i = 0;
        for (JsonElement e : this) {
            if (e.isObject()) {
                JsonElement fieldValue = e.asObject().get(path);
                if (compareElement.equals(fieldValue)) {
                    set(i, e2);
                    return true;
                }
            }
            i++;
        }
        return false;
    }

    @Override
    public JsonElement set(int index, JsonElement element) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.set(index, element);
    }

    @Override
    public boolean addAll(int index, Collection<? extends JsonElement> c) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.addAll(index, c);
    }

    @Override
    public void ensureCapacity(int minCapacity) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        super.ensureCapacity(minCapacity);
    }

    @Override
    public Iterator<JsonElement> iterator() {
        if (immutable) {
            Iterator<JsonElement> it = super.iterator();
            return new Iterator<JsonElement>() {

                @Override
                public boolean hasNext() {
                    return it.hasNext();
                }

                @Override
                public JsonElement next() {
                    return it.next();
                }

                @Override
                public void remove() {
                    throw new IllegalStateException("object is immutable");
                }
            };
        } else {
            return super.iterator();
        }
    }

    @Override
    public JsonElement remove(int index) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.remove(index);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.removeAll(c);
    }

    @Override
    public boolean removeIf(Predicate<? super JsonElement> filter) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.removeIf(filter);
    }

    @Override
    public ListIterator<JsonElement> listIterator() {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.listIterator();
    }

    @Override
    public ListIterator<JsonElement> listIterator(int index) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.listIterator(index);
    }

    @Override
    public void replaceAll(UnaryOperator<JsonElement> operator) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        super.replaceAll(operator);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        if (immutable) {
            throw new IllegalStateException("object is immutable");
        }
        return super.retainAll(c);
    }

    /**
     * Convenience method to prevent casting JsonElement to JsonObject when iterating in the common case that you have
     * an array of JsonObjects.
     *
     * @return iterable that iterates over JsonObjects instead of JsonElements.
     */
    public Iterable<JsonObject> objects() {
        final JsonArray parent = this;
        return new Iterable<JsonObject>() {

            @Override
            public Iterator<JsonObject> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<JsonObject>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public JsonObject next() {
                        return iterator.next().asObject();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }

    public Stream<JsonObject> streamObjects() {
        return stream().map(e -> e.asObject());
    }

    public Stream<JsonArray> streamArrays() {
        return stream().map(e -> e.asArray());
    }

    public Stream<String> streamStrings() {
        return stream().map(e -> e.asString());
    }

    public Stream<JsonElement> map(Function<JsonElement, JsonElement> f) {
        return stream().map(f);
    }

    public Stream<JsonObject> mapObjects(Function<JsonObject, JsonObject> f) {
        return streamObjects().map(f);
    }

    public void forEachObject(Consumer<? super JsonObject> action) {
        for (JsonElement e : this) {
            action.accept(e.asObject());
        }
    }

    /**
     * Convenience method to prevent casting JsonElement to JsonArray when iterating in the common case that you have
     * an array of JsonArrays.
     *
     * @return iterable that iterates over JsonArrays instead of JsonElements.
     */
    public Iterable<JsonArray> arrays() {
        final JsonArray parent = this;
        return new Iterable<JsonArray>() {

            @Override
            public Iterator<JsonArray> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<JsonArray>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public JsonArray next() {
                        return iterator.next().asArray();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }

    /**
     * Convenience method to prevent casting JsonElement to String when iterating in the common case that you have
     * an array of strings.
     *
     * @return iterable that iterates over Strings instead of JsonElements.
     */
    public Iterable<String> strings() {
        final JsonArray parent = this;
        return new Iterable<String>() {

            @Override
            public Iterator<String> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<String>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public String next() {
                        return iterator.next().asString();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }

    /**
     * Convenience method to prevent casting JsonElement to Double when iterating in the common case that you have
     * an array of doubles.
     *
     * @return iterable that iterates over Doubles instead of JsonElements.
     */
    public Iterable<Double> doubles() {
        final JsonArray parent = this;
        return new Iterable<Double>() {

            @Override
            public Iterator<Double> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<Double>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public Double next() {
                        return iterator.next().asDouble();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }

    /**
     * Convenience method to prevent casting JsonElement to Long when iterating in the common case that you have
     * an array of longs.
     *
     * @return iterable that iterates over Longs instead of JsonElements.
     */
    public Iterable<Long> longs() {
        final JsonArray parent = this;
        return new Iterable<Long>() {

            @Override
            public Iterator<Long> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<Long>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public Long next() {
                        return iterator.next().asLong();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }

    /**
     * Convenience method to prevent casting JsonElement to Long when iterating in the common case that you have
     * an array of longs.
     *
     * @return iterable that iterates over Longs instead of JsonElements.
     */
    public Iterable<Integer> ints() {
        final JsonArray parent = this;
        return new Iterable<Integer>() {

            @Override
            public Iterator<Integer> iterator() {
                final Iterator<JsonElement> iterator = parent.iterator();
                return new Iterator<Integer>() {

                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public Integer next() {
                        return iterator.next().asInt();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }
        };
    }
}