Skip to content

Commit 14cfcdc

Browse files
authored
Remove specialization-related conditional logic from PojoCodecImpl by introducing LazyPropertyModelCodec.NeedsSpecializationCodec (#1136)
This commit moves some of the conditional logic from `PojoCodecImpl` under the rug, thus allegedly simplifying `PojoCodecImpl`, but not the POJO codec implementation overall. JAVA-4954
1 parent 50a01d4 commit 14cfcdc

File tree

4 files changed

+79
-45
lines changed

4 files changed

+79
-45
lines changed

bson/src/main/org/bson/codecs/pojo/LazyPropertyModelCodec.java

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,21 @@
2828
import java.util.ArrayList;
2929
import java.util.List;
3030

31+
import static java.lang.String.format;
3132
import static org.bson.codecs.pojo.PojoSpecializationHelper.specializeTypeData;
3233

3334
class LazyPropertyModelCodec<T> implements Codec<T> {
3435
private final PropertyModel<T> propertyModel;
3536
private final CodecRegistry registry;
3637
private final PropertyCodecRegistry propertyCodecRegistry;
37-
private final DiscriminatorLookup discriminatorLookup;
3838

3939
private volatile Codec<T> codec;
4040

4141
LazyPropertyModelCodec(final PropertyModel<T> propertyModel, final CodecRegistry registry,
42-
final PropertyCodecRegistry propertyCodecRegistry, final DiscriminatorLookup discriminatorLookup) {
42+
final PropertyCodecRegistry propertyCodecRegistry) {
4343
this.propertyModel = propertyModel;
4444
this.registry = registry;
4545
this.propertyCodecRegistry = propertyCodecRegistry;
46-
this.discriminatorLookup = discriminatorLookup;
4746
}
4847

4948
@Override
@@ -77,7 +76,7 @@ private Codec<T> createCodec() {
7776
if (localCodec instanceof PojoCodec) {
7877
PojoCodec<T> pojoCodec = (PojoCodec<T>) localCodec;
7978
ClassModel<T> specialized = getSpecializedClassModel(pojoCodec.getClassModel(), propertyModel);
80-
localCodec = new PojoCodecImpl<>(specialized, registry, propertyCodecRegistry, pojoCodec.getDiscriminatorLookup(), true);
79+
localCodec = new PojoCodecImpl<>(specialized, registry, propertyCodecRegistry, pojoCodec.getDiscriminatorLookup());
8180
}
8281
return localCodec;
8382
}
@@ -150,4 +149,46 @@ private <V> PropertyModel<V> getSpecializedPropertyModel(final PropertyModel<V>
150149
propertyModel.getPropertyAccessor(), propertyModel.getError(), propertyModel.getBsonRepresentation());
151150
}
152151

152+
/**
153+
* Instances of this codec are supposed to be replaced with usable implementations by {@link LazyPropertyModelCodec#createCodec()}.
154+
*/
155+
static final class NeedSpecializationCodec<T> extends PojoCodec<T> {
156+
private final ClassModel<T> classModel;
157+
private final DiscriminatorLookup discriminatorLookup;
158+
159+
NeedSpecializationCodec(final ClassModel<T> classModel, final DiscriminatorLookup discriminatorLookup) {
160+
this.classModel = classModel;
161+
this.discriminatorLookup = discriminatorLookup;
162+
}
163+
164+
@Override
165+
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
166+
throw exception();
167+
}
168+
169+
@Override
170+
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
171+
throw exception();
172+
}
173+
174+
@Override
175+
public Class<T> getEncoderClass() {
176+
return classModel.getType();
177+
}
178+
179+
private CodecConfigurationException exception() {
180+
return new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n"
181+
+ "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName()));
182+
}
183+
184+
@Override
185+
ClassModel<T> getClassModel() {
186+
return classModel;
187+
}
188+
189+
@Override
190+
DiscriminatorLookup getDiscriminatorLookup() {
191+
return discriminatorLookup;
192+
}
193+
}
153194
}

bson/src/main/org/bson/codecs/pojo/PojoCodecImpl.java

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,36 +50,28 @@ final class PojoCodecImpl<T> extends PojoCodec<T> {
5050
private final CodecRegistry registry;
5151
private final PropertyCodecRegistry propertyCodecRegistry;
5252
private final DiscriminatorLookup discriminatorLookup;
53-
private final boolean specialized;
5453

5554
PojoCodecImpl(final ClassModel<T> classModel, final CodecRegistry codecRegistry,
56-
final List<PropertyCodecProvider> propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) {
55+
final List<PropertyCodecProvider> propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) {
5756
this.classModel = classModel;
5857
this.registry = codecRegistry;
5958
this.discriminatorLookup = discriminatorLookup;
6059
this.propertyCodecRegistry = new PropertyCodecRegistryImpl(this, registry, propertyCodecProviders);
61-
this.specialized = shouldSpecialize(classModel);
6260
specialize();
6361
}
6462

65-
PojoCodecImpl(final ClassModel<T> classModel, final CodecRegistry codecRegistry, final PropertyCodecRegistry propertyCodecRegistry,
66-
final DiscriminatorLookup discriminatorLookup, final boolean specialized) {
63+
PojoCodecImpl(final ClassModel<T> classModel, final CodecRegistry codecRegistry,
64+
final PropertyCodecRegistry propertyCodecRegistry, final DiscriminatorLookup discriminatorLookup) {
6765
this.classModel = classModel;
6866
this.registry = codecRegistry;
6967
this.discriminatorLookup = discriminatorLookup;
7068
this.propertyCodecRegistry = propertyCodecRegistry;
71-
this.specialized = specialized;
7269
specialize();
7370
}
7471

7572
@SuppressWarnings("unchecked")
7673
@Override
7774
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
78-
if (!specialized) {
79-
throw new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n"
80-
+ "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName()));
81-
}
82-
8375
if (areEquivalentTypes(value.getClass(), classModel.getType())) {
8476
writer.writeStartDocument();
8577

@@ -104,10 +96,6 @@ public void encode(final BsonWriter writer, final T value, final EncoderContext
10496
@Override
10597
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
10698
if (decoderContext.hasCheckedDiscriminator()) {
107-
if (!specialized) {
108-
throw new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n"
109-
+ "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName()));
110-
}
11199
InstanceCreator<T> instanceCreator = classModel.getInstanceCreator();
112100
decodeProperties(reader, decoderContext, instanceCreator);
113101
return instanceCreator.getInstance();
@@ -264,15 +252,13 @@ private <S> void setPropertyValueBsonExtraElements(final InstanceCreator<T> inst
264252
}
265253

266254
private void specialize() {
267-
if (specialized) {
268-
classModel.getPropertyModels().forEach(this::cachePropertyModelCodec);
269-
}
255+
classModel.getPropertyModels().forEach(this::cachePropertyModelCodec);
270256
}
271257

272258
private <S> void cachePropertyModelCodec(final PropertyModel<S> propertyModel) {
273259
if (propertyModel.getCachedCodec() == null) {
274260
Codec<S> codec = propertyModel.getCodec() != null ? propertyModel.getCodec()
275-
: new LazyPropertyModelCodec<>(propertyModel, registry, propertyCodecRegistry, discriminatorLookup);
261+
: new LazyPropertyModelCodec<>(propertyModel, registry, propertyCodecRegistry);
276262
propertyModel.cachedCodec(codec);
277263
}
278264
}
@@ -328,21 +314,6 @@ private PropertyModel<?> getPropertyModelByWriteName(final ClassModel<T> classMo
328314
return null;
329315
}
330316

331-
private static <T> boolean shouldSpecialize(final ClassModel<T> classModel) {
332-
if (!classModel.hasTypeParameters()) {
333-
return true;
334-
}
335-
336-
for (Map.Entry<String, TypeParameterMap> entry : classModel.getPropertyNameToTypeParameterMap().entrySet()) {
337-
TypeParameterMap typeParameterMap = entry.getValue();
338-
PropertyModel<?> propertyModel = classModel.getPropertyModel(entry.getKey());
339-
if (typeParameterMap.hasTypeParameters() && (propertyModel == null || propertyModel.getCodec() == null)) {
340-
return false;
341-
}
342-
}
343-
return true;
344-
}
345-
346317
@Override
347318
DiscriminatorLookup getDiscriminatorLookup() {
348319
return discriminatorLookup;

bson/src/main/org/bson/codecs/pojo/PojoCodecProvider.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,20 @@ public static Builder builder() {
6969

7070
@Override
7171
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
72-
return getPojoCodec(clazz, registry);
72+
return createCodec(clazz, registry);
7373
}
7474

7575
@SuppressWarnings("unchecked")
76-
private <T> PojoCodec<T> getPojoCodec(final Class<T> clazz, final CodecRegistry registry) {
76+
private <T> PojoCodec<T> createCodec(final Class<T> clazz, final CodecRegistry registry) {
7777
ClassModel<T> classModel = (ClassModel<T>) classModels.get(clazz);
7878
if (classModel != null) {
79-
return new PojoCodecImpl<>(classModel, registry, propertyCodecProviders, discriminatorLookup);
79+
return createCodec(classModel, registry, propertyCodecProviders, discriminatorLookup);
8080
} else if (automatic || (clazz.getPackage() != null && packages.contains(clazz.getPackage().getName()))) {
8181
try {
8282
classModel = createClassModel(clazz, conventions);
8383
if (clazz.isInterface() || !classModel.getPropertyModels().isEmpty()) {
8484
discriminatorLookup.addClassModel(classModel);
85-
return new AutomaticPojoCodec<>(new PojoCodecImpl<>(classModel, registry, propertyCodecProviders,
85+
return new AutomaticPojoCodec<>(createCodec(classModel, registry, propertyCodecProviders,
8686
discriminatorLookup));
8787
}
8888
} catch (Exception e) {
@@ -93,6 +93,13 @@ private <T> PojoCodec<T> getPojoCodec(final Class<T> clazz, final CodecRegistry
9393
return null;
9494
}
9595

96+
private static <T> PojoCodec<T> createCodec(final ClassModel<T> classModel, final CodecRegistry codecRegistry,
97+
final List<PropertyCodecProvider> propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) {
98+
return shouldSpecialize(classModel)
99+
? new PojoCodecImpl<>(classModel, codecRegistry, propertyCodecProviders, discriminatorLookup)
100+
: new LazyPropertyModelCodec.NeedSpecializationCodec<>(classModel, discriminatorLookup);
101+
}
102+
96103
/**
97104
* A Builder for the PojoCodecProvider
98105
*/
@@ -218,4 +225,19 @@ private static <T> ClassModel<T> createClassModel(final Class<T> clazz, final Li
218225
}
219226
return builder.build();
220227
}
228+
229+
private static boolean shouldSpecialize(final ClassModel<?> classModel) {
230+
if (!classModel.hasTypeParameters()) {
231+
return true;
232+
}
233+
234+
for (Map.Entry<String, TypeParameterMap> entry : classModel.getPropertyNameToTypeParameterMap().entrySet()) {
235+
TypeParameterMap typeParameterMap = entry.getValue();
236+
PropertyModel<?> propertyModel = classModel.getPropertyModel(entry.getKey());
237+
if (typeParameterMap.hasTypeParameters() && (propertyModel == null || propertyModel.getCodec() == null)) {
238+
return false;
239+
}
240+
}
241+
return true;
242+
}
221243
}

bson/src/test/unit/org/bson/codecs/pojo/PojoTestCase.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ static PojoCodecProvider.Builder getPojoCodecProviderBuilder(final Class<?>... c
176176
return builder;
177177
}
178178

179-
<T> PojoCodecImpl<T> getCodec(final PojoCodecProvider.Builder builder, final Class<T> clazz) {
180-
return (PojoCodecImpl<T>) getCodecRegistry(builder).get(clazz);
179+
<T> PojoCodec<T> getCodec(final PojoCodecProvider.Builder builder, final Class<T> clazz) {
180+
return (PojoCodec<T>) getCodecRegistry(builder).get(clazz);
181181
}
182182

183-
<T> PojoCodecImpl<T> getCodec(final Class<T> clazz) {
183+
<T> PojoCodec<T> getCodec(final Class<T> clazz) {
184184
return getCodec(getPojoCodecProviderBuilder(clazz), clazz);
185185
}
186186

0 commit comments

Comments
 (0)