From ffaa5a09966cdd78486093434a508ed390563969 Mon Sep 17 00:00:00 2001 From: Robert Guo Date: Sun, 28 Sep 2014 22:15:15 -0400 Subject: [PATCH 1/8] JAVA-403 allow user to specify UUID format --- .../src/main/org/bson/AbstractBsonReader.java | 33 ++- bson/src/main/org/bson/BsonBinaryReader.java | 36 ++- .../src/main/org/bson/BsonDocumentReader.java | 135 ++++++++- bson/src/main/org/bson/BsonReader.java | 15 + .../src/main/org/bson/UuidRepresentation.java | 57 ++++ .../main/org/bson/codecs/DecoderContext.java | 12 +- .../main/org/bson/codecs/EncoderContext.java | 30 +- bson/src/main/org/bson/io/BsonInput.java | 7 + .../main/org/bson/io/ByteBufferBsonInput.java | 7 + bson/src/main/org/bson/json/JsonReader.java | 40 +++ bson/src/main/org/bson/json/JsonScanner.java | 14 + .../codecs/BinaryToUUIDTransformer.java | 64 ++++- .../main/com/mongodb/codecs/CodecHelper.java | 38 +++ .../com/mongodb/codecs/DocumentCodec.java | 13 +- .../mongodb/codecs/DocumentCodecProvider.java | 1 + .../main/com/mongodb/codecs/UUIDCodec.java | 83 ++++-- .../com/mongodb/codecs/UUIDCodecProvider.java | 58 ++++ .../LimitedLookaheadMarkSpecification.groovy | 269 ++++++++++++++++++ ...inaryToUUIDTransformerSpecification.groovy | 6 +- .../codecs/DocumentCodecSpecification.groovy | 33 ++- .../codecs/UUIDCodecSpecification.groovy | 158 ++++++++++ .../com/mongodb/codecs/UUIDCodecTest.java | 69 ----- .../src/main/com/mongodb/DBObjectCodec.java | 3 +- .../mongodb/DBObjectCodecSpecification.groovy | 53 ++++ 24 files changed, 1103 insertions(+), 131 deletions(-) create mode 100644 bson/src/main/org/bson/UuidRepresentation.java create mode 100644 driver-core/src/main/com/mongodb/codecs/CodecHelper.java create mode 100644 driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java create mode 100644 driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy create mode 100644 driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy delete mode 100644 driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecTest.java create mode 100644 driver/src/test/unit/com/mongodb/DBObjectCodecSpecification.groovy diff --git a/bson/src/main/org/bson/AbstractBsonReader.java b/bson/src/main/org/bson/AbstractBsonReader.java index 6b550f5b416..1c3f9e6baec 100644 --- a/bson/src/main/org/bson/AbstractBsonReader.java +++ b/bson/src/main/org/bson/AbstractBsonReader.java @@ -725,13 +725,44 @@ private void setStateOnEnd() { throw new BSONException(format("Unexpected ContextType %s.", getContext().getContextType())); } } + protected class Mark { + private State state; + private Context parentContext; + private BsonContextType contextType; + private BsonType currentBsonType; + private String currentName; + + protected Context getParentContext() { + return parentContext; + } + + protected BsonContextType getContextType() { + return contextType; + } + + protected Mark() { + state = AbstractBsonReader.this.state; + parentContext = AbstractBsonReader.this.context.parentContext; + contextType = AbstractBsonReader.this.context.contextType; + currentBsonType = AbstractBsonReader.this.currentBsonType; + currentName = AbstractBsonReader.this.currentName; + } + + protected void reset() { + AbstractBsonReader.this.state = state; + AbstractBsonReader.this.currentBsonType = currentBsonType; + AbstractBsonReader.this.currentName = currentName; + } + } + /** * The context for the reader. Records the parent context, creating a bread crumb trail to trace back up to the root context of the * reader. Also records the {@link org.bson.BsonContextType}, indicating whether the reader is reading a document, array, or other * complex sub-structure. */ - protected static class Context { + protected abstract class Context { + private final Context parentContext; private final BsonContextType contextType; diff --git a/bson/src/main/org/bson/BsonBinaryReader.java b/bson/src/main/org/bson/BsonBinaryReader.java index 46a537c7783..6d07857f8f6 100644 --- a/bson/src/main/org/bson/BsonBinaryReader.java +++ b/bson/src/main/org/bson/BsonBinaryReader.java @@ -30,6 +30,7 @@ public class BsonBinaryReader extends AbstractBsonReader { private final BsonInput bsonInput; private final boolean closeInput; + private Mark mark; /** * Construct an instance. @@ -338,9 +339,42 @@ private int readSize() { protected Context getContext() { return (Context) super.getContext(); } + @Override + public void mark() { + if (mark != null) { + throw new BSONException("A mark already exists; it needs to be reset before creating a new one"); + } + mark = new Mark(); + } + @Override + public void reset() { + if (mark == null) { + throw new BSONException("trying to reset a mark before creating it"); + } + mark.reset(); + mark = null; + } - private static class Context extends AbstractBsonReader.Context { + protected class Mark extends AbstractBsonReader.Mark { + private int startPosition; + private int size; + private int currentPosition; + + protected Mark() { + super(); + startPosition = BsonBinaryReader.this.getContext().startPosition; + size = BsonBinaryReader.this.getContext().size; + currentPosition = BsonBinaryReader.this.bsonInput.getPosition(); + } + + protected void reset() { + super.reset(); + BsonBinaryReader.this.bsonInput.setPosition(currentPosition); + BsonBinaryReader.this.setContext(new Context((Context) getParentContext(), getContextType(), startPosition, size)); + } + } + protected class Context extends AbstractBsonReader.Context { private final int startPosition; private final int size; diff --git a/bson/src/main/org/bson/BsonDocumentReader.java b/bson/src/main/org/bson/BsonDocumentReader.java index 9ad1c47dee7..577502171d3 100644 --- a/bson/src/main/org/bson/BsonDocumentReader.java +++ b/bson/src/main/org/bson/BsonDocumentReader.java @@ -2,7 +2,9 @@ import org.bson.types.ObjectId; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; /** @@ -16,6 +18,7 @@ */ public class BsonDocumentReader extends AbstractBsonReader { private BsonValue currentValue; + private Mark mark; /** * Construct a new instance. @@ -200,23 +203,122 @@ public BsonType readBsonType() { return getCurrentBsonType(); } + @Override + public void mark() { + if (mark != null) { + throw new BSONException("A mark already exists; it needs to be reset before creating a new one"); + } + mark = new Mark(); + } + + @Override + public void reset() { + if (mark == null) { + throw new BSONException("trying to reset a mark before creating it"); + } + mark.reset(); + mark = null; + } + @Override protected Context getContext() { return (Context) super.getContext(); } + protected class Mark extends AbstractBsonReader.Mark { + private BsonValue currentValue; + private Context context; + + protected Mark() { + super(); + currentValue = BsonDocumentReader.this.currentValue; + context = BsonDocumentReader.this.getContext(); + context.mark(); + } + + protected void reset() { + super.reset(); + BsonDocumentReader.this.currentValue = currentValue; + BsonDocumentReader.this.setContext(context); + context.reset(); + } + } + + private static class BsonDocumentMarkableIterator implements Iterator { + + private Iterator baseIterator; + private List markIterator = new ArrayList(); + private int curIndex; // index of the cursor + private boolean marking; + + protected BsonDocumentMarkableIterator(final Iterator baseIterator) { + this.baseIterator = baseIterator; + curIndex = 0; + marking = false; + } + + /** + * + */ + protected void mark() { + marking = true; + } + + /** + * + */ + protected void reset() { + curIndex = 0; + marking = false; + } + + + @Override + public boolean hasNext() { + return baseIterator.hasNext() || curIndex < markIterator.size(); + } + + @Override + public T next() { + T value; + //TODO: check closed + if (curIndex < markIterator.size()) { + value = markIterator.get(curIndex); + if (marking) { + curIndex++; + } else { + markIterator.remove(0); + } + } else { + value = baseIterator.next(); + if (marking) { + markIterator.add(value); + curIndex++; + } + } + + + return value; + } + + @Override + public void remove() { + // iterator is read only + } + } + + protected class Context extends AbstractBsonReader.Context { - protected static class Context extends AbstractBsonReader.Context { - private Iterator> documentIterator; - private Iterator arrayIterator; + private BsonDocumentMarkableIterator> documentIterator; + private BsonDocumentMarkableIterator arrayIterator; protected Context(final Context parentContext, final BsonContextType contextType, final BsonArray array) { super(parentContext, contextType); - arrayIterator = array.iterator(); + arrayIterator = new BsonDocumentMarkableIterator(array.iterator()); } protected Context(final Context parentContext, final BsonContextType contextType, final BsonDocument document) { super(parentContext, contextType); - documentIterator = document.entrySet().iterator(); + documentIterator = new BsonDocumentMarkableIterator>(document.entrySet().iterator()); } public Map.Entry getNextElement() { @@ -226,6 +328,29 @@ public Map.Entry getNextElement() { return null; } } + protected void mark() { + if (documentIterator != null) { + documentIterator.mark(); + } else { + arrayIterator.mark(); + } + + if (getParentContext() != null) { + ((Context) getParentContext()).mark(); + } + } + + protected void reset() { + if (documentIterator != null) { + documentIterator.reset(); + } else { + arrayIterator.reset(); + } + + if (getParentContext() != null) { + ((Context) getParentContext()).reset(); + } + } public BsonValue getNextValue() { if (arrayIterator.hasNext()) { diff --git a/bson/src/main/org/bson/BsonReader.java b/bson/src/main/org/bson/BsonReader.java index 4101d49dfd9..423fcf9665b 100644 --- a/bson/src/main/org/bson/BsonReader.java +++ b/bson/src/main/org/bson/BsonReader.java @@ -344,4 +344,19 @@ public interface BsonReader { * Skips the value (reader must be positioned on a value). */ void skipValue(); + + /** + * Creates a bookmark in the BsonReader's input + * + * The previous mark must be cleared before creating a new one + */ + void mark(); + + /** + * Go back to the state at the last mark and removes the mark + * + * @throws org.bson.BSONException if no mark has been set + */ + void reset(); + } diff --git a/bson/src/main/org/bson/UuidRepresentation.java b/bson/src/main/org/bson/UuidRepresentation.java new file mode 100644 index 00000000000..22c9a64bc31 --- /dev/null +++ b/bson/src/main/org/bson/UuidRepresentation.java @@ -0,0 +1,57 @@ +/* + * + * * Copyright (c) 2008-2014 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; + +/** + * The representation to use when converting a UUID to a BSON binary value. + * This class is necessary because the different drivers used to have different + * ways of encoding UUID, with the BSON subtype: \x03 UUID old. + * + * @since 3.0 + */ +public enum UuidRepresentation { + /** + * The canonical representation of UUID + * + * BSON binary subtype 4 + */ + STANDARD, + + /** + * The legacy representation of UUID used by the C# driver + * + * BSON binary subtype 3 + */ + C_SHARP_LEGACY, + + /** + * The legacy representation of UUID used by the Java driver + * + * BSON binary subtype 3 + */ + JAVA_LEGACY, + + /** + * The legacy representation of UUID used by the Python driver, which is the same + * format as STANDARD, but has the UUID old BSON subtype (\x03) + * + * BSON binary subtype 3 + */ + PYTHON_LEGACY +} \ No newline at end of file diff --git a/bson/src/main/org/bson/codecs/DecoderContext.java b/bson/src/main/org/bson/codecs/DecoderContext.java index 0c89547daf2..542aaf67f5f 100644 --- a/bson/src/main/org/bson/codecs/DecoderContext.java +++ b/bson/src/main/org/bson/codecs/DecoderContext.java @@ -23,6 +23,11 @@ * @since 3.0 */ public final class DecoderContext { + + + private DecoderContext() { + } + /** * Create a builder. * @@ -36,7 +41,9 @@ public static Builder builder() { * A builder for {@code DecoderContext} instances. */ public static final class Builder { + private Builder() { + } /** @@ -44,10 +51,7 @@ private Builder() { * @return the decoder context */ public DecoderContext build() { - return new DecoderContext(this); + return new DecoderContext(); } } - - private DecoderContext(final Builder builder) { - } } diff --git a/bson/src/main/org/bson/codecs/EncoderContext.java b/bson/src/main/org/bson/codecs/EncoderContext.java index 96b7d1fbede..1816f91cf85 100644 --- a/bson/src/main/org/bson/codecs/EncoderContext.java +++ b/bson/src/main/org/bson/codecs/EncoderContext.java @@ -30,6 +30,19 @@ public final class EncoderContext { private final boolean encodingCollectibleDocument; + /** + * Returns true if the the value to be encoded is a document that will be + * put in a MongoDB collection. Encoders for such documents + * might choose to act differently when encoding such as documents, + * e.g. by re-ordering the fields in some way (like encoding the _id + * field first). + * + * @return true if the value to be encoded is a document that will be put in a MongoDB collection + */ + public boolean isEncodingCollectibleDocument() { + return encodingCollectibleDocument; + } + /** * Create a builder. * @@ -49,9 +62,11 @@ private Builder() { } /** - * Set to true if the the value to be encoded is a document that will be put in a MongoDB collection. + * Set to true if the the value to be encoded is a + * document that will be put in a MongoDB collection. * - * @param encodingCollectibleDocument true if the value to be encoded is a document that will be put in a MongoDB collection + * @param encodingCollectibleDocument true if the value to be encoded is a document + * that will be put in a MongoDB collection * @return this */ public Builder isEncodingCollectibleDocument(final boolean encodingCollectibleDocument) { @@ -68,17 +83,6 @@ public EncoderContext build() { } } - /** - * Returns true if the the value to be encoded is a document that will be put in a MongoDB collection. Encoders for such documents - * might choose to act differently when encoding such as documents, e.g. by re-ordering the fields in some way (like encoding the _id - * field first). - * - * @return true if the value to be encoded is a document that will be put in a MongoDB collection - */ - public boolean isEncodingCollectibleDocument() { - return encodingCollectibleDocument; - } - /** * Creates a child context based on this and serializes the value with it to the writer. * diff --git a/bson/src/main/org/bson/io/BsonInput.java b/bson/src/main/org/bson/io/BsonInput.java index 472674d99e2..b468bc3a2b1 100644 --- a/bson/src/main/org/bson/io/BsonInput.java +++ b/bson/src/main/org/bson/io/BsonInput.java @@ -33,6 +33,13 @@ public interface BsonInput extends Closeable { */ int getPosition(); + /** + * Sets the position in the stream + * + * @param newPosition the new position + */ + void setPosition(int newPosition); + /** * Reads a single byte from the stream * diff --git a/bson/src/main/org/bson/io/ByteBufferBsonInput.java b/bson/src/main/org/bson/io/ByteBufferBsonInput.java index 03cdbf5efff..1fbfbde09fa 100644 --- a/bson/src/main/org/bson/io/ByteBufferBsonInput.java +++ b/bson/src/main/org/bson/io/ByteBufferBsonInput.java @@ -53,6 +53,13 @@ public int getPosition() { return buffer.position(); } + @Override + public void setPosition(final int newPosition) { + ensureOpen(); + buffer.position(newPosition); + } + + @Override public byte readByte() { ensureOpen(); diff --git a/bson/src/main/org/bson/json/JsonReader.java b/bson/src/main/org/bson/json/JsonReader.java index 13ae03e75ac..e3a6bce3ee8 100644 --- a/bson/src/main/org/bson/json/JsonReader.java +++ b/bson/src/main/org/bson/json/JsonReader.java @@ -18,6 +18,7 @@ import org.bson.AbstractBsonReader; +import org.bson.BSONException; import org.bson.BsonBinary; import org.bson.BsonBinarySubType; import org.bson.BsonContextType; @@ -61,6 +62,7 @@ public class JsonReader extends AbstractBsonReader { private final JsonScanner scanner; private JsonToken pushedToken; private Object currentValue; + private Mark mark; /** * Constructs a new instance with the given JSON string. @@ -964,11 +966,49 @@ private Long visitNumberLongExtendedJson() { return nameToken.getValue(Long.class); } + @Override + public void mark() { + if (mark != null) { + throw new BSONException("A mark already exists; it needs to be reset before creating a new one"); + } + mark = new Mark(); + } + + @Override + public void reset() { + if (mark == null) { + throw new BSONException("trying to reset a mark before creating it"); + } + mark.reset(); + mark = null; + } + @Override protected Context getContext() { return (Context) super.getContext(); } + protected class Mark extends AbstractBsonReader.Mark { + private JsonToken pushedToken; + private Object currentValue; + private int position; + + protected Mark() { + super(); + pushedToken = JsonReader.this.pushedToken; + currentValue = JsonReader.this.currentValue; + position = JsonReader.this.scanner.getBufferPosition(); + } + + protected void reset() { + super.reset(); + JsonReader.this.pushedToken = pushedToken; + JsonReader.this.currentValue = currentValue; + JsonReader.this.scanner.setBufferPosition(position); + JsonReader.this.setContext(new Context(getParentContext(), getContextType())); + } + } + protected class Context extends AbstractBsonReader.Context { protected Context(final AbstractBsonReader.Context parentContext, final BsonContextType contextType) { super(parentContext, contextType); diff --git a/bson/src/main/org/bson/json/JsonScanner.java b/bson/src/main/org/bson/json/JsonScanner.java index ed4fadd008a..2fa6704551d 100644 --- a/bson/src/main/org/bson/json/JsonScanner.java +++ b/bson/src/main/org/bson/json/JsonScanner.java @@ -27,6 +27,20 @@ class JsonScanner { private final JsonBuffer buffer; + /** + * @param newPosition the new position of the cursor position in the buffer + */ + public void setBufferPosition(final int newPosition) { + buffer.setPosition(newPosition); + } + + /** + * @return the current location of the cursor in the buffer + */ + public int getBufferPosition() { + return buffer.getPosition(); + } + /** * Constructs a a new {@code JSONScanner} that produces values scanned from specified {@code JSONBuffer}. * diff --git a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java b/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java index b8f49927213..1a0417e4104 100644 --- a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java +++ b/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java @@ -16,31 +16,75 @@ package com.mongodb.codecs; +import org.bson.BSONException; import org.bson.BsonBinary; +import org.bson.BsonBinarySubType; +import org.bson.BsonSerializationException; +import org.bson.UuidRepresentation; import java.util.UUID; +import static com.mongodb.codecs.CodecHelper.reverseByteArray; + /** * A transformer from {@code BsonBinary} to {@code UUID}. * * @since 3.0 */ public class BinaryToUUIDTransformer implements BinaryTransformer { + + private UuidRepresentation uuidRepresentation = UuidRepresentation.JAVA_LEGACY; + + public BinaryToUUIDTransformer() { + } + + public BinaryToUUIDTransformer(final UuidRepresentation uuidRepresentation) { + this.uuidRepresentation = uuidRepresentation; + } + @Override public UUID transform(final BsonBinary binary) { - return new UUID(readLongFromArrayLittleEndian(binary.getData(), 0), readLongFromArrayLittleEndian(binary.getData(), 8)); + byte[] binaryData = binary.getData(); + + if (binaryData.length != 16) { + throw new BsonSerializationException(String.format("Expected length to be 16, not %d.", binaryData.length)); + } + if (binary.getType() == BsonBinarySubType.UUID_LEGACY.getValue()) { + switch (uuidRepresentation) { + case C_SHARP_LEGACY: + reverseByteArray(binaryData, 0, 4); + reverseByteArray(binaryData, 4, 2); + reverseByteArray(binaryData, 6, 2); + break; + case JAVA_LEGACY: + reverseByteArray(binaryData, 0, 8); + reverseByteArray(binaryData, 8, 8); + break; + case PYTHON_LEGACY: + case STANDARD: + break; + default: + throw new BSONException("Unexpected UUID representation"); + } + } + if (binary.getType() == BsonBinarySubType.UUID_LEGACY.getValue() + || binary.getType() == BsonBinarySubType.UUID_STANDARD.getValue()) { + return new UUID(readLongFromArrayBigEndian(binaryData, 0), readLongFromArrayBigEndian(binaryData, 8)); + } else { + throw new BSONException("Unexpected BsonBinarySubType"); + } } - private static long readLongFromArrayLittleEndian(final byte[] bytes, final int offset) { + private static long readLongFromArrayBigEndian(final byte[] bytes, final int offset) { long x = 0; - x |= (0xFFL & bytes[offset]); - x |= (0xFFL & bytes[offset + 1]) << 8; - x |= (0xFFL & bytes[offset + 2]) << 16; - x |= (0xFFL & bytes[offset + 3]) << 24; - x |= (0xFFL & bytes[offset + 4]) << 32; - x |= (0xFFL & bytes[offset + 5]) << 40; - x |= (0xFFL & bytes[offset + 6]) << 48; - x |= (0xFFL & bytes[offset + 7]) << 56; + x |= (0xFFL & bytes[offset + 7]); + x |= (0xFFL & bytes[offset + 6]) << 8; + x |= (0xFFL & bytes[offset + 5]) << 16; + x |= (0xFFL & bytes[offset + 4]) << 24; + x |= (0xFFL & bytes[offset + 3]) << 32; + x |= (0xFFL & bytes[offset + 2]) << 40; + x |= (0xFFL & bytes[offset + 1]) << 48; + x |= (0xFFL & bytes[offset]) << 56; return x; } } \ No newline at end of file diff --git a/driver-core/src/main/com/mongodb/codecs/CodecHelper.java b/driver-core/src/main/com/mongodb/codecs/CodecHelper.java new file mode 100644 index 00000000000..a4da5514b4d --- /dev/null +++ b/driver-core/src/main/com/mongodb/codecs/CodecHelper.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright (c) 2008-2014 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 com.mongodb.codecs; + +/** + * helper functions for codecs + */ +public final class CodecHelper { + + private CodecHelper() { + + } + // reverse elements in the subarray data[start:start+length] + public static void reverseByteArray(final byte[] data, final int start, final int length) { + for (int left = start, right = start + length - 1; left < right; left++, right--) { + // swap the values at the left and right indices + byte temp = data[left]; + data[left] = data[right]; + data[right] = temp; + } + } +} \ No newline at end of file diff --git a/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java b/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java index 1fc6ca010b4..4f091a4ea47 100644 --- a/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java +++ b/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java @@ -16,6 +16,7 @@ package com.mongodb.codecs; +import org.bson.BsonBinarySubType; import org.bson.BsonDocument; import org.bson.BsonDocumentWriter; import org.bson.BsonReader; @@ -32,6 +33,7 @@ import java.util.Arrays; import java.util.Map; +import java.util.UUID; import static com.mongodb.assertions.Assertions.notNull; @@ -175,8 +177,15 @@ private Object readValue(final BsonReader reader, final DecoderContext decoderCo if (bsonType == BsonType.NULL) { reader.readNull(); return null; - } else { - return registry.get(bsonTypeClassMap.get(bsonType)).decode(reader, decoderContext); + } else if (bsonType == BsonType.BINARY) { + reader.mark(); + byte bsonSubType = reader.readBinaryData().getType(); + reader.reset(); + if (bsonSubType == BsonBinarySubType.UUID_STANDARD.getValue() + || bsonSubType == BsonBinarySubType.UUID_LEGACY.getValue()) { + return registry.get(UUID.class).decode(reader, decoderContext); + } } + return registry.get(bsonTypeClassMap.get(bsonType)).decode(reader, decoderContext); } } diff --git a/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java b/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java index 8369e449986..8e81d754e3b 100644 --- a/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java +++ b/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java @@ -102,6 +102,7 @@ private void addCodecs() { addCodec(new SymbolCodec()); addCodec(new BsonTimestampCodec()); addCodec(new BsonUndefinedCodec()); + addCodec(new UUIDCodec()); } private void addCodec(final Codec codec) { diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java b/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java index 86c06fd57b0..fbb0f862399 100644 --- a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java +++ b/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java @@ -16,50 +16,99 @@ package com.mongodb.codecs; +import org.bson.BSONException; import org.bson.BsonBinary; import org.bson.BsonBinarySubType; import org.bson.BsonReader; import org.bson.BsonWriter; +import org.bson.UuidRepresentation; import org.bson.codecs.Codec; import org.bson.codecs.DecoderContext; import org.bson.codecs.EncoderContext; import java.util.UUID; +import static com.mongodb.codecs.CodecHelper.reverseByteArray; + /** * Encodes and decodes {@code UUID} objects. * * @since 3.0 */ public class UUIDCodec implements Codec { - @Override - public void encode(final BsonWriter writer, final UUID value, final EncoderContext encoderContext) { - byte[] bytes = new byte[16]; - writeLongToArrayLittleEndian(bytes, 0, value.getMostSignificantBits()); - writeLongToArrayLittleEndian(bytes, 8, value.getLeastSignificantBits()); + private final UuidRepresentation encoderUuidRepresentation; + private final UuidRepresentation decoderUuidRepresentation; + + /** + * The default UUIDRepresentation is JAVA_LEGACY to be compatible with existing documents + * + * @param uuidRepresentation the representation of UUID + * + * @since 3.0 + * @see org.bson.UuidRepresentation + */ + public UUIDCodec(final UuidRepresentation uuidRepresentation) { + this.encoderUuidRepresentation = uuidRepresentation; + this.decoderUuidRepresentation = uuidRepresentation; + } - writer.writeBinaryData(new BsonBinary(BsonBinarySubType.UUID_LEGACY, bytes)); + /** + * The constructor for UUIDCodec, default is JAVA_LEGACY + */ + public UUIDCodec() { + this.encoderUuidRepresentation = UuidRepresentation.JAVA_LEGACY; + this.decoderUuidRepresentation = UuidRepresentation.JAVA_LEGACY; + } + + @Override + public void encode(final BsonWriter writer, final UUID value, final EncoderContext encoderContext) { + byte[] binaryData = new byte[16]; + writeLongToArrayBigEndian(binaryData, 0, value.getMostSignificantBits()); + writeLongToArrayBigEndian(binaryData, 8, value.getLeastSignificantBits()); + switch (encoderUuidRepresentation) { + case C_SHARP_LEGACY: + reverseByteArray(binaryData, 0, 4); + reverseByteArray(binaryData, 4, 2); + reverseByteArray(binaryData, 6, 2); + break; + case JAVA_LEGACY: + reverseByteArray(binaryData, 0, 8); + reverseByteArray(binaryData, 8, 8); + break; + case PYTHON_LEGACY: + case STANDARD: + break; + default: + throw new BSONException("Unexpected UUID representation"); + } + // changed the default subtype to STANDARD since 3.0 + if (encoderUuidRepresentation == UuidRepresentation.STANDARD) { + writer.writeBinaryData(new BsonBinary(BsonBinarySubType.UUID_STANDARD, binaryData)); + } else { + writer.writeBinaryData(new BsonBinary(BsonBinarySubType.UUID_LEGACY, binaryData)); + } } @Override public UUID decode(final BsonReader reader, final DecoderContext decoderContext) { - return new BinaryToUUIDTransformer().transform(reader.readBinaryData()); + BsonBinary binaryData = reader.readBinaryData(); + BinaryToUUIDTransformer transformer = new BinaryToUUIDTransformer(decoderUuidRepresentation); + return transformer.transform(binaryData); } @Override public Class getEncoderClass() { return UUID.class; } - - private static void writeLongToArrayLittleEndian(final byte[] bytes, final int offset, final long x) { - bytes[offset] = (byte) (0xFFL & (x)); - bytes[offset + 1] = (byte) (0xFFL & (x >> 8)); - bytes[offset + 2] = (byte) (0xFFL & (x >> 16)); - bytes[offset + 3] = (byte) (0xFFL & (x >> 24)); - bytes[offset + 4] = (byte) (0xFFL & (x >> 32)); - bytes[offset + 5] = (byte) (0xFFL & (x >> 40)); - bytes[offset + 6] = (byte) (0xFFL & (x >> 48)); - bytes[offset + 7] = (byte) (0xFFL & (x >> 56)); + private static void writeLongToArrayBigEndian(final byte[] bytes, final int offset, final long x) { + bytes[offset + 7] = (byte) (0xFFL & (x)); + bytes[offset + 6] = (byte) (0xFFL & (x >> 8)); + bytes[offset + 5] = (byte) (0xFFL & (x >> 16)); + bytes[offset + 4] = (byte) (0xFFL & (x >> 24)); + bytes[offset + 3] = (byte) (0xFFL & (x >> 32)); + bytes[offset + 2] = (byte) (0xFFL & (x >> 40)); + bytes[offset + 1] = (byte) (0xFFL & (x >> 48)); + bytes[offset] = (byte) (0xFFL & (x >> 56)); } } diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java b/driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java new file mode 100644 index 00000000000..ede9ec728f8 --- /dev/null +++ b/driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java @@ -0,0 +1,58 @@ + /* + * + * * Copyright (c) 2008-2014 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 com.mongodb.codecs; + + import org.bson.UuidRepresentation; + import org.bson.codecs.Codec; + import org.bson.codecs.configuration.CodecProvider; + import org.bson.codecs.configuration.CodecRegistry; + + import java.util.UUID; + + /** + * A {@code CodecProvider} for UUID Codecs with custom UUID representations + * + * @since 3.0 + */ + public class UUIDCodecProvider implements CodecProvider { + + private UuidRepresentation uuidRepresentation; + + /** + * Set the UUIDRepresentation to be used in the codec + * default is JAVA_LEGACY to be compatible with existing documents + * + * @param uuidRepresentation the representation of UUID + * + * @since 3.0 + * @see org.bson.UuidRepresentation + */ + public UUIDCodecProvider(final UuidRepresentation uuidRepresentation) { + this.uuidRepresentation = uuidRepresentation; + } + + @Override + @SuppressWarnings("unchecked") + public Codec get(final Class clazz, final CodecRegistry registry) { + if (clazz == UUID.class) { + return (Codec) (new UUIDCodec(uuidRepresentation)); + } + return null; + } + } \ No newline at end of file diff --git a/driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy b/driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy new file mode 100644 index 00000000000..fc70990a5e7 --- /dev/null +++ b/driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy @@ -0,0 +1,269 @@ +/* + * + * * Copyright (c) 2008-2014 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 com.mongodb + +import org.bson.BSONException +import org.bson.BsonBinaryReader +import org.bson.BsonBinaryWriter +import org.bson.BsonDocument +import org.bson.BsonDocumentReader +import org.bson.BsonDocumentWriter +import org.bson.BsonReader +import org.bson.BsonWriter +import org.bson.io.BasicOutputBuffer +import org.bson.io.ByteBufferBsonInput +import org.bson.json.JsonReader +import org.bson.json.JsonWriter +import spock.lang.Specification + +/** + * + */ +@SuppressWarnings('UnnecessaryObjectReferences') +class LimitedLookaheadMarkSpecification extends Specification { + + static BsonDocument bsonDoc + static BasicOutputBuffer buffer + static StringWriter stringWriter + + def setupSpec() { + bsonDoc = new BsonDocument() + buffer = new BasicOutputBuffer() + stringWriter = new StringWriter() + } + + def cleanup() { + bsonDoc = new BsonDocument() + buffer = new BasicOutputBuffer() + stringWriter = new StringWriter() + } + + def 'should throw if mark without resetting previous mark'(BsonWriter writer) { + given: + writer.with { + writeStartDocument() + writeInt64('int64', 52L) + writeEndDocument() + } + + when: + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(bsonDoc) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) + } else { + reader = new JsonReader(stringWriter.toString()) + } + reader.readStartDocument() + reader.mark() + reader.mark() + + then: + thrown(BSONException) + + where: + writer << [ + new BsonDocumentWriter(bsonDoc), + new BsonBinaryWriter(buffer, false), + new JsonWriter(stringWriter) + ] + } + + def 'should throw if reset without mark'(BsonWriter writer) { + given: + writer.with { + writeStartDocument() + writeInt64('int64', 52L) + writeEndDocument() + } + + when: + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(bsonDoc) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) + } else { + reader = new JsonReader(stringWriter.toString()) + } + + reader.readStartDocument() + reader.reset() + + then: + thrown(BSONException) + + where: + writer << [ + new BsonDocumentWriter(bsonDoc), + new BsonBinaryWriter(buffer, false), + new JsonWriter(stringWriter) + ] + } + + def 'Lookahead should work at various states'(BsonWriter writer) { + given: + writer.with { + writeStartDocument() + writeInt64('int64', 52L) + writeStartArray('array') + writeInt32(1) + writeInt64(2L) + writeStartArray() + writeInt32(3) + writeInt32(4) + writeEndArray() + writeStartDocument() + writeInt32('a', 5) + writeEndDocument() + writeNull() + writeEndArray() + writeStartDocument('document') + writeInt32('a', 6) + writeEndDocument() + writeEndDocument() + } + + + when: + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(bsonDoc) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) + } else { + reader = new JsonReader(stringWriter.toString()) + } + + then: + + reader.readStartDocument() + // mark beginning of document * 1 + reader.mark() + reader.readName() == 'int64' + reader.readInt64() == 52L + reader.readStartArray() + // reset to beginning of document * 2 + reader.reset() + // mark beginning of document * 2 + reader.mark() + reader.readName() == 'int64' + reader.readInt64() == 52L + // reset to beginning of document * 3 + reader.reset() + // mark beginning of document * 3 + reader.mark() + reader.readName() == 'int64' + reader.readInt64() == 52L + reader.readName() == 'array' + reader.readStartArray() + reader.readInt32() == 1 + reader.readInt64() == 2 + reader.readStartArray() + reader.readInt32() == 3 + reader.readInt32() == 4 + reader.readEndArray() + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 5 + reader.readEndDocument() + reader.readNull() + reader.readEndArray() + reader.readName() == 'document' + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 6 + reader.readEndDocument() + reader.readEndDocument() + // read entire document, reset to beginning + reader.reset() + reader.readName() == 'int64' + reader.readInt64() == 52L + reader.readName() == 'array' + // mar in outer-document * 1 + reader.mark() + reader.readStartArray() + reader.readInt32() == 1 + reader.readInt64() == 2 + reader.readStartArray() + // reset in sub-document * 1 + reader.reset() + // mark in outer-document * 2 + reader.mark() + reader.readStartArray() + reader.readInt32() == 1 + reader.readInt64() == 2 + reader.readStartArray() + reader.readInt32() == 3 + // reset in sub-document * 2 + reader.reset() + reader.readStartArray() + reader.readInt32() == 1 + reader.readInt64() == 2 + reader.readStartArray() + reader.readInt32() == 3 + reader.readInt32() == 4 + // mark in sub-document * 1 + reader.mark() + reader.readEndArray() + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 5 + reader.readEndDocument() + reader.readNull() + reader.readEndArray() + // reset in outer-document * 1 + reader.reset() + // mark in sub-document * 2 + reader.mark() + reader.readEndArray() + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 5 + reader.readEndDocument() + reader.readNull() + reader.readEndArray() + // reset in out-document * 2 + reader.reset() + reader.readEndArray() + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 5 + reader.readEndDocument() + reader.readNull() + reader.readEndArray() + reader.readName() == 'document' + reader.readStartDocument() + reader.readName() == 'a' + reader.readInt32() == 6 + reader.readEndDocument() + reader.readEndDocument() + + where: + writer << [ + new BsonDocumentWriter(bsonDoc), + new BsonBinaryWriter(buffer, false), +// new JsonWriter(stringWriter) + ] + } +} \ No newline at end of file diff --git a/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy b/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy index ae5e15bc564..2621b2cbacc 100644 --- a/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy @@ -30,7 +30,7 @@ class BinaryToUUIDTransformerSpecification extends Specification { @Subject private final BinaryToUUIDTransformer binaryToUUIDTransformer = new BinaryToUUIDTransformer(); - def 'should read little endian encoded longs'() { + def 'should read big endian encoded longs'() { given: byte[] binaryTypeWithUUIDAsBytes = [ 0, 0, 0, 0, // document @@ -38,8 +38,8 @@ class BinaryToUUIDTransformerSpecification extends Specification { 95, 105, 100, 0, // "_id" 16, 0, 0, 0, // int "16" (length) 4, // type (B_UUID_STANDARD) - 2, 0, 0, 0, 0, 0, 0, 0, // - 1, 0, 0, 0, 0, 0, 0, 0, // 8 bytes for long, 2 longs for UUID + 0, 0, 0, 0, 0, 0, 0, 2, // + 0, 0, 0, 0, 0, 0, 0, 1, // 8 bytes for long, 2 longs for UUID 0]; // EOM BsonBinaryReader reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(wrap(binaryTypeWithUUIDAsBytes))), true); diff --git a/driver-core/src/test/unit/com/mongodb/codecs/DocumentCodecSpecification.groovy b/driver-core/src/test/unit/com/mongodb/codecs/DocumentCodecSpecification.groovy index e4ce833e796..8cf49a1caad 100644 --- a/driver-core/src/test/unit/com/mongodb/codecs/DocumentCodecSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/codecs/DocumentCodecSpecification.groovy @@ -20,15 +20,20 @@ import org.bson.BsonBinaryReader import org.bson.BsonBinaryWriter import org.bson.BsonDbPointer import org.bson.BsonDocument +import org.bson.BsonDocumentReader import org.bson.BsonDocumentWriter +import org.bson.BsonReader import org.bson.BsonRegularExpression import org.bson.BsonTimestamp import org.bson.BsonUndefined +import org.bson.BsonWriter import org.bson.ByteBufNIO import org.bson.codecs.DecoderContext import org.bson.codecs.EncoderContext import org.bson.io.BasicOutputBuffer import org.bson.io.ByteBufferBsonInput +import org.bson.json.JsonReader +import org.bson.json.JsonWriter import org.bson.types.Binary import org.bson.types.Code import org.bson.types.MaxKey @@ -37,6 +42,7 @@ import org.bson.types.ObjectId import org.bson.types.Symbol import org.mongodb.CodeWithScope import org.mongodb.Document +import spock.lang.Shared import spock.lang.Specification import java.nio.ByteBuffer @@ -44,7 +50,10 @@ import java.nio.ByteBuffer import static java.util.Arrays.asList class DocumentCodecSpecification extends Specification { - def 'should encode and decode all default types'() { + @Shared BsonDocument bsonDoc = new BsonDocument() + @Shared StringWriter stringWriter = new StringWriter() + + def 'should encode and decode all default types with all readers and writers' (BsonWriter writer) { given: def originalDocument = new Document() originalDocument.with { @@ -68,15 +77,22 @@ class DocumentCodecSpecification extends Specification { put('undefined', new BsonUndefined()) put('binary', new Binary((byte) 80, [5, 4, 3, 2, 1] as byte[])) put('array', asList(1, 1L, true, [1, 2, 3], new Document('a', 1), null)) + put('uuid', new UUID(1L, 2L)) put('document', new Document('a', 2)) } when: - def buffer = new BasicOutputBuffer() - BsonBinaryWriter writer = new BsonBinaryWriter(buffer, false) new DocumentCodec().encode(writer, originalDocument, EncoderContext.builder().build()) - BsonBinaryReader reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(buffer.toByteArray()))), - true) + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(bsonDoc) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO( + ByteBuffer.wrap(buffer.toByteArray()))), true) + } else { + reader = new JsonReader(stringWriter.toString()) + } def decodedDoc = new DocumentCodec().decode(reader, DecoderContext.builder().build()) then: @@ -99,8 +115,15 @@ class DocumentCodecSpecification extends Specification { decodedDoc.get('timestamp') == originalDocument.get('timestamp') decodedDoc.get('undefined') == originalDocument.get('undefined') decodedDoc.get('binary') == originalDocument.get('binary') + decodedDoc.get('uuid') == originalDocument.get('uuid') decodedDoc.get('array') == originalDocument.get('array') decodedDoc.get('document') == originalDocument.get('document') + where: + writer << [ + new BsonDocumentWriter(bsonDoc), + new BsonBinaryWriter(new BasicOutputBuffer(), false), +// new JsonWriter(stringWriter) + ] } def 'should respect encodeIdFirst property in encoder context'() { diff --git a/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy b/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy new file mode 100644 index 00000000000..a21d55ff220 --- /dev/null +++ b/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy @@ -0,0 +1,158 @@ +/* + * + * * Copyright (c) 2008-2014 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 com.mongodb.codecs + +import org.bson.BsonBinaryReader +import org.bson.BsonBinaryWriter +import org.bson.ByteBufNIO +import org.bson.UuidRepresentation +import org.bson.codecs.DecoderContext +import org.bson.codecs.EncoderContext +import org.bson.io.ByteBufferBsonInput +import org.bson.io.BasicOutputBuffer +import spock.lang.Shared +import spock.lang.Specification + +import java.nio.ByteBuffer + +/** + * + */ +class UUIDCodecSpecification extends Specification { + + @Shared private UUIDCodec uuidCodec; + @Shared private BasicOutputBuffer outputBuffer; + + def setup() { + uuidCodec = new UUIDCodec(); + outputBuffer = new BasicOutputBuffer(); + } + + def 'should decode different types of UUID'(UUIDCodec codec, byte[] list) throws IOException { + + given: + + ByteBufferBsonInput inputBuffer = new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(list))) + BsonBinaryReader bsonReader = new BsonBinaryReader(inputBuffer, false) + UUID expectedUuid = UUID.fromString('08070605-0403-0201-100f-0e0d0c0b0a09') + + bsonReader.readStartDocument() + bsonReader.readName() + + when: + UUID actualUuid = codec.decode(bsonReader, DecoderContext.builder().build()) + + then: + expectedUuid == actualUuid + + cleanup: + bsonReader.close() + + where: + + codec << [ + new UUIDCodec(), + new UUIDCodec(UuidRepresentation.STANDARD), + new UUIDCodec(UuidRepresentation.PYTHON_LEGACY), + new UUIDCodec(UuidRepresentation.C_SHARP_LEGACY), + ] + + list << [ + [0, 0, 0, 0, //Start of document + 5, // type (BINARY) + 95, 105, 100, 0, // "_id" + 16, 0, 0, 0, // int "16" (length) + 3, // type (B_UUID_LEGACY) JAVA_LEGACY + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16], //8 bytes for long, 2 longs for UUID, Little Endian + + [0, 0, 0, 0, //Start of document + 5, // type (BINARY) + 95, 105, 100, 0, // "_id" + 16, 0, 0, 0, // int "16" (length) + 4, // type (UUID) + 8, 7, 6, 5, 4, 3, 2, 1, + 16, 15, 14, 13, 12, 11, 10, 9], //8 bytes for long, 2 longs for UUID, Big Endian + + [0, 0, 0, 0, //Start of document + 5, // type (BINARY) + 95, 105, 100, 0, // "_id" + 16, 0, 0, 0, // int "16" (length) + 3, // type (B_UUID_LEGACY) PYTHON_LEGACY + 8, 7, 6, 5, 4, 3, 2, 1, + 16, 15, 14, 13, 12, 11, 10, 9], //8 bytes for long, 2 longs for UUID, Big Endian + + [0, 0, 0, 0, //Start of document + 5, // type (BINARY) + 95, 105, 100, 0, // "_id" + 16, 0, 0, 0, // int "16" (length) + 3, // type (B_UUID_LEGACY) CSHARP_LEGACY + 5, 6, 7, 8, 3, 4, 1, 2, + 16, 15, 14, 13, 12, 11, 10, 9], //8 bytes for long, 2 longs for UUID, Big Endian + ] + + } + + def 'should encode different types of UUIDs'(Byte bsonSubType, + UUIDCodec codec, + UUID uuid) throws IOException { + given: + + byte[] encodedDoc = [0, 0, 0, 0, //Start of document + 5, // type (BINARY) + 95, 105, 100, 0, // "_id" + 16, 0, 0, 0, // int "16" (length) + 0, // bsonSubType + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16] //8 bytes for long, 2 longs for UUID + + encodedDoc[13] = bsonSubType + + BsonBinaryWriter bsonWriter = new BsonBinaryWriter(outputBuffer, false) + bsonWriter.writeStartDocument() + bsonWriter.writeName('_id') + + when: + codec.encode(bsonWriter, uuid, EncoderContext.builder().build()) + + then: + outputBuffer.toByteArray() == encodedDoc + + cleanup: + bsonWriter.close() + + where: + + bsonSubType << [3, 4, 3, 3] + + codec << [ + new UUIDCodec(), + new UUIDCodec(UuidRepresentation.STANDARD), + new UUIDCodec(UuidRepresentation.PYTHON_LEGACY), + new UUIDCodec(UuidRepresentation.C_SHARP_LEGACY), + ] + + uuid << [ + UUID.fromString('08070605-0403-0201-100f-0e0d0c0b0a09'), // Java legacy UUID + UUID.fromString('01020304-0506-0708-090a-0b0c0d0e0f10'), // simulated standard UUID + UUID.fromString('01020304-0506-0708-090a-0b0c0d0e0f10'), // simulated Python UUID + UUID.fromString('04030201-0605-0807-090a-0b0c0d0e0f10') // simulated C# UUID + ] + } +} \ No newline at end of file diff --git a/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecTest.java b/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecTest.java deleted file mode 100644 index 27c5b13dde0..00000000000 --- a/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2008-2014 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 com.mongodb.codecs; - -import org.bson.BsonBinaryWriter; -import org.bson.codecs.EncoderContext; -import org.bson.io.BasicOutputBuffer; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.util.UUID; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -public class UUIDCodecTest { - - private UUIDCodec uuidCodec; - private BasicOutputBuffer outputBuffer; - - @Before - public void setUp() throws Exception { - uuidCodec = new UUIDCodec(); - outputBuffer = new BasicOutputBuffer(); - } - - @Test - public void shouldEncodeLongAsLittleEndian() throws IOException { - // Given - UUID uuid = new UUID(2L, 1L); - BsonBinaryWriter bsonWriter = new BsonBinaryWriter(outputBuffer, false); - try { - bsonWriter.writeStartDocument(); - bsonWriter.writeName("_id"); - - // When - uuidCodec.encode(bsonWriter, uuid, EncoderContext.builder().build()); - } finally { - bsonWriter.close(); - } - - // Then - byte[] expectedList = {0, 0, 0, 0, //Start of document - 5, // type (BINARY) - 95, 105, 100, 0, // "_id" - 16, 0, 0, 0, // int "16" (length) - 3, // type (B_UUID_LEGACY) - 2, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0}; //8 bytes for long, 2 longs for UUID, Little Endian - - assertThat(outputBuffer.toByteArray(), is(expectedList)); - } - -} diff --git a/driver/src/main/com/mongodb/DBObjectCodec.java b/driver/src/main/com/mongodb/DBObjectCodec.java index 8113be64221..73429bf8080 100644 --- a/driver/src/main/com/mongodb/DBObjectCodec.java +++ b/driver/src/main/com/mongodb/DBObjectCodec.java @@ -267,7 +267,8 @@ private Object readBinary(final BsonReader reader) { return new BinaryToByteArrayTransformer().transform(binary); } else if (binary.getType() == BsonBinarySubType.OLD_BINARY.getValue()) { return new BinaryToByteArrayTransformer().transform(binary); - } else if (binary.getType() == BsonBinarySubType.UUID_LEGACY.getValue()) { + } else if (binary.getType() == BsonBinarySubType.UUID_LEGACY.getValue() + || binary.getType() == BsonBinarySubType.UUID_STANDARD.getValue()) { return new BinaryToUUIDTransformer().transform(binary); } else { return new Binary(binary.getType(), binary.getData()); diff --git a/driver/src/test/unit/com/mongodb/DBObjectCodecSpecification.groovy b/driver/src/test/unit/com/mongodb/DBObjectCodecSpecification.groovy new file mode 100644 index 00000000000..d4edf45f545 --- /dev/null +++ b/driver/src/test/unit/com/mongodb/DBObjectCodecSpecification.groovy @@ -0,0 +1,53 @@ +/* + * + * * Copyright (c) 2008-2014 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 com.mongodb + +import org.bson.BsonDocument +import org.bson.BsonDocumentReader +import org.bson.BsonDocumentWriter +import org.bson.codecs.DecoderContext +import org.bson.codecs.EncoderContext +import org.bson.codecs.configuration.CodecProvider +import org.bson.codecs.configuration.RootCodecRegistry +import spock.lang.Shared +import spock.lang.Specification + +/** + * + */ +class DBObjectCodecSpecification extends Specification { + + @Shared BsonDocument bsonDoc = new BsonDocument() + + def 'should encode and decode UUIDs'() { + given: + UUID uuid = UUID.fromString('01020304-0506-0708-090a-0b0c0d0e0f10') + DBObjectCodec dbObjectCodec = new DBObjectCodec(null, new BasicDBObjectFactory(), + new RootCodecRegistry(Arrays.asList(new DBObjectCodecProvider())), + DBObjectCodecProvider.createDefaultBsonTypeClassMap()); + BasicDBObject uuidObj = new BasicDBObject('uuid', uuid) + BsonDocumentWriter writer = new BsonDocumentWriter(bsonDoc) + dbObjectCodec.encode(writer, uuidObj, EncoderContext.builder().build()) + BsonDocumentReader reader = new BsonDocumentReader(bsonDoc) + DBObject decodedUuid = dbObjectCodec.decode(reader, DecoderContext.builder().build()) + + expect: + decodedUuid.get('uuid') == uuid + } +} \ No newline at end of file From 413d9b2cdbec1b0203ac480a01a253d0e5a21f8b Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Oct 2014 16:12:04 -0400 Subject: [PATCH 2/8] Reverted formatting changes that are not related to the work at hand. --- .../main/org/bson/codecs/DecoderContext.java | 12 +++----- .../main/org/bson/codecs/EncoderContext.java | 30 ++++++++----------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/bson/src/main/org/bson/codecs/DecoderContext.java b/bson/src/main/org/bson/codecs/DecoderContext.java index 542aaf67f5f..0c89547daf2 100644 --- a/bson/src/main/org/bson/codecs/DecoderContext.java +++ b/bson/src/main/org/bson/codecs/DecoderContext.java @@ -23,11 +23,6 @@ * @since 3.0 */ public final class DecoderContext { - - - private DecoderContext() { - } - /** * Create a builder. * @@ -41,9 +36,7 @@ public static Builder builder() { * A builder for {@code DecoderContext} instances. */ public static final class Builder { - private Builder() { - } /** @@ -51,7 +44,10 @@ private Builder() { * @return the decoder context */ public DecoderContext build() { - return new DecoderContext(); + return new DecoderContext(this); } } + + private DecoderContext(final Builder builder) { + } } diff --git a/bson/src/main/org/bson/codecs/EncoderContext.java b/bson/src/main/org/bson/codecs/EncoderContext.java index 1816f91cf85..96b7d1fbede 100644 --- a/bson/src/main/org/bson/codecs/EncoderContext.java +++ b/bson/src/main/org/bson/codecs/EncoderContext.java @@ -30,19 +30,6 @@ public final class EncoderContext { private final boolean encodingCollectibleDocument; - /** - * Returns true if the the value to be encoded is a document that will be - * put in a MongoDB collection. Encoders for such documents - * might choose to act differently when encoding such as documents, - * e.g. by re-ordering the fields in some way (like encoding the _id - * field first). - * - * @return true if the value to be encoded is a document that will be put in a MongoDB collection - */ - public boolean isEncodingCollectibleDocument() { - return encodingCollectibleDocument; - } - /** * Create a builder. * @@ -62,11 +49,9 @@ private Builder() { } /** - * Set to true if the the value to be encoded is a - * document that will be put in a MongoDB collection. + * Set to true if the the value to be encoded is a document that will be put in a MongoDB collection. * - * @param encodingCollectibleDocument true if the value to be encoded is a document - * that will be put in a MongoDB collection + * @param encodingCollectibleDocument true if the value to be encoded is a document that will be put in a MongoDB collection * @return this */ public Builder isEncodingCollectibleDocument(final boolean encodingCollectibleDocument) { @@ -83,6 +68,17 @@ public EncoderContext build() { } } + /** + * Returns true if the the value to be encoded is a document that will be put in a MongoDB collection. Encoders for such documents + * might choose to act differently when encoding such as documents, e.g. by re-ordering the fields in some way (like encoding the _id + * field first). + * + * @return true if the value to be encoded is a document that will be put in a MongoDB collection + */ + public boolean isEncodingCollectibleDocument() { + return encodingCollectibleDocument; + } + /** * Creates a child context based on this and serializes the value with it to the writer. * From dcb89eb2b2df5ad1d98905e246af29f2c69a2ffc Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Oct 2014 16:12:52 -0400 Subject: [PATCH 3/8] Using BsonInput.mark/reset and removed BsonInput.setPosition. --- bson/src/main/org/bson/BsonBinaryReader.java | 5 ++--- bson/src/main/org/bson/io/BsonInput.java | 7 ------- bson/src/main/org/bson/io/ByteBufferBsonInput.java | 6 ------ 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/bson/src/main/org/bson/BsonBinaryReader.java b/bson/src/main/org/bson/BsonBinaryReader.java index 6d07857f8f6..95ec89b5de5 100644 --- a/bson/src/main/org/bson/BsonBinaryReader.java +++ b/bson/src/main/org/bson/BsonBinaryReader.java @@ -359,18 +359,17 @@ public void reset() { protected class Mark extends AbstractBsonReader.Mark { private int startPosition; private int size; - private int currentPosition; protected Mark() { super(); startPosition = BsonBinaryReader.this.getContext().startPosition; size = BsonBinaryReader.this.getContext().size; - currentPosition = BsonBinaryReader.this.bsonInput.getPosition(); + BsonBinaryReader.this.bsonInput.mark(Integer.MAX_VALUE); } protected void reset() { super.reset(); - BsonBinaryReader.this.bsonInput.setPosition(currentPosition); + BsonBinaryReader.this.bsonInput.reset(); BsonBinaryReader.this.setContext(new Context((Context) getParentContext(), getContextType(), startPosition, size)); } } diff --git a/bson/src/main/org/bson/io/BsonInput.java b/bson/src/main/org/bson/io/BsonInput.java index b468bc3a2b1..472674d99e2 100644 --- a/bson/src/main/org/bson/io/BsonInput.java +++ b/bson/src/main/org/bson/io/BsonInput.java @@ -33,13 +33,6 @@ public interface BsonInput extends Closeable { */ int getPosition(); - /** - * Sets the position in the stream - * - * @param newPosition the new position - */ - void setPosition(int newPosition); - /** * Reads a single byte from the stream * diff --git a/bson/src/main/org/bson/io/ByteBufferBsonInput.java b/bson/src/main/org/bson/io/ByteBufferBsonInput.java index 1fbfbde09fa..5b10325314c 100644 --- a/bson/src/main/org/bson/io/ByteBufferBsonInput.java +++ b/bson/src/main/org/bson/io/ByteBufferBsonInput.java @@ -53,12 +53,6 @@ public int getPosition() { return buffer.position(); } - @Override - public void setPosition(final int newPosition) { - ensureOpen(); - buffer.position(newPosition); - } - @Override public byte readByte() { From 1cabf7fb75a97e090d205463ce36a10263550fb0 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Oct 2014 16:14:06 -0400 Subject: [PATCH 4/8] Renamed CodecHelper to UUIDCodecHelper and made it non-public --- .../com/mongodb/codecs/BinaryToUUIDTransformer.java | 2 +- .../src/main/com/mongodb/codecs/UUIDCodec.java | 2 +- .../{CodecHelper.java => UUIDCodecHelper.java} | 12 ++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) rename driver-core/src/main/com/mongodb/codecs/{CodecHelper.java => UUIDCodecHelper.java} (91%) diff --git a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java b/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java index 1a0417e4104..2df54c9748a 100644 --- a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java +++ b/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java @@ -24,7 +24,7 @@ import java.util.UUID; -import static com.mongodb.codecs.CodecHelper.reverseByteArray; +import static com.mongodb.codecs.UUIDCodecHelper.reverseByteArray; /** * A transformer from {@code BsonBinary} to {@code UUID}. diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java b/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java index fbb0f862399..5626c9a1381 100644 --- a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java +++ b/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java @@ -28,7 +28,7 @@ import java.util.UUID; -import static com.mongodb.codecs.CodecHelper.reverseByteArray; +import static com.mongodb.codecs.UUIDCodecHelper.reverseByteArray; /** * Encodes and decodes {@code UUID} objects. diff --git a/driver-core/src/main/com/mongodb/codecs/CodecHelper.java b/driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java similarity index 91% rename from driver-core/src/main/com/mongodb/codecs/CodecHelper.java rename to driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java index a4da5514b4d..1c141afea0b 100644 --- a/driver-core/src/main/com/mongodb/codecs/CodecHelper.java +++ b/driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java @@ -18,14 +18,7 @@ package com.mongodb.codecs; -/** - * helper functions for codecs - */ -public final class CodecHelper { - - private CodecHelper() { - - } +final class UUIDCodecHelper { // reverse elements in the subarray data[start:start+length] public static void reverseByteArray(final byte[] data, final int start, final int length) { for (int left = start, right = start + length - 1; left < right; left++, right--) { @@ -35,4 +28,7 @@ public static void reverseByteArray(final byte[] data, final int start, final in data[right] = temp; } } + + private UUIDCodecHelper() { + } } \ No newline at end of file From 375db09b1e6746524ee5a089e8488b3cc79b5d0c Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Oct 2014 16:17:38 -0400 Subject: [PATCH 5/8] Lower-casing UUID in all class names to match UuidRepresentation. --- ...rmer.java => BinaryToUuidTransformer.java} | 8 +++--- .../mongodb/codecs/DocumentCodecProvider.java | 2 +- .../codecs/TransformingBinaryDecoder.java | 2 +- .../codecs/{UUIDCodec.java => UuidCodec.java} | 10 +++---- ...DCodecHelper.java => UuidCodecHelper.java} | 4 +-- ...ecProvider.java => UuidCodecProvider.java} | 6 ++-- ...naryToUuidTransformerSpecification.groovy} | 4 +-- .../codecs/TransformingBinaryDecoderTest.java | 2 +- ...n.groovy => UuidCodecSpecification.groovy} | 28 +++++++++---------- .../src/main/com/mongodb/DBObjectCodec.java | 4 +-- .../com/mongodb/DBObjectCodecProvider.java | 4 +-- .../com/mongodb/util/JSONSerializers.java | 6 ++-- 12 files changed, 40 insertions(+), 40 deletions(-) rename driver-core/src/main/com/mongodb/codecs/{BinaryToUUIDTransformer.java => BinaryToUuidTransformer.java} (92%) rename driver-core/src/main/com/mongodb/codecs/{UUIDCodec.java => UuidCodec.java} (93%) rename driver-core/src/main/com/mongodb/codecs/{UUIDCodecHelper.java => UuidCodecHelper.java} (94%) rename driver-core/src/main/com/mongodb/codecs/{UUIDCodecProvider.java => UuidCodecProvider.java} (89%) rename driver-core/src/test/unit/com/mongodb/codecs/{BinaryToUUIDTransformerSpecification.groovy => BinaryToUuidTransformerSpecification.groovy} (92%) rename driver-core/src/test/unit/com/mongodb/codecs/{UUIDCodecSpecification.groovy => UuidCodecSpecification.groovy} (87%) diff --git a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java b/driver-core/src/main/com/mongodb/codecs/BinaryToUuidTransformer.java similarity index 92% rename from driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java rename to driver-core/src/main/com/mongodb/codecs/BinaryToUuidTransformer.java index 2df54c9748a..f1845f13dcb 100644 --- a/driver-core/src/main/com/mongodb/codecs/BinaryToUUIDTransformer.java +++ b/driver-core/src/main/com/mongodb/codecs/BinaryToUuidTransformer.java @@ -24,21 +24,21 @@ import java.util.UUID; -import static com.mongodb.codecs.UUIDCodecHelper.reverseByteArray; +import static com.mongodb.codecs.UuidCodecHelper.reverseByteArray; /** * A transformer from {@code BsonBinary} to {@code UUID}. * * @since 3.0 */ -public class BinaryToUUIDTransformer implements BinaryTransformer { +public class BinaryToUuidTransformer implements BinaryTransformer { private UuidRepresentation uuidRepresentation = UuidRepresentation.JAVA_LEGACY; - public BinaryToUUIDTransformer() { + public BinaryToUuidTransformer() { } - public BinaryToUUIDTransformer(final UuidRepresentation uuidRepresentation) { + public BinaryToUuidTransformer(final UuidRepresentation uuidRepresentation) { this.uuidRepresentation = uuidRepresentation; } diff --git a/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java b/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java index 8e81d754e3b..fde948758f9 100644 --- a/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java +++ b/driver-core/src/main/com/mongodb/codecs/DocumentCodecProvider.java @@ -102,7 +102,7 @@ private void addCodecs() { addCodec(new SymbolCodec()); addCodec(new BsonTimestampCodec()); addCodec(new BsonUndefinedCodec()); - addCodec(new UUIDCodec()); + addCodec(new UuidCodec()); } private void addCodec(final Codec codec) { diff --git a/driver-core/src/main/com/mongodb/codecs/TransformingBinaryDecoder.java b/driver-core/src/main/com/mongodb/codecs/TransformingBinaryDecoder.java index fff1a277543..4f8ff1be32a 100644 --- a/driver-core/src/main/com/mongodb/codecs/TransformingBinaryDecoder.java +++ b/driver-core/src/main/com/mongodb/codecs/TransformingBinaryDecoder.java @@ -39,7 +39,7 @@ public class TransformingBinaryDecoder implements Decoder { subTypeTransformerMap = new HashMap>(); subTypeTransformerMap.put(BsonBinarySubType.BINARY.getValue(), new BinaryToByteArrayTransformer()); subTypeTransformerMap.put(BsonBinarySubType.OLD_BINARY.getValue(), new BinaryToByteArrayTransformer()); - subTypeTransformerMap.put(BsonBinarySubType.UUID_LEGACY.getValue(), new BinaryToUUIDTransformer()); + subTypeTransformerMap.put(BsonBinarySubType.UUID_LEGACY.getValue(), new BinaryToUuidTransformer()); } @Override diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java b/driver-core/src/main/com/mongodb/codecs/UuidCodec.java similarity index 93% rename from driver-core/src/main/com/mongodb/codecs/UUIDCodec.java rename to driver-core/src/main/com/mongodb/codecs/UuidCodec.java index 5626c9a1381..e8e43162293 100644 --- a/driver-core/src/main/com/mongodb/codecs/UUIDCodec.java +++ b/driver-core/src/main/com/mongodb/codecs/UuidCodec.java @@ -28,14 +28,14 @@ import java.util.UUID; -import static com.mongodb.codecs.UUIDCodecHelper.reverseByteArray; +import static com.mongodb.codecs.UuidCodecHelper.reverseByteArray; /** * Encodes and decodes {@code UUID} objects. * * @since 3.0 */ -public class UUIDCodec implements Codec { +public class UuidCodec implements Codec { private final UuidRepresentation encoderUuidRepresentation; private final UuidRepresentation decoderUuidRepresentation; @@ -48,7 +48,7 @@ public class UUIDCodec implements Codec { * @since 3.0 * @see org.bson.UuidRepresentation */ - public UUIDCodec(final UuidRepresentation uuidRepresentation) { + public UuidCodec(final UuidRepresentation uuidRepresentation) { this.encoderUuidRepresentation = uuidRepresentation; this.decoderUuidRepresentation = uuidRepresentation; } @@ -56,7 +56,7 @@ public UUIDCodec(final UuidRepresentation uuidRepresentation) { /** * The constructor for UUIDCodec, default is JAVA_LEGACY */ - public UUIDCodec() { + public UuidCodec() { this.encoderUuidRepresentation = UuidRepresentation.JAVA_LEGACY; this.decoderUuidRepresentation = UuidRepresentation.JAVA_LEGACY; } @@ -93,7 +93,7 @@ public void encode(final BsonWriter writer, final UUID value, final EncoderConte @Override public UUID decode(final BsonReader reader, final DecoderContext decoderContext) { BsonBinary binaryData = reader.readBinaryData(); - BinaryToUUIDTransformer transformer = new BinaryToUUIDTransformer(decoderUuidRepresentation); + BinaryToUuidTransformer transformer = new BinaryToUuidTransformer(decoderUuidRepresentation); return transformer.transform(binaryData); } diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java b/driver-core/src/main/com/mongodb/codecs/UuidCodecHelper.java similarity index 94% rename from driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java rename to driver-core/src/main/com/mongodb/codecs/UuidCodecHelper.java index 1c141afea0b..34b9bbd24b6 100644 --- a/driver-core/src/main/com/mongodb/codecs/UUIDCodecHelper.java +++ b/driver-core/src/main/com/mongodb/codecs/UuidCodecHelper.java @@ -18,7 +18,7 @@ package com.mongodb.codecs; -final class UUIDCodecHelper { +final class UuidCodecHelper { // reverse elements in the subarray data[start:start+length] public static void reverseByteArray(final byte[] data, final int start, final int length) { for (int left = start, right = start + length - 1; left < right; left++, right--) { @@ -29,6 +29,6 @@ public static void reverseByteArray(final byte[] data, final int start, final in } } - private UUIDCodecHelper() { + private UuidCodecHelper() { } } \ No newline at end of file diff --git a/driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java b/driver-core/src/main/com/mongodb/codecs/UuidCodecProvider.java similarity index 89% rename from driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java rename to driver-core/src/main/com/mongodb/codecs/UuidCodecProvider.java index ede9ec728f8..f8839b0c87e 100644 --- a/driver-core/src/main/com/mongodb/codecs/UUIDCodecProvider.java +++ b/driver-core/src/main/com/mongodb/codecs/UuidCodecProvider.java @@ -30,7 +30,7 @@ * * @since 3.0 */ - public class UUIDCodecProvider implements CodecProvider { + public class UuidCodecProvider implements CodecProvider { private UuidRepresentation uuidRepresentation; @@ -43,7 +43,7 @@ public class UUIDCodecProvider implements CodecProvider { * @since 3.0 * @see org.bson.UuidRepresentation */ - public UUIDCodecProvider(final UuidRepresentation uuidRepresentation) { + public UuidCodecProvider(final UuidRepresentation uuidRepresentation) { this.uuidRepresentation = uuidRepresentation; } @@ -51,7 +51,7 @@ public UUIDCodecProvider(final UuidRepresentation uuidRepresentation) { @SuppressWarnings("unchecked") public Codec get(final Class clazz, final CodecRegistry registry) { if (clazz == UUID.class) { - return (Codec) (new UUIDCodec(uuidRepresentation)); + return (Codec) (new UuidCodec(uuidRepresentation)); } return null; } diff --git a/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy b/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUuidTransformerSpecification.groovy similarity index 92% rename from driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy rename to driver-core/src/test/unit/com/mongodb/codecs/BinaryToUuidTransformerSpecification.groovy index 2621b2cbacc..f07ad8f6b82 100644 --- a/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUUIDTransformerSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/codecs/BinaryToUuidTransformerSpecification.groovy @@ -25,10 +25,10 @@ import spock.lang.Subject import static java.nio.ByteBuffer.wrap -class BinaryToUUIDTransformerSpecification extends Specification { +class BinaryToUuidTransformerSpecification extends Specification { @Subject - private final BinaryToUUIDTransformer binaryToUUIDTransformer = new BinaryToUUIDTransformer(); + private final BinaryToUuidTransformer binaryToUUIDTransformer = new BinaryToUuidTransformer(); def 'should read big endian encoded longs'() { given: diff --git a/driver-core/src/test/unit/com/mongodb/codecs/TransformingBinaryDecoderTest.java b/driver-core/src/test/unit/com/mongodb/codecs/TransformingBinaryDecoderTest.java index e0b2428f62b..d8c749a1a24 100644 --- a/driver-core/src/test/unit/com/mongodb/codecs/TransformingBinaryDecoderTest.java +++ b/driver-core/src/test/unit/com/mongodb/codecs/TransformingBinaryDecoderTest.java @@ -42,7 +42,7 @@ public void testDecode() { writer.writeBinaryData("subtype2", new BsonBinary(BsonBinarySubType.OLD_BINARY, new byte[]{2})); writer.writeName("subtype3"); - new UUIDCodec().encode(writer, UUID.randomUUID(), EncoderContext.builder().build()); + new UuidCodec().encode(writer, UUID.randomUUID(), EncoderContext.builder().build()); writer.writeBinaryData("subtype4", new BsonBinary(BsonBinarySubType.UUID_STANDARD, new byte[]{4})); writer.writeBinaryData("subtype5", new BsonBinary(BsonBinarySubType.MD5, new byte[]{5})); diff --git a/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy b/driver-core/src/test/unit/com/mongodb/codecs/UuidCodecSpecification.groovy similarity index 87% rename from driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy rename to driver-core/src/test/unit/com/mongodb/codecs/UuidCodecSpecification.groovy index a21d55ff220..76eccfc2cba 100644 --- a/driver-core/src/test/unit/com/mongodb/codecs/UUIDCodecSpecification.groovy +++ b/driver-core/src/test/unit/com/mongodb/codecs/UuidCodecSpecification.groovy @@ -24,8 +24,8 @@ import org.bson.ByteBufNIO import org.bson.UuidRepresentation import org.bson.codecs.DecoderContext import org.bson.codecs.EncoderContext -import org.bson.io.ByteBufferBsonInput import org.bson.io.BasicOutputBuffer +import org.bson.io.ByteBufferBsonInput import spock.lang.Shared import spock.lang.Specification @@ -34,17 +34,17 @@ import java.nio.ByteBuffer /** * */ -class UUIDCodecSpecification extends Specification { +class UuidCodecSpecification extends Specification { - @Shared private UUIDCodec uuidCodec; + @Shared private UuidCodec uuidCodec; @Shared private BasicOutputBuffer outputBuffer; def setup() { - uuidCodec = new UUIDCodec(); + uuidCodec = new UuidCodec(); outputBuffer = new BasicOutputBuffer(); } - def 'should decode different types of UUID'(UUIDCodec codec, byte[] list) throws IOException { + def 'should decode different types of UUID'(UuidCodec codec, byte[] list) throws IOException { given: @@ -67,10 +67,10 @@ class UUIDCodecSpecification extends Specification { where: codec << [ - new UUIDCodec(), - new UUIDCodec(UuidRepresentation.STANDARD), - new UUIDCodec(UuidRepresentation.PYTHON_LEGACY), - new UUIDCodec(UuidRepresentation.C_SHARP_LEGACY), + new UuidCodec(), + new UuidCodec(UuidRepresentation.STANDARD), + new UuidCodec(UuidRepresentation.PYTHON_LEGACY), + new UuidCodec(UuidRepresentation.C_SHARP_LEGACY), ] list << [ @@ -110,7 +110,7 @@ class UUIDCodecSpecification extends Specification { } def 'should encode different types of UUIDs'(Byte bsonSubType, - UUIDCodec codec, + UuidCodec codec, UUID uuid) throws IOException { given: @@ -142,10 +142,10 @@ class UUIDCodecSpecification extends Specification { bsonSubType << [3, 4, 3, 3] codec << [ - new UUIDCodec(), - new UUIDCodec(UuidRepresentation.STANDARD), - new UUIDCodec(UuidRepresentation.PYTHON_LEGACY), - new UUIDCodec(UuidRepresentation.C_SHARP_LEGACY), + new UuidCodec(), + new UuidCodec(UuidRepresentation.STANDARD), + new UuidCodec(UuidRepresentation.PYTHON_LEGACY), + new UuidCodec(UuidRepresentation.C_SHARP_LEGACY), ] uuid << [ diff --git a/driver/src/main/com/mongodb/DBObjectCodec.java b/driver/src/main/com/mongodb/DBObjectCodec.java index 73429bf8080..06e2b7b74e4 100644 --- a/driver/src/main/com/mongodb/DBObjectCodec.java +++ b/driver/src/main/com/mongodb/DBObjectCodec.java @@ -17,7 +17,7 @@ package com.mongodb; import com.mongodb.codecs.BinaryToByteArrayTransformer; -import com.mongodb.codecs.BinaryToUUIDTransformer; +import com.mongodb.codecs.BinaryToUuidTransformer; import com.mongodb.codecs.CollectibleCodec; import com.mongodb.codecs.IdGenerator; import com.mongodb.codecs.ObjectIdGenerator; @@ -269,7 +269,7 @@ private Object readBinary(final BsonReader reader) { return new BinaryToByteArrayTransformer().transform(binary); } else if (binary.getType() == BsonBinarySubType.UUID_LEGACY.getValue() || binary.getType() == BsonBinarySubType.UUID_STANDARD.getValue()) { - return new BinaryToUUIDTransformer().transform(binary); + return new BinaryToUuidTransformer().transform(binary); } else { return new Binary(binary.getType(), binary.getData()); } diff --git a/driver/src/main/com/mongodb/DBObjectCodecProvider.java b/driver/src/main/com/mongodb/DBObjectCodecProvider.java index 296e6730eae..e2d44792d47 100644 --- a/driver/src/main/com/mongodb/DBObjectCodecProvider.java +++ b/driver/src/main/com/mongodb/DBObjectCodecProvider.java @@ -27,7 +27,7 @@ import com.mongodb.codecs.PatternCodec; import com.mongodb.codecs.ShortCodec; import com.mongodb.codecs.StringCodec; -import com.mongodb.codecs.UUIDCodec; +import com.mongodb.codecs.UuidCodec; import org.bson.BsonDbPointer; import org.bson.BsonType; import org.bson.BsonUndefined; @@ -120,7 +120,7 @@ private void addCodecs() { addCodec(new ShortCodec()); addCodec(new ByteArrayCodec()); addCodec(new FloatCodec()); - addCodec(new UUIDCodec()); + addCodec(new UuidCodec()); } private void addCodec(final Codec codec) { diff --git a/driver/src/main/com/mongodb/util/JSONSerializers.java b/driver/src/main/com/mongodb/util/JSONSerializers.java index 8e8dae6cda8..f409cf60e4a 100644 --- a/driver/src/main/com/mongodb/util/JSONSerializers.java +++ b/driver/src/main/com/mongodb/util/JSONSerializers.java @@ -104,7 +104,7 @@ static ClassMapBasedObjectSerializer addCommonSerializers() { serializer.addObjectSerializer(ObjectId.class, new ObjectIdSerializer(serializer)); serializer.addObjectSerializer(Pattern.class, new PatternSerializer(serializer)); serializer.addObjectSerializer(String.class, new StringSerializer()); - serializer.addObjectSerializer(UUID.class, new UUIDSerializer(serializer)); + serializer.addObjectSerializer(UUID.class, new UuidSerializer(serializer)); serializer.addObjectSerializer(BsonUndefined.class, new UndefinedSerializer(serializer)); return serializer; } @@ -389,9 +389,9 @@ public void serialize(final Object obj, final StringBuilder buf) { } } - private static class UUIDSerializer extends CompoundObjectSerializer { + private static class UuidSerializer extends CompoundObjectSerializer { - UUIDSerializer(final ObjectSerializer serializer) { + UuidSerializer(final ObjectSerializer serializer) { super(serializer); } From 51582d33bbb5f9fe5927cf5e0ab160af8ff0ffa1 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Fri, 3 Oct 2014 16:24:50 -0400 Subject: [PATCH 6/8] Moved LimitedLookaheadMarkSpecification into bson module --- .../bson}/LimitedLookaheadMarkSpecification.groovy | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) rename {driver-core/src/test/unit/com/mongodb => bson/src/test/org/bson}/LimitedLookaheadMarkSpecification.groovy (95%) diff --git a/driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy similarity index 95% rename from driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy rename to bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy index fc70990a5e7..296dfea3e78 100644 --- a/driver-core/src/test/unit/com/mongodb/LimitedLookaheadMarkSpecification.groovy +++ b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy @@ -16,16 +16,8 @@ * */ -package com.mongodb +package org.bson -import org.bson.BSONException -import org.bson.BsonBinaryReader -import org.bson.BsonBinaryWriter -import org.bson.BsonDocument -import org.bson.BsonDocumentReader -import org.bson.BsonDocumentWriter -import org.bson.BsonReader -import org.bson.BsonWriter import org.bson.io.BasicOutputBuffer import org.bson.io.ByteBufferBsonInput import org.bson.json.JsonReader @@ -36,7 +28,7 @@ import spock.lang.Specification * */ @SuppressWarnings('UnnecessaryObjectReferences') -class LimitedLookaheadMarkSpecification extends Specification { +class LimitedLookAheadMarkSpecification extends Specification { static BsonDocument bsonDoc static BasicOutputBuffer buffer From 5193ecdf13e257c1c5c7a67e81dc350dd9ee9882 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Sat, 4 Oct 2014 08:15:14 -0400 Subject: [PATCH 7/8] Fixed problem with use of static fields in LimitedLookaheadMarkSpecification so that tests all pass with JsonWriter/JsonReader --- .../src/main/org/bson/BsonDocumentWriter.java | 9 + bson/src/main/org/bson/json/JsonWriter.java | 9 + .../LimitedLookaheadMarkSpecification.groovy | 173 ++++++++---------- 3 files changed, 96 insertions(+), 95 deletions(-) diff --git a/bson/src/main/org/bson/BsonDocumentWriter.java b/bson/src/main/org/bson/BsonDocumentWriter.java index 986014c2ef8..ef0112eb14b 100644 --- a/bson/src/main/org/bson/BsonDocumentWriter.java +++ b/bson/src/main/org/bson/BsonDocumentWriter.java @@ -45,6 +45,15 @@ public BsonDocumentWriter(final BsonDocument document) { setContext(new Context()); } + /** + * Gets the document that the writer is writing to. + * + * @return the document + */ + public BsonDocument getDocument() { + return document; + } + @Override protected void doWriteStartDocument() { switch (getState()) { diff --git a/bson/src/main/org/bson/json/JsonWriter.java b/bson/src/main/org/bson/json/JsonWriter.java index 7a3c89affcb..3b8cee4329e 100644 --- a/bson/src/main/org/bson/json/JsonWriter.java +++ b/bson/src/main/org/bson/json/JsonWriter.java @@ -64,6 +64,15 @@ public JsonWriter(final Writer writer, final JsonWriterSettings settings) { setContext(new Context(null, BsonContextType.TOP_LEVEL, "")); } + /** + * Gets the {@code Writer}. + * + * @return the writer + */ + public Writer getWriter() { + return writer; + } + @Override protected Context getContext() { return (Context) super.getContext(); diff --git a/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy index 296dfea3e78..d54b13b61be 100644 --- a/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy +++ b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy @@ -24,48 +24,31 @@ import org.bson.json.JsonReader import org.bson.json.JsonWriter import spock.lang.Specification -/** - * - */ @SuppressWarnings('UnnecessaryObjectReferences') -class LimitedLookAheadMarkSpecification extends Specification { - - static BsonDocument bsonDoc - static BasicOutputBuffer buffer - static StringWriter stringWriter - - def setupSpec() { - bsonDoc = new BsonDocument() - buffer = new BasicOutputBuffer() - stringWriter = new StringWriter() - } - - def cleanup() { - bsonDoc = new BsonDocument() - buffer = new BasicOutputBuffer() - stringWriter = new StringWriter() - } +class LimitedLookaheadMarkSpecification extends Specification { def 'should throw if mark without resetting previous mark'(BsonWriter writer) { given: writer.with { - writeStartDocument() - writeInt64('int64', 52L) - writeEndDocument() - } + writeStartDocument() + writeInt64('int64', 52L) + writeEndDocument() + } - when: BsonReader reader if (writer instanceof BsonDocumentWriter) { - reader = new BsonDocumentReader(bsonDoc) + reader = new BsonDocumentReader(writer.document) } else if (writer instanceof BsonBinaryWriter) { - BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + BasicOutputBuffer buffer = (BasicOutputBuffer) writer.getBsonOutput(); reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) - } else { - reader = new JsonReader(stringWriter.toString()) + } else if (writer instanceof JsonWriter) { + reader = new JsonReader(writer.writer.toString()) } + reader.readStartDocument() reader.mark() + + when: reader.mark() then: @@ -73,82 +56,82 @@ class LimitedLookAheadMarkSpecification extends Specification { where: writer << [ - new BsonDocumentWriter(bsonDoc), - new BsonBinaryWriter(buffer, false), - new JsonWriter(stringWriter) + new BsonDocumentWriter(new BsonDocument()), + new BsonBinaryWriter(new BasicOutputBuffer(), false), + new JsonWriter(new StringWriter()) ] - } + } def 'should throw if reset without mark'(BsonWriter writer) { - given: - writer.with { - writeStartDocument() - writeInt64('int64', 52L) - writeEndDocument() - } + given: + writer.with { + writeStartDocument() + writeInt64('int64', 52L) + writeEndDocument() + } + + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(writer.document) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer) writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) + } else if (writer instanceof JsonWriter) { + reader = new JsonReader(writer.writer.toString()) + } - when: - BsonReader reader - if (writer instanceof BsonDocumentWriter) { - reader = new BsonDocumentReader(bsonDoc) - } else if (writer instanceof BsonBinaryWriter) { - BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); - reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) - } else { - reader = new JsonReader(stringWriter.toString()) - } + reader.readStartDocument() - reader.readStartDocument() - reader.reset() + when: + reader.reset() - then: - thrown(BSONException) + then: + thrown(BSONException) - where: - writer << [ - new BsonDocumentWriter(bsonDoc), - new BsonBinaryWriter(buffer, false), - new JsonWriter(stringWriter) - ] - } + where: + writer << [ + new BsonDocumentWriter(new BsonDocument()), + new BsonBinaryWriter(new BasicOutputBuffer(), false), + new JsonWriter(new StringWriter()) + ] + } - def 'Lookahead should work at various states'(BsonWriter writer) { - given: - writer.with { - writeStartDocument() - writeInt64('int64', 52L) - writeStartArray('array') - writeInt32(1) - writeInt64(2L) - writeStartArray() - writeInt32(3) - writeInt32(4) - writeEndArray() - writeStartDocument() - writeInt32('a', 5) - writeEndDocument() - writeNull() - writeEndArray() - writeStartDocument('document') - writeInt32('a', 6) - writeEndDocument() - writeEndDocument() - } + def 'Lookahead should work at various states'(BsonWriter writer) { + given: + writer.with { + writeStartDocument() + writeInt64('int64', 52L) + writeStartArray('array') + writeInt32(1) + writeInt64(2L) + writeStartArray() + writeInt32(3) + writeInt32(4) + writeEndArray() + writeStartDocument() + writeInt32('a', 5) + writeEndDocument() + writeNull() + writeEndArray() + writeStartDocument('document') + writeInt32('a', 6) + writeEndDocument() + writeEndDocument() + } - when: - BsonReader reader - if (writer instanceof BsonDocumentWriter) { - reader = new BsonDocumentReader(bsonDoc) - } else if (writer instanceof BsonBinaryWriter) { - BasicOutputBuffer buffer = (BasicOutputBuffer)writer.getBsonOutput(); + when: + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(writer.document) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer) writer.getBsonOutput(); reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) - } else { - reader = new JsonReader(stringWriter.toString()) - } + } else if (writer instanceof JsonWriter) { + reader = new JsonReader(writer.writer.toString()) + } then: - reader.readStartDocument() // mark beginning of document * 1 reader.mark() @@ -253,9 +236,9 @@ class LimitedLookAheadMarkSpecification extends Specification { where: writer << [ - new BsonDocumentWriter(bsonDoc), - new BsonBinaryWriter(buffer, false), -// new JsonWriter(stringWriter) + new BsonDocumentWriter(new BsonDocument()), + new BsonBinaryWriter(new BasicOutputBuffer(), false), + new JsonWriter(new StringWriter()) ] } } \ No newline at end of file From 9f1e62b83e0cc92be5254cb9c4f9566579e104d6 Mon Sep 17 00:00:00 2001 From: Jeff Yemin Date: Sat, 4 Oct 2014 08:21:33 -0400 Subject: [PATCH 8/8] Added BsonReader.peekBinarySubType as a more efficient alternative to BsonReader.mark/reset, to avoid a double copy of the byte array. --- .../src/main/org/bson/AbstractBsonReader.java | 12 ++++++ bson/src/main/org/bson/BsonBinaryReader.java | 9 +++++ .../src/main/org/bson/BsonDocumentReader.java | 5 +++ bson/src/main/org/bson/BsonReader.java | 9 ++++- bson/src/main/org/bson/json/JsonReader.java | 5 +++ .../LimitedLookaheadMarkSpecification.groovy | 40 +++++++++++++++++++ .../com/mongodb/codecs/DocumentCodec.java | 7 +--- 7 files changed, 81 insertions(+), 6 deletions(-) diff --git a/bson/src/main/org/bson/AbstractBsonReader.java b/bson/src/main/org/bson/AbstractBsonReader.java index 1c3f9e6baec..89e78da241e 100644 --- a/bson/src/main/org/bson/AbstractBsonReader.java +++ b/bson/src/main/org/bson/AbstractBsonReader.java @@ -112,6 +112,12 @@ protected boolean isClosed() { */ protected abstract BsonBinary doReadBinaryData(); + /** + * Handles the logic to peek at the binary subtype. + * + * @return the binary subtype + */ + protected abstract byte doPeekBinarySubType(); /** * Handles the logic to read booleans @@ -261,6 +267,12 @@ public BsonBinary readBinaryData() { return doReadBinaryData(); } + @Override + public byte peekBinarySubType() { + checkPreconditions("readBinaryData", BsonType.BINARY); + return doPeekBinarySubType(); + } + @Override public boolean readBoolean() { checkPreconditions("readBoolean", BsonType.BOOLEAN); diff --git a/bson/src/main/org/bson/BsonBinaryReader.java b/bson/src/main/org/bson/BsonBinaryReader.java index 95ec89b5de5..0eb72c74f85 100644 --- a/bson/src/main/org/bson/BsonBinaryReader.java +++ b/bson/src/main/org/bson/BsonBinaryReader.java @@ -130,6 +130,15 @@ protected BsonBinary doReadBinaryData() { return new BsonBinary(type, bytes); } + @Override + protected byte doPeekBinarySubType() { + mark(); + bsonInput.readInt32(); + byte type = bsonInput.readByte(); + reset(); + return type; + } + @Override protected boolean doReadBoolean() { return bsonInput.readByte() == 0x1; diff --git a/bson/src/main/org/bson/BsonDocumentReader.java b/bson/src/main/org/bson/BsonDocumentReader.java index 577502171d3..09dc92cff5f 100644 --- a/bson/src/main/org/bson/BsonDocumentReader.java +++ b/bson/src/main/org/bson/BsonDocumentReader.java @@ -36,6 +36,11 @@ protected BsonBinary doReadBinaryData() { return currentValue.asBinary(); } + @Override + protected byte doPeekBinarySubType() { + return currentValue.asBinary().getType(); + } + @Override protected boolean doReadBoolean() { return currentValue.asBoolean().getValue(); diff --git a/bson/src/main/org/bson/BsonReader.java b/bson/src/main/org/bson/BsonReader.java index 423fcf9665b..f16c68f3c10 100644 --- a/bson/src/main/org/bson/BsonReader.java +++ b/bson/src/main/org/bson/BsonReader.java @@ -43,6 +43,14 @@ public interface BsonReader { */ BsonBinary readBinaryData(); + /** + * Peeks the subtype of the binary data that the reader is positioned at. This operation is not permitted if the mark is already set. + * + * @return the subtype + * @see #mark() + */ + byte peekBinarySubType(); + /** * Reads a BSON Binary data element from the reader. * @@ -358,5 +366,4 @@ public interface BsonReader { * @throws org.bson.BSONException if no mark has been set */ void reset(); - } diff --git a/bson/src/main/org/bson/json/JsonReader.java b/bson/src/main/org/bson/json/JsonReader.java index e3a6bce3ee8..18b549dd403 100644 --- a/bson/src/main/org/bson/json/JsonReader.java +++ b/bson/src/main/org/bson/json/JsonReader.java @@ -80,6 +80,11 @@ protected BsonBinary doReadBinaryData() { return (BsonBinary) currentValue; } + @Override + protected byte doPeekBinarySubType() { + return doReadBinaryData().getType(); + } + @Override protected boolean doReadBoolean() { return (Boolean) currentValue; diff --git a/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy index d54b13b61be..7797fd8dd99 100644 --- a/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy +++ b/bson/src/test/org/bson/LimitedLookaheadMarkSpecification.groovy @@ -241,4 +241,44 @@ class LimitedLookaheadMarkSpecification extends Specification { new JsonWriter(new StringWriter()) ] } + + def 'should peek binary subtype'(BsonWriter writer) { + given: + writer.with { + writeStartDocument() + writeBinaryData('binary', new BsonBinary(BsonBinarySubType.UUID_LEGACY, new byte[16])) + writeInt64('int64', 52L) + writeEndDocument() + } + + when: + BsonReader reader + if (writer instanceof BsonDocumentWriter) { + reader = new BsonDocumentReader(writer.document) + } else if (writer instanceof BsonBinaryWriter) { + BasicOutputBuffer buffer = (BasicOutputBuffer) writer.getBsonOutput(); + reader = new BsonBinaryReader(new ByteBufferBsonInput(buffer.getByteBuffers().get(0)), true) + } else if (writer instanceof JsonWriter) { + reader = new JsonReader(writer.writer.toString()) + } + + reader.readStartDocument() + reader.readName() + def subType = reader.peekBinarySubType() + def binary = reader.readBinaryData() + def longValue = reader.readInt64('int64') + reader.readEndDocument() + + then: + subType == BsonBinarySubType.UUID_LEGACY.value + binary == new BsonBinary(BsonBinarySubType.UUID_LEGACY, new byte[16]) + longValue == 52L + + where: + writer << [ + new BsonDocumentWriter(new BsonDocument()), + new BsonBinaryWriter(new BasicOutputBuffer(), false), + new JsonWriter(new StringWriter()) + ] + } } \ No newline at end of file diff --git a/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java b/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java index 4f091a4ea47..ac94fb366a6 100644 --- a/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java +++ b/driver-core/src/main/com/mongodb/codecs/DocumentCodec.java @@ -178,11 +178,8 @@ private Object readValue(final BsonReader reader, final DecoderContext decoderCo reader.readNull(); return null; } else if (bsonType == BsonType.BINARY) { - reader.mark(); - byte bsonSubType = reader.readBinaryData().getType(); - reader.reset(); - if (bsonSubType == BsonBinarySubType.UUID_STANDARD.getValue() - || bsonSubType == BsonBinarySubType.UUID_LEGACY.getValue()) { + byte bsonSubType = reader.peekBinarySubType(); + if (bsonSubType == BsonBinarySubType.UUID_STANDARD.getValue() || bsonSubType == BsonBinarySubType.UUID_LEGACY.getValue()) { return registry.get(UUID.class).decode(reader, decoderContext); } }