package org.bson;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.Codec;
import org.bson.codecs.Decoder;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.RawBsonDocumentCodec;
import org.bson.io.BasicOutputBuffer;
import org.bson.io.ByteBufferBsonInput;
import org.bson.json.JsonReader;
import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import static org.bson.assertions.Assertions.isTrueArgument;
import static org.bson.assertions.Assertions.notNull;
public final class RawBsonDocument extends BsonDocument {
private static final long serialVersionUID = 1L;
private static final int MIN_BSON_DOCUMENT_SIZE = 5;
private final byte[] bytes;
private final int offset;
private final int length;
public static RawBsonDocument parse(final String json) {
notNull("json", json);
return new RawBsonDocumentCodec().decode(new JsonReader(json), DecoderContext.builder().build());
}
public RawBsonDocument(final byte[] bytes) {
this(notNull("bytes", bytes), 0, bytes.length);
}
public RawBsonDocument(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_DOCUMENT_SIZE);
this.bytes = bytes;
this.offset = offset;
this.length = length;
}
public <T> RawBsonDocument(final T document, final Codec<T> codec) {
notNull("document", document);
notNull("codec", codec);
BasicOutputBuffer buffer = new BasicOutputBuffer();
BsonBinaryWriter writer = new BsonBinaryWriter(buffer);
try {
codec.encode(writer, document, EncoderContext.builder().build());
this.bytes = buffer.getInternalBuffer();
this.offset = 0;
this.length = buffer.getPosition();
} finally {
writer.close();
}
}
public ByteBuf getByteBuffer() {
ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, length);
buffer.order(ByteOrder.LITTLE_ENDIAN);
return new ByteBufNIO(buffer);
}
public <T> T decode(final Codec<T> codec) {
return decode((Decoder<T>) codec);
}
public <T> T decode(final Decoder<T> decoder) {
BsonBinaryReader reader = createReader();
try {
return decoder.decode(reader, DecoderContext.builder().build());
} finally {
reader.close();
}
}
@Override
public void clear() {
throw new UnsupportedOperationException("RawBsonDocument instances are immutable");
}
@Override
public BsonValue put(final String key, final BsonValue value) {
throw new UnsupportedOperationException("RawBsonDocument instances are immutable");
}
@Override
public BsonDocument append(final String key, final BsonValue value) {
throw new UnsupportedOperationException("RawBsonDocument instances are immutable");
}
@Override
public void putAll(final Map<? extends String, ? extends BsonValue> m) {
throw new UnsupportedOperationException("RawBsonDocument instances are immutable");
}
@Override
public BsonValue remove(final Object key) {
throw new UnsupportedOperationException("RawBsonDocument instances are immutable");
}
@Override
public boolean isEmpty() {
BsonBinaryReader bsonReader = createReader();
try {
bsonReader.readStartDocument();
if (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
return false;
}
bsonReader.readEndDocument();
} finally {
bsonReader.close();
}
return true;
}
@Override
public int size() {
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();
}
return size;
}
@Override
public Set<Entry<String, BsonValue>> entrySet() {
return toBsonDocument().entrySet();
}
@Override
public Collection<BsonValue> values() {
return toBsonDocument().values();
}
@Override
public Set<String> keySet() {
return toBsonDocument().keySet();
}
@Override
public String getFirstKey() {
BsonBinaryReader bsonReader = createReader();
try {
bsonReader.readStartDocument();
try {
return bsonReader.readName();
} catch (BsonInvalidOperationException e) {
throw new NoSuchElementException();
}
} finally {
bsonReader.close();
}
}
@Override
public boolean containsKey(final Object key) {
if (key == null) {
throw new IllegalArgumentException("key can not be null");
}
BsonBinaryReader bsonReader = createReader();
try {
bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
if (bsonReader.readName().equals(key)) {
return true;
}
bsonReader.skipValue();
}
bsonReader.readEndDocument();
} finally {
bsonReader.close();
}
return false;
}
@Override
public boolean containsValue(final Object value) {
BsonBinaryReader bsonReader = createReader();
try {
bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
bsonReader.skipName();
if (RawBsonValueHelper.decode(bytes, bsonReader).equals(value)) {
return true;
}
}
bsonReader.readEndDocument();
} finally {
bsonReader.close();
}
return false;
}
@Override
public BsonValue get(final Object key) {
notNull("key", key);
BsonBinaryReader bsonReader = createReader();
try {
bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
if (bsonReader.readName().equals(key)) {
return RawBsonValueHelper.decode(bytes, bsonReader);
}
bsonReader.skipValue();
}
bsonReader.readEndDocument();
} finally {
bsonReader.close();
}
return null;
}
@Override
@SuppressWarnings("deprecation")
public String toJson() {
return toJson(new JsonWriterSettings());
}
@Override
public String toJson(final JsonWriterSettings settings) {
StringWriter writer = new StringWriter();
new RawBsonDocumentCodec().encode(new JsonWriter(writer, settings), this, EncoderContext.builder().build());
return writer.toString();
}
@Override
public boolean equals(final Object o) {
return toBsonDocument().equals(o);
}
@Override
public int hashCode() {
return toBsonDocument().hashCode();
}
@Override
public BsonDocument clone() {
return new RawBsonDocument(bytes.clone(), offset, length);
}
private BsonBinaryReader createReader() {
return new BsonBinaryReader(new ByteBufferBsonInput(getByteBuffer()));
}
private BsonDocument toBsonDocument() {
BsonBinaryReader bsonReader = createReader();
try {
return new BsonDocumentCodec().decode(bsonReader, DecoderContext.builder().build());
} finally {
bsonReader.close();
}
}
private Object writeReplace() {
return new SerializationProxy(this.bytes, offset, length);
}
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 RawBsonDocument(bytes);
}
}
}