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

org.bson.RawBsonArray Maven / Gradle / Ivy

Go to download

The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson

There is a newer version: 3.12.14
Show newest version
/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 org.bson;

import org.bson.io.ByteBufferBsonInput;

import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import static org.bson.assertions.Assertions.isTrueArgument;
import static org.bson.assertions.Assertions.notNull;

/**
 * An immutable BSON array that is represented using only the raw bytes.
 *
 * @since 3.7
 */
public class RawBsonArray extends BsonArray implements Serializable {
    private static final long serialVersionUID = 2L;
    private static final String IMMUTABLE_MSG = "RawBsonArray instances are immutable";

    private final transient RawBsonArrayList delegate;

    /**
     * Constructs a new instance with the given byte array. Note that it does not make a copy of the array, so do not modify it after
     * passing it to this constructor.
     *
     * @param bytes the bytes representing a BSON document. Note that the byte array is NOT copied, so care must be taken not to modify it
     *              after passing it to this construction, unless of course that is your intention.
     */
    public RawBsonArray(final byte[] bytes) {
        this(notNull("bytes", bytes), 0, bytes.length);
    }

    /**
     * Constructs a new instance with the given byte array, offset, and length. Note that it does not make a copy of the array, so do not
     * modify it after passing it to this constructor.
     *
     * @param bytes the bytes representing a BSON document.  Note that the byte array is NOT copied, so care must be taken not to modify it
     *              after passing it to this construction, unless of course that is your intention.
     * @param offset the offset into the byte array
     * @param length the length of the subarray to use
     */
    public RawBsonArray(final byte[] bytes, final int offset, final int length) {
        this(new RawBsonArrayList(bytes, offset, length));
    }

    private RawBsonArray(final RawBsonArrayList values) {
        super(values, false);
        this.delegate = values;
    }

    ByteBuf getByteBuffer() {
        return delegate.getByteBuffer();
    }

    @Override
    public boolean add(final BsonValue bsonValue) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public boolean remove(final Object o) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public boolean addAll(final Collection c) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public boolean addAll(final int index, final Collection c) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public boolean removeAll(final Collection c) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public boolean retainAll(final Collection c) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public BsonValue set(final int index, final BsonValue element) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public void add(final int index, final BsonValue element) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public BsonValue remove(final int index) {
        throw new UnsupportedOperationException(IMMUTABLE_MSG);
    }

    @Override
    public BsonArray clone() {
        return new RawBsonArray(delegate.bytes.clone(), delegate.offset, delegate.length);
    }

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

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

    // see https://docs.oracle.com/javase/6/docs/platform/serialization/spec/output.html
    private Object writeReplace() {
        return new SerializationProxy(delegate.bytes, delegate.offset, delegate.length);
    }

    // see https://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html
    private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
        throw new InvalidObjectException("Proxy required");
    }

    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 1L;

        private final byte[] bytes;

        SerializationProxy(final byte[] bytes, final int offset, final int length) {
            if (bytes.length == length) {
                this.bytes = bytes;
            } else {
                this.bytes = new byte[length];
                System.arraycopy(bytes, offset, this.bytes, 0, length);
            }
        }

        private Object readResolve() {
            return new RawBsonArray(bytes);
        }
    }

    static class RawBsonArrayList extends AbstractList {
        private static final int MIN_BSON_ARRAY_SIZE = 5;
        private Integer cachedSize;
        private final byte[] bytes;
        private final int offset;
        private final int length;

        RawBsonArrayList(final byte[] bytes, final int offset, final int length) {
            notNull("bytes", bytes);
            isTrueArgument("offset >= 0", offset >= 0);
            isTrueArgument("offset < bytes.length", offset < bytes.length);
            isTrueArgument("length <= bytes.length - offset", length <= bytes.length - offset);
            isTrueArgument("length >= 5", length >= MIN_BSON_ARRAY_SIZE);
            this.bytes = bytes;
            this.offset = offset;
            this.length = length;
        }

        @Override
        public BsonValue get(final int index) {
            if (index < 0) {
                throw new IndexOutOfBoundsException();
            }
            int curIndex = 0;
            BsonBinaryReader bsonReader = createReader();
            try {
                bsonReader.readStartDocument();
                while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                    bsonReader.skipName();
                    if (curIndex == index) {
                        return RawBsonValueHelper.decode(bytes, bsonReader);
                    }
                    bsonReader.skipValue();
                    curIndex++;
                }
                bsonReader.readEndDocument();
            } finally {
                bsonReader.close();
            }
            throw new IndexOutOfBoundsException();
        }

        @Override
        public int size() {
            if (cachedSize != null) {
                return cachedSize;
            }
            int size = 0;
            BsonBinaryReader bsonReader = createReader();
            try {
                bsonReader.readStartDocument();
                while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                    size++;
                    bsonReader.readName();
                    bsonReader.skipValue();
                }
                bsonReader.readEndDocument();
            } finally {
                bsonReader.close();
            }
            cachedSize = size;
            return cachedSize;
        }

        @Override
        public Iterator iterator() {
            return new Itr();
        }

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

        @Override
        public ListIterator listIterator(final int index) {
            return new ListItr(index);
        }

        private class Itr implements Iterator {
            private int cursor = 0;
            private BsonBinaryReader bsonReader;
            private int currentPosition = 0;

            Itr() {
                this(0);
            }

            Itr(final int cursorPosition) {
                setIterator(cursorPosition);
            }

            public boolean hasNext() {
                boolean hasNext = cursor != size();
                if (!hasNext) {
                    bsonReader.close();
                }
                return hasNext;
            }

            public BsonValue next() {
                while (cursor > currentPosition && bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                    bsonReader.skipName();
                    bsonReader.skipValue();
                    currentPosition++;
                }

                if (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                    bsonReader.skipName();
                    cursor += 1;
                    currentPosition = cursor;
                    return RawBsonValueHelper.decode(bytes, bsonReader);
                } else {
                    bsonReader.close();
                    throw new NoSuchElementException();
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException(IMMUTABLE_MSG);
            }

            public int getCursor() {
                return cursor;
            }

            public void setCursor(final int cursor) {
                this.cursor = cursor;
            }

            void setIterator(final int cursorPosition) {
                cursor = cursorPosition;
                currentPosition = 0;
                if (bsonReader != null) {
                    bsonReader.close();
                }
                bsonReader = createReader();
                bsonReader.readStartDocument();
            }
        }

        private class ListItr extends Itr implements ListIterator {
            ListItr(final int index) {
                super(index);
            }

            public boolean hasPrevious() {
                return getCursor() != 0;
            }

            public BsonValue previous() {
                try {
                    BsonValue previous = get(previousIndex());
                    setIterator(previousIndex());
                    return previous;
                } catch (IndexOutOfBoundsException e) {
                    throw new NoSuchElementException();
                }
            }

            public int nextIndex() {
                return getCursor();
            }

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

            @Override
            public void set(final BsonValue bsonValue) {
                throw new UnsupportedOperationException(IMMUTABLE_MSG);
            }

            @Override
            public void add(final BsonValue bsonValue) {
                throw new UnsupportedOperationException(IMMUTABLE_MSG);
            }
        }

        private BsonBinaryReader createReader() {
            return new BsonBinaryReader(new ByteBufferBsonInput(getByteBuffer()));
        }

        ByteBuf getByteBuffer() {
            ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, length);
            buffer.order(ByteOrder.LITTLE_ENDIAN);
            return new ByteBufNIO(buffer);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy