diff --git a/bson/src/main/org/bson/codecs/pojo/LazyPropertyModelCodec.java b/bson/src/main/org/bson/codecs/pojo/LazyPropertyModelCodec.java index 011ecaa4d1..694d271dad 100644 --- a/bson/src/main/org/bson/codecs/pojo/LazyPropertyModelCodec.java +++ b/bson/src/main/org/bson/codecs/pojo/LazyPropertyModelCodec.java @@ -28,22 +28,21 @@ import java.util.ArrayList; import java.util.List; +import static java.lang.String.format; import static org.bson.codecs.pojo.PojoSpecializationHelper.specializeTypeData; class LazyPropertyModelCodec implements Codec { private final PropertyModel propertyModel; private final CodecRegistry registry; private final PropertyCodecRegistry propertyCodecRegistry; - private final DiscriminatorLookup discriminatorLookup; private volatile Codec codec; LazyPropertyModelCodec(final PropertyModel propertyModel, final CodecRegistry registry, - final PropertyCodecRegistry propertyCodecRegistry, final DiscriminatorLookup discriminatorLookup) { + final PropertyCodecRegistry propertyCodecRegistry) { this.propertyModel = propertyModel; this.registry = registry; this.propertyCodecRegistry = propertyCodecRegistry; - this.discriminatorLookup = discriminatorLookup; } @Override @@ -77,7 +76,7 @@ private Codec createCodec() { if (localCodec instanceof PojoCodec) { PojoCodec pojoCodec = (PojoCodec) localCodec; ClassModel specialized = getSpecializedClassModel(pojoCodec.getClassModel(), propertyModel); - localCodec = new PojoCodecImpl<>(specialized, registry, propertyCodecRegistry, pojoCodec.getDiscriminatorLookup(), true); + localCodec = new PojoCodecImpl<>(specialized, registry, propertyCodecRegistry, pojoCodec.getDiscriminatorLookup()); } return localCodec; } @@ -150,4 +149,46 @@ private PropertyModel getSpecializedPropertyModel(final PropertyModel propertyModel.getPropertyAccessor(), propertyModel.getError(), propertyModel.getBsonRepresentation()); } + /** + * Instances of this codec are supposed to be replaced with usable implementations by {@link LazyPropertyModelCodec#createCodec()}. + */ + static final class NeedSpecializationCodec extends PojoCodec { + private final ClassModel classModel; + private final DiscriminatorLookup discriminatorLookup; + + NeedSpecializationCodec(final ClassModel classModel, final DiscriminatorLookup discriminatorLookup) { + this.classModel = classModel; + this.discriminatorLookup = discriminatorLookup; + } + + @Override + public T decode(final BsonReader reader, final DecoderContext decoderContext) { + throw exception(); + } + + @Override + public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) { + throw exception(); + } + + @Override + public Class getEncoderClass() { + return classModel.getType(); + } + + private CodecConfigurationException exception() { + return new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n" + + "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName())); + } + + @Override + ClassModel getClassModel() { + return classModel; + } + + @Override + DiscriminatorLookup getDiscriminatorLookup() { + return discriminatorLookup; + } + } } diff --git a/bson/src/main/org/bson/codecs/pojo/PojoCodecImpl.java b/bson/src/main/org/bson/codecs/pojo/PojoCodecImpl.java index 2864f65e6a..bccadfb3e0 100644 --- a/bson/src/main/org/bson/codecs/pojo/PojoCodecImpl.java +++ b/bson/src/main/org/bson/codecs/pojo/PojoCodecImpl.java @@ -50,36 +50,28 @@ final class PojoCodecImpl extends PojoCodec { private final CodecRegistry registry; private final PropertyCodecRegistry propertyCodecRegistry; private final DiscriminatorLookup discriminatorLookup; - private final boolean specialized; PojoCodecImpl(final ClassModel classModel, final CodecRegistry codecRegistry, - final List propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) { + final List propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) { this.classModel = classModel; this.registry = codecRegistry; this.discriminatorLookup = discriminatorLookup; this.propertyCodecRegistry = new PropertyCodecRegistryImpl(this, registry, propertyCodecProviders); - this.specialized = shouldSpecialize(classModel); specialize(); } - PojoCodecImpl(final ClassModel classModel, final CodecRegistry codecRegistry, final PropertyCodecRegistry propertyCodecRegistry, - final DiscriminatorLookup discriminatorLookup, final boolean specialized) { + PojoCodecImpl(final ClassModel classModel, final CodecRegistry codecRegistry, + final PropertyCodecRegistry propertyCodecRegistry, final DiscriminatorLookup discriminatorLookup) { this.classModel = classModel; this.registry = codecRegistry; this.discriminatorLookup = discriminatorLookup; this.propertyCodecRegistry = propertyCodecRegistry; - this.specialized = specialized; specialize(); } @SuppressWarnings("unchecked") @Override public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) { - if (!specialized) { - throw new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n" - + "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName())); - } - if (areEquivalentTypes(value.getClass(), classModel.getType())) { writer.writeStartDocument(); @@ -104,10 +96,6 @@ public void encode(final BsonWriter writer, final T value, final EncoderContext @Override public T decode(final BsonReader reader, final DecoderContext decoderContext) { if (decoderContext.hasCheckedDiscriminator()) { - if (!specialized) { - throw new CodecConfigurationException(format("%s contains generic types that have not been specialised.%n" - + "Top level classes with generic types are not supported by the PojoCodec.", classModel.getName())); - } InstanceCreator instanceCreator = classModel.getInstanceCreator(); decodeProperties(reader, decoderContext, instanceCreator); return instanceCreator.getInstance(); @@ -264,15 +252,13 @@ private void setPropertyValueBsonExtraElements(final InstanceCreator inst } private void specialize() { - if (specialized) { - classModel.getPropertyModels().forEach(this::cachePropertyModelCodec); - } + classModel.getPropertyModels().forEach(this::cachePropertyModelCodec); } private void cachePropertyModelCodec(final PropertyModel propertyModel) { if (propertyModel.getCachedCodec() == null) { Codec codec = propertyModel.getCodec() != null ? propertyModel.getCodec() - : new LazyPropertyModelCodec<>(propertyModel, registry, propertyCodecRegistry, discriminatorLookup); + : new LazyPropertyModelCodec<>(propertyModel, registry, propertyCodecRegistry); propertyModel.cachedCodec(codec); } } @@ -328,21 +314,6 @@ private PropertyModel getPropertyModelByWriteName(final ClassModel classMo return null; } - private static boolean shouldSpecialize(final ClassModel classModel) { - if (!classModel.hasTypeParameters()) { - return true; - } - - for (Map.Entry entry : classModel.getPropertyNameToTypeParameterMap().entrySet()) { - TypeParameterMap typeParameterMap = entry.getValue(); - PropertyModel propertyModel = classModel.getPropertyModel(entry.getKey()); - if (typeParameterMap.hasTypeParameters() && (propertyModel == null || propertyModel.getCodec() == null)) { - return false; - } - } - return true; - } - @Override DiscriminatorLookup getDiscriminatorLookup() { return discriminatorLookup; diff --git a/bson/src/main/org/bson/codecs/pojo/PojoCodecProvider.java b/bson/src/main/org/bson/codecs/pojo/PojoCodecProvider.java index 7ddb2f3ce7..6a3e8bfc83 100644 --- a/bson/src/main/org/bson/codecs/pojo/PojoCodecProvider.java +++ b/bson/src/main/org/bson/codecs/pojo/PojoCodecProvider.java @@ -69,20 +69,20 @@ public static Builder builder() { @Override public Codec get(final Class clazz, final CodecRegistry registry) { - return getPojoCodec(clazz, registry); + return createCodec(clazz, registry); } @SuppressWarnings("unchecked") - private PojoCodec getPojoCodec(final Class clazz, final CodecRegistry registry) { + private PojoCodec createCodec(final Class clazz, final CodecRegistry registry) { ClassModel classModel = (ClassModel) classModels.get(clazz); if (classModel != null) { - return new PojoCodecImpl<>(classModel, registry, propertyCodecProviders, discriminatorLookup); + return createCodec(classModel, registry, propertyCodecProviders, discriminatorLookup); } else if (automatic || (clazz.getPackage() != null && packages.contains(clazz.getPackage().getName()))) { try { classModel = createClassModel(clazz, conventions); if (clazz.isInterface() || !classModel.getPropertyModels().isEmpty()) { discriminatorLookup.addClassModel(classModel); - return new AutomaticPojoCodec<>(new PojoCodecImpl<>(classModel, registry, propertyCodecProviders, + return new AutomaticPojoCodec<>(createCodec(classModel, registry, propertyCodecProviders, discriminatorLookup)); } } catch (Exception e) { @@ -93,6 +93,13 @@ private PojoCodec getPojoCodec(final Class clazz, final CodecRegistry return null; } + private static PojoCodec createCodec(final ClassModel classModel, final CodecRegistry codecRegistry, + final List propertyCodecProviders, final DiscriminatorLookup discriminatorLookup) { + return shouldSpecialize(classModel) + ? new PojoCodecImpl<>(classModel, codecRegistry, propertyCodecProviders, discriminatorLookup) + : new LazyPropertyModelCodec.NeedSpecializationCodec<>(classModel, discriminatorLookup); + } + /** * A Builder for the PojoCodecProvider */ @@ -218,4 +225,19 @@ private static ClassModel createClassModel(final Class clazz, final Li } return builder.build(); } + + private static boolean shouldSpecialize(final ClassModel classModel) { + if (!classModel.hasTypeParameters()) { + return true; + } + + for (Map.Entry entry : classModel.getPropertyNameToTypeParameterMap().entrySet()) { + TypeParameterMap typeParameterMap = entry.getValue(); + PropertyModel propertyModel = classModel.getPropertyModel(entry.getKey()); + if (typeParameterMap.hasTypeParameters() && (propertyModel == null || propertyModel.getCodec() == null)) { + return false; + } + } + return true; + } } diff --git a/bson/src/test/unit/org/bson/codecs/pojo/PojoTestCase.java b/bson/src/test/unit/org/bson/codecs/pojo/PojoTestCase.java index 6383e6b1de..9b3be471db 100644 --- a/bson/src/test/unit/org/bson/codecs/pojo/PojoTestCase.java +++ b/bson/src/test/unit/org/bson/codecs/pojo/PojoTestCase.java @@ -176,11 +176,11 @@ static PojoCodecProvider.Builder getPojoCodecProviderBuilder(final Class... c return builder; } - PojoCodecImpl getCodec(final PojoCodecProvider.Builder builder, final Class clazz) { - return (PojoCodecImpl) getCodecRegistry(builder).get(clazz); + PojoCodec getCodec(final PojoCodecProvider.Builder builder, final Class clazz) { + return (PojoCodec) getCodecRegistry(builder).get(clazz); } - PojoCodecImpl getCodec(final Class clazz) { + PojoCodec getCodec(final Class clazz) { return getCodec(getPojoCodecProviderBuilder(clazz), clazz); }