Skip to content

Commit 77b549d

Browse files
authored
Deprecate Parameterizable, introduce default CodecProvider.get(Class<T>, List<Type>, CodecRegistry) instead (#1115)
JAVA-4967
1 parent 548bacd commit 77b549d

22 files changed

+416
-211
lines changed

bson-kotlin/src/main/kotlin/org/bson/codecs/kotlin/DataClassCodec.kt

Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import org.bson.BsonWriter
3535
import org.bson.codecs.Codec
3636
import org.bson.codecs.DecoderContext
3737
import org.bson.codecs.EncoderContext
38-
import org.bson.codecs.Parameterizable
3938
import org.bson.codecs.RepresentationConfigurable
4039
import org.bson.codecs.configuration.CodecConfigurationException
4140
import org.bson.codecs.configuration.CodecRegistry
@@ -136,31 +135,22 @@ internal data class DataClassCodec<T : Any>(
136135
): Codec<R>? {
137136
return if (!kClass.isData) {
138137
null
139-
} else if (kClass.typeParameters.isNotEmpty()) {
140-
RawDataClassCodec(kClass)
141138
} else {
142-
createDataClassCodec(kClass, codecRegistry, types)
139+
validateAnnotations(kClass)
140+
val primaryConstructor =
141+
kClass.primaryConstructor ?: throw CodecConfigurationException("No primary constructor for $kClass")
142+
val typeMap = types.mapIndexed { i, k -> primaryConstructor.typeParameters[i].createType() to k }
143+
.toMap()
144+
145+
val propertyModels =
146+
primaryConstructor.parameters.map { kParameter ->
147+
PropertyModel(
148+
kParameter, computeFieldName(kParameter), getCodec(kParameter, typeMap, codecRegistry))
149+
}
150+
return DataClassCodec(kClass, primaryConstructor, propertyModels)
143151
}
144152
}
145153

146-
private fun <R : Any> createDataClassCodec(
147-
kClass: KClass<R>,
148-
codecRegistry: CodecRegistry,
149-
types: List<Type> = emptyList()
150-
): Codec<R> {
151-
validateAnnotations(kClass)
152-
val primaryConstructor =
153-
kClass.primaryConstructor ?: throw CodecConfigurationException("No primary constructor for $kClass")
154-
val typeMap = types.mapIndexed { i, k -> primaryConstructor.typeParameters[i].createType() to k }.toMap()
155-
156-
val propertyModels =
157-
primaryConstructor.parameters.map { kParameter ->
158-
PropertyModel(
159-
kParameter, computeFieldName(kParameter), getCodec(kParameter, typeMap, codecRegistry))
160-
}
161-
return DataClassCodec(kClass, primaryConstructor, propertyModels)
162-
}
163-
164154
private fun <R : Any> validateAnnotations(kClass: KClass<R>) {
165155
codecConfigurationRequires(kClass.findAnnotation<BsonDiscriminator>() == null) {
166156
"""Annotation 'BsonDiscriminator' is not supported on kotlin data classes,
@@ -251,29 +241,5 @@ internal data class DataClassCodec<T : Any>(
251241
throw CodecConfigurationException(lazyMessage.invoke())
252242
}
253243
}
254-
255-
/**
256-
* A Raw unparameterized data class
257-
*
258-
* It cannot encode or decode it just can create parameterized DataClassCodecs
259-
*/
260-
internal data class RawDataClassCodec<T : Any>(private val kClass: KClass<T>) : Codec<T>, Parameterizable {
261-
262-
override fun getEncoderClass(): Class<T> = kClass.java
263-
264-
override fun parameterize(codecRegistry: CodecRegistry, types: List<Type>): Codec<*> {
265-
return createDataClassCodec(kClass, codecRegistry, types)
266-
}
267-
268-
override fun decode(reader: BsonReader?, decoderContext: DecoderContext?): T {
269-
throw CodecConfigurationException(
270-
"Can not decode to ${kClass.simpleName} as it has type parameters and has not been parameterized.")
271-
}
272-
273-
override fun encode(writer: BsonWriter?, value: T, encoderContext: EncoderContext?) {
274-
throw CodecConfigurationException(
275-
"Can not encode to ${kClass.simpleName} as it has type parameters and has not been parameterized.")
276-
}
277-
}
278244
}
279245
}

bson-kotlin/src/main/kotlin/org/bson/codecs/kotlin/DataClassCodecProvider.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ package org.bson.codecs.kotlin
1818
import org.bson.codecs.Codec
1919
import org.bson.codecs.configuration.CodecProvider
2020
import org.bson.codecs.configuration.CodecRegistry
21+
import java.lang.reflect.Type
2122

2223
/** A Kotlin reflection based Codec Provider for data classes */
2324
public class DataClassCodecProvider : CodecProvider {
2425
override fun <T : Any> get(clazz: Class<T>, registry: CodecRegistry): Codec<T>? =
25-
DataClassCodec.create(clazz.kotlin, registry)
26+
get(clazz, emptyList(), registry)
27+
28+
override fun <T : Any> get(clazz: Class<T>, typeArguments: List<Type>, registry: CodecRegistry): Codec<T>? =
29+
DataClassCodec.create(clazz.kotlin, registry, typeArguments)
2630
}

bson-kotlin/src/test/kotlin/org/bson/codecs/kotlin/DataClassCodecProviderTest.kt

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,16 @@
1616
package org.bson.codecs.kotlin
1717

1818
import com.mongodb.MongoClientSettings
19-
import kotlin.test.assertEquals
20-
import kotlin.test.assertNotNull
21-
import kotlin.test.assertNull
22-
import kotlin.test.assertTrue
23-
import org.bson.BsonDocument
24-
import org.bson.BsonDocumentReader
25-
import org.bson.BsonDocumentWriter
26-
import org.bson.codecs.DecoderContext
27-
import org.bson.codecs.EncoderContext
2819
import org.bson.codecs.configuration.CodecConfigurationException
29-
import org.bson.codecs.kotlin.samples.DataClassEmbedded
3020
import org.bson.codecs.kotlin.samples.DataClassParameterized
31-
import org.bson.codecs.kotlin.samples.DataClassWithParameterizedDataClass
3221
import org.bson.codecs.kotlin.samples.DataClassWithSimpleValues
3322
import org.bson.conversions.Bson
3423
import org.junit.jupiter.api.Test
3524
import org.junit.jupiter.api.assertThrows
25+
import kotlin.test.assertEquals
26+
import kotlin.test.assertNotNull
27+
import kotlin.test.assertNull
28+
import kotlin.test.assertTrue
3629

3730
class DataClassCodecProviderTest {
3831

@@ -52,27 +45,9 @@ class DataClassCodecProviderTest {
5245
}
5346

5447
@Test
55-
fun shouldReturnRawDataClassCodecForParameterizedDataClass() {
56-
val provider = DataClassCodecProvider()
57-
val codec = provider.get(DataClassParameterized::class.java, Bson.DEFAULT_CODEC_REGISTRY)
58-
59-
assertNotNull(codec)
60-
assertTrue { codec is DataClassCodec.Companion.RawDataClassCodec }
61-
assertEquals(DataClassParameterized::class.java, codec.encoderClass)
62-
63-
assertThrows<CodecConfigurationException> {
64-
val writer = BsonDocumentWriter(BsonDocument())
65-
val dataClass =
66-
DataClassWithParameterizedDataClass(
67-
"myId", DataClassParameterized(2.0, "myString", listOf(DataClassEmbedded("embedded1"))))
68-
codec.encode(writer, dataClass.parameterizedDataClass, EncoderContext.builder().build())
69-
}
70-
48+
fun shouldRequireTypeArgumentsForDataClassParameterized() {
7149
assertThrows<CodecConfigurationException> {
72-
val value =
73-
BsonDocument.parse(
74-
"""{"number": 2.0, "string": "myString", "parameterizedList": [{"name": "embedded1"}]}""")
75-
codec.decode(BsonDocumentReader(value), DecoderContext.builder().build())
50+
DataClassCodecProvider().get(DataClassParameterized::class.java, Bson.DEFAULT_CODEC_REGISTRY)
7651
}
7752
}
7853

bson-record-codec/src/main/org/bson/codecs/record/RecordCodec.java

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.bson.codecs.Codec;
2323
import org.bson.codecs.DecoderContext;
2424
import org.bson.codecs.EncoderContext;
25-
import org.bson.codecs.Parameterizable;
2625
import org.bson.codecs.RepresentationConfigurable;
2726
import org.bson.codecs.configuration.CodecConfigurationException;
2827
import org.bson.codecs.configuration.CodecRegistry;
@@ -50,10 +49,9 @@
5049
import static java.lang.String.format;
5150
import static org.bson.assertions.Assertions.notNull;
5251

53-
final class RecordCodec<T extends Record> implements Codec<T>, Parameterizable {
52+
final class RecordCodec<T extends Record> implements Codec<T> {
5453
private static final Logger LOGGER = Loggers.getLogger("RecordCodec");
5554
private final Class<T> clazz;
56-
private final boolean requiresParameterization;
5755
private final Constructor<?> canonicalConstructor;
5856
private final List<ComponentModel> componentModels;
5957
private final ComponentModel componentModelForId;
@@ -251,49 +249,21 @@ private static <T extends Annotation> void validateAnnotationOnlyOnField(final R
251249
}
252250
}
253251

254-
RecordCodec(final Class<T> clazz, final CodecRegistry codecRegistry) {
255-
this.clazz = notNull("class", clazz);
256-
if (clazz.getTypeParameters().length > 0) {
257-
requiresParameterization = true;
258-
canonicalConstructor = null;
259-
componentModels = null;
260-
fieldNameToComponentModel = null;
261-
componentModelForId = null;
262-
} else {
263-
requiresParameterization = false;
264-
canonicalConstructor = notNull("canonicalConstructor", getCanonicalConstructor(clazz));
265-
componentModels = getComponentModels(clazz, codecRegistry, List.of());
266-
fieldNameToComponentModel = componentModels.stream()
267-
.collect(Collectors.toMap(ComponentModel::getFieldName, Function.identity()));
268-
componentModelForId = getComponentModelForId(clazz, componentModels);
269-
}
270-
}
271-
272-
RecordCodec(final Class<T> clazz, final CodecRegistry codecRegistry, final List<Type> types) {
273-
if (types.size() != clazz.getTypeParameters().length || types.isEmpty()) {
252+
RecordCodec(final Class<T> clazz, final List<Type> types, final CodecRegistry codecRegistry) {
253+
if (types.size() != clazz.getTypeParameters().length) {
274254
throw new CodecConfigurationException("Unexpected number of type parameters for record class " + clazz);
275255
}
276256
this.clazz = notNull("class", clazz);
277-
requiresParameterization = false;
278257
canonicalConstructor = notNull("canonicalConstructor", getCanonicalConstructor(clazz));
279258
componentModels = getComponentModels(clazz, codecRegistry, types);
280259
fieldNameToComponentModel = componentModels.stream()
281260
.collect(Collectors.toMap(ComponentModel::getFieldName, Function.identity()));
282261
componentModelForId = getComponentModelForId(clazz, componentModels);
283262
}
284263

285-
@Override
286-
public Codec<?> parameterize(final CodecRegistry codecRegistry, final List<Type> types) {
287-
return new RecordCodec<>(clazz, codecRegistry, types);
288-
}
289-
290264
@SuppressWarnings("unchecked")
291265
@Override
292266
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
293-
if (requiresParameterization) {
294-
throw new CodecConfigurationException("Can not decode to a record with type parameters that has not been parameterized");
295-
}
296-
297267
reader.readStartDocument();
298268

299269
Object[] constructorArguments = new Object[componentModels.size()];
@@ -320,10 +290,6 @@ public T decode(final BsonReader reader, final DecoderContext decoderContext) {
320290

321291
@Override
322292
public void encode(final BsonWriter writer, final T record, final EncoderContext encoderContext) {
323-
if (requiresParameterization) {
324-
throw new CodecConfigurationException("Can not decode to a record with type parameters that has not been parameterized");
325-
}
326-
327293
writer.writeStartDocument();
328294
if (componentModelForId != null) {
329295
writeComponent(writer, record, componentModelForId);

bson-record-codec/src/main/org/bson/codecs/record/RecordCodecProvider.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,30 @@
2020
import org.bson.codecs.configuration.CodecProvider;
2121
import org.bson.codecs.configuration.CodecRegistry;
2222

23+
import java.lang.reflect.Type;
24+
import java.util.List;
25+
26+
import static org.bson.assertions.Assertions.assertNotNull;
27+
2328
/**
2429
* Provides Codec instances for Java records.
2530
*
2631
* @since 4.6
2732
* @see Record
2833
*/
2934
public final class RecordCodecProvider implements CodecProvider {
30-
@SuppressWarnings({"unchecked", "rawtypes"})
3135
@Override
3236
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
33-
if (!clazz.isRecord()) {
37+
return get(clazz, List.of(), registry);
38+
}
39+
40+
@Override
41+
public <T> Codec<T> get(final Class<T> clazz, final List<Type> typeArguments, final CodecRegistry registry) {
42+
if (!assertNotNull(clazz).isRecord()) {
3443
return null;
3544
}
36-
37-
return (Codec<T>) new RecordCodec(clazz, registry);
45+
@SuppressWarnings({"unchecked", "rawtypes"})
46+
Codec<T> result = new RecordCodec(clazz, assertNotNull(typeArguments), registry);
47+
return result;
3848
}
3949
}

0 commit comments

Comments
 (0)