|
15 | 15 | */
|
16 | 16 | package org.springframework.data.mongodb.util;
|
17 | 17 |
|
18 |
| -import java.time.Instant; |
19 |
| -import java.time.LocalDate; |
20 |
| -import java.time.LocalDateTime; |
21 |
| -import java.time.LocalTime; |
22 |
| -import java.time.ZoneOffset; |
23 |
| -import java.time.temporal.Temporal; |
| 18 | +import java.util.ArrayList; |
24 | 19 | import java.util.Arrays;
|
25 | 20 | import java.util.Collection;
|
26 | 21 | import java.util.Collections;
|
27 | 22 | import java.util.Date;
|
| 23 | +import java.util.List; |
28 | 24 | import java.util.Map;
|
29 | 25 | import java.util.StringJoiner;
|
30 | 26 | import java.util.function.Function;
|
31 | 27 | import java.util.stream.StreamSupport;
|
32 | 28 |
|
33 | 29 | import org.bson.*;
|
| 30 | +import org.bson.codecs.Codec; |
34 | 31 | import org.bson.codecs.DocumentCodec;
|
| 32 | +import org.bson.codecs.EncoderContext; |
| 33 | +import org.bson.codecs.configuration.CodecConfigurationException; |
35 | 34 | import org.bson.codecs.configuration.CodecRegistry;
|
36 | 35 | import org.bson.conversions.Bson;
|
37 | 36 | import org.bson.json.JsonParseException;
|
38 | 37 | import org.bson.types.Binary;
|
| 38 | +import org.bson.types.Decimal128; |
39 | 39 | import org.bson.types.ObjectId;
|
40 | 40 | import org.springframework.core.convert.converter.Converter;
|
41 | 41 | import org.springframework.data.mongodb.CodecRegistryProvider;
|
42 | 42 | import org.springframework.lang.Nullable;
|
43 | 43 | import org.springframework.util.Assert;
|
| 44 | +import org.springframework.util.ClassUtils; |
44 | 45 | import org.springframework.util.CollectionUtils;
|
45 | 46 | import org.springframework.util.ObjectUtils;
|
46 | 47 | import org.springframework.util.StringUtils;
|
@@ -109,7 +110,7 @@ public static Map<String, Object> asMap(@Nullable Bson bson, CodecRegistry codec
|
109 | 110 | return dbo.toMap();
|
110 | 111 | }
|
111 | 112 |
|
112 |
| - return new Document((Map) bson.toBsonDocument(Document.class, codecRegistry)); |
| 113 | + return new Document(bson.toBsonDocument(Document.class, codecRegistry)); |
113 | 114 | }
|
114 | 115 |
|
115 | 116 | /**
|
@@ -327,6 +328,20 @@ public static Object toJavaType(BsonValue value) {
|
327 | 328 | * @since 3.0
|
328 | 329 | */
|
329 | 330 | public static BsonValue simpleToBsonValue(Object source) {
|
| 331 | + return simpleToBsonValue(source, MongoClientSettings.getDefaultCodecRegistry()); |
| 332 | + } |
| 333 | + |
| 334 | + /** |
| 335 | + * Convert a given simple value (eg. {@link String}, {@link Long}) to its corresponding {@link BsonValue}. |
| 336 | + * |
| 337 | + * @param source must not be {@literal null}. |
| 338 | + * @param codecRegistry The {@link CodecRegistry} used as a fallback to convert types using native {@link Codec}. Must |
| 339 | + * not be {@literal null}. |
| 340 | + * @return the corresponding {@link BsonValue} representation. |
| 341 | + * @throws IllegalArgumentException if {@literal source} does not correspond to a {@link BsonValue} type. |
| 342 | + * @since 4.2 |
| 343 | + */ |
| 344 | + public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegistry) { |
330 | 345 |
|
331 | 346 | if (source instanceof BsonValue bsonValue) {
|
332 | 347 | return bsonValue;
|
@@ -364,31 +379,30 @@ public static BsonValue simpleToBsonValue(Object source) {
|
364 | 379 | return new BsonDouble(floatValue);
|
365 | 380 | }
|
366 | 381 |
|
367 |
| - if(source instanceof Binary binary) { |
| 382 | + if (source instanceof Binary binary) { |
368 | 383 | return new BsonBinary(binary.getType(), binary.getData());
|
369 | 384 | }
|
370 | 385 |
|
371 |
| - if(source instanceof Temporal) { |
372 |
| - if (source instanceof Instant value) { |
373 |
| - return new BsonDateTime(value.toEpochMilli()); |
374 |
| - } |
375 |
| - if (source instanceof LocalDateTime value) { |
376 |
| - return new BsonDateTime(value.toInstant(ZoneOffset.UTC).toEpochMilli()); |
377 |
| - } |
378 |
| - if(source instanceof LocalDate value) { |
379 |
| - return new BsonDateTime(value.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli()); |
380 |
| - } |
381 |
| - if(source instanceof LocalTime value) { |
382 |
| - return new BsonDateTime(value.atDate(LocalDate.ofEpochDay(0L)).toInstant(ZoneOffset.UTC).toEpochMilli()); |
383 |
| - } |
384 |
| - } |
385 |
| - |
386 |
| - if(source instanceof Date date) { |
| 386 | + if (source instanceof Date date) { |
387 | 387 | new BsonDateTime(date.getTime());
|
388 | 388 | }
|
389 | 389 |
|
390 |
| - throw new IllegalArgumentException(String.format("Unable to convert %s (%s) to BsonValue.", source, |
391 |
| - source != null ? source.getClass().getName() : "null")); |
| 390 | + try { |
| 391 | + |
| 392 | + Object value = source; |
| 393 | + if (ClassUtils.isPrimitiveArray(source.getClass())) { |
| 394 | + value = CollectionUtils.arrayToList(source); |
| 395 | + } |
| 396 | + |
| 397 | + Codec codec = codecRegistry.get(value.getClass()); |
| 398 | + BsonCapturingWriter writer = new BsonCapturingWriter(value.getClass()); |
| 399 | + codec.encode(writer, value, |
| 400 | + ObjectUtils.isArray(value) || value instanceof Collection<?> ? EncoderContext.builder().build() : null); |
| 401 | + return writer.getCapturedValue(); |
| 402 | + } catch (CodecConfigurationException e) { |
| 403 | + throw new IllegalArgumentException( |
| 404 | + String.format("Unable to convert %s to BsonValue.", source != null ? source.getClass().getName() : "null")); |
| 405 | + } |
392 | 406 | }
|
393 | 407 |
|
394 | 408 | /**
|
@@ -694,7 +708,7 @@ private static String toJson(@Nullable Object value) {
|
694 | 708 |
|
695 | 709 | if (value instanceof Collection<?> collection) {
|
696 | 710 | return toString(collection);
|
697 |
| - } else if (value instanceof Map<?,?> map) { |
| 711 | + } else if (value instanceof Map<?, ?> map) { |
698 | 712 | return toString(map);
|
699 | 713 | } else if (ObjectUtils.isArray(value)) {
|
700 | 714 | return toString(Arrays.asList(ObjectUtils.toObjectArray(value)));
|
@@ -733,4 +747,162 @@ private static <T> String iterableToDelimitedString(Iterable<T> source, String p
|
733 | 747 |
|
734 | 748 | return joiner.toString();
|
735 | 749 | }
|
| 750 | + |
| 751 | + private static class BsonCapturingWriter extends AbstractBsonWriter { |
| 752 | + |
| 753 | + List<BsonValue> values = new ArrayList<>(0); |
| 754 | + |
| 755 | + public BsonCapturingWriter(Class<?> type) { |
| 756 | + super(new BsonWriterSettings()); |
| 757 | + if (ClassUtils.isAssignable(Map.class, type)) { |
| 758 | + setContext(new Context(null, BsonContextType.DOCUMENT)); |
| 759 | + } else if (ClassUtils.isAssignable(List.class, type) || type.isArray()) { |
| 760 | + setContext(new Context(null, BsonContextType.ARRAY)); |
| 761 | + } else { |
| 762 | + setContext(new Context(null, BsonContextType.DOCUMENT)); |
| 763 | + } |
| 764 | + } |
| 765 | + |
| 766 | + BsonValue getCapturedValue() { |
| 767 | + |
| 768 | + if (values.isEmpty()) { |
| 769 | + return null; |
| 770 | + } |
| 771 | + if (!getContext().getContextType().equals(BsonContextType.ARRAY)) { |
| 772 | + return values.get(0); |
| 773 | + } |
| 774 | + |
| 775 | + return new BsonArray(values); |
| 776 | + } |
| 777 | + |
| 778 | + @Override |
| 779 | + protected void doWriteStartDocument() { |
| 780 | + |
| 781 | + } |
| 782 | + |
| 783 | + @Override |
| 784 | + protected void doWriteEndDocument() { |
| 785 | + |
| 786 | + } |
| 787 | + |
| 788 | + @Override |
| 789 | + public void writeStartArray() { |
| 790 | + setState(State.VALUE); |
| 791 | + } |
| 792 | + |
| 793 | + @Override |
| 794 | + public void writeEndArray() { |
| 795 | + setState(State.NAME); |
| 796 | + } |
| 797 | + |
| 798 | + @Override |
| 799 | + protected void doWriteStartArray() { |
| 800 | + |
| 801 | + } |
| 802 | + |
| 803 | + @Override |
| 804 | + protected void doWriteEndArray() { |
| 805 | + |
| 806 | + } |
| 807 | + |
| 808 | + @Override |
| 809 | + protected void doWriteBinaryData(BsonBinary value) { |
| 810 | + values.add(value); |
| 811 | + } |
| 812 | + |
| 813 | + @Override |
| 814 | + protected void doWriteBoolean(boolean value) { |
| 815 | + values.add(BsonBoolean.valueOf(value)); |
| 816 | + } |
| 817 | + |
| 818 | + @Override |
| 819 | + protected void doWriteDateTime(long value) { |
| 820 | + values.add(new BsonDateTime(value)); |
| 821 | + } |
| 822 | + |
| 823 | + @Override |
| 824 | + protected void doWriteDBPointer(BsonDbPointer value) { |
| 825 | + values.add(value); |
| 826 | + } |
| 827 | + |
| 828 | + @Override |
| 829 | + protected void doWriteDouble(double value) { |
| 830 | + values.add(new BsonDouble(value)); |
| 831 | + } |
| 832 | + |
| 833 | + @Override |
| 834 | + protected void doWriteInt32(int value) { |
| 835 | + values.add(new BsonInt32(value)); |
| 836 | + } |
| 837 | + |
| 838 | + @Override |
| 839 | + protected void doWriteInt64(long value) { |
| 840 | + values.add(new BsonInt64(value)); |
| 841 | + } |
| 842 | + |
| 843 | + @Override |
| 844 | + protected void doWriteDecimal128(Decimal128 value) { |
| 845 | + values.add(new BsonDecimal128(value)); |
| 846 | + } |
| 847 | + |
| 848 | + @Override |
| 849 | + protected void doWriteJavaScript(String value) { |
| 850 | + values.add(new BsonJavaScript(value)); |
| 851 | + } |
| 852 | + |
| 853 | + @Override |
| 854 | + protected void doWriteJavaScriptWithScope(String value) { |
| 855 | + values.add(new BsonJavaScriptWithScope(value, null)); |
| 856 | + } |
| 857 | + |
| 858 | + @Override |
| 859 | + protected void doWriteMaxKey() { |
| 860 | + |
| 861 | + } |
| 862 | + |
| 863 | + @Override |
| 864 | + protected void doWriteMinKey() { |
| 865 | + |
| 866 | + } |
| 867 | + |
| 868 | + @Override |
| 869 | + protected void doWriteNull() { |
| 870 | + values.add(new BsonNull()); |
| 871 | + } |
| 872 | + |
| 873 | + @Override |
| 874 | + protected void doWriteObjectId(ObjectId value) { |
| 875 | + values.add(new BsonObjectId(value)); |
| 876 | + } |
| 877 | + |
| 878 | + @Override |
| 879 | + protected void doWriteRegularExpression(BsonRegularExpression value) { |
| 880 | + values.add(value); |
| 881 | + } |
| 882 | + |
| 883 | + @Override |
| 884 | + protected void doWriteString(String value) { |
| 885 | + values.add(new BsonString(value)); |
| 886 | + } |
| 887 | + |
| 888 | + @Override |
| 889 | + protected void doWriteSymbol(String value) { |
| 890 | + values.add(new BsonSymbol(value)); |
| 891 | + } |
| 892 | + |
| 893 | + @Override |
| 894 | + protected void doWriteTimestamp(BsonTimestamp value) { |
| 895 | + values.add(value); |
| 896 | + } |
| 897 | + |
| 898 | + @Override |
| 899 | + protected void doWriteUndefined() { |
| 900 | + values.add(new BsonUndefined()); |
| 901 | + } |
| 902 | + |
| 903 | + @Override |
| 904 | + public void flush() { |
| 905 | + values.clear(); |
| 906 | + } |
| 907 | + } |
736 | 908 | }
|
0 commit comments