/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bson;

import org.bson.io.BasicOutputBuffer;
import org.bson.io.OutputBuffer;
import org.bson.types.BSONTimestamp;
import org.bson.types.Binary;
import org.bson.types.Code;
import org.bson.types.CodeWScope;
import org.bson.types.Decimal128;
import org.bson.types.MaxKey;
import org.bson.types.MinKey;
import org.bson.types.ObjectId;
import org.bson.types.Symbol;

import java.lang.reflect.Array;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

This is meant to be pooled or cached. There is some per instance memory for string conversion, etc...
/** * This is meant to be pooled or cached. There is some per instance memory for string conversion, etc... */
@SuppressWarnings("deprecation") public class BasicBSONEncoder implements BSONEncoder { private BsonBinaryWriter bsonWriter; private OutputBuffer outputBuffer; @Override public byte[] encode(final BSONObject document) { OutputBuffer outputBuffer = new BasicOutputBuffer(); set(outputBuffer); putObject(document); done(); return outputBuffer.toByteArray(); } @Override public void done() { this.bsonWriter.close(); this.bsonWriter = null; } @Override public void set(final OutputBuffer buffer) { if (this.bsonWriter != null) { throw new IllegalStateException("Performing another operation at this moment"); } outputBuffer = buffer; this.bsonWriter = new BsonBinaryWriter(buffer); }
Gets the buffer the BSON is being encoded into.
Returns:the OutputBuffer
/** * Gets the buffer the BSON is being encoded into. * * @return the OutputBuffer */
protected OutputBuffer getOutputBuffer() { return outputBuffer; }
Gets the writer responsible for writing the encoded BSON.
Returns:the writer used to write the encoded BSON
/** * Gets the writer responsible for writing the encoded BSON. * * @return the writer used to write the encoded BSON */
protected BsonBinaryWriter getBsonWriter() { return bsonWriter; }
Encodes a BSONObject. This is for the higher level api calls.
Params:
  • document – the document to encode
Returns:the number of characters in the encoding
/** * Encodes a {@code BSONObject}. This is for the higher level api calls. * * @param document the document to encode * @return the number of characters in the encoding */
@Override public int putObject(final BSONObject document) { int startPosition = getOutputBuffer().getPosition(); bsonWriter.writeStartDocument(); if (isTopLevelDocument() && document.containsField("_id")) { _putObjectField("_id", document.get("_id")); } for (final String key : document.keySet()) { if (isTopLevelDocument() && key.equals("_id")) { continue; } _putObjectField(key, document.get(key)); } bsonWriter.writeEndDocument(); return getOutputBuffer().getPosition() - startPosition; } private boolean isTopLevelDocument() { return bsonWriter.getContext().getParentContext() == null; }
Writes a field name
Params:
  • name – the field name
/** * Writes a field name * * @param name the field name */
protected void putName(final String name) { if (bsonWriter.getState() == AbstractBsonWriter.State.NAME) { bsonWriter.writeName(name); } }
Encodes any Object type
Params:
  • name – the field name
  • initialValue – the value to write
/** * Encodes any Object type * * @param name the field name * @param initialValue the value to write */
protected void _putObjectField(final String name, final Object initialValue) { if ("_transientFields".equals(name)) { return; } if (name.contains("\0")) { throw new IllegalArgumentException("Document field names can't have a NULL character. (Bad Key: '" + name + "')"); } if ("$where".equals(name) && initialValue instanceof String) { putCode(name, new Code((String) initialValue)); } Object value = BSON.applyEncodingHooks(initialValue); if (value == null) { putNull(name); } else if (value instanceof Date) { putDate(name, (Date) value); } else if (value instanceof Decimal128) { putDecimal128(name, (Decimal128) value); } else if (value instanceof Number) { putNumber(name, (Number) value); } else if (value instanceof Character) { putString(name, value.toString()); } else if (value instanceof String) { putString(name, value.toString()); } else if (value instanceof ObjectId) { putObjectId(name, (ObjectId) value); } else if (value instanceof Boolean) { putBoolean(name, (Boolean) value); } else if (value instanceof Pattern) { putPattern(name, (Pattern) value); } else if (value instanceof Iterable) { putIterable(name, (Iterable) value); } else if (value instanceof BSONObject) { putObject(name, (BSONObject) value); } else if (value instanceof Map) { putMap(name, (Map) value); } else if (value instanceof byte[]) { putBinary(name, (byte[]) value); } else if (value instanceof Binary) { putBinary(name, (Binary) value); } else if (value instanceof UUID) { putUUID(name, (UUID) value); } else if (value.getClass().isArray()) { putArray(name, value); } else if (value instanceof Symbol) { putSymbol(name, (Symbol) value); } else if (value instanceof BSONTimestamp) { putTimestamp(name, (BSONTimestamp) value); } else if (value instanceof CodeWScope) { putCodeWScope(name, (CodeWScope) value); } else if (value instanceof Code) { putCode(name, (Code) value); } else if (value instanceof MinKey) { putMinKey(name); } else if (value instanceof MaxKey) { putMaxKey(name); } else if (putSpecial(name, value)) { // no-op } else { throw new IllegalArgumentException("Can't serialize " + value.getClass()); } }
Encodes a null value
Params:
  • name – the field name
See Also:
/** * Encodes a null value * * @param name the field name * @see org.bson.BsonType#NULL */
protected void putNull(final String name) { putName(name); bsonWriter.writeNull(); }
Encodes an undefined value
Params:
  • name – the field name
See Also:
/** * Encodes an undefined value * * @param name the field name * @see org.bson.BsonType#UNDEFINED */
protected void putUndefined(final String name) { putName(name); bsonWriter.writeUndefined(); }
Encodes a BSON timestamp
Params:
  • name – the field name
  • timestamp – the timestamp to encode
See Also:
/** * Encodes a BSON timestamp * * @param name the field name * @param timestamp the timestamp to encode * @see org.bson.BsonType#TIMESTAMP */
protected void putTimestamp(final String name, final BSONTimestamp timestamp) { putName(name); bsonWriter.writeTimestamp(new BsonTimestamp(timestamp.getTime(), timestamp.getInc())); }
Encodes a field to a BsonType.JAVASCRIPT value.
Params:
  • name – the field name
  • code – the value
/** * Encodes a field to a {@link org.bson.BsonType#JAVASCRIPT} value. * * @param name the field name * @param code the value */
protected void putCode(final String name, final Code code) { putName(name); bsonWriter.writeJavaScript(code.getCode()); }
Encodes a field to a BsonType.JAVASCRIPT_WITH_SCOPE value.
Params:
  • name – the field name
  • codeWScope – the value
/** * Encodes a field to a {@link org.bson.BsonType#JAVASCRIPT_WITH_SCOPE} value. * * @param name the field name * @param codeWScope the value */
protected void putCodeWScope(final String name, final CodeWScope codeWScope) { putName(name); bsonWriter.writeJavaScriptWithScope(codeWScope.getCode()); putObject(codeWScope.getScope()); }
Encodes a field with a Boolean or boolean value
Params:
  • name – the field name
  • value – the value
/** * Encodes a field with a {@code Boolean} or {@code boolean} value * * @param name the field name * @param value the value */
protected void putBoolean(final String name, final Boolean value) { putName(name); bsonWriter.writeBoolean(value); }
Encodes a field with data and time value.
Params:
  • name – the field name
  • date – the value
See Also:
/** * Encodes a field with data and time value. * * @param name the field name * @param date the value * @see org.bson.BsonType#DATE_TIME */
protected void putDate(final String name, final Date date) { putName(name); bsonWriter.writeDateTime(date.getTime()); }
Encodes any number field.
Params:
  • name – the field name
  • number – the value
/** * Encodes any number field. * * @param name the field name * @param number the value */
protected void putNumber(final String name, final Number number) { putName(name); if (number instanceof Integer || number instanceof Short || number instanceof Byte || number instanceof AtomicInteger) { bsonWriter.writeInt32(number.intValue()); } else if (number instanceof Long || number instanceof AtomicLong) { bsonWriter.writeInt64(number.longValue()); } else if (number instanceof Float || number instanceof Double) { bsonWriter.writeDouble(number.doubleValue()); } else { throw new IllegalArgumentException("Can't serialize " + number.getClass()); } }
Encodes a Decimal128 field.
Params:
  • name – the field name
  • value – the value
Since:3.4
@mongodb.server.release3.4
/** * Encodes a Decimal128 field. * * @param name the field name * @param value the value * @since 3.4 * @mongodb.server.release 3.4 */
protected void putDecimal128(final String name, final Decimal128 value) { putName(name); bsonWriter.writeDecimal128(value); }
Encodes a byte array field
Params:
  • name – the field name
  • bytes – the value
See Also:
/** * Encodes a byte array field * * @param name the field name * @param bytes the value * @see org.bson.BsonType#BINARY */
protected void putBinary(final String name, final byte[] bytes) { putName(name); bsonWriter.writeBinaryData(new BsonBinary(bytes)); }
Encodes a Binary field
Params:
  • name – the field name
  • binary – the value
See Also:
/** * Encodes a Binary field * * @param name the field name * @param binary the value * @see org.bson.BsonType#BINARY */
protected void putBinary(final String name, final Binary binary) { putName(name); bsonWriter.writeBinaryData(new BsonBinary(binary.getType(), binary.getData())); }
Encodes a field with a UUID value. This is encoded to a binary value of subtype BsonBinarySubType.UUID_LEGACY
Params:
  • name – the field name
  • uuid – the value
/** * Encodes a field with a {@link java.util.UUID} value. This is encoded to a binary value of subtype {@link * org.bson.BsonBinarySubType#UUID_LEGACY} * * @param name the field name * @param uuid the value */
protected void putUUID(final String name, final UUID uuid) { putName(name); byte[] bytes = new byte[16]; writeLongToArrayLittleEndian(bytes, 0, uuid.getMostSignificantBits()); writeLongToArrayLittleEndian(bytes, 8, uuid.getLeastSignificantBits()); bsonWriter.writeBinaryData(new BsonBinary(BsonBinarySubType.UUID_LEGACY, bytes)); }
Encodes a Symbol field
Params:
  • name – the field name
  • symbol – the value
See Also:
/** * Encodes a Symbol field * * @param name the field name * @param symbol the value * @see org.bson.BsonType#SYMBOL */
protected void putSymbol(final String name, final Symbol symbol) { putName(name); bsonWriter.writeSymbol(symbol.getSymbol()); }
Encodes a String field
Params:
  • name – the field name
  • value – the value
See Also:
/** * Encodes a String field * * @param name the field name * @param value the value * @see org.bson.BsonType#STRING */
protected void putString(final String name, final String value) { putName(name); bsonWriter.writeString(value); }
Encodes a Pattern field to a BsonType.REGULAR_EXPRESSION.
Params:
  • name – the field name
  • value – the value
See Also:
@mongodb.driver.manualreference/operator/query/regex/ $regex
/** * Encodes a Pattern field to a {@link org.bson.BsonType#REGULAR_EXPRESSION}. * * @param name the field name * @param value the value * @mongodb.driver.manual reference/operator/query/regex/ $regex * @see org.bson.BsonType#BINARY */
protected void putPattern(final String name, final Pattern value) { putName(name); bsonWriter.writeRegularExpression(new BsonRegularExpression(value.pattern(), org.bson.BSON.regexFlags(value.flags()))); }
Encodes an ObjectId field to a BsonType.OBJECT_ID.
Params:
  • name – the field name
  • objectId – the value
/** * Encodes an ObjectId field to a {@link org.bson.BsonType#OBJECT_ID}. * * @param name the field name * @param objectId the value */
protected void putObjectId(final String name, final ObjectId objectId) { putName(name); bsonWriter.writeObjectId(objectId); }
Encodes an array field.
Params:
  • name – the field name
  • object – the array, which can be any sort of primitive or String array
/** * Encodes an array field. * * @param name the field name * @param object the array, which can be any sort of primitive or String array */
protected void putArray(final String name, final Object object) { putName(name); bsonWriter.writeStartArray(); if (object instanceof int[]) { for (final int i : (int[]) object) { bsonWriter.writeInt32(i); } } else if (object instanceof long[]) { for (final long i : (long[]) object) { bsonWriter.writeInt64(i); } } else if (object instanceof float[]) { for (final float i : (float[]) object) { bsonWriter.writeDouble(i); } } else if (object instanceof short[]) { for (final short i : (short[]) object) { bsonWriter.writeInt32(i); } } else if (object instanceof byte[]) { for (final byte i : (byte[]) object) { bsonWriter.writeInt32(i); } } else if (object instanceof double[]) { for (final double i : (double[]) object) { bsonWriter.writeDouble(i); } } else if (object instanceof boolean[]) { for (final boolean i : (boolean[]) object) { bsonWriter.writeBoolean(i); } } else if (object instanceof String[]) { for (final String i : (String[]) object) { bsonWriter.writeString(i); } } else { int length = Array.getLength(object); for (int i = 0; i < length; i++) { _putObjectField(String.valueOf(i), Array.get(object, i)); } } bsonWriter.writeEndArray(); }
Encodes an Iterable, for example List values
Params:
  • name – the field name
  • iterable – the value
/** * Encodes an Iterable, for example {@code List} values * * @param name the field name * @param iterable the value */
@SuppressWarnings("rawtypes") protected void putIterable(final String name, final Iterable iterable) { putName(name); bsonWriter.writeStartArray(); int i = 0; for (final Object o : iterable) { _putObjectField(String.valueOf(i), o); } bsonWriter.writeEndArray(); }
Encodes a map, as a BSON document
Params:
  • name – the field name
  • map – the value
/** * Encodes a map, as a BSON document * * @param name the field name * @param map the value */
@SuppressWarnings({"rawtypes", "unchecked"}) protected void putMap(final String name, final Map map) { putName(name); bsonWriter.writeStartDocument(); for (final Map.Entry entry : (Set<Map.Entry>) map.entrySet()) { _putObjectField((String) entry.getKey(), entry.getValue()); } bsonWriter.writeEndDocument(); }
Encodes any BSONObject, as a document
Params:
  • name – the field name
  • document – the value
Returns:the number of characters in the encoding
/** * Encodes any {@code BSONObject}, as a document * * @param name the field name * @param document the value * @return the number of characters in the encoding */
protected int putObject(final String name, final BSONObject document) { putName(name); return putObject(document); }
Special values are not encoded into documents.
Params:
  • name – the field name
  • special – the value
Returns:true if the operation is successful. This implementation always returns false.
/** * Special values are not encoded into documents. * * @param name the field name * @param special the value * @return true if the operation is successful. This implementation always returns false. */
protected boolean putSpecial(final String name, final Object special) { return false; }
Encodes a field to a BsonType.MIN_KEY value.
Params:
  • name – the field name
/** * Encodes a field to a {@link org.bson.BsonType#MIN_KEY} value. * * @param name the field name */
protected void putMinKey(final String name) { putName(name); bsonWriter.writeMinKey(); }
Encodes a field to a BsonType.MAX_KEY value.
Params:
  • name – the field name
/** * Encodes a field to a {@link org.bson.BsonType#MAX_KEY} value. * * @param name the field name */
protected void putMaxKey(final String name) { putName(name); bsonWriter.writeMaxKey(); } private static void writeLongToArrayLittleEndian(final byte[] bytes, final int offset, final long x) { bytes[offset] = (byte) (0xFFL & (x)); bytes[offset + 1] = (byte) (0xFFL & (x >> 8)); bytes[offset + 2] = (byte) (0xFFL & (x >> 16)); bytes[offset + 3] = (byte) (0xFFL & (x >> 24)); bytes[offset + 4] = (byte) (0xFFL & (x >> 32)); bytes[offset + 5] = (byte) (0xFFL & (x >> 40)); bytes[offset + 6] = (byte) (0xFFL & (x >> 48)); bytes[offset + 7] = (byte) (0xFFL & (x >> 56)); } }