|
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