Skip to content

Commit c5f8ebc

Browse files
authored
Improve constructible BSON toString (#972)
1 parent 4d323a3 commit c5f8ebc

File tree

9 files changed

+154
-20
lines changed

9 files changed

+154
-20
lines changed

bson/src/main/org/bson/Document.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public Document(final String key, final Object value) {
9595
*
9696
* @param map initial map
9797
*/
98-
public Document(final Map<String, Object> map) {
98+
public Document(final Map<String, ?> map) {
9999
documentAsMap = new LinkedHashMap<String, Object>(map);
100100
}
101101

driver-core/src/main/com/mongodb/client/model/search/SearchScoreExpression.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ public <TDocument> BsonDocument toBsonDocument(final Class<TDocument> documentCl
101101

102102
@Override
103103
public String toString() {
104-
return "{\"origin\": " + origin
105-
+ ", \"path\": " + path
106-
+ ", \"scale\": " + scale
107-
+ '}';
104+
return new Document("origin", origin)
105+
.append("path", path)
106+
.append("scale", scale)
107+
.toString();
108108
}
109109
};
110110
return new SearchConstructibleBsonElement("gauss", value);

driver-core/src/main/com/mongodb/internal/Iterables.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
import com.mongodb.lang.Nullable;
1919

2020
import java.util.Iterator;
21+
import java.util.Objects;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.Stream;
24+
import java.util.stream.StreamSupport;
2125

2226
import static com.mongodb.assertions.Assertions.fail;
2327
import static java.util.Arrays.asList;
@@ -27,16 +31,25 @@ public final class Iterables {
2731
@SafeVarargs
2832
@SuppressWarnings("varargs")
2933
public static <T> Iterable<T> concat(@Nullable final T first, @Nullable final T... more) {
30-
if (more == null) {
31-
return singleton(first);
32-
} else {
33-
Iterable<? extends T> moreIterable = asList(more);
34-
return () -> new ConcatIterator<>(first, moreIterable);
35-
}
34+
return more == null ? singleton(first) : concat(first, asList(more));
3635
}
3736

3837
public static <T> Iterable<T> concat(@Nullable final T first, final Iterable<? extends T> more) {
39-
return () -> new ConcatIterator<>(first, more);
38+
return new Iterable<T>() {
39+
@Override
40+
public Iterator<T> iterator() {
41+
return new ConcatIterator<>(first, more);
42+
}
43+
44+
@Override
45+
public String toString() {
46+
return '['
47+
+ Stream.concat(Stream.of(first), StreamSupport.stream(more.spliterator(), false))
48+
.map(Objects::toString)
49+
.collect(Collectors.joining(", "))
50+
+ ']';
51+
}
52+
};
4053
}
4154

4255
private Iterables() {

driver-core/src/main/com/mongodb/internal/client/model/AbstractConstructibleBson.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
import org.bson.codecs.configuration.CodecRegistry;
2222
import org.bson.conversions.Bson;
2323

24+
import java.util.LinkedHashMap;
25+
import java.util.Map;
2426
import java.util.Objects;
27+
import java.util.Optional;
2528
import java.util.function.Consumer;
2629

2730
/**
@@ -31,7 +34,7 @@
3134
* @param <S> A type introduced by the concrete class that extends this abstract class.
3235
* @see AbstractConstructibleBsonElement
3336
*/
34-
public abstract class AbstractConstructibleBson<S extends AbstractConstructibleBson<S>> implements Bson {
37+
public abstract class AbstractConstructibleBson<S extends AbstractConstructibleBson<S>> implements Bson, ToMap {
3538
private static final Document EMPTY_DOC = new Document();
3639
/**
3740
* An {@linkplain Immutable immutable} {@link BsonDocument#isEmpty() empty} instance.
@@ -91,6 +94,16 @@ protected final S newMutated(final Consumer<Document> mutator) {
9194
return newSelf(base, newAppended);
9295
}
9396

97+
@Override
98+
public Optional<Map<String, ?>> tryToMap() {
99+
return ToMap.tryToMap(base)
100+
.map(baseMap -> {
101+
Map<String, Object> result = new LinkedHashMap<>(baseMap);
102+
result.putAll(appended);
103+
return result;
104+
});
105+
}
106+
94107
public static AbstractConstructibleBson<?> of(final Bson doc) {
95108
return doc instanceof AbstractConstructibleBson
96109
// prevent double wrapping
@@ -117,9 +130,12 @@ public final int hashCode() {
117130

118131
@Override
119132
public String toString() {
120-
return "{base=" + base
121-
+ ", appended=" + appended
122-
+ '}';
133+
return tryToMap()
134+
.map(Document::new)
135+
.map(Document::toString)
136+
.orElseGet(() -> "ConstructibleBson{base=" + base
137+
+ ", appended=" + appended
138+
+ '}');
123139
}
124140

125141
static BsonDocument newMerged(final BsonDocument base, final BsonDocument appended) {

driver-core/src/main/com/mongodb/internal/client/model/AbstractConstructibleBsonElement.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import org.bson.codecs.configuration.CodecRegistry;
2222
import org.bson.conversions.Bson;
2323

24+
import java.util.LinkedHashMap;
2425
import java.util.Map;
2526
import java.util.Objects;
27+
import java.util.Optional;
2628
import java.util.function.Consumer;
2729

2830
import static com.mongodb.internal.client.model.AbstractConstructibleBson.EMPTY_IMMUTABLE;
@@ -37,7 +39,7 @@
3739
* @param <S> A type introduced by the concrete class that extends this abstract class.
3840
* @see AbstractConstructibleBson
3941
*/
40-
public abstract class AbstractConstructibleBsonElement<S extends AbstractConstructibleBsonElement<S>> implements Bson {
42+
public abstract class AbstractConstructibleBsonElement<S extends AbstractConstructibleBsonElement<S>> implements Bson, ToMap {
4143
private final Bson baseElement;
4244
private final AbstractConstructibleBson<?> appendedElementValue;
4345

@@ -99,6 +101,32 @@ public final <TDocument> BsonDocument toBsonDocument(final Class<TDocument> docu
99101
: new BsonDocument(baseElementName, newMerged(baseElementValueDoc, appendedElementValueDoc));
100102
}
101103

104+
@Override
105+
public Optional<Map<String, ?>> tryToMap() {
106+
Map<String, ?> baseElementMap = ToMap.tryToMap(baseElement).orElse(null);
107+
Map<String, ?> appendedElementValueMap = baseElementMap == null ? null : appendedElementValue.tryToMap().orElse(null);
108+
if (baseElementMap != null && appendedElementValueMap != null) {
109+
if (baseElementMap.size() != 1) {
110+
throw new IllegalStateException(format("baseElement must contain exactly one element, but contains %s", baseElementMap.size()));
111+
}
112+
Map.Entry<String, ?> baseEntry = baseElementMap.entrySet().iterator().next();
113+
String elementName = baseEntry.getKey();
114+
return ToMap.tryToMap(baseEntry.getValue())
115+
.map(elementValueMap -> {
116+
Map<String, Object> result = new LinkedHashMap<>(elementValueMap);
117+
result.putAll(appendedElementValueMap);
118+
return result;
119+
})
120+
.map(mergedElementValueMap -> {
121+
Map<String, Object> result = new LinkedHashMap<>();
122+
result.put(elementName, new Document(mergedElementValueMap));
123+
return result;
124+
});
125+
} else {
126+
return Optional.empty();
127+
}
128+
}
129+
102130
public static AbstractConstructibleBsonElement<?> of(final Bson baseElement) {
103131
return baseElement instanceof AbstractConstructibleBsonElement
104132
// prevent double wrapping
@@ -125,9 +153,12 @@ public final int hashCode() {
125153

126154
@Override
127155
public String toString() {
128-
return "{baseElement=" + baseElement
129-
+ ", appendedValue=" + appendedElementValue
130-
+ '}';
156+
return tryToMap()
157+
.map(Document::new)
158+
.map(Document::toString)
159+
.orElseGet(() -> "ConstructibleBsonElement{baseElement=" + baseElement
160+
+ ", appendedElementValue=" + appendedElementValue
161+
+ '}');
131162
}
132163

133164
private static final class ConstructibleBsonElement extends AbstractConstructibleBsonElement<ConstructibleBsonElement> {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
package com.mongodb.internal.client.model;
17+
18+
import org.bson.BsonDocument;
19+
import org.bson.Document;
20+
21+
import java.util.Map;
22+
import java.util.Optional;
23+
24+
interface ToMap {
25+
Optional<Map<String, ?>> tryToMap();
26+
27+
static Optional<Map<String, ?>> tryToMap(final Object o) {
28+
if (o instanceof ToMap) {
29+
return ((ToMap) o).tryToMap();
30+
} else if (o instanceof Document || o instanceof BsonDocument) {
31+
@SuppressWarnings("unchecked")
32+
Map<String, ?> map = (Map<String, ?>) o;
33+
return Optional.of(map);
34+
} else {
35+
return Optional.empty();
36+
}
37+
}
38+
}

driver-core/src/test/unit/com/mongodb/internal/IterablesTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@ void concat() {
5050

5151
private static <T> void assertIterable(final List<? extends T> expected, final Iterable<? extends T> actual) {
5252
assertEquals(expected, stream(actual).collect(toList()));
53+
assertEquals(expected.toString(), actual.toString());
5354
}
5455
}

driver-core/src/test/unit/com/mongodb/internal/client/model/AbstractConstructibleBsonElementTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.bson.BsonDocument;
1919
import org.bson.BsonString;
20+
import org.bson.Document;
2021
import org.bson.conversions.Bson;
2122
import org.junit.jupiter.api.Test;
2223

@@ -99,6 +100,21 @@ void newWithAppendedValue() {
99100
appendedConstructible.toBsonDocument());
100101
}
101102

103+
@Test
104+
void tostring() {
105+
assertEquals(
106+
new Document("name",
107+
new Document("double", 0.5)
108+
.append("doc", new Document("i", 42))
109+
.append("constructible", new Document("s", "")))
110+
.toString(),
111+
AbstractConstructibleBsonElement.of(new Document("name",
112+
AbstractConstructibleBson.of(new Document("double", 0.5))
113+
.newAppended("doc", new Document("i", 42))))
114+
.newWithAppendedValue("constructible", AbstractConstructibleBson.of(AbstractConstructibleBson.of(new Document("s", ""))))
115+
.toString());
116+
}
117+
102118
private static void assertUnmodifiable(final AbstractConstructibleBsonElement<?> constructible) {
103119
String expected = constructible.toBsonDocument().toJson();
104120
constructible.newWithAppendedValue("assertUnmodifiableN", "assertUnmodifiableV");

driver-core/src/test/unit/com/mongodb/internal/client/model/AbstractConstructibleBsonTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
*/
1616
package com.mongodb.internal.client.model;
1717

18+
import org.bson.BsonArray;
1819
import org.bson.BsonDocument;
1920
import org.bson.BsonString;
2021
import org.bson.Document;
2122
import org.bson.conversions.Bson;
2223
import org.junit.jupiter.api.Test;
2324

25+
import static java.util.Arrays.asList;
2426
import static org.junit.jupiter.api.Assertions.assertEquals;
2527
import static org.junit.jupiter.api.Assertions.assertSame;
2628

@@ -68,6 +70,23 @@ void appendedCannotBeMutatedViaToBsonDocument() {
6870
appendedCannotBeMutatedViaToBsonDocument(new Document("appendedName", "appendedValue"));
6971
}
7072

73+
@Test
74+
void tostring() {
75+
assertEquals(
76+
new Document(
77+
"array", new BsonArray(asList(new BsonString("e1"), new BsonString("e2"))))
78+
.append("double", 0.5)
79+
.append("doc", new Document("i", 42))
80+
.append("constructible", new Document("s", ""))
81+
.toString(),
82+
AbstractConstructibleBson.of(
83+
new BsonDocument("array", new BsonArray(asList(new BsonString("e1"), new BsonString("e2")))))
84+
.newAppended("double", 0.5)
85+
.newAppended("doc", new Document("i", 42))
86+
.newAppended("constructible", AbstractConstructibleBson.of(AbstractConstructibleBson.of(new Document("s", ""))))
87+
.toString());
88+
}
89+
7190
private static void appendedCannotBeMutatedViaToBsonDocument(final Document appended) {
7291
String expected = appended.toBsonDocument().toJson();
7392
final class Constructible extends AbstractConstructibleBson<Constructible> {

0 commit comments

Comments
 (0)