-
Notifications
You must be signed in to change notification settings - Fork 215
/
BsonUtil.java
222 lines (196 loc) · 7.61 KB
/
BsonUtil.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
* Copyright (c) 2017 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.internal.utils.persistence.mongo;
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import com.mongodb.MongoClientSettings;
/**
* Utility methods for criteria creation.
*/
@Immutable
public final class BsonUtil {
private static final String NULL_STRING = Objects.toString(null);
private static final CodecRegistry CODEC_REGISTRY = MongoClientSettings.builder().build().getCodecRegistry();
private BsonUtil() {
throw new AssertionError();
}
/**
* Converts the given {@link Bson} object to a {@link BsonDocument}.
*
* @param bsonObj the Bson object.
* @return the Bson document.
* @throws NullPointerException if {@code bsonObj} is {@code null}.
*/
public static BsonDocument toBsonDocument(final Bson bsonObj) {
checkNotNull(bsonObj, "BSON object to be converted");
return bsonObj.toBsonDocument(BsonDocument.class, CODEC_REGISTRY);
}
/**
* Converts the given {@link Bson} object to a {@link BsonDocument}, if it is not {@code null}. Returns {@code
* null} otherwise.
*
* @param bsonObj the Bson object or {@code null}.
* @return the Bson document or {@code null}.
*/
public static @Nullable
BsonDocument toBsonDocumentOrNull(@Nullable final Bson bsonObj) {
if (bsonObj == null) {
return null;
}
return toBsonDocument(bsonObj);
}
/**
* Converts the given collection of {@link Bson} objects to a List of {@link BsonDocument}s.
*
* @param bsonObjs the Bson objects.
* @return the Bson documents.
* @throws NullPointerException if {@code bsonObjs} is {@code null}.
*/
public static List<BsonDocument> toBsonDocuments(final Collection<Bson> bsonObjs) {
checkNotNull(bsonObjs, "BSON objects to be converted");
return bsonObjs.stream()
.map(BsonUtil::toBsonDocument)
.toList();
}
/**
* Returns the value to which the specified key is mapped. The value is cast to type {@code <T>}.
*
* @param document the document whose value is requested
* @param key the key which holds the value
* @param clazz the class to cast the value to
* @param <T> the type to which the value will be cast.
* @return the value.
* @throws ClassCastException if the value has not the expected type
* @throws NullPointerException if this document contains no mapping for the key
* @see Document#get(Object)
*/
public static <T> T getRequiredDocumentValueAt(final Document document, final String key, final Class<T> clazz) {
final T value = getDocumentValueOrNullAt(document, key, clazz);
if (value == null) {
throw new NullPointerException("Key not found: " + key);
}
return value;
}
/**
* Returns the value to which the specified key is mapped or the specified {@code defaultValue}.
* The value is cast to type {@code <T>}.
*
* <p>
* <strong>NOTE:</strong> Method {@link Document#get(Object, Object)} does the same, but throws a
* {@link ClassCastException} if the {@code defaultValue} is not exactly the same type as {@code <T>}, which
* happens quite often: E.g., you want to define {@code <T>} as {@link Collection} and {@code defaultValue}
* as {@link Collections#emptyList()}, which is a subtype of {@link Collection}.
* </p>
*
* @param document the document whose value is requested
* @param key the key which holds the value
* @param clazz the class to cast the value to
* @param defaultValue the default value
* @param <T> the type to which the value will be cast.
* @return the value.
* @throws ClassCastException if the value has not the expected type
* @throws NullPointerException if this document contains no mapping for the key
* @see Document#get(Object)
*/
public static <T> T getDocumentWithDefaultAt(final Document document, final String key, final Class<T>
clazz, final T defaultValue) {
final T value = getDocumentValueOrNullAt(document, key, clazz);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Allows to extract values from {@link Bson} objects by specifying a dot-separated path,
* e.g. {@code thing.policyId}.
*
* <p>NOTE: Arrays are currently not supported.</p>
*
* @param bsonObj the Bson object.
* @param path the path.
* @param <T> the type to which the value will be cast.
* @return the value or {@code null}.
* @throws NullPointerException if any argument is {@code null}.
* @throws ClassCastException if the value has not the expected type.
*/
@Nullable
public static <T> T getValueByPath(final Bson bsonObj, final String path) {
checkNotNull(bsonObj, "BSON object which provides the value");
checkNotNull(path, "path");
final BsonDocument doc = toBsonDocument(bsonObj);
final List<String> paths = Arrays.asList(path.split("\\."));
if (paths.isEmpty()) {
throw new IllegalArgumentException("Empty path not allowed");
}
final String topLevelKey = paths.get(0);
final String remainingPath = String.join(".", paths.subList(1, paths.size()));
final BsonValue subBson = doc.get(topLevelKey);
if (subBson == null) {
return null;
} else if (remainingPath.isEmpty()) {
@SuppressWarnings("unchecked") final T result = (T) subBson;
return result;
}
return getValueByPath((Bson) subBson, remainingPath);
}
/**
* Pretty-prints Bson objects.
*
* @param bsons Bson objects to be printed, may be {@code null}.
* @return String representation of the Bson objects.
*/
public static String prettyPrint(@Nullable final Collection<Bson> bsons) {
if (bsons == null) {
return NULL_STRING;
}
return "[" + bsons.stream()
.map(BsonUtil::prettyPrint)
.collect(Collectors.joining(",\n")) + "]";
}
/**
* Pretty-prints a Bson object.
*
* @param bson Bson object to be printed, may be {@code null}.
* @return String representation of the Bson.
*/
public static String prettyPrint(@Nullable final Bson bson) {
if (bson == null) {
return NULL_STRING;
}
return toBsonDocument(bson).toJson();
}
/**
* Returns the codec registry.
*
* @return the codec registry.
*/
public static CodecRegistry getCodecRegistry() {
return CODEC_REGISTRY;
}
private static @Nullable
<T> T getDocumentValueOrNullAt(final Document document, final String key, final Class<T> clazz) {
return document.get(key, clazz);
}
}