Skip to content

Commit 658917b

Browse files
authored
Add codec provider for enums (#856)
JAVA-4440
1 parent fb5bc20 commit 658917b

File tree

13 files changed

+219
-37
lines changed

13 files changed

+219
-37
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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;
18+
19+
import org.bson.BsonReader;
20+
import org.bson.BsonWriter;
21+
22+
/**
23+
* A codec for classes that extends {@link Enum}
24+
*
25+
* @param <T> The enum type
26+
* @since 4.5
27+
*/
28+
public final class EnumCodec<T extends Enum<T>> implements Codec<T> {
29+
private final Class<T> clazz;
30+
31+
public EnumCodec(final Class<T> clazz) {
32+
this.clazz = clazz;
33+
}
34+
35+
@Override
36+
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
37+
return Enum.valueOf(clazz, reader.readString());
38+
}
39+
40+
@Override
41+
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
42+
writer.writeString(value.name());
43+
}
44+
45+
@Override
46+
public Class<T> getEncoderClass() {
47+
return clazz;
48+
}
49+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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;
18+
19+
import org.bson.codecs.configuration.CodecProvider;
20+
import org.bson.codecs.configuration.CodecRegistry;
21+
22+
/**
23+
* A codec provider for classes that extend {@link Enum}.
24+
*
25+
* @since 4.5
26+
*/
27+
public final class EnumCodecProvider implements CodecProvider {
28+
@Override
29+
@SuppressWarnings({"unchecked", "rawtypes"})
30+
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
31+
if (Enum.class.isAssignableFrom(clazz)) {
32+
return (Codec<T>) new EnumCodec(clazz);
33+
}
34+
return null;
35+
}
36+
}

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

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616

1717
package org.bson.codecs.pojo;
1818

19-
import org.bson.BsonReader;
20-
import org.bson.BsonWriter;
2119
import org.bson.codecs.Codec;
22-
import org.bson.codecs.DecoderContext;
23-
import org.bson.codecs.EncoderContext;
20+
import org.bson.codecs.EnumCodec;
2421
import org.bson.codecs.configuration.CodecConfigurationException;
2522
import org.bson.codecs.configuration.CodecRegistry;
2623

@@ -45,28 +42,4 @@ public <T> Codec<T> get(final TypeWithTypeParameters<T> type, final PropertyCode
4542
}
4643
return null;
4744
}
48-
49-
private static class EnumCodec<T extends Enum<T>> implements Codec<T> {
50-
private final Class<T> clazz;
51-
52-
EnumCodec(final Class<T> clazz) {
53-
this.clazz = clazz;
54-
}
55-
56-
@Override
57-
public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
58-
writer.writeString(value.name());
59-
}
60-
61-
@Override
62-
public Class<T> getEncoderClass() {
63-
return clazz;
64-
}
65-
66-
@Override
67-
public T decode(final BsonReader reader, final DecoderContext decoderContext) {
68-
return Enum.valueOf(clazz, reader.readString());
69-
}
70-
}
71-
7245
}

bson/src/main/org/bson/conversions/Bson.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.bson.codecs.BsonCodecProvider;
2121
import org.bson.codecs.BsonValueCodecProvider;
2222
import org.bson.codecs.DocumentCodecProvider;
23+
import org.bson.codecs.EnumCodecProvider;
2324
import org.bson.codecs.IterableCodecProvider;
2425
import org.bson.codecs.JsonObjectCodecProvider;
2526
import org.bson.codecs.MapCodecProvider;
@@ -47,6 +48,7 @@ public interface Bson {
4748
* <li>{@link Jsr310CodecProvider}</li>
4849
* <li>{@link JsonObjectCodecProvider}</li>
4950
* <li>{@link BsonCodecProvider}</li>
51+
* <li>{@link EnumCodecProvider}</li>
5052
* </ul>
5153
* <p>
5254
* Additional providers may be added in a future release.
@@ -63,7 +65,8 @@ public interface Bson {
6365
new MapCodecProvider(),
6466
new Jsr310CodecProvider(),
6567
new JsonObjectCodecProvider(),
66-
new BsonCodecProvider()));
68+
new BsonCodecProvider(),
69+
new EnumCodecProvider()));
6770

6871
/**
6972
* Render into a BsonDocument.

bson/src/test/unit/org/bson/codecs/CodecTestCase.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818

1919
import org.bson.BsonBinaryReader;
2020
import org.bson.BsonBinaryWriter;
21+
import org.bson.BsonDocument;
22+
import org.bson.BsonDocumentReader;
23+
import org.bson.BsonDocumentWriter;
2124
import org.bson.BsonType;
25+
import org.bson.BsonValue;
2226
import org.bson.BsonWriter;
2327
import org.bson.ByteBufNIO;
2428
import org.bson.Document;
@@ -44,6 +48,23 @@ CodecRegistry getRegistry() {
4448
return fromProviders(asList(new ValueCodecProvider(), getDocumentCodecProvider()));
4549
}
4650

51+
<T> T getDecodedValue(final BsonValue bsonValue, final Decoder<T> decoder) {
52+
BsonDocument document = new BsonDocument("val", bsonValue);
53+
BsonDocumentReader reader = new BsonDocumentReader(document);
54+
reader.readStartDocument();
55+
reader.readName("val");
56+
return decoder.decode(reader, DecoderContext.builder().build());
57+
}
58+
59+
<T> BsonValue getEncodedValue(final T value, final Encoder<T> encoder) {
60+
BsonDocumentWriter writer = new BsonDocumentWriter(new BsonDocument());
61+
writer.writeStartDocument();
62+
writer.writeName("val");
63+
encoder.encode(writer, value, EncoderContext.builder().build());
64+
writer.writeEndDocument();
65+
return writer.getDocument().get("val");
66+
}
67+
4768
<T> void roundTrip(final T value) {
4869
roundTrip(value, new DefaultComparator<T>(value));
4970
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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;
18+
19+
import org.bson.codecs.configuration.CodecRegistries;
20+
import org.junit.jupiter.api.Test;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
24+
import static org.junit.jupiter.api.Assertions.assertNull;
25+
26+
public class EnumCodecProviderTest {
27+
@Test
28+
public void shouldProvideCodecForEnum() {
29+
EnumCodecProvider provider = new EnumCodecProvider();
30+
Codec<SimpleEnum> codec = provider.get(SimpleEnum.class, CodecRegistries.fromProviders(provider));
31+
assertNotNull(codec);
32+
assertEquals(EnumCodec.class, codec.getClass());
33+
}
34+
35+
@Test
36+
public void shouldNotProvideCodecForNonEnum() {
37+
EnumCodecProvider provider = new EnumCodecProvider();
38+
Codec<String> codec = provider.get(String.class, CodecRegistries.fromProviders(provider));
39+
assertNull(codec);
40+
}
41+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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;
18+
19+
import org.bson.BsonString;
20+
import org.bson.BsonValue;
21+
import org.junit.jupiter.api.Test;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
public class EnumCodecTest extends CodecTestCase {
26+
@Test
27+
public void shouldEncodeEnum() {
28+
Codec<SimpleEnum> codec = new EnumCodec<>(SimpleEnum.class);
29+
BsonValue encodedValue = getEncodedValue(SimpleEnum.BRAVO, codec);
30+
assertEquals(SimpleEnum.BRAVO.name(), encodedValue.asString().getValue());
31+
}
32+
33+
@Test
34+
public void shouldDecodeEnum() {
35+
Codec<SimpleEnum> codec = new EnumCodec<>(SimpleEnum.class);
36+
SimpleEnum decodedValue = getDecodedValue(new BsonString(SimpleEnum.BRAVO.name()), codec);
37+
assertEquals(SimpleEnum.BRAVO, decodedValue);
38+
}
39+
}

bson/src/test/unit/org/bson/codecs/pojo/entities/SimpleEnum.java renamed to bson/src/test/unit/org/bson/codecs/SimpleEnum.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.bson.codecs.pojo.entities;
17+
package org.bson.codecs;
1818

1919
public enum SimpleEnum {
2020
ALPHA,

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
import org.bson.codecs.pojo.entities.PrimitivesModel;
5656
import org.bson.codecs.pojo.entities.PrivateSetterFieldModel;
5757
import org.bson.codecs.pojo.entities.PropertyWithMultipleTypeParamsModel;
58-
import org.bson.codecs.pojo.entities.SimpleEnum;
58+
import org.bson.codecs.SimpleEnum;
5959
import org.bson.codecs.pojo.entities.SimpleEnumModel;
6060
import org.bson.codecs.pojo.entities.SimpleGenericsModel;
6161
import org.bson.codecs.pojo.entities.SimpleIdImmutableModel;
@@ -352,11 +352,19 @@ public void testUseGettersForSettersConventionNotEmptyMap() {
352352

353353
@Test
354354
public void testEnumSupportWithCustomCodec() {
355-
CodecRegistry registry = fromRegistries(getCodecRegistry(getPojoCodecProviderBuilder(SimpleEnumModel.class)),
356-
fromCodecs(new SimpleEnumCodec()));
355+
CodecRegistry registry = fromRegistries(fromCodecs(new SimpleEnumCodec()),
356+
getCodecRegistry(getPojoCodecProviderBuilder(SimpleEnumModel.class)));
357357
roundTrip(registry, new SimpleEnumModel(SimpleEnum.BRAVO), "{ 'myEnum': 1 }");
358358
}
359359

360+
@Test
361+
public void testEnumSupportWithFallback() {
362+
// Create a registry without EnumCodecProvider, to test the fallback in EnumPropertyCodecProvider#get
363+
CodecRegistry registry = fromRegistries(fromProviders(new ValueCodecProvider(),
364+
getPojoCodecProviderBuilder(SimpleEnumModel.class).build()));
365+
roundTrip(registry, new SimpleEnumModel(SimpleEnum.BRAVO), "{ 'myEnum': 'BRAVO' }");
366+
}
367+
360368
@Test
361369
@SuppressWarnings("unchecked")
362370
public void testCustomCodec() {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
import org.bson.codecs.pojo.entities.ShapeModelAbstract;
6767
import org.bson.codecs.pojo.entities.ShapeModelCircle;
6868
import org.bson.codecs.pojo.entities.ShapeModelRectangle;
69-
import org.bson.codecs.pojo.entities.SimpleEnum;
69+
import org.bson.codecs.SimpleEnum;
7070
import org.bson.codecs.pojo.entities.SimpleEnumModel;
7171
import org.bson.codecs.pojo.entities.SimpleGenericsModel;
7272
import org.bson.codecs.pojo.entities.SimpleIdImmutableModel;

0 commit comments

Comments
 (0)