diff --git a/src/main/java/com/github/kohanyirobert/ebson/BasicObjectId.java b/src/main/java/com/github/kohanyirobert/ebson/BasicObjectId.java new file mode 100644 index 0000000..49b9f57 --- /dev/null +++ b/src/main/java/com/github/kohanyirobert/ebson/BasicObjectId.java @@ -0,0 +1,83 @@ +package com.github.kohanyirobert.ebson; + +import com.google.common.base.Objects; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import javax.xml.bind.DatatypeConverter; + +final class BasicObjectId implements ObjectId { + + private static final int TIME_LENGTH = 4; + private static final int MACHINE_ID_LENGTH = 3; + private static final int PROCESS_ID_LENGTH = 2; + private static final int INCREMENT_LENGTH = 3; + private static final int OBJECT_ID_LENGTH = + TIME_LENGTH + MACHINE_ID_LENGTH + PROCESS_ID_LENGTH + INCREMENT_LENGTH; + + private final ByteBuffer time; + private final ByteBuffer machineId; + private final ByteBuffer processId; + private final ByteBuffer increment; + + private final ByteBuffer objectId = ByteBuffer.allocate(OBJECT_ID_LENGTH).order(ByteOrder.BIG_ENDIAN); + + BasicObjectId(ByteBuffer buffer) { + int oldLimit = buffer.limit(); + + buffer.limit(buffer.position() + OBJECT_ID_LENGTH); + objectId.put(buffer).flip(); + + assert buffer.limit() == oldLimit; + + time = ByteBuffer.wrap(objectId.array(), 0, TIME_LENGTH).order(ByteOrder.BIG_ENDIAN); + machineId = ByteBuffer.wrap(objectId.array(), TIME_LENGTH, MACHINE_ID_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + processId = ByteBuffer.wrap(objectId.array(), MACHINE_ID_LENGTH, PROCESS_ID_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + increment = ByteBuffer.wrap(objectId.array(), PROCESS_ID_LENGTH, INCREMENT_LENGTH).order(ByteOrder.BIG_ENDIAN); + } + + @Override + public ByteBuffer getObjectId() { + return objectId.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getTime() { + return time.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getMachineId() { + return machineId.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getProcessId() { + return processId.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getIncrement() { + return increment.asReadOnlyBuffer(); + } + + @Override + public int hashCode() { + return Objects.hashCode(objectId); + } + + @Override + public boolean equals(Object object) { + if (object instanceof ObjectId) { + ObjectId other = (ObjectId) object; + return objectId.equals(other.getObjectId()); + } + return false; + } + + @Override + public String toString() { + return DatatypeConverter.printHexBinary(objectId.array()).toLowerCase(); + } +} diff --git a/src/main/java/com/github/kohanyirobert/ebson/BasicTimestamp.java b/src/main/java/com/github/kohanyirobert/ebson/BasicTimestamp.java new file mode 100644 index 0000000..3f806fc --- /dev/null +++ b/src/main/java/com/github/kohanyirobert/ebson/BasicTimestamp.java @@ -0,0 +1,66 @@ +package com.github.kohanyirobert.ebson; + +import com.google.common.base.Objects; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import javax.xml.bind.DatatypeConverter; + +final class BasicTimestamp implements Timestamp { + + private static final int TIME_LENGTH = 4; + private static final int INCREMENT_LENGTH = 4; + private static final int TIMESTAMP_LENGTH = TIME_LENGTH + INCREMENT_LENGTH; + + private final ByteBuffer time; + private final ByteBuffer increment; + + private final ByteBuffer timestamp = ByteBuffer.allocate(TIMESTAMP_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + + BasicTimestamp(ByteBuffer buffer) { + int oldLimit = buffer.limit(); + + buffer.limit(buffer.position() + TIMESTAMP_LENGTH); + timestamp.put(buffer).flip(); + + assert buffer.limit() == oldLimit; + + time = ByteBuffer.wrap(timestamp.array(), 0, TIME_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + increment = ByteBuffer.wrap(timestamp.array(), INCREMENT_LENGTH, TIME_LENGTH).order(ByteOrder.LITTLE_ENDIAN); + } + + @Override + public ByteBuffer getTimestamp() { + return timestamp.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getTime() { + return time.asReadOnlyBuffer(); + } + + @Override + public ByteBuffer getIncrement() { + return increment.asReadOnlyBuffer(); + } + + @Override + public int hashCode() { + return Objects.hashCode(timestamp); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Timestamp) { + Timestamp other = (Timestamp) object; + return timestamp.equals(other.getTimestamp()); + } + return false; + } + + @Override + public String toString() { + return DatatypeConverter.printHexBinary(timestamp.array()).toLowerCase(); + } +} diff --git a/src/main/java/com/github/kohanyirobert/ebson/BsonObject.java b/src/main/java/com/github/kohanyirobert/ebson/BsonObject.java index a95f124..02c757d 100644 --- a/src/main/java/com/github/kohanyirobert/ebson/BsonObject.java +++ b/src/main/java/com/github/kohanyirobert/ebson/BsonObject.java @@ -63,7 +63,8 @@ public enum BsonObject { * Note: special MongoDB related type. *

*/ - OBJECT_ID(BsonBytes.OBJECT_ID), + OBJECT_ID(BsonBytes.OBJECT_ID, DefaultPredicate.OBJECT_ID, + DefaultReader.OBJECT_ID, DefaultWriter.OBJECT_ID), /** * Boolean. @@ -138,7 +139,8 @@ public enum BsonObject { * sharding. *

*/ - TIMESTAMP(BsonBytes.TIMESTAMP), + TIMESTAMP(BsonBytes.TIMESTAMP, DefaultPredicate.TIMESTAMP, + DefaultReader.TIMESTAMP, DefaultWriter.TIMESTAMP), /** * 64-bit signed integer. diff --git a/src/main/java/com/github/kohanyirobert/ebson/DefaultPredicate.java b/src/main/java/com/github/kohanyirobert/ebson/DefaultPredicate.java index 1b9189f..daf2a27 100644 --- a/src/main/java/com/github/kohanyirobert/ebson/DefaultPredicate.java +++ b/src/main/java/com/github/kohanyirobert/ebson/DefaultPredicate.java @@ -69,6 +69,16 @@ public boolean apply(Class input) { } }, + OBJECT_ID { + + @Override + public boolean apply(Class input) { + return input == null + ? false + : ObjectId.class.isAssignableFrom(input); + } + }, + BOOLEAN { @Override @@ -117,6 +127,16 @@ public boolean apply(Class input) { } }, + TIMESTAMP { + + @Override + public boolean apply(Class input) { + return input == null + ? false + : Timestamp.class.isAssignableFrom(input); + } + }, + INT64 { @Override diff --git a/src/main/java/com/github/kohanyirobert/ebson/DefaultReader.java b/src/main/java/com/github/kohanyirobert/ebson/DefaultReader.java index 7f0c7f4..5a8ab13 100644 --- a/src/main/java/com/github/kohanyirobert/ebson/DefaultReader.java +++ b/src/main/java/com/github/kohanyirobert/ebson/DefaultReader.java @@ -108,6 +108,14 @@ public Object checkedReadFrom(ByteBuffer buffer) { } }, + OBJECT_ID { + + @Override + Object checkedReadFrom(ByteBuffer buffer) { + return new BasicObjectId(buffer); + } + }, + BOOLEAN { @Override @@ -180,6 +188,14 @@ public Object checkedReadFrom(ByteBuffer buffer) { } }, + TIMESTAMP { + + @Override + Object checkedReadFrom(ByteBuffer buffer) { + return new BasicTimestamp(buffer); + } + }, + INT64 { @Override diff --git a/src/main/java/com/github/kohanyirobert/ebson/DefaultWriter.java b/src/main/java/com/github/kohanyirobert/ebson/DefaultWriter.java index 5be2daa..52a8552 100644 --- a/src/main/java/com/github/kohanyirobert/ebson/DefaultWriter.java +++ b/src/main/java/com/github/kohanyirobert/ebson/DefaultWriter.java @@ -104,6 +104,14 @@ public void writeTo(ByteBuffer buffer, Object reference) { } }, + OBJECT_ID { + + @Override + public void writeTo(ByteBuffer buffer, Object reference) { + buffer.put(((ObjectId) reference).getObjectId()); + } + }, + BOOLEAN { @Override @@ -169,6 +177,14 @@ public void writeTo(ByteBuffer buffer, Object reference) { } }, + TIMESTAMP { + + @Override + public void writeTo(ByteBuffer buffer, Object reference) { + buffer.put(((Timestamp) reference).getTimestamp()); + } + }, + INT64 { @Override diff --git a/src/main/java/com/github/kohanyirobert/ebson/ObjectId.java b/src/main/java/com/github/kohanyirobert/ebson/ObjectId.java new file mode 100644 index 0000000..62c2dc7 --- /dev/null +++ b/src/main/java/com/github/kohanyirobert/ebson/ObjectId.java @@ -0,0 +1,17 @@ +package com.github.kohanyirobert.ebson; + +import java.nio.ByteBuffer; + +// @checkstyle:off . +public interface ObjectId { + + ByteBuffer getObjectId(); + + ByteBuffer getTime(); + + ByteBuffer getMachineId(); + + ByteBuffer getProcessId(); + + ByteBuffer getIncrement(); +} diff --git a/src/main/java/com/github/kohanyirobert/ebson/Timestamp.java b/src/main/java/com/github/kohanyirobert/ebson/Timestamp.java new file mode 100644 index 0000000..245b9e1 --- /dev/null +++ b/src/main/java/com/github/kohanyirobert/ebson/Timestamp.java @@ -0,0 +1,13 @@ +package com.github.kohanyirobert.ebson; + +import java.nio.ByteBuffer; + +// @checkstyle:off . +public interface Timestamp { + + ByteBuffer getTimestamp(); + + ByteBuffer getTime(); + + ByteBuffer getIncrement(); +} diff --git a/src/test/java/com/github/kohanyirobert/ebson/DefaultObjectIdReaderWriterTest.java b/src/test/java/com/github/kohanyirobert/ebson/DefaultObjectIdReaderWriterTest.java new file mode 100644 index 0000000..152b18d --- /dev/null +++ b/src/test/java/com/github/kohanyirobert/ebson/DefaultObjectIdReaderWriterTest.java @@ -0,0 +1,32 @@ +package com.github.kohanyirobert.ebson; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Random; + +public final class DefaultObjectIdReaderWriterTest extends AbstractReaderWriterTest { + + private static final ThreadLocal RANDOM_OBJECT_ID = new ThreadLocal() { + + @Override + protected ByteBuffer initialValue() { + // @checkstyle:off MagicNumber + byte[] bytes = new byte[12]; + new Random().nextBytes(bytes); + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + } + }; + + public DefaultObjectIdReaderWriterTest() { + super(DefaultReader.OBJECT_ID, DefaultWriter.OBJECT_ID); + } + + @Test + public void randomObjectId() { + assertEquals(writeTo(new BasicObjectId(RANDOM_OBJECT_ID.get())), readFrom()); + } +} diff --git a/src/test/java/com/github/kohanyirobert/ebson/DefaultTimestampReaderWriterTest.java b/src/test/java/com/github/kohanyirobert/ebson/DefaultTimestampReaderWriterTest.java new file mode 100644 index 0000000..9a529a2 --- /dev/null +++ b/src/test/java/com/github/kohanyirobert/ebson/DefaultTimestampReaderWriterTest.java @@ -0,0 +1,32 @@ +package com.github.kohanyirobert.ebson; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Random; + +public final class DefaultTimestampReaderWriterTest extends AbstractReaderWriterTest { + + private static final ThreadLocal RANDOM_TIMESTAMP = new ThreadLocal() { + + @Override + protected ByteBuffer initialValue() { + // @checkstyle:off MagicNumber + byte[] bytes = new byte[8]; + new Random().nextBytes(bytes); + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + } + }; + + public DefaultTimestampReaderWriterTest() { + super(DefaultReader.TIMESTAMP, DefaultWriter.TIMESTAMP); + } + + @Test + public void randomTimestamp() { + assertEquals(writeTo(new BasicTimestamp(RANDOM_TIMESTAMP.get())), readFrom()); + } +}