/*
* Copyright 2017 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.codecs.pojo;
import org.bson.BsonBinaryReader;
import org.bson.BsonBinaryWriter;
import org.bson.BsonDocument;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.ByteBufNIO;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.ValueCodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.entities.CollectionNestedPojoModel;
import org.bson.codecs.pojo.entities.ConcreteCollectionsModel;
import org.bson.codecs.pojo.entities.ConventionModel;
import org.bson.codecs.pojo.entities.GenericHolderModel;
import org.bson.codecs.pojo.entities.GenericTreeModel;
import org.bson.codecs.pojo.entities.NestedGenericHolderMapModel;
import org.bson.codecs.pojo.entities.NestedGenericHolderModel;
import org.bson.codecs.pojo.entities.NestedReusedGenericsModel;
import org.bson.codecs.pojo.entities.PrimitivesModel;
import org.bson.codecs.pojo.entities.ReusedGenericsModel;
import org.bson.codecs.pojo.entities.ShapeModelCircle;
import org.bson.codecs.pojo.entities.ShapeModelRectangle;
import org.bson.codecs.pojo.entities.SimpleGenericsModel;
import org.bson.codecs.pojo.entities.SimpleModel;
import org.bson.codecs.pojo.entities.SimpleNestedPojoModel;
import org.bson.io.BasicOutputBuffer;
import org.bson.io.ByteBufferBsonInput;
import org.bson.io.OutputBuffer;
import org.bson.types.ObjectId;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.junit.Assert.assertEquals;
abstract class PojoTestCase {
static final BsonDocumentCodec DOCUMENT_CODEC = new BsonDocumentCodec();
@SuppressWarnings("unchecked")
<T> void roundTrip(final PojoCodecProvider.Builder builder, final T value, final String json) {
encodesTo(getCodecRegistry(builder), value, json);
decodesTo(getCodecRegistry(builder), json, value);
}
@SuppressWarnings("unchecked")
<T> void roundTrip(final CodecRegistry registry, final T value, final String json) {
encodesTo(registry, value, json);
decodesTo(registry, json, value);
}
@SuppressWarnings("unchecked")
<T> void encodesTo(final CodecRegistry registry, final T value, final String json) {
Codec<T> codec = (Codec<T>) registry.get(value.getClass());
encodesTo(codec, value, json);
}
@SuppressWarnings("unchecked")
<T> void encodesTo(final Codec<T> codec, final T value, final String json) {
OutputBuffer encoded = encode(codec, value);
BsonDocument asBsonDocument = decode(DOCUMENT_CODEC, encoded);
assertEquals("Encoded value", BsonDocument.parse(json), asBsonDocument);
}
@SuppressWarnings("unchecked")
<T> void decodesTo(final CodecRegistry registry, final String json, final T expected) {
Codec<T> codec = (Codec<T>) registry.get(expected.getClass());
decodesTo(codec, json, expected);
}
<T> void decodesTo(final Codec<T> codec, final String json, final T expected) {
OutputBuffer encoded = encode(DOCUMENT_CODEC, BsonDocument.parse(json));
T result = decode(codec, encoded);
assertEquals("Decoded value", expected, result);
}
<T> void decodingShouldFail(final Codec<T> codec, final String json) {
decodesTo(codec, json, null);
}
<T> OutputBuffer encode(final Codec<T> codec, final T value) {
OutputBuffer buffer = new BasicOutputBuffer();
BsonWriter writer = new BsonBinaryWriter(buffer);
codec.encode(writer, value, EncoderContext.builder().build());
return buffer;
}
<T> T decode(final Codec<T> codec, final OutputBuffer buffer) {
BsonBinaryReader reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(buffer.toByteArray()))));
return codec.decode(reader, DecoderContext.builder().build());
}
PojoCodecProvider.Builder getPojoCodecProviderBuilder(final Class<?>... classes) {
PojoCodecProvider.Builder builder = PojoCodecProvider.builder();
for (final Class<?> clazz : classes) {
builder.register(clazz);
}
return builder;
}
<T> PojoCodec<T> getCodec(final PojoCodecProvider.Builder builder, final Class<T> clazz) {
return (PojoCodec<T>) getCodecRegistry(builder).get(clazz);
}
<T> PojoCodec<T> getCodec(final Class<T> clazz) {
return getCodec(getPojoCodecProviderBuilder(clazz), clazz);
}
PojoCodecProvider.Builder getPojoCodecProviderBuilder(final ClassModelBuilder<?>... classModelBuilders) {
List<ClassModel<?>> builders = new ArrayList<ClassModel<?>>();
for (ClassModelBuilder<?> classModelBuilder : classModelBuilders) {
builders.add(classModelBuilder.build());
}
return PojoCodecProvider.builder().register(builders.toArray(new ClassModel<?>[builders.size()]));
}
CodecRegistry getCodecRegistry(final Class<?>... classes) {
return getCodecRegistry(getPojoCodecProviderBuilder(classes));
}
CodecRegistry getCodecRegistry(final PojoCodecProvider.Builder builder) {
return fromProviders(builder.build(), new ValueCodecProvider());
}
SimpleModel getSimpleModel() {
return new SimpleModel(42, "myString");
}
PrimitivesModel getPrimitivesModel() {
return new PrimitivesModel(true, Byte.parseByte("1", 2), '1', 1.0, 2f, 3, 5L, (short) 6);
}
SimpleGenericsModel<String, String, Integer> getSimpleGenericsModel() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("D", 2);
map.put("E", 3);
map.put("F", 4);
return new SimpleGenericsModel<String, String, Integer>(42, "A", asList("B", "C"), map);
}
SimpleGenericsModel<Long, String, Integer> getSimpleGenericsModelAlt() {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("D", 2);
map.put("E", 3);
map.put("F", 4);
return new SimpleGenericsModel<Long, String, Integer>(42, 101L, asList("B", "C"), map);
}
ConcreteCollectionsModel getConcreteCollectionsModel() {
Collection<Integer> collection = asList(1, 2, 3);
List<Integer> list = asList(4, 5, 6);
LinkedList<Integer> linked = new LinkedList<Integer>(asList(7, 8, 9));
Map<String, Double> map = new HashMap<String, Double>();
map.put("A", 1.1);
map.put("B", 2.2);
map.put("C", 3.3);
ConcurrentHashMap<String, Double> concurrent = new ConcurrentHashMap<String, Double>();
concurrent.put("D", 4.4);
concurrent.put("E", 5.5);
concurrent.put("F", 6.6);
return new ConcreteCollectionsModel(collection, list, linked, map, concurrent);
}
SimpleNestedPojoModel getSimpleNestedPojoModel() {
SimpleModel simpleModel = getSimpleModel();
return new SimpleNestedPojoModel(simpleModel);
}
CollectionNestedPojoModel getCollectionNestedPojoModel() {
SimpleModel simpleModel = getSimpleModel();
List<SimpleModel> listSimple = singletonList(simpleModel);
List<List<SimpleModel>> listListSimple = singletonList(listSimple);
Set<SimpleModel> setSimple = new HashSet<SimpleModel>(listSimple);
Set<Set<SimpleModel>> setSetSimple = new HashSet<Set<SimpleModel>>(singletonList(setSimple));
Map<String, SimpleModel> mapSimple = new HashMap<String, SimpleModel>();
mapSimple.put("s", simpleModel);
Map<String, Map<String, SimpleModel>> mapMapSimple = new HashMap<String, Map<String, SimpleModel>>();
mapMapSimple.put("ms", mapSimple);
Map<String, List<SimpleModel>> mapListSimple = new HashMap<String, List<SimpleModel>>();
mapListSimple.put("ls", listSimple);
Map<String, List<Map<String, SimpleModel>>> mapListMapSimple = new HashMap<String, List<Map<String, SimpleModel>>>();
mapListMapSimple.put("lm", singletonList(mapSimple));
Map<String, Set<SimpleModel>> mapSetSimple = new HashMap<String, Set<SimpleModel>>();
mapSetSimple.put("s", setSimple);
List<Map<String, SimpleModel>> listMapSimple = singletonList(mapSimple);
List<Map<String, List<SimpleModel>>> listMapListSimple = singletonList(mapListSimple);
List<Map<String, Set<SimpleModel>>> listMapSetSimple = singletonList(mapSetSimple);
return new CollectionNestedPojoModel(listSimple, listListSimple, setSimple, setSetSimple, mapSimple, mapMapSimple, mapListSimple,
mapListMapSimple, mapSetSimple, listMapSimple, listMapListSimple, listMapSetSimple);
}
ConventionModel getConventionModel() {
SimpleModel simpleModel = getSimpleModel();
ConventionModel child = new ConventionModel("child", null, simpleModel);
return new ConventionModel("id", child, null);
}
ShapeModelCircle getShapeModelCircle() {
return new ShapeModelCircle("orange", 4.2);
}
ShapeModelRectangle getShapeModelRectangle() {
return new ShapeModelRectangle("green", 22.1, 105.0);
}
NestedGenericHolderModel getNestedGenericHolderModel() {
return new NestedGenericHolderModel(new GenericHolderModel<String>("generic", 1L));
}
NestedGenericHolderMapModel getNestedGenericHolderMapModel() {
Map<String, SimpleModel> mapSimple = new HashMap<String, SimpleModel>();
mapSimple.put("s", getSimpleModel());
return new NestedGenericHolderMapModel(new GenericHolderModel<Map<String, SimpleModel>>(mapSimple, 1L));
}
NestedReusedGenericsModel getNestedReusedGenericsModel() {
return new NestedReusedGenericsModel(new ReusedGenericsModel<Long, List<SimpleModel>, String>(1L,
singletonList(getSimpleModel()), "field3", 42, "field5", asList(getSimpleModel(), getSimpleModel()), 2L, "field8"));
}
GenericTreeModel<String, Integer> getGenericTreeModel() {
return new GenericTreeModel<String, Integer>("top", 1,
new GenericTreeModel<String, Integer>("left", 2,
new GenericTreeModel<String, Integer>("left", 3, null, null), null),
new GenericTreeModel<String, Integer>("right", 4,
new GenericTreeModel<String, Integer>("left", 5, null, null), null));
}
GenericTreeModel<String, String> getGenericTreeModelStrings() {
return new GenericTreeModel<String, String>("top", "1",
new GenericTreeModel<String, String>("left", "2",
new GenericTreeModel<String, String>("left", "3", null, null), null),
new GenericTreeModel<String, String>("right", "4",
new GenericTreeModel<String, String>("left", "5", null, null), null));
}
static final String SIMPLE_MODEL_JSON = "{'integerField': 42, 'stringField': 'myString'}";
class StringToObjectIdCodec implements Codec<String> {
@Override
public void encode(final BsonWriter writer, final String value, final EncoderContext encoderContext) {
writer.writeObjectId(new ObjectId(value));
}
@Override
public Class<String> getEncoderClass() {
return String.class;
}
@Override
public String decode(final BsonReader reader, final DecoderContext decoderContext) {
return reader.readObjectId().toHexString();
}
}
}