Skip to content

Commit eb4a479

Browse files
committed
Delay the validation of PropertyModel type data
Now ensures the conventions have been applied before validation takes place. JAVA-3286
1 parent a4df4b4 commit eb4a479

File tree

9 files changed

+140
-10
lines changed

9 files changed

+140
-10
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ private void validatePropertyModels(final String declaringClass, final List<Prop
321321
Map<String, Integer> propertyWriteNameMap = new HashMap<String, Integer>();
322322

323323
for (PropertyModel<?> propertyModel : propertyModels) {
324+
if (propertyModel.hasError()) {
325+
throw new CodecConfigurationException(propertyModel.getError());
326+
}
324327
checkForDuplicates("property", propertyModel.getName(), propertyNameMap, declaringClass);
325328
if (propertyModel.isReadable()) {
326329
checkForDuplicates("read property", propertyModel.getReadName(), propertyReadNameMap, declaringClass);

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.bson.codecs.pojo;
1818

19-
import org.bson.codecs.configuration.CodecConfigurationException;
20-
2119
import java.lang.annotation.Annotation;
2220
import java.lang.reflect.Constructor;
2321
import java.lang.reflect.Field;
@@ -38,11 +36,12 @@
3836
import static java.util.Arrays.asList;
3937
import static java.util.Collections.reverse;
4038
import static org.bson.assertions.Assertions.notNull;
41-
import static org.bson.codecs.pojo.PropertyReflectionUtils.isGetter;
4239
import static org.bson.codecs.pojo.PropertyReflectionUtils.getPropertyMethods;
40+
import static org.bson.codecs.pojo.PropertyReflectionUtils.isGetter;
4341
import static org.bson.codecs.pojo.PropertyReflectionUtils.toPropertyName;
4442

4543
final class PojoBuilderHelper {
44+
4645
@SuppressWarnings("unchecked")
4746
static <T> void configureClassModelBuilder(final ClassModelBuilder<T> classModelBuilder, final Class<T> clazz) {
4847
classModelBuilder.type(notNull("clazz", clazz));
@@ -158,11 +157,10 @@ private static <T, S> PropertyMetadata<T> getOrCreateMethodPropertyMetadata(fina
158157
final Type genericType) {
159158
PropertyMetadata<T> propertyMetadata = getOrCreatePropertyMetadata(propertyName, declaringClassName, propertyNameMap, typeData);
160159
if (!isAssignableClass(propertyMetadata.getTypeData().getType(), typeData.getType())) {
161-
throw new CodecConfigurationException(format("Property '%s' in %s, has differing data types: %s and %s", propertyName,
160+
propertyMetadata.setError(format("Property '%s' in %s, has differing data types: %s and %s.", propertyName,
162161
declaringClassName, propertyMetadata.getTypeData(), typeData));
163162
}
164-
cachePropertyTypeData(propertyMetadata, propertyTypeParameterMap, parentClassTypeData, genericTypeNames,
165-
genericType);
163+
cachePropertyTypeData(propertyMetadata, propertyTypeParameterMap, parentClassTypeData, genericTypeNames, genericType);
166164
return propertyMetadata;
167165
}
168166

@@ -223,7 +221,8 @@ static <T> PropertyModelBuilder<T> createPropertyModelBuilder(final PropertyMeta
223221
.readAnnotations(propertyMetadata.getReadAnnotations())
224222
.writeAnnotations(propertyMetadata.getWriteAnnotations())
225223
.propertySerialization(new PropertyModelSerializationImpl<T>())
226-
.propertyAccessor(new PropertyAccessorImpl<T>(propertyMetadata));
224+
.propertyAccessor(new PropertyAccessorImpl<T>(propertyMetadata))
225+
.setError(propertyMetadata.getError());
227226

228227
if (propertyMetadata.getTypeParameters() != null) {
229228
specializePropertyModelBuilder(propertyModelBuilder, propertyMetadata);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ private <V> PropertyModel<V> getSpecializedPropertyModel(final PropertyModel<V>
332332

333333
return new PropertyModel<V>(propertyModel.getName(), propertyModel.getReadName(), propertyModel.getWriteName(),
334334
specializedPropertyType, null, propertyModel.getPropertySerialization(), propertyModel.useDiscriminator(),
335-
propertyModel.getPropertyAccessor());
335+
propertyModel.getPropertyAccessor(), propertyModel.getError());
336336
}
337337

338338
@SuppressWarnings("unchecked")

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ final class PropertyMetadata<T> {
4141
private TypeParameterMap typeParameterMap;
4242
private List<TypeData<?>> typeParameters;
4343

44+
private String error;
4445
private Field field;
4546
private Method getter;
4647
private Method setter;
@@ -136,6 +137,14 @@ public <S> PropertyMetadata<T> typeParameterInfo(final TypeParameterMap typePara
136137
return this;
137138
}
138139

140+
String getError() {
141+
return error;
142+
}
143+
144+
void setError(final String error) {
145+
this.error = error;
146+
}
147+
139148
public boolean isSerializable() {
140149
if (getter != null) {
141150
return field == null || notStaticOrTransient(field.getModifiers());

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ public final class PropertyModel<T> {
3333
private final PropertySerialization<T> propertySerialization;
3434
private final Boolean useDiscriminator;
3535
private final PropertyAccessor<T> propertyAccessor;
36+
private final String error;
3637
private volatile Codec<T> cachedCodec;
3738

3839
PropertyModel(final String name, final String readName, final String writeName, final TypeData<T> typeData,
3940
final Codec<T> codec, final PropertySerialization<T> propertySerialization, final Boolean useDiscriminator,
40-
final PropertyAccessor<T> propertyAccessor) {
41+
final PropertyAccessor<T> propertyAccessor, final String error) {
4142
this.name = name;
4243
this.readName = readName;
4344
this.writeName = writeName;
@@ -47,6 +48,7 @@ public final class PropertyModel<T> {
4748
this.propertySerialization = propertySerialization;
4849
this.useDiscriminator = useDiscriminator;
4950
this.propertyAccessor = propertyAccessor;
51+
this.error = error;
5052
}
5153

5254
/**
@@ -182,6 +184,11 @@ public boolean equals(final Object o) {
182184
: that.getPropertyAccessor() != null) {
183185
return false;
184186
}
187+
188+
if (getError() != null ? !getError().equals(that.getError()) : that.getError() != null) {
189+
return false;
190+
}
191+
185192
if (getCachedCodec() != null ? !getCachedCodec().equals(that.getCachedCodec()) : that.getCachedCodec() != null) {
186193
return false;
187194
}
@@ -199,10 +206,19 @@ public int hashCode() {
199206
result = 31 * result + (getPropertySerialization() != null ? getPropertySerialization().hashCode() : 0);
200207
result = 31 * result + (useDiscriminator != null ? useDiscriminator.hashCode() : 0);
201208
result = 31 * result + (getPropertyAccessor() != null ? getPropertyAccessor().hashCode() : 0);
209+
result = 31 * result + (getError() != null ? getError().hashCode() : 0);
202210
result = 31 * result + (getCachedCodec() != null ? getCachedCodec().hashCode() : 0);
203211
return result;
204212
}
205213

214+
boolean hasError() {
215+
return error != null;
216+
}
217+
218+
String getError() {
219+
return error;
220+
}
221+
206222
PropertySerialization<T> getPropertySerialization() {
207223
return propertySerialization;
208224
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public final class PropertyModelBuilder<T> {
4545
private List<Annotation> readAnnotations = emptyList();
4646
private List<Annotation> writeAnnotations = emptyList();
4747
private Boolean discriminatorEnabled;
48+
private String error;
4849

4950
PropertyModelBuilder() {
5051
}
@@ -246,7 +247,8 @@ public PropertyModel<T> build() {
246247
codec,
247248
stateNotNull("propertySerialization", propertySerialization),
248249
discriminatorEnabled,
249-
stateNotNull("propertyAccessor", propertyAccessor));
250+
stateNotNull("propertyAccessor", propertyAccessor),
251+
error);
250252
}
251253

252254
@Override
@@ -267,4 +269,9 @@ PropertyModelBuilder<T> typeData(final TypeData<T> typeData) {
267269
this.typeData = notNull("typeData", typeData);
268270
return this;
269271
}
272+
273+
PropertyModelBuilder<T> setError(final String error) {
274+
this.error = error;
275+
return this;
276+
}
270277
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.bson.codecs.pojo.entities.conventions.AnnotationNameCollision;
2525
import org.bson.codecs.pojo.entities.conventions.AnnotationWithObjectIdModel;
2626
import org.bson.codecs.pojo.entities.conventions.AnnotationWriteCollision;
27+
import org.bson.codecs.pojo.entities.conventions.BsonIgnoreDuplicatePropertyMultipleTypes;
2728
import org.bson.codecs.pojo.entities.conventions.CreatorInvalidConstructorModel;
2829
import org.bson.codecs.pojo.entities.conventions.CreatorInvalidMethodModel;
2930
import org.bson.codecs.pojo.entities.conventions.CreatorInvalidMethodReturnTypeModel;
@@ -40,6 +41,7 @@
4041
import static org.bson.codecs.pojo.Conventions.ANNOTATION_CONVENTION;
4142
import static org.bson.codecs.pojo.Conventions.CLASS_AND_PROPERTY_CONVENTION;
4243
import static org.bson.codecs.pojo.Conventions.DEFAULT_CONVENTIONS;
44+
import static org.bson.codecs.pojo.Conventions.NO_CONVENTIONS;
4345
import static org.junit.Assert.assertEquals;
4446
import static org.junit.Assert.assertNotNull;
4547
import static org.junit.Assert.assertNull;
@@ -203,6 +205,12 @@ public void testCreatorInvalidTypeMethodModel() {
203205
.conventions(singletonList(ANNOTATION_CONVENTION)).build();
204206
}
205207

208+
@Test(expected = CodecConfigurationException.class)
209+
public void testBsonIgnoreDuplicatePropertyMultipleTypesModel() {
210+
ClassModel.builder(BsonIgnoreDuplicatePropertyMultipleTypes.class)
211+
.conventions(NO_CONVENTIONS).build();
212+
}
213+
206214
private class PropertyAccessorTest<T> implements PropertyAccessor<T> {
207215

208216
@Override

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.bson.codecs.pojo.entities.TreeWithIdModel;
6868
import org.bson.codecs.pojo.entities.UpperBoundsConcreteModel;
6969
import org.bson.codecs.pojo.entities.conventions.AnnotationBsonPropertyIdModel;
70+
import org.bson.codecs.pojo.entities.conventions.BsonIgnoreDuplicatePropertyMultipleTypes;
7071
import org.bson.codecs.pojo.entities.conventions.BsonIgnoreInvalidMapModel;
7172
import org.bson.codecs.pojo.entities.conventions.BsonIgnoreSyntheticProperty;
7273
import org.bson.codecs.pojo.entities.conventions.CollectionDiscriminatorAbstractClassesModel;
@@ -427,6 +428,11 @@ private static List<TestData> testCases() {
427428
getPojoCodecProviderBuilder(DuplicateAnnotationAllowedModel.class).conventions(Conventions.DEFAULT_CONVENTIONS),
428429
"{'_id': 'abc'}"));
429430

431+
data.add(new TestData("BsonIgnore duplicate property with multiple types",
432+
new BsonIgnoreDuplicatePropertyMultipleTypes("string value"),
433+
getPojoCodecProviderBuilder(BsonIgnoreDuplicatePropertyMultipleTypes.class).conventions(Conventions.DEFAULT_CONVENTIONS),
434+
"{stringField: 'string value'}"));
435+
430436
return data;
431437
}
432438

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.bson.codecs.pojo.entities.conventions;
18+
19+
import org.bson.codecs.pojo.annotations.BsonCreator;
20+
import org.bson.codecs.pojo.annotations.BsonIgnore;
21+
import org.bson.codecs.pojo.annotations.BsonProperty;
22+
23+
public class BsonIgnoreDuplicatePropertyMultipleTypes {
24+
private final String stringField;
25+
private String altStringField;
26+
27+
@BsonCreator
28+
public BsonIgnoreDuplicatePropertyMultipleTypes(@BsonProperty("stringField") final String stringField) {
29+
this.stringField = stringField;
30+
}
31+
32+
public String getStringField() {
33+
return stringField;
34+
}
35+
36+
@BsonIgnore
37+
public String getAltStringField() {
38+
return altStringField;
39+
}
40+
41+
@BsonIgnore
42+
public void setAltStringField(String altStringField) {
43+
this.altStringField = altStringField;
44+
}
45+
46+
@BsonIgnore
47+
public void setAltStringField(Integer i) {
48+
this.altStringField = i.toString();
49+
}
50+
51+
@Override
52+
public String toString() {
53+
return "BsonIgnoreDuplicatePropertyMultipleTypes{"
54+
+ "stringField='" + stringField + '\''
55+
+ ", altStringField='" + altStringField + '\''
56+
+ '}';
57+
}
58+
59+
@Override
60+
public boolean equals(final Object o) {
61+
if (this == o) {
62+
return true;
63+
}
64+
if (o == null || getClass() != o.getClass()) {
65+
return false;
66+
}
67+
68+
BsonIgnoreDuplicatePropertyMultipleTypes that = (BsonIgnoreDuplicatePropertyMultipleTypes) o;
69+
70+
if (stringField != null ? !stringField.equals(that.stringField) : that.stringField != null) {
71+
return false;
72+
}
73+
return altStringField != null ? altStringField.equals(that.altStringField) : that.altStringField == null;
74+
}
75+
76+
@Override
77+
public int hashCode() {
78+
int result = stringField != null ? stringField.hashCode() : 0;
79+
result = 31 * result + (altStringField != null ? altStringField.hashCode() : 0);
80+
return result;
81+
}
82+
}

0 commit comments

Comments
 (0)