Skip to content

Commit 19aac82

Browse files
committed
Added MapCodec
JAVA-2424
1 parent f41d11d commit 19aac82

File tree

10 files changed

+449
-9
lines changed

10 files changed

+449
-9
lines changed

bson/src/main/org/bson/codecs/DocumentCodec.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,20 @@ public class DocumentCodec implements CollectibleCodec<Document> {
5656
private final Transformer valueTransformer;
5757

5858
/**
59-
* Construct a new instance with a default {@code CodecRegistry} and
59+
* Construct a new instance with a default {@code CodecRegistry}.
6060
*/
6161
public DocumentCodec() {
62-
this(DEFAULT_REGISTRY, DEFAULT_BSON_TYPE_CLASS_MAP);
62+
this(DEFAULT_REGISTRY);
63+
}
64+
65+
/**
66+
* Construct a new instance with the given registry.
67+
*
68+
* @param registry the registry
69+
* @since 3.5
70+
*/
71+
public DocumentCodec(final CodecRegistry registry) {
72+
this(registry, DEFAULT_BSON_TYPE_CLASS_MAP);
6373
}
6474

6575
/**

bson/src/main/org/bson/codecs/IterableCodecProvider.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public IterableCodecProvider() {
4444
* IterableCodec as a last step when decoding values.
4545
*
4646
* @param valueTransformer the value transformer for decoded values
47-
* @see org.bson.codecs.DocumentCodec#DocumentCodec(org.bson.codecs.configuration.CodecRegistry, BsonTypeClassMap, org.bson.Transformer)
4847
*/
4948
public IterableCodecProvider(final Transformer valueTransformer) {
5049
this(new BsonTypeClassMap(), valueTransformer);
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (c) 2008-2014 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.BsonBinarySubType;
20+
import org.bson.BsonReader;
21+
import org.bson.BsonType;
22+
import org.bson.BsonWriter;
23+
import org.bson.Transformer;
24+
import org.bson.codecs.configuration.CodecRegistry;
25+
26+
import java.util.HashMap;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.UUID;
30+
31+
import static java.util.Arrays.asList;
32+
import static org.bson.assertions.Assertions.notNull;
33+
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
34+
35+
/**
36+
* A Codec for Map instances.
37+
*
38+
* @since 3.5
39+
*/
40+
public class MapCodec implements Codec<Map<String, Object>> {
41+
42+
private static final CodecRegistry DEFAULT_REGISTRY = fromProviders(asList(new ValueCodecProvider(), new BsonValueCodecProvider(),
43+
new DocumentCodecProvider(), new IterableCodecProvider(), new MapCodecProvider()));
44+
private static final BsonTypeClassMap DEFAULT_BSON_TYPE_CLASS_MAP = new BsonTypeClassMap();
45+
private final BsonTypeCodecMap bsonTypeCodecMap;
46+
private final CodecRegistry registry;
47+
private final Transformer valueTransformer;
48+
49+
/**
50+
* Construct a new instance with a default {@code CodecRegistry}
51+
*/
52+
public MapCodec() {
53+
this(DEFAULT_REGISTRY);
54+
}
55+
56+
/**
57+
Construct a new instance with the given registry
58+
*
59+
* @param registry the registry
60+
*/
61+
public MapCodec(final CodecRegistry registry) {
62+
this(registry, DEFAULT_BSON_TYPE_CLASS_MAP);
63+
}
64+
65+
/**
66+
* Construct a new instance with the given registry and BSON type class map.
67+
*
68+
* @param registry the registry
69+
* @param bsonTypeClassMap the BSON type class map
70+
*/
71+
public MapCodec(final CodecRegistry registry, final BsonTypeClassMap bsonTypeClassMap) {
72+
this(registry, bsonTypeClassMap, null);
73+
}
74+
75+
/**
76+
* Construct a new instance with the given registry and BSON type class map. The transformer is applied as a last step when decoding
77+
* values, which allows users of this codec to control the decoding process. For example, a user of this class could substitute a
78+
* value decoded as a Document with an instance of a special purpose class (e.g., one representing a DBRef in MongoDB).
79+
*
80+
* @param registry the registry
81+
* @param bsonTypeClassMap the BSON type class map
82+
* @param valueTransformer the value transformer to use as a final step when decoding the value of any field in the map
83+
*/
84+
public MapCodec(final CodecRegistry registry, final BsonTypeClassMap bsonTypeClassMap, final Transformer valueTransformer) {
85+
this.registry = notNull("registry", registry);
86+
this.bsonTypeCodecMap = new BsonTypeCodecMap(notNull("bsonTypeClassMap", bsonTypeClassMap), registry);
87+
this.valueTransformer = valueTransformer != null ? valueTransformer : new Transformer() {
88+
@Override
89+
public Object transform(final Object value) {
90+
return value;
91+
}
92+
};
93+
}
94+
95+
@Override
96+
public void encode(final BsonWriter writer, final Map<String, Object> map, final EncoderContext encoderContext) {
97+
writer.writeStartDocument();
98+
for (final Map.Entry<String, Object> entry : map.entrySet()) {
99+
writer.writeName(entry.getKey());
100+
writeValue(writer, encoderContext, entry.getValue());
101+
}
102+
writer.writeEndDocument();
103+
}
104+
105+
@Override
106+
public Map<String, Object> decode(final BsonReader reader, final DecoderContext decoderContext) {
107+
Map<String, Object> map = new HashMap<String, Object>();
108+
109+
reader.readStartDocument();
110+
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
111+
String fieldName = reader.readName();
112+
map.put(fieldName, readValue(reader, decoderContext));
113+
}
114+
115+
reader.readEndDocument();
116+
return map;
117+
}
118+
119+
@SuppressWarnings("unchecked")
120+
@Override
121+
public Class<Map<String, Object>> getEncoderClass() {
122+
return (Class<Map<String, Object>>) ((Class) Map.class);
123+
}
124+
125+
private Object readValue(final BsonReader reader, final DecoderContext decoderContext) {
126+
BsonType bsonType = reader.getCurrentBsonType();
127+
if (bsonType == BsonType.NULL) {
128+
reader.readNull();
129+
return null;
130+
} else if (bsonType == BsonType.ARRAY) {
131+
return decoderContext.decodeWithChildContext(registry.get(List.class), reader);
132+
} else if (bsonType == BsonType.BINARY && BsonBinarySubType.isUuid(reader.peekBinarySubType()) && reader.peekBinarySize() == 16) {
133+
return decoderContext.decodeWithChildContext(registry.get(UUID.class), reader);
134+
}
135+
return valueTransformer.transform(bsonTypeCodecMap.get(bsonType).decode(reader, decoderContext));
136+
}
137+
138+
@SuppressWarnings({"unchecked", "rawtypes"})
139+
private void writeValue(final BsonWriter writer, final EncoderContext encoderContext, final Object value) {
140+
if (value == null) {
141+
writer.writeNull();
142+
} else {
143+
Codec codec = registry.get(value.getClass());
144+
encoderContext.encodeWithChildContext(codec, writer, value);
145+
}
146+
}
147+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2008-2014 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.Transformer;
20+
import org.bson.codecs.configuration.CodecProvider;
21+
import org.bson.codecs.configuration.CodecRegistry;
22+
23+
import java.util.Map;
24+
25+
import static org.bson.assertions.Assertions.notNull;
26+
27+
/**
28+
* A {@code CodecProvider} for the Map class and all the default Codec implementations on which it depends.
29+
*
30+
* @since 3.5
31+
*/
32+
public class MapCodecProvider implements CodecProvider {
33+
private final BsonTypeClassMap bsonTypeClassMap;
34+
private final Transformer valueTransformer;
35+
36+
/**
37+
* Construct a new instance with a default {@code BsonTypeClassMap}.
38+
*/
39+
public MapCodecProvider() {
40+
this(new BsonTypeClassMap());
41+
}
42+
43+
/**
44+
* Construct a new instance with the given instance of {@code BsonTypeClassMap}.
45+
*
46+
* @param bsonTypeClassMap the non-null {@code BsonTypeClassMap} with which to construct instances of {@code DocumentCodec} and {@code
47+
* ListCodec}
48+
*/
49+
public MapCodecProvider(final BsonTypeClassMap bsonTypeClassMap) {
50+
this(bsonTypeClassMap, null);
51+
}
52+
53+
/**
54+
* Construct a new instance with a default {@code BsonTypeClassMap} and the given {@code Transformer}. The transformer is used by the
55+
* MapCodec as a last step when decoding values.
56+
*
57+
* @param valueTransformer the value transformer for decoded values
58+
*/
59+
public MapCodecProvider(final Transformer valueTransformer) {
60+
this(new BsonTypeClassMap(), valueTransformer);
61+
}
62+
63+
/**
64+
* Construct a new instance with the given instance of {@code BsonTypeClassMap}.
65+
*
66+
* @param bsonTypeClassMap the non-null {@code BsonTypeClassMap} with which to construct instances of {@code MapCodec}.
67+
* @param valueTransformer the value transformer for decoded values
68+
*/
69+
public MapCodecProvider(final BsonTypeClassMap bsonTypeClassMap, final Transformer valueTransformer) {
70+
this.bsonTypeClassMap = notNull("bsonTypeClassMap", bsonTypeClassMap);
71+
this.valueTransformer = valueTransformer;
72+
}
73+
74+
@Override
75+
@SuppressWarnings("unchecked")
76+
public <T> Codec<T> get(final Class<T> clazz, final CodecRegistry registry) {
77+
if (Map.class.isAssignableFrom(clazz)) {
78+
return (Codec<T>) new MapCodec(registry, bsonTypeClassMap, valueTransformer);
79+
}
80+
81+
return null;
82+
}
83+
84+
@Override
85+
public boolean equals(final Object o) {
86+
if (this == o) {
87+
return true;
88+
}
89+
if (o == null || getClass() != o.getClass()) {
90+
return false;
91+
}
92+
93+
MapCodecProvider that = (MapCodecProvider) o;
94+
if (!bsonTypeClassMap.equals(that.bsonTypeClassMap)) {
95+
return false;
96+
}
97+
if (valueTransformer != null ? !valueTransformer.equals(that.valueTransformer) : that.valueTransformer != null) {
98+
return false;
99+
}
100+
101+
return true;
102+
}
103+
104+
@Override
105+
public int hashCode() {
106+
int result = bsonTypeClassMap.hashCode();
107+
result = 31 * result + (valueTransformer != null ? valueTransformer.hashCode() : 0);
108+
return result;
109+
}
110+
}

0 commit comments

Comments
 (0)