Skip to content

Commit 762108d

Browse files
committed
Added Pojo id generation
Added IdGenerator to ClassModel to support to Pojos generating their own id's. Supports both mutable and immutable Pojos. Mutable Pojos will update their Id property's value. JAVA-2674
1 parent 358f133 commit 762108d

26 files changed

+997
-55
lines changed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ public final class ClassModel<T> {
3333
private final boolean discriminatorEnabled;
3434
private final String discriminatorKey;
3535
private final String discriminator;
36-
private final PropertyModel<?> idProperty;
36+
private final IdPropertyModelHolder<?> idPropertyModelHolder;
3737
private final List<PropertyModel<?>> propertyModels;
3838
private final Map<String, TypeParameterMap> propertyNameToTypeParameterMap;
3939

4040
ClassModel(final Class<T> clazz, final Map<String, TypeParameterMap> propertyNameToTypeParameterMap,
4141
final InstanceCreatorFactory<T> instanceCreatorFactory, final Boolean discriminatorEnabled, final String discriminatorKey,
42-
final String discriminator, final PropertyModel<?> idProperty, final List<PropertyModel<?>> propertyModels) {
42+
final String discriminator, final IdPropertyModelHolder<?> idPropertyModelHolder,
43+
final List<PropertyModel<?>> propertyModels) {
4344
this.name = clazz.getSimpleName();
4445
this.type = clazz;
4546
this.hasTypeParameters = clazz.getTypeParameters().length > 0;
@@ -48,7 +49,7 @@ public final class ClassModel<T> {
4849
this.discriminatorEnabled = discriminatorEnabled;
4950
this.discriminatorKey = discriminatorKey;
5051
this.discriminator = discriminator;
51-
this.idProperty = idProperty;
52+
this.idPropertyModelHolder = idPropertyModelHolder;
5253
this.propertyModels = propertyModels;
5354
}
5455

@@ -139,7 +140,11 @@ public List<PropertyModel<?>> getPropertyModels() {
139140
* @return the PropertyModel for the id
140141
*/
141142
public PropertyModel<?> getIdPropertyModel() {
142-
return idProperty;
143+
return idPropertyModelHolder != null ? idPropertyModelHolder.getPropertyModel() : null;
144+
}
145+
146+
IdPropertyModelHolder<?> getIdPropertyModelHolder() {
147+
return idPropertyModelHolder;
143148
}
144149

145150
/**
@@ -185,7 +190,8 @@ public boolean equals(final Object o) {
185190
if (getDiscriminator() != null ? !getDiscriminator().equals(that.getDiscriminator()) : that.getDiscriminator() != null) {
186191
return false;
187192
}
188-
if (idProperty != null ? !idProperty.equals(that.idProperty) : that.idProperty != null) {
193+
if (idPropertyModelHolder != null ? !idPropertyModelHolder.equals(that.idPropertyModelHolder)
194+
: that.idPropertyModelHolder != null) {
189195
return false;
190196
}
191197
if (!getPropertyModels().equals(that.getPropertyModels())) {
@@ -205,7 +211,7 @@ public int hashCode() {
205211
result = 31 * result + (discriminatorEnabled ? 1 : 0);
206212
result = 31 * result + (getDiscriminatorKey() != null ? getDiscriminatorKey().hashCode() : 0);
207213
result = 31 * result + (getDiscriminator() != null ? getDiscriminator().hashCode() : 0);
208-
result = 31 * result + (idProperty != null ? idProperty.hashCode() : 0);
214+
result = 31 * result + (getIdPropertyModelHolder() != null ? getIdPropertyModelHolder().hashCode() : 0);
209215
result = 31 * result + getPropertyModels().hashCode();
210216
result = 31 * result + getPropertyNameToTypeParameterMap().hashCode();
211217
return result;

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
public class ClassModelBuilder<T> {
4646
static final String ID_PROPERTY_NAME = "_id";
4747
private final List<PropertyModelBuilder<?>> propertyModelBuilders = new ArrayList<PropertyModelBuilder<?>>();
48+
private IdGenerator<?> idGenerator;
4849
private InstanceCreatorFactory<T> instanceCreatorFactory;
4950
private Class<T> type;
5051
private Map<String, TypeParameterMap> propertyNameToTypeParameterMap = emptyMap();
@@ -59,6 +60,26 @@ public class ClassModelBuilder<T> {
5960
configureClassModelBuilder(this, notNull("type", type));
6061
}
6162

63+
/**
64+
* Sets the IdGenerator for the ClassModel
65+
*
66+
* @param idGenerator the IdGenerator
67+
* @return this
68+
* @since 3.10
69+
*/
70+
public ClassModelBuilder<T> idGenerator(final IdGenerator<?> idGenerator) {
71+
this.idGenerator = idGenerator;
72+
return this;
73+
}
74+
75+
/**
76+
* @return the IdGenerator for the ClassModel, or null if not set
77+
* @since 3.10
78+
*/
79+
public IdGenerator<?> getIdGenerator() {
80+
return idGenerator;
81+
}
82+
6283
/**
6384
* Sets the InstanceCreatorFactory for the ClassModel
6485
*
@@ -271,10 +292,8 @@ public ClassModel<T> build() {
271292
}
272293
}
273294
validatePropertyModels(type.getSimpleName(), propertyModels);
274-
275-
276295
return new ClassModel<T>(type, propertyNameToTypeParameterMap, instanceCreatorFactory, discriminatorEnabled, discriminatorKey,
277-
discriminator, idPropertyModel, unmodifiableList(propertyModels));
296+
discriminator, IdPropertyModelHolder.create(type, idPropertyModel, idGenerator), unmodifiableList(propertyModels));
278297
}
279298

280299
@Override
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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;
18+
19+
import org.bson.BsonObjectId;
20+
import org.bson.types.ObjectId;
21+
22+
final class ConventionObjectIdGeneratorsImpl implements Convention {
23+
@Override
24+
public void apply(final ClassModelBuilder<?> classModelBuilder) {
25+
if (classModelBuilder.getIdGenerator() == null && classModelBuilder.getIdPropertyName() != null) {
26+
PropertyModelBuilder<?> idProperty = classModelBuilder.getProperty(classModelBuilder.getIdPropertyName());
27+
if (idProperty != null) {
28+
Class<?> idType = idProperty.getTypeData().getType();
29+
if (classModelBuilder.getIdGenerator() == null && idType.equals(ObjectId.class)) {
30+
classModelBuilder.idGenerator(IdGenerators.OBJECT_ID_GENERATOR);
31+
} else if (classModelBuilder.getIdGenerator() == null && idType.equals(BsonObjectId.class)) {
32+
classModelBuilder.idGenerator(IdGenerators.BSON_OBJECT_ID_GENERATOR);
33+
}
34+
}
35+
}
36+
}
37+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ && isPrivate(propertyMetaData.getField().getModifiers())) {
4242

4343
@SuppressWarnings("unchecked")
4444
private <T> void setPropertyAccessor(final PropertyModelBuilder<T> propertyModelBuilder) {
45-
propertyModelBuilder.propertyAccessor(new PrivateProperyAccessor<T>(
45+
propertyModelBuilder.propertyAccessor(new PrivatePropertyAccessor<T>(
4646
(PropertyAccessorImpl<T>) propertyModelBuilder.getPropertyAccessor()));
4747
}
4848

49-
private static final class PrivateProperyAccessor<T> implements PropertyAccessor<T> {
49+
private static final class PrivatePropertyAccessor<T> implements PropertyAccessor<T> {
5050
private final PropertyAccessorImpl<T> wrapped;
5151

52-
private PrivateProperyAccessor(final PropertyAccessorImpl<T> wrapped) {
52+
private PrivatePropertyAccessor(final PropertyAccessorImpl<T> wrapped) {
5353
this.wrapped = wrapped;
5454
try {
5555
wrapped.getPropertyMetadata().getField().setAccessible(true);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ private <T> boolean isMapOrCollection(final Class<T> clazz) {
4848

4949
@SuppressWarnings("unchecked")
5050
private <T> void setPropertyAccessor(final PropertyModelBuilder<T> propertyModelBuilder) {
51-
propertyModelBuilder.propertyAccessor(new PrivateProperyAccessor<T>(
51+
propertyModelBuilder.propertyAccessor(new PrivatePropertyAccessor<T>(
5252
(PropertyAccessorImpl<T>) propertyModelBuilder.getPropertyAccessor()));
5353
}
5454

5555
@SuppressWarnings({"rawtypes", "unchecked"})
56-
private static final class PrivateProperyAccessor<T> implements PropertyAccessor<T> {
56+
private static final class PrivatePropertyAccessor<T> implements PropertyAccessor<T> {
5757
private final PropertyAccessorImpl<T> wrapped;
5858

59-
private PrivateProperyAccessor(final PropertyAccessorImpl<T> wrapped) {
59+
private PrivatePropertyAccessor(final PropertyAccessorImpl<T> wrapped) {
6060
this.wrapped = wrapped;
6161
}
6262

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,26 @@ public final class Conventions {
6969
*/
7070
public static final Convention USE_GETTERS_FOR_SETTERS = new ConventionUseGettersAsSettersImpl();
7171

72+
73+
/**
74+
* A convention that sets the IdGenerator if the id property is either a {@link org.bson.types.ObjectId} or
75+
* {@link org.bson.BsonObjectId}.
76+
*
77+
* @since 3.10
78+
*/
79+
public static final Convention OBJECT_ID_GENERATORS = new ConventionObjectIdGeneratorsImpl();
80+
7281
/**
7382
* The default conventions list
7483
*/
7584
public static final List<Convention> DEFAULT_CONVENTIONS =
76-
unmodifiableList(asList(CLASS_AND_PROPERTY_CONVENTION, ANNOTATION_CONVENTION));
85+
unmodifiableList(asList(CLASS_AND_PROPERTY_CONVENTION, ANNOTATION_CONVENTION, OBJECT_ID_GENERATORS));
7786

7887
/**
7988
* An empty conventions list
8089
*/
8190
public static final List<Convention> NO_CONVENTIONS = Collections.emptyList();
8291

83-
8492
private Conventions() {
8593
}
8694
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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;
18+
19+
/**
20+
* Classes that implement this interface define a way to create Ids for Pojo's.
21+
*
22+
* @param <T> the type of the id value.
23+
* @since 3.10
24+
*/
25+
public interface IdGenerator<T> {
26+
/**
27+
* Generates an id for a Pojo.
28+
*
29+
* @return the generated id value
30+
*/
31+
T generate();
32+
33+
/**
34+
* @return the type of the generated id.
35+
*/
36+
Class<T> getType();
37+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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;
18+
19+
import org.bson.BsonObjectId;
20+
import org.bson.types.ObjectId;
21+
22+
/**
23+
* The default IdGenerators
24+
*
25+
* @see IdGenerator
26+
* @since 3.10
27+
*/
28+
public final class IdGenerators {
29+
30+
/**
31+
* A IdGenerator for {@code ObjectId}
32+
*/
33+
public static final IdGenerator<ObjectId> OBJECT_ID_GENERATOR = new IdGenerator<ObjectId>() {
34+
35+
@Override
36+
public ObjectId generate() {
37+
return new ObjectId();
38+
}
39+
40+
@Override
41+
public Class<ObjectId> getType() {
42+
return ObjectId.class;
43+
}
44+
};
45+
46+
/**
47+
* A IdGenerator for {@code BsonObjectId}
48+
*/
49+
public static final IdGenerator<BsonObjectId> BSON_OBJECT_ID_GENERATOR = new IdGenerator<BsonObjectId>() {
50+
51+
@Override
52+
public BsonObjectId generate() {
53+
return new BsonObjectId();
54+
}
55+
56+
@Override
57+
public Class<BsonObjectId> getType() {
58+
return BsonObjectId.class;
59+
}
60+
};
61+
62+
private IdGenerators(){
63+
}
64+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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;
18+
19+
import org.bson.codecs.configuration.CodecConfigurationException;
20+
21+
import static java.lang.String.format;
22+
23+
final class IdPropertyModelHolder<I> {
24+
private final PropertyModel<I> propertyModel;
25+
private final IdGenerator<I> idGenerator;
26+
27+
static <T, I> IdPropertyModelHolder<I> create(final ClassModel<T> classModel, final PropertyModel<I> idPropertyModel) {
28+
return create(classModel.getType(), idPropertyModel, classModel.getIdPropertyModelHolder().getIdGenerator());
29+
}
30+
31+
@SuppressWarnings("unchecked")
32+
static <T, I, V> IdPropertyModelHolder<I> create(final Class<T> type, final PropertyModel<I> idProperty,
33+
final IdGenerator<V> idGenerator) {
34+
if (idProperty == null && idGenerator != null) {
35+
throw new CodecConfigurationException(format("Invalid IdGenerator. There is no IdProperty set for: %s", type));
36+
} else if (idGenerator != null && !idProperty.getTypeData().getType().isAssignableFrom(idGenerator.getType())) {
37+
throw new CodecConfigurationException(format("Invalid IdGenerator. Mismatching types, the IdProperty type is: %s but"
38+
+ " the IdGenerator type is: %s", idProperty.getTypeData().getType(), idGenerator.getType()));
39+
}
40+
return new IdPropertyModelHolder<I>(idProperty, (IdGenerator<I>) idGenerator);
41+
}
42+
43+
private IdPropertyModelHolder(final PropertyModel<I> propertyModel, final IdGenerator<I> idGenerator) {
44+
this.propertyModel = propertyModel;
45+
this.idGenerator = idGenerator;
46+
}
47+
48+
PropertyModel<I> getPropertyModel() {
49+
return propertyModel;
50+
}
51+
52+
IdGenerator<I> getIdGenerator() {
53+
return idGenerator;
54+
}
55+
56+
@Override
57+
public boolean equals(final Object o) {
58+
if (this == o) {
59+
return true;
60+
}
61+
if (o == null || getClass() != o.getClass()) {
62+
return false;
63+
}
64+
65+
IdPropertyModelHolder<?> that = (IdPropertyModelHolder<?>) o;
66+
67+
if (propertyModel != null ? !propertyModel.equals(that.propertyModel) : that.propertyModel != null) {
68+
return false;
69+
}
70+
return idGenerator != null ? idGenerator.equals(that.idGenerator) : that.idGenerator == null;
71+
}
72+
73+
@Override
74+
public int hashCode() {
75+
int result = propertyModel != null ? propertyModel.hashCode() : 0;
76+
result = 31 * result + (idGenerator != null ? idGenerator.hashCode() : 0);
77+
return result;
78+
}
79+
}

0 commit comments

Comments
 (0)