├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── github │ └── kohanyirobert │ └── ebson │ ├── BasicObjectId.java │ ├── BasicSymbol.java │ ├── BasicTimestamp.java │ ├── BsonBinary.java │ ├── BsonBytes.java │ ├── BsonDocument.java │ ├── BsonDocuments.java │ ├── BsonObject.java │ ├── BsonObjectId.java │ ├── BsonReader.java │ ├── BsonSymbol.java │ ├── BsonTimestamp.java │ ├── BsonToken.java │ ├── BsonWriter.java │ ├── DefaultDocument.java │ ├── DefaultDocumentBuilder.java │ ├── DefaultPredicate.java │ ├── DefaultReader.java │ ├── DefaultWriter.java │ └── package-info.java └── test └── java └── com └── github └── kohanyirobert └── ebson ├── AbstractBsonTest.java ├── AbstractReaderWriterTest.java ├── BsonDocumentTest.java ├── BsonDocumentsTest.java ├── BsonEncodingTest.java ├── BsonRandom.java ├── DefaultArrayReaderWriterTest.java ├── DefaultBinaryReaderWriterTest.java ├── DefaultBooleanReaderWriterTest.java ├── DefaultDoubleReaderWriterTest.java ├── DefaultFieldReaderWriterTest.java ├── DefaultInt32ReaderWriterTest.java ├── DefaultInt64ReaderWriterTest.java ├── DefaultKeyReaderWriterTest.java ├── DefaultNullReaderWriterTest.java ├── DefaultObjectIdReaderWriterTest.java ├── DefaultRegularExpressionReaderWriterTest.java ├── DefaultSymbolReaderWriterTest.java ├── DefaultTimestampReaderWriterTest.java └── DefaultUtcDateTimeReaderWriterTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011-Ad Infinitum by Kohányi Róbert 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ebson 2 | *ebson* is an extensible [BSON][] encoder/decoder library written in Java. The 3 | library is extensible in the sense that the mappings between Java and BSON types 4 | are configurable and the logic to serialize custom Java types is pluggable. Its 5 | single dependency is the [Guava libraries][] by Google. 6 | 7 | ## License 8 | Released under the permissive [MIT License][]. 9 | 10 | ## Author 11 | [Kohányi Róbert][]. 12 | 13 | ## Download 14 | Add the library as a dependency in your project's *pom.xml* like this. 15 | 16 | ```xml 17 | 18 | com.github.kohanyirobert 19 | ebson 20 | ... 21 | 22 | ``` 23 | 24 | Releases and snapshots are deployed to [Sonatype's][] [OSS repository][] (and 25 | synced to the [Central Maven Repository][] from there). To download JARs from 26 | Sonatype's repository include the following repository tag inside your Maven 27 | installation's *settings.xml* or your project's *pom.xml*. 28 | 29 | ```xml 30 | 31 | sonatype-oss 32 | https://oss.sonatype.org/content/groups/public 33 | 34 | ``` 35 | 36 | ## Build 37 | As the project is managed with [Maven][] you simply clone it and issue *mvn 38 | install* or *mvn package* inside the clone's directory. 39 | 40 | ``` 41 | git clone git://github.com/kohanyirobert/ebson.git 42 | cd ebson/ 43 | mvn package 44 | # and/or 45 | mvn install 46 | ``` 47 | 48 | ## Usage 49 | ### Serialization 50 | ```java 51 | // create documents to serialize 52 | BsonDocument document = BsonDocuments.of("key", new Date()); 53 | 54 | // grab a little-endian byte buffer 55 | ByteBuffer buffer = ByteBuffer.allocate(32).order(ByteOrder.LITTLE_ENDIAN); 56 | 57 | // use the documents utility class to write the document into the buffer 58 | BsonDocuments.writeTo(buffer, document); 59 | 60 | // use the serialized data 61 | buffer.flip(); 62 | ``` 63 | 64 | ### Deserialization 65 | ```java 66 | // given the previous buffer 67 | BsonDocument newDocument = BsonDocuments.readFrom(buffer); 68 | 69 | // prints true 70 | System.out.println(document.equals(newDocument)); 71 | ``` 72 | 73 | ### Extensibility 74 | ```java 75 | // to use joda-time's date-time instead of java's date supply 76 | // a predicate (to test whether an input class is compatible with 77 | // date-time or not) for the appropriate bson type 78 | BsonObject.UTC_DATE_TIME.predicate(new Predicate>() { 79 | @Override public boolean apply(Class input) { 80 | return input == null ? false : DateTime.class.isAssignableFrom(input); 81 | } 82 | }); 83 | 84 | // register a writer with the same bson type which is 85 | // able to serialize date-times into byte buffers 86 | BsonObject.UTC_DATE_TIME.writer(new BsonWriter() { 87 | @Override public void writeTo(ByteBuffer buffer, Object reference) { 88 | buffer.putLong(((DateTime) reference).getMillis()); 89 | } 90 | }); 91 | 92 | // finally register a reader to do all this ass backwards 93 | BsonObject.UTC_DATE_TIME.reader(new BsonReader() { 94 | @Override public Object readFrom(ByteBuffer buffer) { 95 | return new DateTime(buffer.getLong()); 96 | } 97 | }); 98 | ``` 99 | 100 | [BSON]: http://bsonspec.org 101 | [Guava libraries]: http://code.google.com/p/guava-libraries 102 | [Kohányi Róbert]: http://kohanyirobert.github.com 103 | [MIT License]: https://raw.github.com/kohanyirobert/ebson/master/LICENSE.txt 104 | [Sonatype's]: http://sonatype.com 105 | [OSS repository]: https://oss.sonatype.org 106 | [Central Maven Repository]: http://search.maven.org 107 | [Maven]: http://maven.apache.org 108 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.kohanyirobert 7 | ebson 8 | 0.4-SNAPSHOT 9 | 10 | ebson 11 | 12 | Extensible BSON encoder/decoder library written 13 | in Java with pluggable Java-to-BSON type mappings. 14 | 15 | https://github.com/kohanyirobert/ebson 16 | 17 | 18 | 19 | MIT License 20 | LICENSE.txt 21 | repo 22 | 23 | 24 | 25 | 26 | scm:git:git@github.com:kohanyirobert/ebson.git 27 | scm:git:git@github.com:kohanyirobert/ebson.git 28 | git@github.com:kohanyirobert/ebson.git 29 | 30 | 31 | 32 | 33 | sonatype-nexus-staging 34 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 35 | 36 | 37 | sonatype-nexus-snapshots 38 | https://oss.sonatype.org/content/repositories/snapshots/ 39 | 40 | 41 | 42 | 43 | 44 | kohanyi.robert 45 | Kohányi Róbert 46 | kohanyi.robert@gmail.com 47 | 48 | owner 49 | developer 50 | 51 | +1 52 | 53 | 54 | 55 | 56 | UTF-8 57 | ${project.build.sourceEncoding} 58 | 59 | 60 | 61 | 62 | com.google.guava 63 | guava 64 | 13.0.1 65 | 66 | 67 | com.google.code.findbugs 68 | jsr305 69 | 2.0.1 70 | 71 | 72 | junit 73 | junit 74 | 4.10 75 | test 76 | 77 | 78 | org.mongodb 79 | mongo-java-driver 80 | 2.9.1 81 | test 82 | 83 | 84 | 85 | 86 | 87 | 88 | ${project.basedir} 89 | ${project.build.outputDirectory}/META-INF 90 | false 91 | 92 | LICENSE.txt 93 | README.md 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-compiler-plugin 102 | 2.5.1 103 | 104 | 1.7 105 | 1.7 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-source-plugin 111 | 2.2 112 | 113 | 114 | attach-sources 115 | 116 | jar 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-javadoc-plugin 124 | 2.9 125 | 126 | 127 | attach-javadocs 128 | 129 | jar 130 | 131 | 132 | 133 | 134 | 135 | http://docs.guava-libraries.googlecode.com/git/javadoc 136 | 137 | 138 | 139 | 140 | org.apache.maven.plugins 141 | maven-checkstyle-plugin 142 | 2.9.1 143 | 144 | 145 | checkstyle-main 146 | verify 147 | 148 | check 149 | 150 | 151 | https://raw.github.com/kohanyirobert/checkstyle/master/main.xml 152 | 153 | 154 | 155 | checkstyle-test 156 | verify 157 | 158 | check 159 | 160 | 161 | https://raw.github.com/kohanyirobert/checkstyle/master/test.xml 162 | true 163 | 164 | 165 | 166 | 167 | true 168 | true 169 | false 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-gpg-plugin 175 | 1.4 176 | 177 | 178 | sign-artifacts 179 | verify 180 | 181 | sign 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BasicObjectId.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Objects; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | import javax.xml.bind.DatatypeConverter; 9 | 10 | final class BasicObjectId implements BsonObjectId { 11 | 12 | private static final int TIME_LENGTH = 4; 13 | private static final int MACHINE_ID_LENGTH = 3; 14 | private static final int PROCESS_ID_LENGTH = 2; 15 | private static final int INCREMENT_LENGTH = 3; 16 | private static final int OBJECT_ID_LENGTH = 17 | TIME_LENGTH + MACHINE_ID_LENGTH + PROCESS_ID_LENGTH + INCREMENT_LENGTH; 18 | 19 | private final ByteBuffer time; 20 | private final ByteBuffer machineId; 21 | private final ByteBuffer processId; 22 | private final ByteBuffer increment; 23 | 24 | private final ByteBuffer objectId = ByteBuffer.allocate(OBJECT_ID_LENGTH).order(ByteOrder.BIG_ENDIAN); 25 | 26 | BasicObjectId(ByteBuffer buffer) { 27 | int oldPosition = buffer.position(); 28 | 29 | int oldLimit = buffer.limit(); 30 | buffer.limit(buffer.position() + OBJECT_ID_LENGTH); 31 | objectId.put(buffer).flip(); 32 | buffer.limit(oldLimit); 33 | 34 | assert buffer.position() == oldPosition + OBJECT_ID_LENGTH; 35 | 36 | time = ByteBuffer.wrap(objectId.array(), 0, TIME_LENGTH).order(ByteOrder.BIG_ENDIAN); 37 | machineId = ByteBuffer.wrap(objectId.array(), TIME_LENGTH, MACHINE_ID_LENGTH).order(ByteOrder.LITTLE_ENDIAN); 38 | processId = ByteBuffer.wrap(objectId.array(), MACHINE_ID_LENGTH, PROCESS_ID_LENGTH).order(ByteOrder.LITTLE_ENDIAN); 39 | increment = ByteBuffer.wrap(objectId.array(), PROCESS_ID_LENGTH, INCREMENT_LENGTH).order(ByteOrder.BIG_ENDIAN); 40 | } 41 | 42 | @Override 43 | public ByteBuffer objectId() { 44 | return objectId.asReadOnlyBuffer(); 45 | } 46 | 47 | @Override 48 | public ByteBuffer time() { 49 | return time.asReadOnlyBuffer(); 50 | } 51 | 52 | @Override 53 | public ByteBuffer machineId() { 54 | return machineId.asReadOnlyBuffer(); 55 | } 56 | 57 | @Override 58 | public ByteBuffer processId() { 59 | return processId.asReadOnlyBuffer(); 60 | } 61 | 62 | @Override 63 | public ByteBuffer increment() { 64 | return increment.asReadOnlyBuffer(); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Objects.hashCode(objectId); 70 | } 71 | 72 | @Override 73 | public boolean equals(Object object) { 74 | if (object instanceof BsonObjectId) { 75 | BsonObjectId other = (BsonObjectId) object; 76 | return objectId.equals(other.objectId()); 77 | } 78 | return false; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return DatatypeConverter.printHexBinary(objectId.array()).toLowerCase(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BasicSymbol.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Objects; 4 | import com.google.common.primitives.Ints; 5 | 6 | import java.nio.ByteBuffer; 7 | 8 | import javax.xml.bind.DatatypeConverter; 9 | 10 | final class BasicSymbol implements BsonSymbol { 11 | 12 | private final ByteBuffer symbol; 13 | 14 | BasicSymbol(ByteBuffer buffer) { 15 | int oldPosition = buffer.position(); 16 | 17 | int symbolLength = buffer.getInt(); 18 | symbol = ByteBuffer.allocate(symbolLength - 1); 19 | 20 | int oldLimit = buffer.limit(); 21 | buffer.limit(buffer.position() + symbolLength - 1); 22 | symbol.put(buffer).flip(); 23 | buffer.limit(buffer.limit() + 1); 24 | buffer.get(); 25 | buffer.limit(oldLimit); 26 | 27 | assert buffer.position() == Ints.BYTES + oldPosition + symbolLength; 28 | } 29 | 30 | @Override 31 | public ByteBuffer symbol() { 32 | return symbol.asReadOnlyBuffer(); 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return Objects.hashCode(symbol); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object object) { 42 | if (object instanceof BasicSymbol) { 43 | BasicSymbol other = (BasicSymbol) object; 44 | return symbol.equals(other.symbol()); 45 | } 46 | return false; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return DatatypeConverter.printHexBinary(symbol.array()).toLowerCase(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BasicTimestamp.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Objects; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.ByteOrder; 7 | 8 | import javax.xml.bind.DatatypeConverter; 9 | 10 | final class BasicTimestamp implements BsonTimestamp { 11 | 12 | private static final int TIME_LENGTH = 4; 13 | private static final int INCREMENT_LENGTH = 4; 14 | private static final int TIMESTAMP_LENGTH = TIME_LENGTH + INCREMENT_LENGTH; 15 | 16 | private final ByteBuffer time; 17 | private final ByteBuffer increment; 18 | 19 | private final ByteBuffer timestamp = ByteBuffer.allocate(TIMESTAMP_LENGTH).order(ByteOrder.LITTLE_ENDIAN); 20 | 21 | BasicTimestamp(ByteBuffer buffer) { 22 | int oldPosition = buffer.position(); 23 | 24 | int oldLimit = buffer.limit(); 25 | buffer.limit(buffer.position() + TIMESTAMP_LENGTH); 26 | timestamp.put(buffer).flip(); 27 | buffer.limit(oldLimit); 28 | 29 | assert buffer.position() == oldPosition + TIMESTAMP_LENGTH; 30 | 31 | time = ByteBuffer.wrap(timestamp.array(), 0, TIME_LENGTH).order(ByteOrder.LITTLE_ENDIAN); 32 | increment = ByteBuffer.wrap(timestamp.array(), INCREMENT_LENGTH, TIME_LENGTH).order(ByteOrder.LITTLE_ENDIAN); 33 | } 34 | 35 | @Override 36 | public ByteBuffer timestamp() { 37 | return timestamp.asReadOnlyBuffer(); 38 | } 39 | 40 | @Override 41 | public ByteBuffer time() { 42 | return time.asReadOnlyBuffer(); 43 | } 44 | 45 | @Override 46 | public ByteBuffer increment() { 47 | return increment.asReadOnlyBuffer(); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hashCode(timestamp); 53 | } 54 | 55 | @Override 56 | public boolean equals(Object object) { 57 | if (object instanceof BsonTimestamp) { 58 | BsonTimestamp other = (BsonTimestamp) object; 59 | return timestamp.equals(other.timestamp()); 60 | } 61 | return false; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return DatatypeConverter.printHexBinary(timestamp.array()).toLowerCase(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonBinary.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.base.Predicates; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | /** 10 | * Representation of a BSON binary 11 | * (sub-)type. 12 | */ 13 | public enum BsonBinary { 14 | 15 | /** 16 | * Generic binary. 17 | *

18 | * Note: the most commonly used binary sub-type and should be the 19 | * 'default' for drivers and tools. 20 | *

21 | */ 22 | GENERIC(BsonBytes.GENERIC, DefaultPredicate.GENERIC, DefaultReader.GENERIC, 23 | DefaultWriter.GENERIC), 24 | 25 | /** 26 | * Function binary. 27 | */ 28 | FUNCTION(BsonBytes.FUNCTION), 29 | 30 | /** 31 | * Old binary. 32 | *

33 | * Note: this used to be the default subtype, but was deprecated in 34 | * favor of the generic binary type. 35 | *

36 | */ 37 | OLD(BsonBytes.OLD), 38 | 39 | /** 40 | * UUID binary. 41 | */ 42 | UUID(BsonBytes.UUID), 43 | 44 | /** 45 | * MD5 binary. 46 | */ 47 | MD5(BsonBytes.MD5), 48 | 49 | /** 50 | * User-defined binary. 51 | */ 52 | USER(BsonBytes.USER); 53 | 54 | private final byte terminal; 55 | 56 | private Predicate> predicate; 57 | private BsonReader reader; 58 | private BsonWriter writer; 59 | 60 | private BsonBinary(byte terminal) { 61 | this(terminal, Predicates.>alwaysFalse(), null, null); 62 | } 63 | 64 | private BsonBinary(byte terminal, Predicate> predicate, 65 | BsonReader reader, BsonWriter writer) { 66 | this.terminal = terminal; 67 | this.predicate = predicate; 68 | this.reader = reader; 69 | this.writer = writer; 70 | } 71 | 72 | /** 73 | * Returns this binary's associated terminal. 74 | * 75 | * @return this binary's associated terminal 76 | */ 77 | public byte terminal() { 78 | return terminal; 79 | } 80 | 81 | /** 82 | * Returns this binary's associated {@linkplain Predicate predicate}. 83 | * 84 | * @return this binary's associated predicate 85 | * @throws IllegalStateException if this binary does not have an associated 86 | * predicate 87 | */ 88 | public Predicate> predicate() { 89 | Preconditions.checkState(predicate != null, "'%s' does not have an associated predicate", this); 90 | return predicate; 91 | } 92 | 93 | /** 94 | * Associates {@link Predicate predicate} with this binary. 95 | * 96 | * @param predicate the predicate to be associated with this binary 97 | */ 98 | public void predicate(Predicate> predicate) { 99 | Preconditions.checkNotNull(predicate, "cannot associate a null predicate with '%s'", this); 100 | this.predicate = predicate; 101 | } 102 | 103 | /** 104 | * Returns this binary's associated {@linkplain BsonReader reader}. 105 | * 106 | * @return this binary's associated reader 107 | * @throws IllegalStateException if this binary does not have an associated 108 | * reader 109 | */ 110 | public BsonReader reader() { 111 | Preconditions.checkState(reader != null, "'%s' does not have an associated reader", this); 112 | return reader; 113 | } 114 | 115 | /** 116 | * Associates {@link BsonReader reader} with this binary. 117 | * 118 | * @param reader the reader to be associated with this binary 119 | */ 120 | public void reader(BsonReader reader) { 121 | Preconditions.checkNotNull(reader, "cannot associate a null reader with '%s'", this); 122 | this.reader = reader; 123 | } 124 | 125 | /** 126 | * Returns this binary's associated {@linkplain BsonWriter writer}. 127 | * 128 | * @return this binary's associated writer 129 | * @throws IllegalStateException if this binary does not have an associated 130 | * writer 131 | */ 132 | public BsonWriter writer() { 133 | Preconditions.checkState(writer != null, "'%s' does not have an associated writer", this); 134 | return writer; 135 | } 136 | 137 | /** 138 | * Associates {@link BsonWriter writer} with this binary. 139 | * 140 | * @param writer the writer to be associated with this binary 141 | */ 142 | public void writer(BsonWriter writer) { 143 | Preconditions.checkNotNull(writer, "cannot associate a null writer with '%s'", this); 144 | this.writer = writer; 145 | } 146 | 147 | /** 148 | * Returns the binary representing {@code clazz}. 149 | * 150 | * @param clazz the class to return a binary representation for 151 | * @return the binary representing {@code clazz} 152 | * @throws IllegalArgumentException if no binary representing {@code clazz} 153 | * was found 154 | */ 155 | public static BsonBinary find(@Nullable Class clazz) { 156 | for (BsonBinary binary : values()) { 157 | if (binary.predicate().apply(clazz)) { 158 | return binary; 159 | } 160 | } 161 | throw new IllegalArgumentException(String.format("no binary " 162 | + "representing the '%s' type value was found", clazz)); 163 | } 164 | 165 | /** 166 | * Returns the binary representing {@code terminal}. 167 | * 168 | * @param terminal the terminal to return a binary representation for 169 | * @return the binary representing {@code terminal} 170 | * @throws IllegalArgumentException if no binary representing {@code terminal} 171 | * was found 172 | */ 173 | public static BsonBinary find(byte terminal) { 174 | for (BsonBinary binary : values()) { 175 | if (binary.terminal() - terminal == 0) { 176 | return binary; 177 | } 178 | } 179 | throw new IllegalArgumentException(String.format("no binary representing " 180 | + "the '%s' terminal value was found", Byte.valueOf(terminal))); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonBytes.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | /** 4 | * Frequently used byte values. 5 | */ 6 | public final class BsonBytes { 7 | 8 | /** 9 | * EOF (-1). 10 | */ 11 | public static final byte EOF = -1; 12 | 13 | /** 14 | * EOO (0). 15 | */ 16 | public static final byte EOO = 0; 17 | 18 | /** 19 | * DOUBLE (1). 20 | */ 21 | public static final byte DOUBLE = 1; 22 | 23 | /** 24 | * STRING (2). 25 | */ 26 | public static final byte STRING = 2; 27 | 28 | /** 29 | * EMBEDDED (3). 30 | */ 31 | public static final byte EMBEDDED = 3; 32 | 33 | /** 34 | * ARRAY (4). 35 | */ 36 | public static final byte ARRAY = 4; 37 | 38 | /** 39 | * BINARY (5). 40 | */ 41 | public static final byte BINARY = 5; 42 | 43 | /** 44 | * GENERIC (0). 45 | */ 46 | public static final byte GENERIC = 0; 47 | 48 | /** 49 | * FUNCTION (1). 50 | */ 51 | public static final byte FUNCTION = 1; 52 | 53 | /** 54 | * OLD (2). 55 | */ 56 | public static final byte OLD = 2; 57 | 58 | /** 59 | * UUID (3). 60 | */ 61 | public static final byte UUID = 3; 62 | 63 | /** 64 | * MD5 (5). 65 | */ 66 | public static final byte MD5 = 5; 67 | 68 | /** 69 | * USER (128). 70 | */ 71 | public static final byte USER = (byte) 128; 72 | 73 | /** 74 | * UNDEFINED (6). 75 | * 76 | * @deprecated See the BSON specification 77 | * for details. 78 | */ 79 | @Deprecated 80 | public static final byte UNDEFINED = 6; 81 | 82 | /** 83 | * OBJECT_ID (7). 84 | */ 85 | public static final byte OBJECT_ID = 7; 86 | 87 | /** 88 | * BOOLEAN (8). 89 | */ 90 | public static final byte BOOLEAN = 8; 91 | 92 | /** 93 | * FALSE (0). 94 | */ 95 | public static final byte FALSE = 0; 96 | 97 | /** 98 | * TRUE (1). 99 | */ 100 | public static final byte TRUE = 1; 101 | 102 | /** 103 | * UTC_DATE_TIME (9). 104 | */ 105 | public static final byte UTC_DATE_TIME = 9; 106 | 107 | /** 108 | * NULL (10). 109 | */ 110 | public static final byte NULL = 10; 111 | 112 | /** 113 | * REGULAR_EXPRESSION (11). 114 | */ 115 | public static final byte REGULAR_EXPRESSION = 11; 116 | 117 | /** 118 | * DB_POINTER (12). 119 | * 120 | * @deprecated See the following 122 | * link for more details. 123 | */ 124 | @Deprecated 125 | public static final byte DB_POINTER = 12; 126 | 127 | /** 128 | * JAVASCRIPT_CODE (13). 129 | */ 130 | public static final byte JAVASCRIPT_CODE = 13; 131 | 132 | /** 133 | * SYMBOL (14). 134 | */ 135 | public static final byte SYMBOL = 14; 136 | 137 | /** 138 | * JAVASCRIPT_CODE_WITH_SCOPE (15). 139 | */ 140 | public static final byte JAVASCRIPT_CODE_WITH_SCOPE = 15; 141 | 142 | /** 143 | * INT32 (16). 144 | */ 145 | public static final byte INT32 = 16; 146 | 147 | /** 148 | * TIMESTAMP (17). 149 | */ 150 | public static final byte TIMESTAMP = 17; 151 | 152 | /** 153 | * INT64 (18). 154 | */ 155 | public static final byte INT64 = 18; 156 | 157 | /** 158 | * MAX_KEY (127). 159 | */ 160 | public static final byte MAX_KEY = 127; 161 | 162 | /** 163 | * MIN_KEY (255). 164 | */ 165 | public static final byte MIN_KEY = (byte) 255; 166 | 167 | private BsonBytes() {} 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonDocument.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | import javax.annotation.CheckForNull; 8 | import javax.annotation.Nullable; 9 | 10 | /** 11 | * Immutable representation of a BSON 12 | * document. 13 | *

14 | * Notes: 15 | *

    16 | *
  • None of the {@linkplain Map map interface's} optional operations are 17 | * supported.
  • 18 | *
  • Use the {@linkplain BsonDocuments documents utility class} to create new 19 | * documents.
  • 20 | *
21 | *

22 | */ 23 | public interface BsonDocument extends Map { 24 | 25 | /** 26 | * Returns this document's size (the number of its keys). 27 | * 28 | * @return this document's size 29 | */ 30 | @Override 31 | int size(); 32 | 33 | /** 34 | * Returns true if this document contains no key-value pairs; 35 | * false otherwise. 36 | * 37 | * @return true if this document contains no key-value pairs; 38 | * false otherwise 39 | */ 40 | @Override 41 | boolean isEmpty(); 42 | 43 | /** 44 | * Returns an immutable view of this document's keys. 45 | * 46 | * @return an immutable view of this document's keys. 47 | */ 48 | @Override 49 | Set keySet(); 50 | 51 | /** 52 | * Returns an immutable view of this document's values. 53 | * 54 | * @return an immutable view of this document's values 55 | */ 56 | @Override 57 | Collection values(); 58 | 59 | /** 60 | * Returns an immutable view of this document's key-value pairs. 61 | * 62 | * @return an immutable view of this document's key-value pairs 63 | */ 64 | @Override 65 | Set> entrySet(); 66 | 67 | /** 68 | * Returns the value associated with {@code key} in a type-safe manner. 69 | *

70 | * Note: {@code type} can and should be null only iff the 71 | * value associated with {@code key} is expected to be null. 72 | *

73 | * 74 | * @param key the key whose associated value is to be returned 75 | * @param type the expected type of the value associated with {@code key} 76 | * @param the expected type of the value associated with {@code key} 77 | * @return the value associated with {@code key} in a type-safe manner 78 | * @throws NullPointerException if {@code key} is null 79 | * @throws IllegalArgumentException if this document does not contain 80 | * {@code key} or if the value associated with it is not assignment-compatible 81 | * with {@code type} 82 | * @throws ClassCastException if {@code key} is not a string 83 | */ 84 | @Nullable 85 | T get(Object key, @Nullable Class type); 86 | 87 | /** 88 | * Returns the value associated with {@code key}. 89 | * 90 | * @param key the key whose associated value is to be returned 91 | * @return the value associated with {@code key} 92 | * @throws NullPointerException if {@code key} is null 93 | * @throws IllegalArgumentException if this document does not contain 94 | * {@code key} 95 | * @throws ClassCastException if {@code key} is not a string 96 | */ 97 | @Override 98 | @CheckForNull 99 | Object get(Object key); 100 | 101 | /** 102 | * Returns true if {@code key} is contained by this document; 103 | * false otherwise. 104 | * 105 | * @param key the key to be tested if it is contained by this document 106 | * @return true if {@code key} is contained by this document; 107 | * false otherwise 108 | * @throws NullPointerException if {@code key} is null 109 | * @throws ClassCastException if {@code key} is not a string 110 | */ 111 | @Override 112 | boolean containsKey(Object key); 113 | 114 | /** 115 | * Returns true if {@code value} is contained by this document; 116 | * false otherwise. 117 | * 118 | * @param value the value to be tested if it is contained by this document 119 | * @return true if {@code value} is contained by this document; 120 | * false otherwise 121 | */ 122 | @Override 123 | boolean containsValue(@Nullable Object value); 124 | 125 | /** 126 | * Returns this document's hash code (calculated using its 127 | * {@linkplain #entrySet() key-value pairs}). 128 | * 129 | * @return this document's hash code 130 | */ 131 | @Override 132 | int hashCode(); 133 | 134 | /** 135 | * Returns true if {@code object} is the same as this document; 136 | * false otherwise. 137 | *

138 | * {@code object} is the same as this document iff: 139 | *

    140 | *
  • {@code this == object} or
  • 141 | *
  • {@code object instanceof Map} and
  • 142 | *
  • {@code this.entrySet().equals(object.entrySet())}.
  • 143 | *
144 | *

145 | * 146 | * @param object the reference object with which to compare 147 | * @return true if {@code object} is the same as this document; 148 | * false otherwise 149 | */ 150 | @Override 151 | boolean equals(@CheckForNull Object object); 152 | 153 | /** 154 | * Returns this document's textual representation. 155 | *

156 | * The general format is the following: 157 | *

    158 | *
  • Empty: {}
  • 159 | *
  • Single key-value pair: {key: value}
  • 160 | *
  • Multiple key-value pair: {key1: value1, key2: value2, ...} 161 | *
  • 162 | *
  • Embedded: {key: {key: value}}
  • 163 | *
  • ...
  • 164 | *
165 | *

166 | * 167 | * @return this document's textual representation 168 | */ 169 | @Override 170 | String toString(); 171 | 172 | /** 173 | * Not supported. 174 | * 175 | * @param key not supported 176 | * @return not supported 177 | * @throws UnsupportedOperationException on every invocation of this method 178 | */ 179 | @Override 180 | @Nullable 181 | Object remove(Object key); 182 | 183 | /** 184 | * Not supported. 185 | * 186 | * @throws UnsupportedOperationException on every invocation of this method 187 | */ 188 | @Override 189 | void clear(); 190 | 191 | /** 192 | * Not supported. 193 | * 194 | * @param map not supported 195 | * @throws UnsupportedOperationException on every invocation of this method 196 | */ 197 | @Override 198 | void putAll(Map map); 199 | 200 | /** 201 | * {@linkplain BsonDocument Document} builder. 202 | *

203 | * Notes: 204 | *

    205 | *
  • Calling a builder's {@linkplain #build} method does not clear its 206 | * state, however subsequent invocations of it returns new immutable 207 | * documents.
  • 208 | *
  • Document builders can be acquired via the {@linkplain BsonDocuments 209 | * documents utility class}.
  • 210 | *
211 | *

212 | */ 213 | public interface Builder { 214 | 215 | /** 216 | * Adds {@code key} and its associated {@code value} to the document being 217 | * built. 218 | * 219 | * @param key the key to be added to the document being built 220 | * @param value the value associated with {@code key} 221 | * @return this builder 222 | * @throws NullPointerException if {@code key} is null 223 | * @throws IllegalArgumentException if {@code key} is already present 224 | */ 225 | Builder put(String key, @Nullable Object value); 226 | 227 | /** 228 | * Adds {@code map}'s key-value pairs to the document being built. 229 | * 230 | * @param map the map whose key-value pairs are to be added to the document 231 | * being built 232 | * @return this builder 233 | * @throws NullPointerException if {@code map} or any of its keys are null 234 | * @throws IllegalArgumentException if {@code map} contains keys that are 235 | * already present 236 | */ 237 | Builder putAll(Map map); 238 | 239 | /** 240 | * Returns a new document using the contents of this builder. 241 | * 242 | * @return a new document using the contents of this builder 243 | */ 244 | BsonDocument build(); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonDocuments.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Map; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * Utility class for working with {@linkplain BsonDocument documents}. 10 | */ 11 | public final class BsonDocuments { 12 | 13 | private BsonDocuments() {} 14 | 15 | /** 16 | * Reads a new document from {@code buffer}. 17 | * 18 | * @param buffer the buffer that contains a document's serialized data 19 | * @return a new document from {@code buffer} 20 | * @throws NullPointerException if {@code buffer} is null 21 | * @throws IllegalArgumentException if {@code buffer} is not using 22 | * little-endian byte ordering 23 | */ 24 | public static BsonDocument readFrom(ByteBuffer buffer) { 25 | return (BsonDocument) BsonToken.DOCUMENT.reader().readFrom(buffer); 26 | } 27 | 28 | /** 29 | * Writes {@code document} to {@code buffer}. 30 | * 31 | * @param buffer the buffer to write to 32 | * @param document the document to be written into {@code buffer} 33 | * @throws NullPointerException if {@code buffer} or {@code document} is null 34 | * @throws IllegalArgumentException if {@code buffer} is not using 35 | * little-endian byte ordering 36 | */ 37 | public static void writeTo(ByteBuffer buffer, BsonDocument document) { 38 | BsonToken.DOCUMENT.writer().writeTo(buffer, document); 39 | } 40 | 41 | /** 42 | * Returns the binary size of the document. This is needed to allocate a 43 | * ByteBuffer that is exactly the correct size. 44 | * 45 | * @param document the document to obtain the binary size of 46 | * @return the documents binary size 47 | */ 48 | public static int binarySize(BsonDocument document){ 49 | return BsonToken.DOCUMENT.writer().getSize(document); 50 | } 51 | 52 | /** 53 | * Returns a new document containing {@code map}'s key-value pairs. 54 | * 55 | * @param map the map whose key-value pairs will be used to initialize the new 56 | * document 57 | * @return a new document containing {@code map}'s key-value pairs 58 | * @throws NullPointerException if {@code map} or any of its keys are 59 | * null 60 | */ 61 | public static BsonDocument copyOf(Map map) { 62 | return builder().putAll(map).build(); 63 | } 64 | 65 | // @checkstyle:off ParameterNumber|JavadocMethod 66 | 67 | /** 68 | * Returns a new document containing the key-value pairs: 69 | * k1: v1, k2: v2, etc. 70 | * 71 | * @return a new document containing the key-value pairs: 72 | * k1: v1, k2: v2 etc. 73 | * @throws NullPointerException if any key-value pair's key is null 74 | * @throws IllegalArgumentException if there are duplicate keys 75 | */ 76 | public static BsonDocument of(String k1, @Nullable Object v1, 77 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 78 | String k4, @Nullable Object v4, String k5, @Nullable Object v5, 79 | String k6, @Nullable Object v6, String k7, @Nullable Object v7, 80 | String k8, @Nullable Object v8, String k9, @Nullable Object v9, 81 | String k10, @Nullable Object v10) { 82 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4).put(k5, v5) 83 | .put(k6, v6).put(k7, v7).put(k8, v8).put(k9, v9).put(k10, v10).build(); 84 | } 85 | 86 | /** 87 | * Returns a new document containing the key-value pairs: 88 | * k1: v1, k2: v2, etc. 89 | * 90 | * @return a new document containing the key-value pairs: 91 | * k1: v1, k2: v2 etc. 92 | * @throws NullPointerException if any key-value pair's key is null 93 | * @throws IllegalArgumentException if there are duplicate keys 94 | */ 95 | public static BsonDocument of(String k1, @Nullable Object v1, 96 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 97 | String k4, @Nullable Object v4, String k5, @Nullable Object v5, 98 | String k6, @Nullable Object v6, String k7, @Nullable Object v7, 99 | String k8, @Nullable Object v8, String k9, @Nullable Object v9) { 100 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4).put(k5, v5) 101 | .put(k6, v6).put(k7, v7).put(k8, v8).put(k9, v9).build(); 102 | } 103 | 104 | /** 105 | * Returns a new document containing the key-value pairs: 106 | * k1: v1, k2: v2, etc. 107 | * 108 | * @return a new document containing the key-value pairs: 109 | * k1: v1, k2: v2 etc. 110 | * @throws NullPointerException if any key-value pair's key is null 111 | * @throws IllegalArgumentException if there are duplicate keys 112 | */ 113 | public static BsonDocument of(String k1, @Nullable Object v1, 114 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 115 | String k4, @Nullable Object v4, String k5, @Nullable Object v5, 116 | String k6, @Nullable Object v6, String k7, @Nullable Object v7, 117 | String k8, @Nullable Object v8) { 118 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4) 119 | .put(k5, v5).put(k6, v6).put(k7, v7).put(k8, v8).build(); 120 | } 121 | 122 | /** 123 | * Returns a new document containing the key-value pairs: 124 | * k1: v1, k2: v2, etc. 125 | * 126 | * @return a new document containing the key-value pairs: 127 | * k1: v1, k2: v2 etc. 128 | * @throws NullPointerException if any key-value pair's key is null 129 | * @throws IllegalArgumentException if there are duplicate keys 130 | */ 131 | public static BsonDocument of(String k1, @Nullable Object v1, 132 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 133 | String k4, @Nullable Object v4, String k5, @Nullable Object v5, 134 | String k6, @Nullable Object v6, String k7, @Nullable Object v7) { 135 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4) 136 | .put(k5, v5).put(k6, v6).put(k7, v7).build(); 137 | } 138 | 139 | /** 140 | * Returns a new document containing the key-value pairs: 141 | * k1: v1, k2: v2, etc. 142 | * 143 | * @return a new document containing the key-value pairs: 144 | * k1: v1, k2: v2 etc. 145 | * @throws NullPointerException if any key-value pair's key is null 146 | * @throws IllegalArgumentException if there are duplicate keys 147 | */ 148 | public static BsonDocument of(String k1, @Nullable Object v1, 149 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 150 | String k4, @Nullable Object v4, String k5, @Nullable Object v5, 151 | String k6, @Nullable Object v6) { 152 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4) 153 | .put(k5, v5).put(k6, v6).build(); 154 | } 155 | 156 | /** 157 | * Returns a new document containing the key-value pairs: 158 | * k1: v1, k2: v2, etc. 159 | * 160 | * @return a new document containing the key-value pairs: 161 | * k1: v1, k2: v2 etc. 162 | * @throws NullPointerException if any key-value pair's key is null 163 | * @throws IllegalArgumentException if there are duplicate keys 164 | */ 165 | public static BsonDocument of(String k1, @Nullable Object v1, 166 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 167 | String k4, @Nullable Object v4, String k5, @Nullable Object v5) { 168 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4).put(k5, v5).build(); 169 | } 170 | 171 | /** 172 | * Returns a new document containing the key-value pairs: 173 | * k1: v1, k2: v2, etc. 174 | * 175 | * @return a new document containing the key-value pairs: 176 | * k1: v1, k2: v2 etc. 177 | * @throws NullPointerException if any key-value pair's key is null 178 | * @throws IllegalArgumentException if there are duplicate keys 179 | */ 180 | public static BsonDocument of(String k1, @Nullable Object v1, 181 | String k2, @Nullable Object v2, String k3, @Nullable Object v3, 182 | String k4, @Nullable Object v4) { 183 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).put(k4, v4).build(); 184 | } 185 | 186 | /** 187 | * Returns a new document containing the key-value pairs: 188 | * k1: v1, k2: v2, etc. 189 | * 190 | * @return a new document containing the key-value pairs: 191 | * k1: v1, k2: v2 etc. 192 | * @throws NullPointerException if any key-value pair's key is null 193 | * @throws IllegalArgumentException if there are duplicate keys 194 | */ 195 | public static BsonDocument of(String k1, @Nullable Object v1, 196 | String k2, @Nullable Object v2, String k3, @Nullable Object v3) { 197 | return builder().put(k1, v1).put(k2, v2).put(k3, v3).build(); 198 | } 199 | 200 | /** 201 | * Returns a new document containing the key-value pairs: 202 | * k1: v1, k2: v2. 203 | * 204 | * @return a new document containing the key-value pairs: 205 | * k1: v1, k2: v2 206 | * @throws NullPointerException if any key-value pair's key is null 207 | * @throws IllegalArgumentException if there are duplicate keys 208 | */ 209 | public static BsonDocument of(String k1, @Nullable Object v1, 210 | String k2, @Nullable Object v2) { 211 | return builder().put(k1, v1).put(k2, v2).build(); 212 | } 213 | 214 | // @checkstyle:on ParameterNumber|JavadocMethod 215 | 216 | /** 217 | * Returns a new document containing {@code key} and its associated 218 | * {@code value}. 219 | * 220 | * @param key the key that will be used to initialize the new document 221 | * @param value the value associated with {@code key} 222 | * @return a new document containing {@code key} and its associated 223 | * {@code value} 224 | * @throws NullPointerException if {@code key} is null 225 | */ 226 | public static BsonDocument of(String key, @Nullable Object value) { 227 | return builder().put(key, value).build(); 228 | } 229 | 230 | /** 231 | * Returns the empty document. 232 | * 233 | * @return the empty document 234 | */ 235 | public static BsonDocument of() { 236 | return builder().build(); 237 | } 238 | 239 | /** 240 | * Returns a new {@linkplain BsonDocument.Builder document builder}. 241 | * 242 | * @return a new document builder 243 | */ 244 | public static BsonDocument.Builder builder() { 245 | return new DefaultDocumentBuilder(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonObject.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.base.Predicate; 5 | import com.google.common.base.Predicates; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | /** 10 | * Representation of a BSON object type. 11 | * 12 | * @see BsonToken 13 | * @see BsonBinary 14 | */ 15 | public enum BsonObject { 16 | 17 | /** 18 | * 64-bit IEEE 754 floating point. 19 | */ 20 | DOUBLE(BsonBytes.DOUBLE, DefaultPredicate.DOUBLE, DefaultReader.DOUBLE, 21 | DefaultWriter.DOUBLE), 22 | 23 | /** 24 | * UTF-8 string. 25 | */ 26 | STRING(BsonBytes.STRING, DefaultPredicate.STRING, DefaultReader.STRING, 27 | DefaultWriter.STRING), 28 | 29 | /** 30 | * Embedded {@linkplain BsonToken#DOCUMENT document}. 31 | */ 32 | EMBEDDED(BsonBytes.EMBEDDED, DefaultPredicate.EMBEDDED, 33 | BsonToken.DOCUMENT.reader(), BsonToken.DOCUMENT.writer()), 34 | 35 | /** 36 | * Special embedded {@linkplain #EMBEDDED document}. 37 | *

38 | * Note: an array is a document whose keys are integer values starting 39 | * with 0 and continuing sequentially. 40 | *

41 | */ 42 | ARRAY(BsonBytes.ARRAY, DefaultPredicate.ARRAY, DefaultReader.ARRAY, 43 | DefaultWriter.ARRAY), 44 | 45 | /** 46 | * Binary data. 47 | */ 48 | BINARY(BsonBytes.BINARY, DefaultPredicate.BINARY, DefaultReader.BINARY, 49 | DefaultWriter.BINARY), 50 | 51 | /** 52 | * Undefined. 53 | * 54 | * @deprecated See the BSON specification 55 | * for details. 56 | */ 57 | @Deprecated 58 | UNDEFINED(BsonBytes.UNDEFINED), 59 | 60 | /** 61 | * Object ID. 62 | *

63 | * Note: special MongoDB related type. 64 | *

65 | */ 66 | OBJECT_ID(BsonBytes.OBJECT_ID, DefaultPredicate.OBJECT_ID, 67 | DefaultReader.OBJECT_ID, DefaultWriter.OBJECT_ID), 68 | 69 | /** 70 | * Boolean. 71 | *

72 | * Note: 0 is false; 1 is true. 73 | *

74 | */ 75 | BOOLEAN(BsonBytes.BOOLEAN, DefaultPredicate.BOOLEAN, DefaultReader.BOOLEAN, 76 | DefaultWriter.BOOLEAN), 77 | 78 | /** 79 | * UTC date-time. 80 | *

81 | * Note: milliseconds since the Unix epoch. 82 | *

83 | */ 84 | UTC_DATE_TIME(BsonBytes.UTC_DATE_TIME, DefaultPredicate.UTC_DATE_TIME, 85 | DefaultReader.UTC_DATE_TIME, DefaultWriter.UTC_DATE_TIME), 86 | 87 | /** 88 | * null. 89 | */ 90 | NULL(BsonBytes.NULL, DefaultPredicate.NULL, DefaultReader.NULL, 91 | DefaultWriter.NULL), 92 | 93 | /** 94 | * Regular expression. 95 | */ 96 | REGULAR_EXPRESSION(BsonBytes.REGULAR_EXPRESSION, DefaultPredicate.REGULAR_EXPRESSION, 97 | DefaultReader.REGULAR_EXPRESSION, DefaultWriter.REGULAR_EXPRESSION), 98 | 99 | /** 100 | * DB pointer. 101 | * 102 | * @deprecated See the following 104 | * link for more details. 105 | */ 106 | @Deprecated 107 | DB_POINTER(BsonBytes.DB_POINTER), 108 | 109 | /** 110 | * JavaScript code. 111 | */ 112 | JAVASCRIPT_CODE(BsonBytes.JAVASCRIPT_CODE), 113 | 114 | /** 115 | * Symbol. 116 | *

117 | * Note: similar to a string but for languages with a distinct symbol 118 | * type. 119 | *

120 | */ 121 | SYMBOL(BsonBytes.SYMBOL, DefaultPredicate.SYMBOL, 122 | DefaultReader.SYMBOL, DefaultWriter.SYMBOL), 123 | 124 | /** 125 | * JavaScript code with scope. 126 | */ 127 | JAVASCRIPT_CODE_WITH_SCOPE(BsonBytes.JAVASCRIPT_CODE_WITH_SCOPE), 128 | 129 | /** 130 | * 32-bit signed integer. 131 | */ 132 | INT32(BsonBytes.INT32, DefaultPredicate.INT32, DefaultReader.INT32, 133 | DefaultWriter.INT32), 134 | 135 | /** 136 | * Timestamp. 138 | *

139 | * Note: special internal type used by MongoDB replication and 140 | * sharding. 141 | *

142 | */ 143 | TIMESTAMP(BsonBytes.TIMESTAMP, DefaultPredicate.TIMESTAMP, 144 | DefaultReader.TIMESTAMP, DefaultWriter.TIMESTAMP), 145 | 146 | /** 147 | * 64-bit signed integer. 148 | */ 149 | INT64(BsonBytes.INT64, DefaultPredicate.INT64, DefaultReader.INT64, 150 | DefaultWriter.INT64), 151 | 152 | /** 153 | * 154 | * Max key. 155 | *

156 | * Note: special type which compares higher than all other possible 157 | * {@code BSON} element values. 158 | *

159 | */ 160 | MAX_KEY(BsonBytes.MAX_KEY), 161 | 162 | /** 163 | * 164 | * Min key. 165 | *

166 | * Note: special type which compares lower than all other possible 167 | * {@code BSON} element values. 168 | *

169 | */ 170 | MIN_KEY(BsonBytes.MIN_KEY); 171 | 172 | private final byte terminal; 173 | 174 | private Predicate> predicate; 175 | private BsonReader reader; 176 | private BsonWriter writer; 177 | 178 | private BsonObject(byte terminal) { 179 | this(terminal, Predicates.>alwaysFalse(), null, null); 180 | } 181 | 182 | private BsonObject(byte terminal, Predicate> predicate, 183 | BsonReader reader, BsonWriter writer) { 184 | this.terminal = terminal; 185 | this.predicate = predicate; 186 | this.reader = reader; 187 | this.writer = writer; 188 | } 189 | 190 | /** 191 | * Returns this object's associated terminal. 192 | * 193 | * @return this object's associated terminal 194 | */ 195 | public byte terminal() { 196 | return terminal; 197 | } 198 | 199 | /** 200 | * Returns this object's associated {@linkplain Predicate predicate}. 201 | * 202 | * @return this object's associated predicate 203 | * @throws IllegalStateException if this object does not have an associated 204 | * predicate 205 | */ 206 | public Predicate> predicate() { 207 | Preconditions.checkState(predicate != null, "'%s' does not have an associated predicate", this); 208 | return predicate; 209 | } 210 | 211 | /** 212 | * Associates {@link Predicate predicate} with this object. 213 | * 214 | * @param predicate the predicate to be associated with this object 215 | */ 216 | public void predicate(Predicate> predicate) { 217 | Preconditions.checkNotNull(predicate, "cannot associate a null predicate with '%s'", this); 218 | this.predicate = predicate; 219 | } 220 | 221 | /** 222 | * Returns this object's associated {@linkplain BsonReader reader}. 223 | * 224 | * @return this object's associated reader 225 | * @throws IllegalStateException if this object does not have an associated 226 | * reader 227 | */ 228 | public BsonReader reader() { 229 | Preconditions.checkState(reader != null, "'%s' does not have an associated reader", this); 230 | return reader; 231 | } 232 | 233 | /** 234 | * Associates {@link BsonReader reader} with this object. 235 | * 236 | * @param reader the reader to be associated with this object 237 | */ 238 | public void reader(BsonReader reader) { 239 | Preconditions.checkNotNull(predicate, "cannot associate a null reader with '%s'", this); 240 | this.reader = reader; 241 | } 242 | 243 | /** 244 | * Returns this object's associated {@linkplain BsonWriter writer}. 245 | * 246 | * @return this object's associated writer 247 | * @throws IllegalStateException if this object does not have an associated 248 | * writer 249 | */ 250 | public BsonWriter writer() { 251 | Preconditions.checkState(writer != null, "'%s' does not have an associated writer", this); 252 | return writer; 253 | } 254 | 255 | /** 256 | * Associates {@link BsonWriter writer} with this object. 257 | * 258 | * @param writer the writer to be associated with this object 259 | */ 260 | public void writer(BsonWriter writer) { 261 | Preconditions.checkNotNull(writer, "cannot associate a null writer with '%s'", this); 262 | this.writer = writer; 263 | } 264 | 265 | /** 266 | * Returns the object representing {@code clazz}. 267 | * 268 | * @param clazz the class to return a object representation for 269 | * @return the object representing {@code clazz} 270 | * @throws IllegalArgumentException if no object representing {@code clazz} 271 | * was found 272 | */ 273 | public static BsonObject find(@Nullable Class clazz) { 274 | for (BsonObject object : values()) { 275 | if (object.predicate().apply(clazz)) { 276 | return object; 277 | } 278 | } 279 | throw new IllegalArgumentException(String.format("no object " 280 | + "representing the '%s' type value was found", clazz)); 281 | } 282 | 283 | /** 284 | * Returns the object representing {@code terminal}. 285 | * 286 | * @param terminal the terminal to return a object representation for 287 | * @return the object representing {@code terminal} 288 | * @throws IllegalArgumentException if no object representing {@code terminal} 289 | * was found 290 | */ 291 | public static BsonObject find(byte terminal) { 292 | for (BsonObject object : values()) { 293 | if (object.terminal() - terminal == 0) { 294 | return object; 295 | } 296 | } 297 | throw new IllegalArgumentException(String.format("no object representing " 298 | + "the '%s' terminal value was found", Byte.valueOf(terminal))); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonObjectId.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | // @checkstyle:off . 6 | public interface BsonObjectId { 7 | 8 | ByteBuffer objectId(); 9 | 10 | ByteBuffer time(); 11 | 12 | ByteBuffer machineId(); 13 | 14 | ByteBuffer processId(); 15 | 16 | ByteBuffer increment(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonReader.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | /** 8 | * Reads Java objects from {@linkplain ByteBuffer buffers}, deserialized from 9 | * bytes as specified by the BSON 10 | * specification. 11 | *

12 | * Note: buffers supplied to {@linkplain #readFrom} must use 13 | * little-endian byte ordering. 14 | *

15 | */ 16 | public interface BsonReader { 17 | 18 | /** 19 | * Reads an arbitrary amount of bytes from {@code buffer} and 20 | * constructs a new object from what was read. 21 | * 22 | * @param buffer the buffer to read from 23 | * @return a new object from the bytes read from {@code buffer} 24 | * @throws NullPointerException if {@code buffer} is null 25 | * @throws IllegalArgumentException if {@code buffer} is not using 26 | * little-endian byte ordering 27 | */ 28 | @Nullable 29 | Object readFrom(ByteBuffer buffer); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonSymbol.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | // @checkstyle:off . 6 | public interface BsonSymbol { 7 | 8 | ByteBuffer symbol(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonTimestamp.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | // @checkstyle:off . 6 | public interface BsonTimestamp { 7 | 8 | ByteBuffer timestamp(); 9 | 10 | ByteBuffer time(); 11 | 12 | ByteBuffer increment(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonToken.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Preconditions; 4 | 5 | /** 6 | * Representation of a BSON token type. 7 | */ 8 | public enum BsonToken { 9 | 10 | /** 11 | * BSON document. 12 | */ 13 | DOCUMENT(DefaultReader.DOCUMENT, DefaultWriter.DOCUMENT), 14 | 15 | /** 16 | * Key-value pair in a {@linkplain #DOCUMENT document}. 17 | */ 18 | FIELD(DefaultReader.FIELD, DefaultWriter.FIELD), 19 | 20 | /** 21 | * Key in a {@linkplain #FIELD field} ({@code \0} delimited UTF-8 string). 22 | */ 23 | KEY(DefaultReader.KEY, DefaultWriter.KEY); 24 | 25 | private BsonReader reader; 26 | private BsonWriter writer; 27 | 28 | private BsonToken(BsonReader reader, BsonWriter writer) { 29 | this.reader = reader; 30 | this.writer = writer; 31 | } 32 | 33 | private BsonToken() {} 34 | 35 | /** 36 | * Returns this token's associated {@linkplain BsonReader reader}. 37 | * 38 | * @return this token's associated reader 39 | * @throws IllegalStateException if this token does not have an associated 40 | * reader 41 | */ 42 | public BsonReader reader() { 43 | Preconditions.checkState(reader != null, "'%s' does not have an associated reader", this); 44 | return reader; 45 | } 46 | 47 | /** 48 | * Associates {@link BsonReader reader} with this token. 49 | * 50 | * @param reader the reader to be associated with this token 51 | */ 52 | public void reader(BsonReader reader) { 53 | Preconditions.checkNotNull(reader, "cannot associate a null reader with '%s'", this); 54 | this.reader = reader; 55 | } 56 | 57 | /** 58 | * Returns this token's associated {@linkplain BsonWriter writer}. 59 | * 60 | * @return this token's associated writer 61 | * @throws IllegalStateException if this token does not have an associated 62 | * writer 63 | */ 64 | public BsonWriter writer() { 65 | Preconditions.checkState(writer != null, "'%s' does not have an associated writer", this); 66 | return writer; 67 | } 68 | 69 | /** 70 | * Associates {@link BsonWriter writer} with this token. 71 | * 72 | * @param writer the writer to be associated with this token 73 | */ 74 | public void writer(BsonWriter writer) { 75 | Preconditions.checkNotNull(reader, "cannot associate a null writer with '%s'", this); 76 | this.writer = writer; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/BsonWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | /** 8 | * Writes Java objects to {@linkplain ByteBuffer buffers}, serialized to bytes 9 | * as specified by the BSON specification. 10 | *

11 | * Note: buffers supplied to {@linkplain #writeTo} must use little-endian 12 | * byte ordering. 13 | *

14 | */ 15 | public interface BsonWriter { 16 | 17 | /** 18 | * Writes {@code reference} to {@code buffer}. 19 | * 20 | * @param buffer the buffer {@code reference} will be written to 21 | * @param reference the reference to be written into {@code buffer} 22 | * @throws NullPointerException if {@code buffer} is null 23 | * @throws IllegalArgumentException if {@code buffer} is not using 24 | * little-endian byte ordering 25 | */ 26 | void writeTo(ByteBuffer buffer, @Nullable Object reference); 27 | 28 | /** 29 | * Returns the size of the reference object. 30 | * 31 | * @param reference the object to return the size of 32 | * @return the computed size 33 | */ 34 | int getSize(@Nullable Object reference); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/DefaultDocument.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.base.Preconditions; 5 | import com.google.common.collect.ForwardingMap; 6 | import com.google.common.collect.Maps; 7 | 8 | import java.util.Collections; 9 | import java.util.Map; 10 | 11 | final class DefaultDocument extends ForwardingMap implements BsonDocument { 12 | 13 | private final Map delegate; 14 | 15 | DefaultDocument(Map map) { 16 | delegate = Collections.unmodifiableMap(Maps.newLinkedHashMap(map)); 17 | } 18 | 19 | @Override 20 | public T get(Object key, Class type) { 21 | Object value = get(key); 22 | Preconditions.checkArgument( 23 | type == null 24 | ? value == null 25 | : type.isInstance(value), 26 | "expected '%s' instead of '%s'", 27 | value == null 28 | ? null 29 | : value.getClass(), type); 30 | return type == null ? null : type.cast(value); 31 | } 32 | 33 | @Override 34 | public Object get(Object key) { 35 | Preconditions.checkArgument(containsKey(key), "key: '%s' is missing", key); 36 | return super.get(key); 37 | } 38 | 39 | @Override 40 | public boolean containsKey(Object key) { 41 | Preconditions.checkNotNull(key, "null key"); 42 | if (!String.class.isInstance(key)) { 43 | throw new ClassCastException(String.format("key: '%s' is not a string", key)); 44 | } 45 | return super.containsKey(key); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return new StringBuilder("{") 51 | .append(Joiner.on(", ") 52 | .withKeyValueSeparator(": ") 53 | .useForNull("null") 54 | .join(this)) 55 | .append("}") 56 | .toString(); 57 | } 58 | 59 | @Override 60 | protected Map delegate() { 61 | return delegate; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/DefaultDocumentBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.collect.Maps; 5 | 6 | import java.util.Collections; 7 | import java.util.Map; 8 | import java.util.Map.Entry; 9 | 10 | import javax.annotation.Nullable; 11 | 12 | final class DefaultDocumentBuilder implements BsonDocument.Builder { 13 | 14 | private final Map builder; 15 | 16 | DefaultDocumentBuilder() { 17 | builder = Maps.newLinkedHashMap(); 18 | } 19 | 20 | @Override 21 | public BsonDocument.Builder putAll(Map map) { 22 | Preconditions.checkNotNull(map, "null map"); 23 | for (Entry entry : map.entrySet()) { 24 | put(entry.getKey(), entry.getValue()); 25 | } 26 | return this; 27 | } 28 | 29 | @Override 30 | public BsonDocument.Builder put(String key, @Nullable Object value) { 31 | Preconditions.checkNotNull(key, "null key"); 32 | Preconditions.checkArgument(!builder.containsKey(key), "key: '%s' is already present", key); 33 | builder.put(key, value); 34 | return this; 35 | } 36 | 37 | @Override 38 | public BsonDocument build() { 39 | return new DefaultDocument(builder.isEmpty() 40 | ? Collections.emptyMap() 41 | : builder); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/DefaultPredicate.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Predicate; 4 | 5 | import java.util.Collection; 6 | import java.util.Date; 7 | import java.util.Map; 8 | import java.util.regex.Pattern; 9 | 10 | enum DefaultPredicate implements Predicate> { 11 | 12 | DOUBLE { 13 | 14 | @Override 15 | public boolean apply(Class input) { 16 | return input == null 17 | ? false 18 | : Double.class.isAssignableFrom(input); 19 | } 20 | }, 21 | 22 | STRING { 23 | 24 | @Override 25 | public boolean apply(Class input) { 26 | return input == null 27 | ? false 28 | : String.class.isAssignableFrom(input); 29 | } 30 | }, 31 | 32 | EMBEDDED { 33 | 34 | @Override 35 | public boolean apply(Class input) { 36 | return input == null 37 | ? false 38 | : Map.class.isAssignableFrom(input); 39 | } 40 | }, 41 | 42 | ARRAY { 43 | 44 | @Override 45 | public boolean apply(Class input) { 46 | return input == null 47 | ? false 48 | : byte[].class.isAssignableFrom(input) 49 | ? false 50 | : Collection.class.isAssignableFrom(input) || input.isArray(); 51 | } 52 | }, 53 | 54 | BINARY { 55 | 56 | @Override 57 | public boolean apply(final Class input) { 58 | for (BsonBinary binary : BsonBinary.values()) { 59 | if (binary.predicate().apply(input)) { 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | }, 66 | 67 | GENERIC { 68 | 69 | @Override 70 | public boolean apply(Class input) { 71 | return input == null 72 | ? false 73 | : byte[].class.isAssignableFrom(input); 74 | } 75 | }, 76 | 77 | OBJECT_ID { 78 | 79 | @Override 80 | public boolean apply(Class input) { 81 | return input == null 82 | ? false 83 | : BsonObjectId.class.isAssignableFrom(input); 84 | } 85 | }, 86 | 87 | BOOLEAN { 88 | 89 | @Override 90 | public boolean apply(Class input) { 91 | return input == null 92 | ? false 93 | : Boolean.class.isAssignableFrom(input); 94 | } 95 | }, 96 | 97 | UTC_DATE_TIME { 98 | 99 | @Override 100 | public boolean apply(Class input) { 101 | return input == null 102 | ? false 103 | : Date.class.isAssignableFrom(input); 104 | } 105 | }, 106 | 107 | NULL { 108 | 109 | @Override 110 | public boolean apply(Class input) { 111 | return input == null; 112 | } 113 | }, 114 | 115 | REGULAR_EXPRESSION { 116 | 117 | @Override 118 | public boolean apply(Class input) { 119 | return input == null 120 | ? false 121 | : Pattern.class.isAssignableFrom(input); 122 | } 123 | }, 124 | 125 | SYMBOL { 126 | 127 | @Override 128 | public boolean apply(Class input) { 129 | return input == null 130 | ? false 131 | : BsonSymbol.class.isAssignableFrom(input); 132 | } 133 | }, 134 | 135 | INT32 { 136 | 137 | @Override 138 | public boolean apply(Class input) { 139 | return input == null 140 | ? false 141 | : Integer.class.isAssignableFrom(input); 142 | } 143 | }, 144 | 145 | TIMESTAMP { 146 | 147 | @Override 148 | public boolean apply(Class input) { 149 | return input == null 150 | ? false 151 | : BsonTimestamp.class.isAssignableFrom(input); 152 | } 153 | }, 154 | 155 | INT64 { 156 | 157 | @Override 158 | public boolean apply(Class input) { 159 | return input == null 160 | ? false 161 | : Long.class.isAssignableFrom(input); 162 | } 163 | }; 164 | 165 | @Override 166 | public abstract boolean apply(Class input); 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/DefaultReader.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Preconditions; 5 | import com.google.common.collect.Maps; 6 | import com.google.common.primitives.Bytes; 7 | import com.google.common.primitives.Ints; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.nio.ByteOrder; 11 | import java.util.Date; 12 | import java.util.Map.Entry; 13 | import java.util.regex.Pattern; 14 | 15 | enum DefaultReader implements BsonReader { 16 | 17 | DOCUMENT { 18 | 19 | @Override 20 | public Object checkedReadFrom(ByteBuffer buffer) { 21 | int documentLength = buffer.getInt(); 22 | BsonReader fieldReader = BsonToken.FIELD.reader(); 23 | BsonDocument.Builder document = BsonDocuments.builder(); 24 | if (documentLength > Ints.BYTES + 1) { 25 | do { 26 | @SuppressWarnings("unchecked") 27 | Entry entry = (Entry) fieldReader.readFrom(buffer); 28 | document.put(entry.getKey(), entry.getValue()); 29 | } while (buffer.get(buffer.position()) != BsonBytes.EOO); 30 | } 31 | buffer.get(); 32 | return document.build(); 33 | } 34 | }, 35 | 36 | FIELD { 37 | 38 | @Override 39 | public Object checkedReadFrom(ByteBuffer buffer) { 40 | BsonObject bsonObject = BsonObject.find(buffer.get()); 41 | BsonReader keyReader = BsonToken.KEY.reader(); 42 | BsonReader valueReader = bsonObject.reader(); 43 | return Maps.immutableEntry(keyReader.readFrom(buffer), 44 | valueReader.readFrom(buffer)); 45 | } 46 | }, 47 | 48 | KEY { 49 | 50 | @Override 51 | public Object checkedReadFrom(ByteBuffer buffer) { 52 | byte[] bytes = new byte[] {}; 53 | byte[] read = new byte[] {BsonBytes.EOF}; 54 | while ((read[0] = buffer.get()) != BsonBytes.EOO) { 55 | bytes = Bytes.concat(bytes, read); 56 | } 57 | return new String(bytes, Charsets.UTF_8); 58 | } 59 | }, 60 | 61 | DOUBLE { 62 | 63 | @Override 64 | public Double checkedReadFrom(ByteBuffer buffer) { 65 | return Double.valueOf(buffer.getDouble()); 66 | } 67 | }, 68 | 69 | STRING { 70 | 71 | @Override 72 | public Object checkedReadFrom(ByteBuffer buffer) { 73 | int stringLength = buffer.getInt(); 74 | byte[] bytes = new byte[stringLength - 1]; 75 | buffer.get(bytes).get(); 76 | return new String(bytes, Charsets.UTF_8); 77 | } 78 | }, 79 | 80 | ARRAY { 81 | 82 | @Override 83 | public Object checkedReadFrom(ByteBuffer buffer) { 84 | return BsonToken.DOCUMENT.reader().readFrom(buffer); 85 | } 86 | }, 87 | 88 | BINARY { 89 | 90 | @Override 91 | public Object checkedReadFrom(ByteBuffer buffer) { 92 | int binaryLength = buffer.getInt(); 93 | byte terminal = buffer.get(); 94 | BsonBinary bsonBinary = BsonBinary.find(terminal); 95 | int oldLimit = buffer.limit(); 96 | buffer.limit(buffer.position() + binaryLength); 97 | Object binary = bsonBinary.reader().readFrom(buffer); 98 | buffer.limit(oldLimit); 99 | return binary; 100 | } 101 | }, 102 | 103 | GENERIC { 104 | 105 | @Override 106 | public Object checkedReadFrom(ByteBuffer buffer) { 107 | byte[] binary = new byte[buffer.remaining()]; 108 | buffer.get(binary); 109 | return binary; 110 | } 111 | }, 112 | 113 | OBJECT_ID { 114 | 115 | @Override 116 | Object checkedReadFrom(ByteBuffer buffer) { 117 | return new BasicObjectId(buffer); 118 | } 119 | }, 120 | 121 | BOOLEAN { 122 | 123 | @Override 124 | public Object checkedReadFrom(ByteBuffer buffer) { 125 | return Boolean.valueOf(buffer.get() == BsonBytes.TRUE); 126 | } 127 | }, 128 | 129 | UTC_DATE_TIME { 130 | 131 | @Override 132 | public Object checkedReadFrom(ByteBuffer buffer) { 133 | return new Date(buffer.getLong()); 134 | } 135 | }, 136 | 137 | NULL { 138 | 139 | @Override 140 | public Object checkedReadFrom(ByteBuffer buffer) { 141 | return null; 142 | } 143 | }, 144 | 145 | REGULAR_EXPRESSION { 146 | 147 | @Override 148 | public Object checkedReadFrom(ByteBuffer buffer) { 149 | BsonReader keyReader = BsonToken.KEY.reader(); 150 | String pattern = (String) keyReader.readFrom(buffer); 151 | String options = (String) keyReader.readFrom(buffer); 152 | return Pattern.compile(pattern, optionsToFlags(options)); 153 | } 154 | 155 | private int optionsToFlags(String options) { 156 | int flags = 0; 157 | for (char option : options.toCharArray()) { 158 | flags |= flags + optionToFlag(option); 159 | } 160 | return flags; 161 | } 162 | 163 | // @do-not-check-next-line CyclomaticComplexity 164 | private int optionToFlag(char option) { 165 | int flag = 0; 166 | switch (option) { 167 | case 'i': 168 | flag = Pattern.CASE_INSENSITIVE; 169 | break; 170 | case 'm': 171 | flag = Pattern.MULTILINE; 172 | break; 173 | case 's': 174 | flag = Pattern.DOTALL; 175 | break; 176 | case 'x': 177 | flag = Pattern.COMMENTS; 178 | break; 179 | default: 180 | break; 181 | } 182 | return flag; 183 | } 184 | }, 185 | 186 | SYMBOL { 187 | 188 | @Override 189 | Object checkedReadFrom(ByteBuffer buffer) { 190 | return new BasicSymbol(buffer); 191 | } 192 | }, 193 | 194 | INT32 { 195 | 196 | @Override 197 | public Object checkedReadFrom(ByteBuffer buffer) { 198 | return Integer.valueOf(buffer.getInt()); 199 | } 200 | }, 201 | 202 | TIMESTAMP { 203 | 204 | @Override 205 | Object checkedReadFrom(ByteBuffer buffer) { 206 | return new BasicTimestamp(buffer); 207 | } 208 | }, 209 | 210 | INT64 { 211 | 212 | @Override 213 | public Object checkedReadFrom(ByteBuffer buffer) { 214 | return Long.valueOf(buffer.getLong()); 215 | } 216 | }; 217 | 218 | @Override 219 | public final Object readFrom(ByteBuffer buffer) { 220 | Preconditions.checkNotNull(buffer, "null buffer"); 221 | Preconditions.checkArgument(buffer.order() == ByteOrder.LITTLE_ENDIAN, 222 | "buffer has big-endian byte order; expected little-endian"); 223 | return checkedReadFrom(buffer); 224 | } 225 | 226 | abstract Object checkedReadFrom(ByteBuffer buffer); 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/DefaultWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Joiner; 5 | import com.google.common.base.Preconditions; 6 | import com.google.common.collect.Maps; 7 | import com.google.common.collect.Sets; 8 | import com.google.common.primitives.Doubles; 9 | import com.google.common.primitives.Ints; 10 | import com.google.common.primitives.Longs; 11 | 12 | import java.lang.reflect.Array; 13 | import java.nio.ByteBuffer; 14 | import java.nio.ByteOrder; 15 | import java.util.Collection; 16 | import java.util.Date; 17 | import java.util.Map; 18 | import java.util.Map.Entry; 19 | import java.util.SortedSet; 20 | import java.util.regex.Pattern; 21 | 22 | import javax.annotation.Nullable; 23 | 24 | enum DefaultWriter implements BsonWriter { 25 | 26 | DOCUMENT { 27 | 28 | @Override 29 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 30 | int markedPosition = buffer.position(); 31 | buffer.position(markedPosition + Ints.BYTES); 32 | BsonWriter fieldWriter = BsonToken.FIELD.writer(); 33 | for (Entry entry : ((Map) reference).entrySet()) { 34 | fieldWriter.writeTo(buffer, entry); 35 | } 36 | buffer.put(BsonBytes.EOO); 37 | buffer.putInt(markedPosition, buffer.position() - markedPosition); 38 | } 39 | 40 | @Override 41 | public int getSize(Object reference) { 42 | int constSize = BYTES_BYTES + Ints.BYTES; 43 | int variableSize = 0; 44 | BsonWriter fieldWriter = BsonToken.FIELD.writer(); 45 | for (Entry entry : ((Map) reference).entrySet()) { 46 | variableSize += fieldWriter.getSize(entry); 47 | } 48 | return constSize + variableSize; 49 | } 50 | }, 51 | 52 | FIELD { 53 | 54 | @Override 55 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 56 | Entry entry = (Entry) reference; 57 | BsonObject bsonObject = BsonObject.find(entry.getValue() == null 58 | ? null 59 | : entry.getValue().getClass()); 60 | buffer.put(bsonObject.terminal()); 61 | BsonToken.KEY.writer().writeTo(buffer, entry.getKey()); 62 | bsonObject.writer().writeTo(buffer, entry.getValue()); 63 | } 64 | 65 | @Override 66 | public int getSize(@Nullable Object reference) { 67 | Entry entry = (Entry) reference; 68 | BsonObject bsonObject = BsonObject.find(entry.getValue() == null 69 | ? null 70 | : entry.getValue().getClass()); 71 | 72 | int constSize = BYTES_BYTES; 73 | int variableSize = BsonToken.KEY.writer().getSize(entry.getKey()) 74 | + bsonObject.writer().getSize(entry.getValue()); 75 | return constSize + variableSize; 76 | } 77 | }, 78 | 79 | KEY { 80 | 81 | @Override 82 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 83 | buffer.put(((String) reference).getBytes(Charsets.UTF_8)).put(BsonBytes.EOO); 84 | } 85 | 86 | @Override 87 | public int getSize(@Nullable Object reference) { 88 | int constSize = BYTES_BYTES; 89 | int variableSize = ((String) reference).getBytes(Charsets.UTF_8).length; 90 | return constSize + variableSize; 91 | } 92 | }, 93 | 94 | DOUBLE { 95 | 96 | @Override 97 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 98 | buffer.putDouble(((Double) reference).doubleValue()); 99 | } 100 | 101 | @Override 102 | public int getSize(@Nullable Object reference) { 103 | return Doubles.BYTES; 104 | } 105 | }, 106 | 107 | STRING { 108 | 109 | @Override 110 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 111 | byte[] bytes = ((String) reference).getBytes(Charsets.UTF_8); 112 | buffer.putInt(bytes.length + 1).put(bytes).put(BsonBytes.EOO); 113 | } 114 | 115 | @Override 116 | public int getSize(@Nullable Object reference) { 117 | int constSize = Ints.BYTES + BYTES_BYTES; 118 | byte[] bytes = ((String) reference).getBytes(Charsets.UTF_8); 119 | int variableSize = bytes.length; 120 | return constSize + variableSize; 121 | } 122 | }, 123 | 124 | ARRAY { 125 | 126 | @Override 127 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 128 | Object array = reference instanceof Collection 129 | ? ((Collection) reference).toArray() 130 | : reference; 131 | Map document = Maps.newLinkedHashMap(); 132 | for (int i = 0; i < Array.getLength(array); i++) { 133 | document.put(String.valueOf(i), Array.get(array, i)); 134 | } 135 | BsonToken.DOCUMENT.writer().writeTo(buffer, document); 136 | } 137 | 138 | @Override 139 | public int getSize(@Nullable Object reference) { 140 | Object array = reference instanceof Collection 141 | ? ((Collection) reference).toArray() 142 | : reference; 143 | Map document = Maps.newLinkedHashMap(); 144 | for (int i = 0; i < Array.getLength(array); i++) { 145 | document.put(String.valueOf(i), Array.get(array, i)); 146 | } 147 | return BsonToken.DOCUMENT.writer().getSize(document); 148 | } 149 | }, 150 | 151 | BINARY { 152 | 153 | @Override 154 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 155 | int markedPosition = buffer.position(); 156 | buffer.position(markedPosition + Ints.BYTES); 157 | BsonBinary bsonBinary = BsonBinary.find(reference.getClass()); 158 | buffer.put(bsonBinary.terminal()); 159 | bsonBinary.writer().writeTo(buffer, reference); 160 | buffer.putInt(markedPosition, buffer.position() - markedPosition - Ints.BYTES - 1); 161 | } 162 | 163 | @Override 164 | public int getSize(@Nullable Object reference) { 165 | BsonBinary bsonBinary = BsonBinary.find(reference.getClass()); 166 | int constSize = Ints.BYTES + BYTES_BYTES; 167 | int variableSize = bsonBinary.writer().getSize(reference); 168 | return constSize + variableSize; 169 | } 170 | }, 171 | 172 | GENERIC { 173 | 174 | @Override 175 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 176 | buffer.put((byte[]) reference); 177 | } 178 | 179 | @Override 180 | public int getSize(@Nullable Object reference) { 181 | return ((byte[]) reference).length; 182 | } 183 | }, 184 | 185 | OBJECT_ID { 186 | 187 | @Override 188 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 189 | buffer.put(((BsonObjectId) reference).objectId()); 190 | } 191 | 192 | @Override 193 | public int getSize(@Nullable Object reference) { 194 | return ((BsonObjectId) reference).objectId().capacity(); 195 | } 196 | }, 197 | 198 | BOOLEAN { 199 | 200 | @Override 201 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 202 | buffer.put(((Boolean) reference).booleanValue() 203 | ? BsonBytes.TRUE 204 | : BsonBytes.FALSE); 205 | } 206 | 207 | @Override 208 | public int getSize(@Nullable Object reference) { 209 | return BOOLEANS_BYTES; 210 | } 211 | }, 212 | 213 | UTC_DATE_TIME { 214 | 215 | @Override 216 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 217 | buffer.putLong(((Date) reference).getTime()); 218 | } 219 | 220 | @Override 221 | public int getSize(@Nullable Object reference) { 222 | return Longs.BYTES; 223 | } 224 | }, 225 | 226 | NULL { 227 | 228 | @Override 229 | public void checkedWriteTo(ByteBuffer buffer, Object reference) {} 230 | 231 | @Override 232 | public int getSize(@Nullable Object reference) { 233 | return 0; 234 | } 235 | }, 236 | 237 | REGULAR_EXPRESSION { 238 | 239 | @Override 240 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 241 | Pattern regularExpression = (Pattern) reference; 242 | BsonWriter keyWriter = BsonToken.KEY.writer(); 243 | keyWriter.writeTo(buffer, regularExpression.pattern()); 244 | keyWriter.writeTo(buffer, flagsToOptions(regularExpression.flags())); 245 | } 246 | 247 | @Override 248 | public int getSize(@Nullable Object reference) { 249 | Pattern regularExpression = (Pattern) reference; 250 | BsonWriter keyWriter = BsonToken.KEY.writer(); 251 | return keyWriter.getSize(regularExpression.pattern()) 252 | + keyWriter.getSize(flagsToOptions(regularExpression.flags())); 253 | } 254 | 255 | // @do-not-check-next-line CyclomaticComplexity 256 | private String flagsToOptions(int flags) { 257 | SortedSet options = Sets.newTreeSet(); 258 | if (hasFlag(flags, Pattern.CASE_INSENSITIVE)) { 259 | options.add(Character.valueOf('i')); 260 | } 261 | 262 | if (hasFlag(flags, Pattern.COMMENTS)) { 263 | options.add(Character.valueOf('x')); 264 | } 265 | 266 | if (hasFlag(flags, Pattern.DOTALL)) { 267 | options.add(Character.valueOf('s')); 268 | } 269 | 270 | if (hasFlag(flags, Pattern.MULTILINE)) { 271 | options.add(Character.valueOf('m')); 272 | } 273 | 274 | return Joiner.on("").join(options); 275 | } 276 | 277 | private boolean hasFlag(int flags, int flag) { 278 | return (flags & flag) != 0; 279 | } 280 | }, 281 | 282 | SYMBOL { 283 | 284 | @Override 285 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 286 | ByteBuffer symbol = ((BsonSymbol) reference).symbol(); 287 | buffer.putInt(symbol.capacity() + 1).put(symbol).put(BsonBytes.EOO); 288 | } 289 | 290 | @Override 291 | public int getSize(@Nullable Object reference) { 292 | ByteBuffer symbol = ((BsonSymbol) reference).symbol(); 293 | int constSize = Ints.BYTES + BYTES_BYTES; 294 | int variableSize = symbol.capacity(); 295 | return constSize + variableSize; 296 | } 297 | }, 298 | 299 | INT32 { 300 | 301 | @Override 302 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 303 | buffer.putInt(((Integer) reference).intValue()); 304 | } 305 | 306 | @Override 307 | public int getSize(@Nullable Object reference) { 308 | return Ints.BYTES; 309 | } 310 | }, 311 | 312 | TIMESTAMP { 313 | 314 | @Override 315 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 316 | buffer.put(((BsonTimestamp) reference).timestamp()); 317 | } 318 | 319 | @Override 320 | public int getSize(@Nullable Object reference) { 321 | return ((BsonTimestamp) reference).timestamp().capacity(); 322 | } 323 | }, 324 | 325 | INT64 { 326 | 327 | @Override 328 | public void checkedWriteTo(ByteBuffer buffer, Object reference) { 329 | buffer.putLong(((Long) reference).longValue()); 330 | } 331 | 332 | @Override 333 | public int getSize(@Nullable Object reference) { 334 | return Longs.BYTES; 335 | } 336 | }; 337 | 338 | private static final int BOOLEANS_BYTES = 1; 339 | private static final int BYTES_BYTES = 1; 340 | 341 | @Override 342 | public final void writeTo(ByteBuffer buffer, Object reference) { 343 | Preconditions.checkNotNull(buffer, "null buffer"); 344 | Preconditions.checkArgument(buffer.order() == ByteOrder.LITTLE_ENDIAN, 345 | "buffer has big-endian byte order; expected little-endian"); 346 | checkedWriteTo(buffer, reference); 347 | } 348 | 349 | abstract void checkedWriteTo(ByteBuffer buffer, Object reference); 350 | } 351 | -------------------------------------------------------------------------------- /src/main/java/com/github/kohanyirobert/ebson/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * BSON encoder/decoder. 3 | */ 4 | @ParametersAreNonnullByDefault 5 | package com.github.kohanyirobert.ebson; 6 | 7 | import javax.annotation.ParametersAreNonnullByDefault; 8 | 9 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/AbstractBsonTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | public abstract class AbstractBsonTest { 7 | 8 | protected static final int MAX_BSON_SIZE = 16 * 1024 * 1024; 9 | 10 | protected static final ThreadLocal BUFFER = new ThreadLocal() { 11 | 12 | @Override 13 | protected ByteBuffer initialValue() { 14 | return ByteBuffer.allocate(MAX_BSON_SIZE).order(ByteOrder.LITTLE_ENDIAN); 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/AbstractReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | public abstract class AbstractReaderWriterTest extends AbstractBsonTest { 8 | 9 | protected final BsonReader reader; 10 | protected final BsonWriter writer; 11 | 12 | protected AbstractReaderWriterTest(BsonReader reader, BsonWriter writer) { 13 | this.reader = reader; 14 | this.writer = writer; 15 | } 16 | 17 | protected final Object writeTo(@Nullable Object object) { 18 | ByteBuffer buffer = BUFFER.get(); 19 | buffer.clear(); 20 | writer.writeTo(buffer, object); 21 | buffer.flip(); 22 | return object; 23 | } 24 | 25 | protected final Object readFrom() { 26 | return reader.readFrom(BUFFER.get()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/BsonDocumentTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Test; 7 | 8 | public final class BsonDocumentTest { 9 | 10 | private static final String STRING_KEY = "string-key"; 11 | private static final String BINARY_KEY = "binary-key"; 12 | private static final String NULL_KEY = "null-key"; 13 | private static final String MISSING_KEY = "string-key-not-present"; 14 | 15 | private static final Object STRING_VALUE = "string-value"; 16 | private static final Object BINARY_VALUE1 = new byte[] {42}; 17 | private static final Object BINARY_VALUE2 = new byte[] {42}; 18 | 19 | private final BsonDocument document1; 20 | private final BsonDocument document2; 21 | 22 | public BsonDocumentTest() { 23 | document1 = BsonDocuments.of( 24 | STRING_KEY, STRING_VALUE, 25 | BINARY_KEY, BINARY_VALUE1, 26 | NULL_KEY, null); 27 | 28 | document2 = BsonDocuments.of( 29 | STRING_KEY, STRING_VALUE, 30 | BINARY_KEY, BINARY_VALUE2, 31 | NULL_KEY, null); 32 | } 33 | 34 | @Test 35 | public void containsKey_withStringKey() { 36 | assertTrue(document1.containsKey(STRING_KEY)); 37 | } 38 | 39 | @Test(expected = ClassCastException.class) 40 | public void containsKey_withNotStringKey() { 41 | document1.containsKey(new Object()); 42 | } 43 | 44 | @Test 45 | public void get_keyPresent_withStringKey() { 46 | assertEquals(STRING_VALUE, document1.get(STRING_KEY)); 47 | } 48 | 49 | @Test(expected = IllegalArgumentException.class) 50 | public void get_keyNotPresent_withStringKey() { 51 | document1.get(MISSING_KEY); 52 | } 53 | 54 | @Test(expected = ClassCastException.class) 55 | public void get_keyPresent_withNotStringKey() { 56 | document1.get(new Object()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/BsonDocumentsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.collect.Maps; 4 | 5 | import static org.junit.Assert.assertArrayEquals; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | import org.junit.Test; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.util.Map; 13 | 14 | @SuppressWarnings("static-method") 15 | public final class BsonDocumentsTest extends AbstractBsonTest { 16 | 17 | private static final String KEY1 = "key1"; 18 | private static final String KEY2 = "key2"; 19 | private static final String KEY3 = "key3"; 20 | 21 | public BsonDocumentsTest() {} 22 | 23 | @Test 24 | public void of_nothing_shouldBeEmpty() { 25 | BsonDocument empty = BsonDocuments.of(); 26 | assertTrue(empty.keySet().isEmpty()); 27 | assertTrue(empty.values().isEmpty()); 28 | } 29 | 30 | @Test 31 | public void of_nothing_shouldEqualOtherEmptyDocuments() { 32 | BsonDocument empty = BsonDocuments.of(); 33 | assertEquals(empty, BsonDocuments.of()); 34 | assertEquals(empty, BsonDocuments.copyOf(empty)); 35 | assertEquals(empty, BsonDocuments.copyOf(Maps.newLinkedHashMap())); 36 | } 37 | 38 | @Test(expected = NullPointerException.class) 39 | public void copyOf_document_withNullDocument() { 40 | BsonDocuments.copyOf((BsonDocument) null); 41 | } 42 | 43 | @Test 44 | public void copyOf_map_withLegalKeys() { 45 | Map map = BsonDocuments.builder() 46 | .put(KEY1, null) 47 | .put(KEY2, null) 48 | .put(KEY3, null) 49 | .build(); 50 | BsonDocument document = BsonDocuments.copyOf(map); 51 | assertArrayEquals(map.keySet().toArray(), document.keySet().toArray()); 52 | assertArrayEquals(map.values().toArray(), document.values().toArray()); 53 | } 54 | 55 | @Test(expected = NullPointerException.class) 56 | public void copyOf_map_withNullMap() { 57 | BsonDocuments.copyOf((Map) null); 58 | } 59 | 60 | @Test(expected = NullPointerException.class) 61 | public void copyOf_map_withNullKey() { 62 | BsonDocuments.copyOf(BsonDocuments.builder() 63 | .put(KEY1, null) 64 | .put(KEY2, null) 65 | .put(null, null) 66 | .build()); 67 | } 68 | 69 | @Test 70 | public void of_singleKeyValuePair_withLegalKey() { 71 | BsonDocument document = BsonDocuments.of(KEY1, null); 72 | assertTrue(document.keySet().contains(KEY1)); 73 | } 74 | 75 | @Test(expected = NullPointerException.class) 76 | public void of_singleKeyValuePair_withNullKey() { 77 | BsonDocuments.of(null, null); 78 | } 79 | 80 | @Test 81 | public void of_multipleKeyValuePairs_withLegalKeys() { 82 | BsonDocument document = BsonDocuments.of(KEY1, null, KEY2, null, KEY3, null); 83 | assertArrayEquals(document.keySet().toArray(), new Object[] {KEY1, KEY2, KEY3}); 84 | } 85 | 86 | @Test(expected = NullPointerException.class) 87 | public void of_multipleKeyValuePairs_withNullKey() { 88 | BsonDocuments.of(KEY1, null, null, null, KEY3, null); 89 | } 90 | 91 | @Test(expected = IllegalArgumentException.class) 92 | public void of_multipleKeyValuePairs_withDuplicateKeys() { 93 | BsonDocuments.of(KEY1, null, KEY1, null, KEY3, null); 94 | } 95 | 96 | @Test 97 | public void readFrom_singleByteBuffer_withMultipleDocuments() { 98 | BsonDocument document1 = BsonDocuments.of(KEY1, null); 99 | BsonDocument document2 = BsonDocuments.of(KEY2, null); 100 | BsonDocument document3 = BsonDocuments.of(KEY3, null); 101 | 102 | ByteBuffer buffer = BUFFER.get(); 103 | 104 | buffer.clear(); 105 | 106 | BsonDocuments.writeTo(buffer, document1); 107 | BsonDocuments.writeTo(buffer, document3); 108 | BsonDocuments.writeTo(buffer, document2); 109 | 110 | buffer.flip(); 111 | 112 | assertEquals(BsonDocuments.readFrom(buffer), document1); 113 | assertEquals(BsonDocuments.readFrom(buffer), document3); 114 | assertEquals(BsonDocuments.readFrom(buffer), document2); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/BsonEncodingTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | 5 | import static org.junit.Assert.assertArrayEquals; 6 | 7 | import org.junit.Test; 8 | 9 | import org.bson.BSON; 10 | import org.bson.BasicBSONObject; 11 | 12 | import java.nio.ByteBuffer; 13 | import java.util.Arrays; 14 | import java.util.Date; 15 | import java.util.Map; 16 | import java.util.regex.Pattern; 17 | 18 | public final class BsonEncodingTest extends AbstractBsonTest { 19 | 20 | private static final String KEY = "key"; 21 | 22 | private final BsonWriter writer; 23 | 24 | public BsonEncodingTest() { 25 | writer = BsonToken.DOCUMENT.writer(); 26 | } 27 | 28 | @Test 29 | public void emptyDocument() { 30 | assertEncoding(ImmutableMap.of()); 31 | } 32 | 33 | @Test 34 | public void simpleDocument() { 35 | assertEncoding(ImmutableMap.of(KEY, "")); 36 | } 37 | 38 | @Test 39 | public void simpleEmbeddedDocument() { 40 | assertEncoding(ImmutableMap.of(KEY, ImmutableMap.of())); 41 | } 42 | 43 | @Test 44 | public void complexDocument() { 45 | assertEncoding(ImmutableMap.builder() 46 | .put("string", "value1") 47 | .put("double", Double.valueOf(-1)) 48 | .put("int32", Integer.valueOf(0)) 49 | .put("int64", Long.valueOf(1)) 50 | .put("binary", new byte[] {0, 1, 2}) 51 | .put("embedded", ImmutableMap.of()) 52 | .put("utc_date_time", new Date()) 53 | .put("regular_expression", Pattern.compile("^fourty-two$", Pattern.CASE_INSENSITIVE)) 54 | .put("array", new String[] {"one", "two", "...", "fourty-two"}) 55 | .put("collection", Arrays.asList( 56 | "Ford Prefect", 57 | "Arthur Dent", 58 | "Zaphod Beeblebrox", 59 | "Trillian", 60 | "Marvin")).build()); 61 | } 62 | 63 | private void assertEncoding(Object actualWrite) { 64 | ByteBuffer buffer = BUFFER.get(); 65 | buffer.clear(); 66 | writer.writeTo(buffer, actualWrite); 67 | buffer.flip(); 68 | byte[] actualBytes = new byte[buffer.remaining()]; 69 | buffer.get(actualBytes); 70 | byte[] expectedBytes = BSON.encode(new BasicBSONObject((Map) actualWrite)); 71 | assertArrayEquals(expectedBytes, actualBytes); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/BsonRandom.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import java.util.Random; 4 | 5 | public final class BsonRandom { 6 | 7 | private static final Random RANDOM = new Random(); 8 | 9 | private BsonRandom() {} 10 | 11 | public static void nextBytes(byte[] bytes) { 12 | RANDOM.nextBytes(bytes); 13 | } 14 | 15 | public static int nextInt() { 16 | return RANDOM.nextInt(); 17 | } 18 | 19 | public static int nextInt(int n) { 20 | return RANDOM.nextInt(n); 21 | } 22 | 23 | public static long nextLong() { 24 | return RANDOM.nextLong(); 25 | } 26 | 27 | public static boolean nextBoolean() { 28 | return RANDOM.nextBoolean(); 29 | } 30 | 31 | public static float nextFloat() { 32 | return RANDOM.nextFloat(); 33 | } 34 | 35 | public static double nextDouble() { 36 | return RANDOM.nextDouble(); 37 | } 38 | 39 | public static double nextGaussian() { 40 | return RANDOM.nextGaussian(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultArrayReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import java.lang.reflect.Array; 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.Map; 11 | 12 | public final class DefaultArrayReaderWriterTest extends AbstractReaderWriterTest { 13 | 14 | public DefaultArrayReaderWriterTest() { 15 | super(DefaultReader.ARRAY, DefaultWriter.ARRAY); 16 | } 17 | 18 | @Test 19 | public void emptyStringArray() { 20 | assertArray(writeTo(new String[] {}), readFrom()); 21 | } 22 | 23 | @Test 24 | public void emptyDoubleArray() { 25 | assertArray(writeTo(new double[] {}), readFrom()); 26 | } 27 | 28 | @Test 29 | public void simpleDoubleArray() { 30 | assertArray(writeTo(new double[] {Double.MIN_VALUE, Double.MAX_VALUE}), readFrom()); 31 | } 32 | 33 | @Test 34 | public void emptyCollection() { 35 | assertArray(writeTo(Collections.emptyList()), readFrom()); 36 | } 37 | 38 | private static void assertArray(Object expected, Object actual) { 39 | assertArrayEquals(toArray(expected), toArray(actual)); 40 | } 41 | 42 | // @do-not-check-next-line CyclomaticComplexity 43 | private static Object[] toArray(Object object) { 44 | Object[] array; 45 | if (object instanceof Object[]) { 46 | array = (Object[]) object; 47 | 48 | } else if (object instanceof Map) { 49 | array = ((Map) object).values().toArray(); 50 | 51 | } else if (object instanceof Collection) { 52 | array = ((Collection) object).toArray(); 53 | 54 | } else { 55 | int lenght = Array.getLength(object); 56 | array = new Object[lenght]; 57 | for (int i = 0; i < lenght; i++) { 58 | array[i] = Array.get(object, i); 59 | } 60 | } 61 | return array; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultBinaryReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Preconditions; 4 | import com.google.common.base.Predicate; 5 | 6 | import static org.junit.Assert.assertArrayEquals; 7 | import static org.junit.Assert.assertEquals; 8 | 9 | import org.junit.Test; 10 | 11 | import java.nio.ByteBuffer; 12 | 13 | import javax.annotation.Nullable; 14 | 15 | public final class DefaultBinaryReaderWriterTest extends AbstractReaderWriterTest { 16 | 17 | private static final int BINARY_LENGTH = 1024; 18 | 19 | private static final ThreadLocal RANDOM_BINARY = new ThreadLocal() { 20 | 21 | @Override 22 | protected byte[] initialValue() { 23 | byte[] bytes = new byte[BINARY_LENGTH]; 24 | BsonRandom.nextBytes(bytes); 25 | return bytes; 26 | } 27 | }; 28 | 29 | public DefaultBinaryReaderWriterTest() { 30 | super(DefaultReader.BINARY, DefaultWriter.BINARY); 31 | } 32 | 33 | @Test 34 | public void randomByteArray() { 35 | assertArrayEquals((byte[]) writeTo(RANDOM_BINARY.get()), (byte[]) readFrom()); 36 | } 37 | 38 | @Test 39 | public void customUserObject() { 40 | BsonBinary.USER.predicate(new UserPredicate()); 41 | BsonBinary.USER.reader(new UserReader()); 42 | BsonBinary.USER.writer(new UserWriter()); 43 | 44 | assertEquals(writeTo(new User("Ford", 42)), readFrom()); 45 | } 46 | 47 | private static final class UserPredicate implements Predicate> { 48 | 49 | public UserPredicate() {} 50 | 51 | @Override 52 | public boolean apply(@Nullable Class input) { 53 | return input == null 54 | ? false 55 | : User.class.isAssignableFrom(input); 56 | } 57 | } 58 | 59 | private static final class UserReader implements BsonReader { 60 | 61 | public UserReader() {} 62 | 63 | @Override 64 | @Nullable 65 | public Object readFrom(ByteBuffer buffer) { 66 | Object name = BsonObject.STRING.reader().readFrom(buffer); 67 | Object age = BsonObject.INT32.reader().readFrom(buffer); 68 | return new User((String) name, ((Integer) age).intValue()); 69 | } 70 | } 71 | 72 | private static final class UserWriter implements BsonWriter { 73 | 74 | public UserWriter() {} 75 | 76 | @Override 77 | public void writeTo(ByteBuffer buffer, Object reference) { 78 | User user = (User) reference; 79 | BsonObject.STRING.writer().writeTo(buffer, user.getName()); 80 | BsonObject.INT32.writer().writeTo(buffer, Integer.valueOf(user.getAge())); 81 | } 82 | 83 | @Override 84 | public int getSize(@Nullable Object reference) { 85 | User user = (User) reference; 86 | return BsonObject.STRING.writer().getSize(user.getName()) 87 | + BsonObject.INT32.writer().getSize(Integer.valueOf(user.getAge())); 88 | } 89 | } 90 | 91 | private static final class User { 92 | 93 | private final String name; 94 | private final int age; 95 | 96 | public User(String name, int age) { 97 | this.name = Preconditions.checkNotNull(name); 98 | this.age = age; 99 | } 100 | 101 | public String getName() { 102 | return name; 103 | } 104 | 105 | public int getAge() { 106 | return age; 107 | } 108 | 109 | @Override 110 | public int hashCode() { 111 | int hashCode = 17; 112 | return hashCode; 113 | } 114 | 115 | @Override 116 | public boolean equals(Object object) { 117 | if (object instanceof User) { 118 | User other = (User) object; 119 | return getName().equals(other.getName()) 120 | && getAge() == other.getAge(); 121 | } 122 | return false; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultBooleanReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultBooleanReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | public DefaultBooleanReaderWriterTest() { 10 | super(DefaultReader.BOOLEAN, DefaultWriter.BOOLEAN); 11 | } 12 | 13 | @Test 14 | public void trueValue() { 15 | assertEquals(writeTo(Boolean.TRUE), readFrom()); 16 | } 17 | 18 | @Test 19 | public void falseValue() { 20 | assertEquals(writeTo(Boolean.FALSE), readFrom()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultDoubleReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultDoubleReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | public DefaultDoubleReaderWriterTest() { 10 | super(DefaultReader.DOUBLE, DefaultWriter.DOUBLE); 11 | } 12 | 13 | @Test 14 | public void minDouble() { 15 | assertEquals(writeTo(Double.valueOf(Double.MIN_VALUE)), readFrom()); 16 | } 17 | 18 | @Test 19 | public void maxDouble() { 20 | assertEquals(writeTo(Double.valueOf(Double.MAX_VALUE)), readFrom()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultFieldReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.collect.Maps; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | import org.junit.Test; 8 | 9 | public final class DefaultFieldReaderWriterTest extends AbstractReaderWriterTest { 10 | 11 | public DefaultFieldReaderWriterTest() { 12 | super(DefaultReader.FIELD, DefaultWriter.FIELD); 13 | } 14 | 15 | @Test 16 | public void simpleField() { 17 | assertEquals(writeTo(Maps.immutableEntry("key", null)), readFrom()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultInt32ReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultInt32ReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | public DefaultInt32ReaderWriterTest() { 10 | super(DefaultReader.INT32, DefaultWriter.INT32); 11 | } 12 | 13 | @Test 14 | public void minInt() { 15 | assertEquals(writeTo(Integer.valueOf(Integer.MIN_VALUE)), readFrom()); 16 | } 17 | 18 | @Test 19 | public void maxInt() { 20 | assertEquals(writeTo(Integer.valueOf(Integer.MAX_VALUE)), readFrom()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultInt64ReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultInt64ReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | public DefaultInt64ReaderWriterTest() { 10 | super(DefaultReader.INT64, DefaultWriter.INT64); 11 | } 12 | 13 | @Test 14 | public void minLong() { 15 | assertEquals(writeTo(Long.valueOf(Long.MIN_VALUE)), readFrom()); 16 | } 17 | 18 | @Test 19 | public void maxLong() { 20 | assertEquals(writeTo(Long.valueOf(Long.MAX_VALUE)), readFrom()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultKeyReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultKeyReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | private static final String ASCII = "arvizturotukorfurogep"; 10 | private static final String NON_ASCII = "árvíztűrőtükörfúrógép"; 11 | 12 | public DefaultKeyReaderWriterTest() { 13 | super(DefaultReader.KEY, DefaultWriter.KEY); 14 | } 15 | 16 | @Test 17 | public void asciiString() { 18 | assertEquals(writeTo(ASCII), readFrom()); 19 | } 20 | 21 | @Test 22 | public void nonAsciiString() { 23 | assertEquals(writeTo(NON_ASCII), readFrom()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultNullReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | public final class DefaultNullReaderWriterTest extends AbstractReaderWriterTest { 8 | 9 | public DefaultNullReaderWriterTest() { 10 | super(DefaultReader.NULL, DefaultWriter.NULL); 11 | } 12 | 13 | @Test 14 | public void nullValue() { 15 | assertEquals(writeTo(null), readFrom()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultObjectIdReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.nio.ByteOrder; 9 | 10 | public final class DefaultObjectIdReaderWriterTest extends AbstractReaderWriterTest { 11 | 12 | private static final int OBJECT_ID_LENGTH = 12; 13 | private static final ThreadLocal RANDOM_OBJECT_ID = new ThreadLocal() { 14 | 15 | @Override 16 | protected ByteBuffer initialValue() { 17 | byte[] bytes = new byte[OBJECT_ID_LENGTH]; 18 | BsonRandom.nextBytes(bytes); 19 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 20 | } 21 | }; 22 | 23 | public DefaultObjectIdReaderWriterTest() { 24 | super(DefaultReader.OBJECT_ID, DefaultWriter.OBJECT_ID); 25 | } 26 | 27 | @Test 28 | public void randomObjectId() { 29 | assertEquals(writeTo(new BasicObjectId(RANDOM_OBJECT_ID.get())), readFrom()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultRegularExpressionReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import static java.util.regex.Pattern.CANON_EQ; 9 | import static java.util.regex.Pattern.CASE_INSENSITIVE; 10 | import static java.util.regex.Pattern.COMMENTS; 11 | import static java.util.regex.Pattern.DOTALL; 12 | import static java.util.regex.Pattern.LITERAL; 13 | import static java.util.regex.Pattern.MULTILINE; 14 | import static java.util.regex.Pattern.UNICODE_CASE; 15 | import static java.util.regex.Pattern.UNICODE_CHARACTER_CLASS; 16 | import static java.util.regex.Pattern.UNIX_LINES; 17 | import static java.util.regex.Pattern.compile; 18 | 19 | import java.util.regex.Pattern; 20 | 21 | public final class DefaultRegularExpressionReaderWriterTest extends AbstractReaderWriterTest { 22 | 23 | public DefaultRegularExpressionReaderWriterTest() { 24 | super(DefaultReader.REGULAR_EXPRESSION, DefaultWriter.REGULAR_EXPRESSION); 25 | } 26 | 27 | @Test 28 | public void emptyPattern() { 29 | assertPatternEquals(writeTo(compile("")), readFrom()); 30 | } 31 | 32 | @Test 33 | public void simplePattern() { 34 | assertPatternEquals(writeTo(compile("^42$")), readFrom()); 35 | } 36 | 37 | @Ignore("the 'CANON_EQ' flag isn't supported") 38 | @Test 39 | public void canonEqFlag() { 40 | assertPatternEquals(writeTo(compile("", CANON_EQ)), readFrom()); 41 | } 42 | 43 | @Test 44 | public void caseInsensitiveFlag() { 45 | assertPatternEquals(writeTo(compile("", CASE_INSENSITIVE)), readFrom()); 46 | } 47 | 48 | @Test 49 | public void commentsFlag() { 50 | assertPatternEquals(writeTo(compile("", COMMENTS)), readFrom()); 51 | } 52 | 53 | @Test 54 | public void dotallFlag() { 55 | assertPatternEquals(writeTo(compile("", DOTALL)), readFrom()); 56 | } 57 | 58 | @Ignore("the 'LITERAL' flag isn't supported") 59 | @Test 60 | public void literalFlag() { 61 | assertPatternEquals(writeTo(compile("", LITERAL)), readFrom()); 62 | } 63 | 64 | @Test 65 | public void multilineFlag() { 66 | assertPatternEquals(writeTo(compile("", MULTILINE)), readFrom()); 67 | } 68 | 69 | @Ignore("the 'UNICODE_CASE' flag isn't supported") 70 | @Test 71 | public void unicodeCaseFlag() { 72 | assertPatternEquals(writeTo(compile("", UNICODE_CASE)), readFrom()); 73 | } 74 | 75 | @Ignore("the 'UNICODE_CHARACTER_CLASS' flag isn't supported") 76 | @Test 77 | public void unicodeCharacterClassFlag() { 78 | assertPatternEquals(writeTo(compile("", UNICODE_CHARACTER_CLASS)), readFrom()); 79 | } 80 | 81 | @Ignore("the 'UNIX_LINES' flag isn't supported") 82 | @Test 83 | public void unixLinesFlag() { 84 | assertPatternEquals(writeTo(compile("", UNIX_LINES)), readFrom()); 85 | } 86 | 87 | @Test 88 | public void multipleFlags() { 89 | assertPatternEquals(writeTo(compile("", MULTILINE | CASE_INSENSITIVE | COMMENTS)), readFrom()); 90 | } 91 | 92 | private static void assertPatternEquals(Object expected, Object actual) { 93 | Pattern expectedPattern = (Pattern) expected; 94 | Pattern actualPattern = (Pattern) actual; 95 | assertEquals(expectedPattern.pattern(), actualPattern.pattern()); 96 | assertEquals(expectedPattern.flags(), actualPattern.flags()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultSymbolReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.primitives.Ints; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | import org.junit.Test; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.nio.ByteOrder; 12 | 13 | public final class DefaultSymbolReaderWriterTest extends AbstractReaderWriterTest { 14 | 15 | private static final String SYMBOL_ALPHABET = "abcdefghijklmnopqrstuvwxyz"; 16 | private static final int MAX_SYMBOL_LENGTH = 1024; 17 | private static final ThreadLocal RANDOM_SYMBOL = new ThreadLocal() { 18 | 19 | @Override 20 | protected ByteBuffer initialValue() { 21 | StringBuilder sb = new StringBuilder(); 22 | for (int i = 0; i < BsonRandom.nextInt(MAX_SYMBOL_LENGTH); i++) { 23 | sb.append(SYMBOL_ALPHABET.charAt(BsonRandom.nextInt(SYMBOL_ALPHABET.length()))); 24 | } 25 | ByteBuffer symbol = ByteBuffer.allocate(Ints.BYTES + sb.length() + 1) 26 | .order(ByteOrder.LITTLE_ENDIAN) 27 | .putInt(sb.length() + 1) 28 | .put(sb.toString().getBytes(Charsets.UTF_8)) 29 | .put(BsonBytes.EOO); 30 | symbol.flip(); 31 | return symbol; 32 | } 33 | }; 34 | 35 | public DefaultSymbolReaderWriterTest() { 36 | super(DefaultReader.SYMBOL, DefaultWriter.SYMBOL); 37 | } 38 | 39 | @Test 40 | public void randomSymbol() { 41 | assertEquals(writeTo(new BasicSymbol(RANDOM_SYMBOL.get())), readFrom()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultTimestampReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.nio.ByteOrder; 9 | 10 | public final class DefaultTimestampReaderWriterTest extends AbstractReaderWriterTest { 11 | 12 | private static final int TIMESTAMP_LENGTH = 8; 13 | private static final ThreadLocal RANDOM_TIMESTAMP = new ThreadLocal() { 14 | 15 | @Override 16 | protected ByteBuffer initialValue() { 17 | byte[] bytes = new byte[TIMESTAMP_LENGTH]; 18 | BsonRandom.nextBytes(bytes); 19 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); 20 | } 21 | }; 22 | 23 | public DefaultTimestampReaderWriterTest() { 24 | super(DefaultReader.TIMESTAMP, DefaultWriter.TIMESTAMP); 25 | } 26 | 27 | @Test 28 | public void randomTimestamp() { 29 | assertEquals(writeTo(new BasicTimestamp(RANDOM_TIMESTAMP.get())), readFrom()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/com/github/kohanyirobert/ebson/DefaultUtcDateTimeReaderWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.kohanyirobert.ebson; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | import java.util.Date; 8 | 9 | public final class DefaultUtcDateTimeReaderWriterTest extends AbstractReaderWriterTest { 10 | 11 | public DefaultUtcDateTimeReaderWriterTest() { 12 | super(DefaultReader.UTC_DATE_TIME, DefaultWriter.UTC_DATE_TIME); 13 | } 14 | 15 | @Test 16 | public void currentDate() { 17 | assertEquals(writeTo(new Date()), readFrom()); 18 | } 19 | } 20 | --------------------------------------------------------------------------------