All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazon.ion.impl.lite.IonSequenceLite Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.amazon.ion.impl.lite;

import com.amazon.ion.ContainedValueException;
import com.amazon.ion.IonSequence;
import com.amazon.ion.IonValue;
import com.amazon.ion.ValueFactory;
import com.amazon.ion.impl._Private_CurriedValueFactory;
import com.amazon.ion.impl._Private_IonValue;

import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;


abstract class IonSequenceLite
    extends IonContainerLite
    implements IonSequence
{
    /**
     * A zero-length array.
     */
    protected static final IonValueLite[] EMPTY_VALUE_ARRAY = new IonValueLite[0];

    IonSequenceLite(ContainerlessContext context, boolean isNull)
    {
        super(context, isNull);
    }

    IonSequenceLite(IonSequenceLite existing, IonContext context) {
        super(existing, context);
    }

    /**
     * Constructs a sequence value not backed by binary.
     *
     * @param elements
     *   the initial set of child elements.  If null, then the new
     *   instance will have {@link #isNullValue()} == true.
     *
     * @throws ContainedValueException if any value in elements
     * has {@link IonValue#getContainer()} != null.
     * @throws IllegalArgumentException
     * @throws NullPointerException
     */
    IonSequenceLite(ContainerlessContext context,
                    Collection elements)
        throws ContainedValueException, NullPointerException,
            IllegalArgumentException
    {
        this(context, (elements == null));
        assert _children == null;

        if (elements != null)
        {
            _children = new IonValueLite[elements.size()];
            for (Iterator i = elements.iterator(); i.hasNext();)
            {
                IonValueLite element = (IonValueLite) i.next();
                super.add(element);
            }
        }
    }

    //=========================================================================

    @Override
    public IonSequence clone() {
        return (IonSequence) deepClone(false);
    }

    @Override
    // Increasing visibility
    public boolean add(IonValue element)
        throws ContainedValueException, NullPointerException
    {
        // super.add will check for the lock
        return super.add(element);
    }

    public boolean addAll(Collection c)
    {
        checkForLock();

        if (c == null) {
            throw new NullPointerException();
        }

        boolean changed = false;

        for (IonValue v : c)
        {
            changed = add(v) || changed;
        }

        return changed;
    }

    public boolean addAll(int index, Collection c)
    {
        checkForLock();

        if (c == null) {
            throw new NullPointerException();
        }
        if (index < 0 || index > size())
        {
            throw new IndexOutOfBoundsException();
        }

        // TODO optimize to avoid n^2 shifting and renumbering of elements.
        boolean changed = false;

        for (IonValue v : c)
        {
            add(index++, v);
            changed = true;
        }

        if (changed) {
            patch_elements_helper(index);
        }

        return changed;
    }


    public ValueFactory add()
    {
        return new _Private_CurriedValueFactory(this.getSystem())
        {
            @Override
            protected void handle(IonValue newValue)
            {
                add(newValue);
            }
        };
    }


    public void add(int index, IonValue element)
        throws ContainedValueException, NullPointerException
    {
        add(index, (IonValueLite) element);
    }

    public ValueFactory add(final int index)
    {
        return new _Private_CurriedValueFactory(getSystem())
        {
            @Override
            protected void handle(IonValue newValue)
            {
                add(index, newValue);
                patch_elements_helper(index + 1);
            }
        };
    }

    public IonValue set(int index, IonValue element)
    {
        checkForLock();
        final IonValueLite concrete = ((IonValueLite) element);

        // NOTE: size calls makeReady() so we don't have to
        if (index < 0 || index >= size())
        {
            throw new IndexOutOfBoundsException("" + index);
        }

        validateNewChild(element);

        assert _children != null; // else index would be out of bounds above.
        concrete._context = getContextForIndex(element, index);
        IonValueLite removed = set_child(index, concrete);
        concrete._elementid(index);

        removed.detachFromContainer();
        // calls setDirty(), UNLESS it hits an IOException

        return removed;
    }

    public IonValue remove(int index)
    {
        checkForLock();

        if (index < 0 || index >= get_child_count()) {
            throw new IndexOutOfBoundsException("" + index);
        }

        IonValueLite v = get_child(index);
        assert(v._elementid() == index);
        remove_child(index);
        patch_elements_helper(index);
        return v;
    }

    public boolean remove(Object o)
    {
        checkForLock();

        int idx = lastIndexOf(o);
        if (idx < 0) {
            return false;
        }
        assert(o instanceof IonValueLite); // since it's in our current array
        assert( ((IonValueLite)o)._elementid() == idx );

        remove_child(idx);
        patch_elements_helper(idx);
        return true;
    }

    public boolean removeAll(Collection c)
    {
        boolean changed = false;

        checkForLock();

        // remove the collection member if it is a
        // member of our child array keep track of
        // the lowest array index for later patching
        for (Object o : c) {
            int idx = lastIndexOf(o);
            if (idx >= 0) {
                assert(o == get_child(idx));
                remove_child(idx);
                patch_elements_helper(idx);
                changed = true;
            }
        }
        return changed;
    }

    public boolean retainAll(Collection c)
    {
        checkForLock();

        if (get_child_count() < 1) return false;

        // TODO this method (and probably several others) needs optimization.
        IdentityHashMap keepers =
            new IdentityHashMap();

        for (Object o : c)
        {
            IonValue v = (IonValue) o;
            if (this == v.getContainer()) keepers.put(v, v);
        }

        boolean changed = false;
        for (int ii = get_child_count(); ii > 0; )
        {
            ii--;
            IonValue v = get_child(ii);
            if (! keepers.containsKey(v))
            {
                remove(v);
                patch_elements_helper(ii);
                changed = true;
            }
        }

        return changed;
    }

    public boolean contains(Object o)
    {
        if (o == null) {
            throw new NullPointerException();
        }
        if (!(o instanceof IonValue)) {
            throw new ClassCastException();
        }
        return ((IonValue)o).getContainer() == this;
    }

    public boolean containsAll(Collection c)
    {
        for (Object o : c)
        {
            if (! contains(o)) return false;
        }
        return true;
    }

    public int indexOf(Object o)
    {
        if (o == null) {
            throw new NullPointerException();
        }
        _Private_IonValue v = (_Private_IonValue) o;
        if (this != v.getContainer()) return -1;
        return v.getElementId();
    }

    public int lastIndexOf(Object o)
    {
        return indexOf(o);
    }

    private static void checkSublistParameters(int size, int fromIndex, int toIndex)
    {
        if(fromIndex < 0)
        {
            throw new IndexOutOfBoundsException("fromIndex is less than zero");
        }
        if(toIndex < fromIndex)
        {
            throw new IllegalArgumentException("toIndex may not be less than fromIndex");
        }
        if(toIndex > size)
        {
            throw new IndexOutOfBoundsException("toIndex exceeds size");
        }
    }

    public List subList(int fromIndex, int toIndex)
    {
        checkSublistParameters(this.size(), fromIndex, toIndex);
        return new SubListView(fromIndex, toIndex);
    }

    public IonValue[] toArray()
    {
        if (get_child_count() < 1) return EMPTY_VALUE_ARRAY;

        IonValue[] array = new IonValue[get_child_count()];
        System.arraycopy(_children, 0, array, 0, get_child_count());
        return array;
    }

    @SuppressWarnings("unchecked")
    public  T[] toArray(T[] a)
    {
        int size = get_child_count();
        if (a.length < size)
        {
            // TODO JDK 1.6 this could use Arrays.copyOf
            Class type = a.getClass().getComponentType();
            // generates unchecked warning
            a = (T[]) Array.newInstance(type, size);
        }
        if (size > 0) {
            System.arraycopy(_children, 0, a, 0, size);
        }
        if (size < a.length) {
            // A surprising bit of spec.
            // this is required even with a 0 entries
            a[size] = null;
        }
        return a;
    }

    @SuppressWarnings("unchecked")
    public  T[] extract(Class type)
    {
        checkForLock();

        if (isNullValue()) return null;
        T[] array = (T[]) Array.newInstance(type, size());
        toArray(array);
        clear();
        return array;
    }

    /**
     * SubListView throws a {@link ConcurrentModificationException} if the parent list has any
     * structural modifications, i.e. any operation that cause its size to change. To determine if
     * a parent structural modification happened it keeps track of the structural modification count
     * to compare against the parent
     *
     * Structural modifications from the sublist itself are allowed.
     */
    private class SubListView extends AbstractList {

        /**
         * index in top level IonSequenceLite that marks the start of this sublist view. For nested
         * sublists fromIndex will be in relation to the root parent which must be a IonSequenceLite
         */
        private final int fromIndex;
        private int size;

        private SubListView(final int fromIndex, final int toIndex) {
            this.fromIndex = fromIndex;
            this.size = toIndex - fromIndex;
            super.modCount = IonSequenceLite.this.structuralModificationCount;
        }

        @Override
        public int size() {
            checkForParentModification();
            return size;
        }

        @Override
        public boolean isEmpty() {
            checkForParentModification();
            return size == 0;
        }

        @Override
        public IonValue get(final int index) {
            checkForParentModification();
            rangeCheck(index);

            return IonSequenceLite.this.get(toParentIndex(index));
        }

        @Override
        public IonValue set(final int index, final IonValue element) {
            checkForParentModification();
            rangeCheck(index);

            return IonSequenceLite.this.set(toParentIndex(index), element);
        }

        @Override
        public boolean contains(final Object o) {
            checkForParentModification();
            return super.contains(o);
        }

        @Override
        public boolean containsAll(final Collection collection) {
            checkForParentModification();
            return super.containsAll(collection);
        }

        @Override
        public Object[] toArray() {
            checkForParentModification();
            return super.toArray();
        }

        @Override
        public  T[] toArray(T[] ts) {
            checkForParentModification();
            return super.toArray(ts);
        }

        @Override
        public boolean add(final IonValue ionValue) {
            checkForParentModification();

            int parentIndex = toParentIndex(size);

            // adds at end of parent list
            if (parentIndex == IonSequenceLite.this.size()) {
                IonSequenceLite.this.add(ionValue);
            } else {
                IonSequenceLite.this.add(parentIndex, ionValue);
            }

            super.modCount = IonSequenceLite.this.structuralModificationCount;
            size++;

            return true;
        }

        @Override
        public void add(final int index, final IonValue ionValue) {
            checkForParentModification();
            rangeCheck(index);

            IonSequenceLite.this.add(toParentIndex(index), ionValue);

            super.modCount = IonSequenceLite.this.structuralModificationCount;
            size++;
        }


        @Override
        public boolean addAll(int i, Collection collection) {
            checkForParentModification();
            return super.addAll(i, collection);
        }


        @Override
        public boolean addAll(Collection collection) {
            checkForParentModification();
            return super.addAll(collection);
        }

        // retainAll has no functionality that is specific to SubListView but
        // needs to be implemented because the AbstractList.retainAll() throws [UnsupportedOperationException].
        @Override
        public boolean retainAll(final Collection c) {
            checkForParentModification();
            if (size < 1) {
                return false;
            }

            final Map toRetain = new IdentityHashMap();
            for (Object o : c) {
                toRetain.put(o, null);
            }

            final List toRemove = new ArrayList(size - c.size());
            for (int i = 0; i < size; i++) {
                final IonValue ionValue = get(i);

                if (!toRetain.containsKey(ionValue)) {
                    toRemove.add(ionValue);
                }
            }

            return removeAll(toRemove);
        }

        @Override
        public void clear() {
            checkForParentModification();

            // remove the first element size times to remove all elements
            int parentIndex = toParentIndex(0);
            for (int i = 0; i < size; i++) {
                IonSequenceLite.this.remove(parentIndex);
            }

            size = 0;
            super.modCount = IonSequenceLite.this.structuralModificationCount;
        }

        @Override
        public IonValue remove(final int index) {
            checkForParentModification();
            rangeCheck(index);

            final IonValue removed = IonSequenceLite.this.remove(toParentIndex(index));

            super.modCount = IonSequenceLite.this.structuralModificationCount;
            size--;

            return removed;
        }

        // remove(Object) contains no functionality that is specific to SubListView but
        // needs to be implemented because the AbstractList.remove(Object) throws [UnsupportedOperationException].
        @Override
        public boolean remove(final Object o) {
            checkForParentModification();
            int index = indexOf(o);
            if (index < 0) {
                return false;
            }

            remove(index);
            return true;
        }

        // removeAll(Collection) contains no functionality that is specific to SubListView but
        // needs to be implemented because the AbstractList.remove(Object) throws [UnsupportedOperationException].
        @Override
        public boolean removeAll(final Collection c) {
            checkForParentModification();
            boolean changed = false;
            for (Object o : c) {
                if (remove(o)) {
                    changed = true;
                }
            }

            return changed;
        }

        @Override
        public int indexOf(final Object o) {
            checkForParentModification();
            return super.indexOf(o);
        }

        @Override
        public int lastIndexOf(Object o) {
            checkForParentModification();
            return super.lastIndexOf(o);
        }

        @Override
        protected void removeRange(int from, int to) {
            checkForParentModification();
            super.removeRange(from, to);
        }


        @Override
        public Iterator iterator() {
            return listIterator(0);
        }

        @Override
        public ListIterator listIterator() {
            return listIterator(0);
        }

        @Override
        public ListIterator listIterator(final int index) {
            checkForParentModification();

            return new ListIterator() {
                private int lastReturnedIndex = index;
                private int nextIndex = index;

                public boolean hasNext() {
                    return nextIndex < SubListView.this.size();
                }

                public IonValue next() {
                    lastReturnedIndex = nextIndex++;
                    return SubListView.this.get(lastReturnedIndex);
                }

                public boolean hasPrevious() {
                    return nextIndex > 0;
                }

                public IonValue previous() {
                    lastReturnedIndex = --nextIndex;
                    return SubListView.this.get(lastReturnedIndex);
                }

                public int nextIndex() {
                    return nextIndex;
                }

                public int previousIndex() {
                    return nextIndex - 1;
                }

                public void remove() {
                    throw new UnsupportedOperationException();
                }

                public void set(final IonValue ionValue) {
                    SubListView.this.set(lastReturnedIndex, ionValue);
                }

                public void add(final IonValue ionValue) {
                    SubListView.this.add(lastReturnedIndex, ionValue);
                }
            };
        }

        @Override
        public List subList(final int fromIndex, final int toIndex) {
            checkSublistParameters(this.size(), fromIndex, toIndex);
            checkForParentModification();
            return new SubListView(toParentIndex(fromIndex), toParentIndex(toIndex));
        }

        @Override
        public boolean equals(Object o) {
            checkForParentModification();
            return super.equals(o);
        }

        @Override
        public int hashCode() {
            checkForParentModification();
            return super.hashCode();
        }

        @Override
        public String toString() {
            checkForParentModification();
            return super.toString();
        }

        private void rangeCheck(int index) {
            if (index < 0 || index >= size) {
                throw new IndexOutOfBoundsException(String.valueOf(index));
            }
        }

        private int toParentIndex(int index) {
            return index + fromIndex;
        }

        private void checkForParentModification() {
            if (super.modCount != IonSequenceLite.this.structuralModificationCount) {
                throw new ConcurrentModificationException();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy