From a94c000c400d77ef0b97349dbe0f366b7c10ebd5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Koh=C3=A1nyi=20R=C3=B3bert?=
Date: Sun, 22 Jan 2012 15:27:15 +0100
Subject: [PATCH] Add MongoDB ObjectId and Timestamp types.
- Create simple interfaces for ObjectId and Timestamp.
- Provide simple implementations for both interface.
- Register new reader, writer and predicate objects for them.
- Add simple tests for the implementations.
---
.../kohanyirobert/ebson/BasicObjectId.java | 83 +++++++++++++++++++
.../kohanyirobert/ebson/BasicTimestamp.java | 66 +++++++++++++++
.../kohanyirobert/ebson/BsonObject.java | 6 +-
.../kohanyirobert/ebson/DefaultPredicate.java | 20 +++++
.../kohanyirobert/ebson/DefaultReader.java | 16 ++++
.../kohanyirobert/ebson/DefaultWriter.java | 16 ++++
.../github/kohanyirobert/ebson/ObjectId.java | 17 ++++
.../github/kohanyirobert/ebson/Timestamp.java | 13 +++
.../DefaultObjectIdReaderWriterTest.java | 32 +++++++
.../DefaultTimestampReaderWriterTest.java | 32 +++++++
10 files changed, 299 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/com/github/kohanyirobert/ebson/BasicObjectId.java
create mode 100644 src/main/java/com/github/kohanyirobert/ebson/BasicTimestamp.java
create mode 100644 src/main/java/com/github/kohanyirobert/ebson/ObjectId.java
create mode 100644 src/main/java/com/github/kohanyirobert/ebson/Timestamp.java
create mode 100644 src/test/java/com/github/kohanyirobert/ebson/DefaultObjectIdReaderWriterTest.java
create mode 100644 src/test/java/com/github/kohanyirobert/ebson/DefaultTimestampReaderWriterTest.java
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());
+ }
+}