diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 36dcb50ad..4d9d5a3b5 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -353,8 +353,76 @@ - + + + nullaway + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + com.querydsl + querydsl-apt + ${querydsl} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh} + + + com.google.errorprone + error_prone_core + ${errorprone} + + + com.uber.nullaway + nullaway + ${nullaway} + + + + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:TreatGeneratedAsUnannotated=true -XepOpt:NullAway:CustomContractAnnotations=org.springframework.lang.Contract + + + + + java-test-compile + test-compile + + testCompile + + + + + + + + + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java index 1f6875c08..3ae41aad3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BindableMongoExpression.java @@ -20,9 +20,10 @@ import java.util.Arrays; import org.bson.Document; import org.bson.codecs.DocumentCodec; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -31,8 +32,7 @@ import org.springframework.util.StringUtils; * A {@link MongoExpression} using the {@link ParameterBindingDocumentCodec} for parsing a raw ({@literal json}) * expression. The expression will be wrapped within { ... } if necessary. The actual parsing and parameter * binding of placeholders like {@code ?0} is delayed upon first call on the target {@link Document} via - * {@link #toDocument()}. - *
+ * {@link #toDocument()}.
* *
  * $toUpper : $name                -> { '$toUpper' : '$name' }
@@ -55,7 +55,7 @@ public class BindableMongoExpression implements MongoExpression {
 
 	private final @Nullable CodecRegistryProvider codecRegistryProvider;
 
-	private final @Nullable Object[] args;
+	private final Object @Nullable [] args;
 
 	private final Lazy target;
 
@@ -63,9 +63,9 @@ public class BindableMongoExpression implements MongoExpression {
 	 * Create a new instance of {@link BindableMongoExpression}.
 	 *
 	 * @param expression must not be {@literal null}.
-	 * @param args can be {@literal null}.
+	 * @param args must not be {@literal null} but may contain {@literal null} elements.
 	 */
-	public BindableMongoExpression(String expression, @Nullable Object[] args) {
+	public BindableMongoExpression(String expression, Object @Nullable [] args) {
 		this(expression, null, args);
 	}
 
@@ -74,10 +74,10 @@ public class BindableMongoExpression implements MongoExpression {
 	 *
 	 * @param expression must not be {@literal null}.
 	 * @param codecRegistryProvider can be {@literal null}.
-	 * @param args can be {@literal null}.
+	 * @param args must not be {@literal null} but may contain {@literal null} elements.
 	 */
 	public BindableMongoExpression(String expression, @Nullable CodecRegistryProvider codecRegistryProvider,
-			@Nullable Object[] args) {
+			Object @Nullable [] args) {
 
 		Assert.notNull(expression, "Expression must not be null");
 
@@ -93,6 +93,7 @@ public class BindableMongoExpression implements MongoExpression {
 	 * @param codecRegistry must not be {@literal null}.
 	 * @return new instance of {@link BindableMongoExpression}.
 	 */
+	@Contract("_ -> new")
 	public BindableMongoExpression withCodecRegistry(CodecRegistry codecRegistry) {
 		return new BindableMongoExpression(expressionString, () -> codecRegistry, args);
 	}
@@ -103,6 +104,7 @@ public class BindableMongoExpression implements MongoExpression {
 	 * @param args must not be {@literal null}.
 	 * @return new instance of {@link BindableMongoExpression}.
 	 */
+	@Contract("_ -> new")
 	public BindableMongoExpression bind(Object... args) {
 		return new BindableMongoExpression(expressionString, codecRegistryProvider, args);
 	}
@@ -139,7 +141,7 @@ public class BindableMongoExpression implements MongoExpression {
 
 	private static String wrapJsonIfNecessary(String json) {
 
-		if(!StringUtils.hasText(json)) {
+		if (!StringUtils.hasText(json)) {
 			return json;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
index b36382a58..12d8c966a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/BulkOperationException.java
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb;
 
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.DataAccessException;
 
 import com.mongodb.MongoBulkWriteException;
@@ -40,10 +41,10 @@ public class BulkOperationException extends DataAccessException {
 	/**
 	 * Creates a new {@link BulkOperationException} with the given message and source {@link MongoBulkWriteException}.
 	 *
-	 * @param message must not be {@literal null}.
+	 * @param message can be {@literal null}.
 	 * @param source must not be {@literal null}.
 	 */
-	public BulkOperationException(String message, MongoBulkWriteException source) {
+	public BulkOperationException(@Nullable String message, MongoBulkWriteException source) {
 
 		super(message, source);
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
index 53acf6547..c59eecb43 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ClientSessionException.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.mongodb;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.dao.NonTransientDataAccessException;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link NonTransientDataAccessException} specific to MongoDB {@link com.mongodb.session.ClientSession} related data
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
index c07e2dbe4..87201ef9e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/DefaultMongoTransactionOptionsResolver.java
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb;
 import java.util.Map;
 import java.util.Set;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Default implementation of {@link MongoTransactionOptions} using {@literal mongo:} as {@link #getLabelPrefix() label
@@ -42,9 +42,8 @@ enum DefaultMongoTransactionOptionsResolver implements MongoTransactionOptionsRe
 		return SimpleMongoTransactionOptions.of(options);
 	}
 
-	@Nullable
 	@Override
-	public String getLabelPrefix() {
+	public @Nullable String getLabelPrefix() {
 		return PREFIX;
 	}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
index f73f9fb7e..042a5ba1d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.mongodb;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.support.ResourceHolderSynchronization;
 import org.springframework.transaction.support.TransactionSynchronization;
 import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -29,8 +29,7 @@ import com.mongodb.client.MongoDatabase;
 /**
  * Helper class for managing a {@link MongoDatabase} instances via {@link MongoDatabaseFactory}. Used for obtaining
  * {@link ClientSession session bound} resources, such as {@link MongoDatabase} and
- * {@link com.mongodb.client.MongoCollection} suitable for transactional usage.
- * 
+ * {@link com.mongodb.client.MongoCollection} suitable for transactional usage.
* Note: Intended for internal usage only. * * @author Christoph Strobl @@ -42,8 +41,7 @@ public class MongoDatabaseUtils { /** * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory} using - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - *
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * @@ -55,8 +53,7 @@ public class MongoDatabaseUtils { } /** - * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}. - *
+ * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * @@ -70,8 +67,7 @@ public class MongoDatabaseUtils { /** * Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory} using - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - *
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the current * {@link Thread} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * @@ -139,8 +135,7 @@ public class MongoDatabaseUtils { return resourceHolder != null && resourceHolder.hasActiveTransaction(); } - @Nullable - private static ClientSession doGetSession(MongoDatabaseFactory dbFactory, + private static @Nullable ClientSession doGetSession(MongoDatabaseFactory dbFactory, SessionSynchronization sessionSynchronization) { MongoResourceHolder resourceHolder = (MongoResourceHolder) TransactionSynchronizationManager.getResource(dbFactory); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java index a1e8344a9..81c25d099 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoResourceHolder.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.support.ResourceHolderSupport; @@ -23,8 +23,7 @@ import com.mongodb.client.ClientSession; /** * MongoDB specific {@link ResourceHolderSupport resource holder}, wrapping a {@link ClientSession}. - * {@link MongoTransactionManager} binds instances of this class to the thread. - *
+ * {@link MongoTransactionManager} binds instances of this class to the thread.
* Note: Intended for internal usage only. * * @author Christoph Strobl diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java index 4215479f6..3d7bec678 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionException.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * A specific {@link ClientSessionException} related to issues with a transaction such as aborted or non existing diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java index eda657f5f..1f97bb69e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionManager.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.InitializingBean; -import org.springframework.lang.Nullable; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionSystemException; @@ -36,19 +36,15 @@ import com.mongodb.client.ClientSession; /** * A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages - * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. - *
- * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. - *
+ * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}.
+ * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread.
* {@link TransactionDefinition#isReadOnly() Readonly} transactions operate on a {@link ClientSession} and enable causal * consistency, and also {@link ClientSession#startTransaction() start}, {@link ClientSession#commitTransaction() - * commit} or {@link ClientSession#abortTransaction() abort} a transaction. - *
+ * commit} or {@link ClientSession#abortTransaction() abort} a transaction.
* Application code is required to retrieve the {@link com.mongodb.client.MongoDatabase} via * {@link MongoDatabaseUtils#getDatabase(MongoDatabaseFactory)} instead of a standard * {@link MongoDatabaseFactory#getMongoDatabase()} call. Spring classes such as - * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. - *
+ * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly.
* By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override * {@link #doCommit(MongoTransactionObject)} to implement the * Retry Commit Operation @@ -80,7 +76,9 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * @see #setTransactionSynchronization(int) */ public MongoTransactionManager() { + this.transactionOptionsResolver = MongoTransactionOptionsResolver.defaultResolver(); + this.options = MongoTransactionOptions.NONE; } /** @@ -151,7 +149,8 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager } try { - MongoTransactionOptions mongoTransactionOptions = transactionOptionsResolver.resolve(definition).mergeWith(options); + MongoTransactionOptions mongoTransactionOptions = transactionOptionsResolver.resolve(definition) + .mergeWith(options); mongoTransactionObject.startTransaction(mongoTransactionOptions.toDriverOptions()); } catch (MongoException ex) { throw new TransactionSystemException(String.format("Could not start Mongo transaction for session %s.", @@ -206,6 +205,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager * By default those labels are ignored, nevertheless one might check for * {@link MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL transient commit errors labels} and retry the the * commit.
+ * *
 	 * 
 	 * int retries = 3;
@@ -302,8 +302,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
 	 *
 	 * @return can be {@literal null}.
 	 */
-	@Nullable
-	public MongoDatabaseFactory getDatabaseFactory() {
+	public @Nullable MongoDatabaseFactory getDatabaseFactory() {
 		return databaseFactory;
 	}
 
@@ -461,8 +460,7 @@ public class MongoTransactionManager extends AbstractPlatformTransactionManager
 			}
 		}
 
-		@Nullable
-		public ClientSession getSession() {
+		public @Nullable ClientSession getSession() {
 			return resourceHolder != null ? resourceHolder.getSession() : null;
 		}
 
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
index e411bd5d2..04bcd36e3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptions.java
@@ -19,15 +19,16 @@ import java.time.Duration;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mongodb.core.ReadConcernAware;
 import org.springframework.data.mongodb.core.ReadPreferenceAware;
 import org.springframework.data.mongodb.core.WriteConcernAware;
-import org.springframework.lang.Nullable;
 
 import com.mongodb.ReadConcern;
 import com.mongodb.ReadPreference;
 import com.mongodb.TransactionOptions;
 import com.mongodb.WriteConcern;
+import org.springframework.lang.Contract;
 
 /**
  * Options to be applied within a specific transaction scope.
@@ -43,27 +44,23 @@ public interface MongoTransactionOptions
 	 */
 	MongoTransactionOptions NONE = new MongoTransactionOptions() {
 
-		@Nullable
 		@Override
-		public Duration getMaxCommitTime() {
+		public @Nullable Duration getMaxCommitTime() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public ReadConcern getReadConcern() {
+		public @Nullable ReadConcern getReadConcern() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public ReadPreference getReadPreference() {
+		public @Nullable ReadPreference getReadPreference() {
 			return null;
 		}
 
-		@Nullable
 		@Override
-		public WriteConcern getWriteConcern() {
+		public @Nullable WriteConcern getWriteConcern() {
 			return null;
 		}
 	};
@@ -76,6 +73,7 @@ public interface MongoTransactionOptions
 	 * @return new instance of {@link MongoTransactionOptions} or this if {@literal fallbackOptions} is {@literal null} or
 	 *         {@link #NONE}.
 	 */
+	@Contract("null -> this")
 	default MongoTransactionOptions mergeWith(@Nullable MongoTransactionOptions fallbackOptions) {
 
 		if (fallbackOptions == null || MongoTransactionOptions.NONE.equals(fallbackOptions)) {
@@ -84,30 +82,26 @@ public interface MongoTransactionOptions
 
 		return new MongoTransactionOptions() {
 
-			@Nullable
 			@Override
-			public Duration getMaxCommitTime() {
+			public @Nullable Duration getMaxCommitTime() {
 				return MongoTransactionOptions.this.hasMaxCommitTime() ? MongoTransactionOptions.this.getMaxCommitTime()
 						: fallbackOptions.getMaxCommitTime();
 			}
 
-			@Nullable
 			@Override
-			public ReadConcern getReadConcern() {
+			public @Nullable ReadConcern getReadConcern() {
 				return MongoTransactionOptions.this.hasReadConcern() ? MongoTransactionOptions.this.getReadConcern()
 						: fallbackOptions.getReadConcern();
 			}
 
-			@Nullable
 			@Override
-			public ReadPreference getReadPreference() {
+			public @Nullable ReadPreference getReadPreference() {
 				return MongoTransactionOptions.this.hasReadPreference() ? MongoTransactionOptions.this.getReadPreference()
 						: fallbackOptions.getReadPreference();
 			}
 
-			@Nullable
 			@Override
-			public WriteConcern getWriteConcern() {
+			public @Nullable WriteConcern getWriteConcern() {
 				return MongoTransactionOptions.this.hasWriteConcern() ? MongoTransactionOptions.this.getWriteConcern()
 						: fallbackOptions.getWriteConcern();
 			}
@@ -128,8 +122,8 @@ public interface MongoTransactionOptions
 	 * @return MongoDB driver native {@link TransactionOptions}.
 	 * @see MongoTransactionOptions#map(Function)
 	 */
-	@Nullable
-	default TransactionOptions toDriverOptions() {
+	@SuppressWarnings("NullAway")
+	default @Nullable TransactionOptions toDriverOptions() {
 
 		return map(it -> {
 
@@ -157,7 +151,7 @@ public interface MongoTransactionOptions
 	/**
 	 * Factory method to wrap given MongoDB driver native {@link TransactionOptions} into {@link MongoTransactionOptions}.
 	 *
-	 * @param options
+	 * @param options can be {@literal null}.
 	 * @return {@link MongoTransactionOptions#NONE} if given object is {@literal null}.
 	 */
 	static MongoTransactionOptions of(@Nullable TransactionOptions options) {
@@ -168,35 +162,30 @@ public interface MongoTransactionOptions
 
 		return new MongoTransactionOptions() {
 
-			@Nullable
 			@Override
-			public Duration getMaxCommitTime() {
+			public @Nullable Duration getMaxCommitTime() {
 
 				Long millis = options.getMaxCommitTime(TimeUnit.MILLISECONDS);
 				return millis != null ? Duration.ofMillis(millis) : null;
 			}
 
-			@Nullable
 			@Override
-			public ReadConcern getReadConcern() {
+			public @Nullable ReadConcern getReadConcern() {
 				return options.getReadConcern();
 			}
 
-			@Nullable
 			@Override
-			public ReadPreference getReadPreference() {
+			public @Nullable ReadPreference getReadPreference() {
 				return options.getReadPreference();
 			}
 
-			@Nullable
 			@Override
-			public WriteConcern getWriteConcern() {
+			public @Nullable WriteConcern getWriteConcern() {
 				return options.getWriteConcern();
 			}
 
-			@Nullable
 			@Override
-			public TransactionOptions toDriverOptions() {
+			public @Nullable TransactionOptions toDriverOptions() {
 				return options;
 			}
 		};
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
index b73b079a9..c4bdbcca5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoTransactionOptionsResolver.java
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.interceptor.TransactionAttribute;
 import org.springframework.util.Assert;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
index f397818a4..3d1c2ee89 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
@@ -18,7 +18,7 @@ package org.springframework.data.mongodb;
 import reactor.core.publisher.Mono;
 import reactor.util.context.Context;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.transaction.NoTransactionException;
 import org.springframework.transaction.reactive.ReactiveResourceSynchronization;
 import org.springframework.transaction.reactive.TransactionSynchronization;
@@ -35,8 +35,7 @@ import com.mongodb.reactivestreams.client.MongoDatabase;
 /**
  * Helper class for managing reactive {@link MongoDatabase} instances via {@link ReactiveMongoDatabaseFactory}. Used for
  * obtaining {@link ClientSession session bound} resources, such as {@link MongoDatabase} and {@link MongoCollection}
- * suitable for transactional usage.
- * 
+ * suitable for transactional usage.
* Note: Intended for internal usage only. * * @author Mark Paluch @@ -74,8 +73,7 @@ public class ReactiveMongoDatabaseUtils { /** * Obtain the default {@link MongoDatabase database} form the given {@link ReactiveMongoDatabaseFactory factory} using - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - *
+ * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * @@ -103,32 +101,32 @@ public class ReactiveMongoDatabaseUtils { /** * Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory - * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - *
+ * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * - * @param dbName the name of the {@link MongoDatabase} to get. + * @param dbName the name of the {@link MongoDatabase} to get. If {@literal null} the default database of the + * {@link ReactiveMongoDatabaseFactory}. * @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from. * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ - public static Mono getDatabase(String dbName, ReactiveMongoDatabaseFactory factory) { + public static Mono getDatabase(@Nullable String dbName, ReactiveMongoDatabaseFactory factory) { return doGetMongoDatabase(dbName, factory, SessionSynchronization.ON_ACTUAL_TRANSACTION); } /** * Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory - * factory}. - *
+ * factory}.
* Registers a {@link MongoSessionSynchronization MongoDB specific transaction synchronization} within the subscriber * {@link Context} if {@link TransactionSynchronizationManager#isSynchronizationActive() synchronization is active}. * - * @param dbName the name of the {@link MongoDatabase} to get. + * @param dbName the name of the {@link MongoDatabase} to get. If {@literal null} the default database of the * + * {@link ReactiveMongoDatabaseFactory}. * @param factory the {@link ReactiveMongoDatabaseFactory} to get the {@link MongoDatabase} from. * @param sessionSynchronization the synchronization to use. Must not be {@literal null}. * @return the {@link MongoDatabase} that is potentially associated with a transactional {@link ClientSession}. */ - public static Mono getDatabase(String dbName, ReactiveMongoDatabaseFactory factory, + public static Mono getDatabase(@Nullable String dbName, ReactiveMongoDatabaseFactory factory, SessionSynchronization sessionSynchronization) { return doGetMongoDatabase(dbName, factory, sessionSynchronization); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java index 33caa5e7f..d01364b20 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoResourceHolder.java @@ -15,16 +15,15 @@ */ package org.springframework.data.mongodb; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; -import org.springframework.lang.Nullable; import org.springframework.transaction.support.ResourceHolderSupport; import com.mongodb.reactivestreams.client.ClientSession; /** * MongoDB specific resource holder, wrapping a {@link ClientSession}. {@link ReactiveMongoTransactionManager} binds - * instances of this class to the subscriber context. - *
+ * instances of this class to the subscriber context.
* Note: Intended for internal usage only. * * @author Mark Paluch @@ -103,8 +102,7 @@ class ReactiveMongoResourceHolder extends ResourceHolderSupport { * @param session * @return */ - @Nullable - public ClientSession setSessionIfAbsent(@Nullable ClientSession session) { + public @Nullable ClientSession setSessionIfAbsent(@Nullable ClientSession session) { if (!hasSession()) { setSession(session); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java index 2c65c26b7..4f293c8ed 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoTransactionManager.java @@ -17,8 +17,8 @@ package org.springframework.data.mongodb; import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.InitializingBean; -import org.springframework.lang.Nullable; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionSystemException; @@ -64,7 +64,7 @@ import com.mongodb.reactivestreams.client.ClientSession; public class ReactiveMongoTransactionManager extends AbstractReactiveTransactionManager implements InitializingBean { private @Nullable ReactiveMongoDatabaseFactory databaseFactory; - private @Nullable MongoTransactionOptions options; + private MongoTransactionOptions options; private final MongoTransactionOptionsResolver transactionOptionsResolver; /** @@ -79,7 +79,9 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction * @see #setDatabaseFactory(ReactiveMongoDatabaseFactory) */ public ReactiveMongoTransactionManager() { + this.transactionOptionsResolver = MongoTransactionOptionsResolver.defaultResolver(); + this.options = MongoTransactionOptions.NONE; } /** @@ -98,7 +100,7 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction * starting a new transaction. * * @param databaseFactory must not be {@literal null}. - * @param options can be {@literal null}. + * @param options can be {@literal null}. Will default {@link MongoTransactionOptions#NONE} if {@literal null}. */ public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory, @Nullable TransactionOptions options) { @@ -112,7 +114,8 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction * * @param databaseFactory must not be {@literal null}. * @param transactionOptionsResolver must not be {@literal null}. - * @param defaultTransactionOptions can be {@literal null}. + * @param defaultTransactionOptions can be {@literal null}. Will default {@link MongoTransactionOptions#NONE} if + * {@literal null}. * @since 4.3 */ public ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFactory, @@ -124,7 +127,7 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction this.databaseFactory = databaseFactory; this.transactionOptionsResolver = transactionOptionsResolver; - this.options = defaultTransactionOptions; + this.options = defaultTransactionOptions != null ? defaultTransactionOptions : MongoTransactionOptions.NONE; } @Override @@ -318,8 +321,7 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction * * @return can be {@literal null}. */ - @Nullable - public ReactiveMongoDatabaseFactory getDatabaseFactory() { + public @Nullable ReactiveMongoDatabaseFactory getDatabaseFactory() { return databaseFactory; } @@ -470,8 +472,7 @@ public class ReactiveMongoTransactionManager extends AbstractReactiveTransaction } } - @Nullable - public ClientSession getSession() { + public @Nullable ClientSession getSession() { return resourceHolder != null ? resourceHolder.getSession() : null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java index 93dbf5db6..ec30478a5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionAwareMethodInterceptor.java @@ -22,8 +22,8 @@ import java.util.function.BiFunction; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodClassKey; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; @@ -34,8 +34,7 @@ import com.mongodb.session.ClientSession; /** * {@link MethodInterceptor} implementation looking up and invoking an alternative target method having - * {@link ClientSession} as its first argument. This allows seamless integration with the existing code base. - *
+ * {@link ClientSession} as its first argument. This allows seamless integration with the existing code base.
* The {@link MethodInterceptor} is aware of methods on {@code MongoCollection} that my return new instances of itself * like (eg. {@link com.mongodb.reactivestreams.client.MongoCollection#withWriteConcern(WriteConcern)} and decorate them * if not already proxied. @@ -95,13 +94,13 @@ public class SessionAwareMethodInterceptor implements MethodInterceptor { this.sessionType = sessionType; } - @Nullable @Override - public Object invoke(MethodInvocation methodInvocation) throws Throwable { + public @Nullable Object invoke(MethodInvocation methodInvocation) throws Throwable { if (requiresDecoration(methodInvocation.getMethod())) { Object target = methodInvocation.proceed(); + Assert.notNull(target, "invocation target was null"); if (target instanceof Proxy) { return target; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java index b52fc0bd7..5c50ba686 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SimpleMongoTransactionOptions.java @@ -21,7 +21,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import com.mongodb.Function; @@ -41,10 +41,10 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions { static final Set KNOWN_KEYS = Arrays.stream(OptionKey.values()).map(OptionKey::getKey) .collect(Collectors.toSet()); - private final Duration maxCommitTime; - private final ReadConcern readConcern; - private final ReadPreference readPreference; - private final WriteConcern writeConcern; + private final @Nullable Duration maxCommitTime; + private final @Nullable ReadConcern readConcern; + private final @Nullable ReadPreference readPreference; + private final @Nullable WriteConcern writeConcern; static SimpleMongoTransactionOptions of(Map options) { return new SimpleMongoTransactionOptions(options); @@ -58,27 +58,23 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions { this.writeConcern = doGetWriteConcern(options); } - @Nullable @Override - public Duration getMaxCommitTime() { + public @Nullable Duration getMaxCommitTime() { return maxCommitTime; } - @Nullable @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return readConcern; } - @Nullable @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return readPreference; } - @Nullable @Override - public WriteConcern getWriteConcern() { + public @Nullable WriteConcern getWriteConcern() { return writeConcern; } @@ -89,8 +85,7 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions { + ", readPreference=" + readPreference + ", writeConcern=" + writeConcern + '}'; } - @Nullable - private static Duration doGetMaxCommitTime(Map options) { + private static @Nullable Duration doGetMaxCommitTime(Map options) { return getValue(options, OptionKey.MAX_COMMIT_TIME, value -> { @@ -100,18 +95,15 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions { }); } - @Nullable - private static ReadConcern doGetReadConcern(Map options) { + private static @Nullable ReadConcern doGetReadConcern(Map options) { return getValue(options, OptionKey.READ_CONCERN, value -> new ReadConcern(ReadConcernLevel.fromString(value))); } - @Nullable - private static ReadPreference doGetReadPreference(Map options) { + private static @Nullable ReadPreference doGetReadPreference(Map options) { return getValue(options, OptionKey.READ_PREFERENCE, ReadPreference::valueOf); } - @Nullable - private static WriteConcern doGetWriteConcern(Map options) { + private static @Nullable WriteConcern doGetWriteConcern(Map options) { return getValue(options, OptionKey.WRITE_CONCERN, value -> { @@ -123,8 +115,8 @@ class SimpleMongoTransactionOptions implements MongoTransactionOptions { }); } - @Nullable - private static T getValue(Map options, OptionKey key, Function convertFunction) { + private static @Nullable T getValue(Map options, OptionKey key, + Function convertFunction) { String value = options.get(key.getKey()); return value != null ? convertFunction.apply(value) : null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java index cd5f58d5b..57ecec034 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionMetadata.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb; import java.time.Duration; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * MongoDB-specific transaction metadata. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java index 37c7e3686..e42c26d95 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/TransactionOptionResolver.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.transaction.TransactionDefinition; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java index bec05d0d6..69ec086e5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/UncategorizedMongoDbException.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb; +import org.jspecify.annotations.Nullable; import org.springframework.dao.UncategorizedDataAccessException; -import org.springframework.lang.Nullable; public class UncategorizedMongoDbException extends UncategorizedDataAccessException { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java index 2fe27a2c9..86a70600a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoAotPredicates.java @@ -17,11 +17,11 @@ package org.springframework.data.mongodb.aot; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.util.ReactiveWrappers; import org.springframework.data.util.ReactiveWrappers.ReactiveLibrary; import org.springframework.data.util.TypeUtils; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java index a33f20ffb..4b7aa10c3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoManagedTypesBeanRegistrationAotProcessor.java @@ -15,11 +15,11 @@ */ package org.springframework.data.mongodb.aot; +import org.jspecify.annotations.Nullable; import org.springframework.aot.generate.GenerationContext; import org.springframework.core.ResolvableType; import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor; import org.springframework.data.mongodb.MongoManagedTypes; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java index 538fe4e81..f2442960e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/MongoRuntimeHints.java @@ -15,10 +15,11 @@ */ package org.springframework.data.mongodb.aot; -import static org.springframework.data.mongodb.aot.MongoAotPredicates.*; +import static org.springframework.data.mongodb.aot.MongoAotPredicates.isReactorPresent; import java.util.Arrays; +import org.jspecify.annotations.Nullable; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -31,7 +32,6 @@ import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertC import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterSaveCallback; import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeConvertCallback; import org.springframework.data.mongodb.core.mapping.event.ReactiveBeforeSaveCallback; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.MongoClientSettings; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java index b070a0190..0f6ba0170 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ConnectionStringPropertyEditor.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; import com.mongodb.ConnectionString; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java index 164b4defb..f3a7dc043 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MappingMongoConverterParser.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.util.List; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; @@ -56,7 +58,6 @@ import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCre import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -76,6 +77,7 @@ import org.w3c.dom.Element; * @author Zied Yaich * @author Tomasz Forys */ +@NullUnmarked public class MappingMongoConverterParser implements BeanDefinitionParser { private static final String BASE_PACKAGE = "base-package"; @@ -157,8 +159,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { return null; } - @Nullable - private BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) { + private @Nullable BeanDefinition potentiallyCreateValidatingMongoEventListener(Element element, ParserContext parserContext) { String disableValidation = element.getAttribute("disable-validation"); boolean validationDisabled = StringUtils.hasText(disableValidation) && Boolean.parseBoolean(disableValidation); @@ -291,8 +292,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { } } - @Nullable - private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) { + private @Nullable BeanDefinition getCustomConversions(Element element, ParserContext parserContext) { List customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters"); @@ -354,8 +354,7 @@ public class MappingMongoConverterParser implements BeanDefinitionParser { return classes; } - @Nullable - public BeanMetadataElement parseConverter(Element element, ParserContext parserContext) { + public @Nullable BeanMetadataElement parseConverter(Element element, ParserContext parserContext) { String converterRef = element.getAttribute("ref"); if (StringUtils.hasText(converterRef)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java index 4e05fe6c3..a30419977 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoAuditingBeanDefinitionParser.java @@ -18,6 +18,8 @@ package org.springframework.data.mongodb.config; import static org.springframework.data.config.ParsingUtils.*; import static org.springframework.data.mongodb.config.BeanNames.*; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -29,7 +31,6 @@ import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDef import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback; import org.springframework.data.mongodb.core.mapping.event.ReactiveAuditingEntityCallback; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -42,6 +43,7 @@ import org.w3c.dom.Element; * @author Oliver Gierke * @author Mark Paluch */ +@NullUnmarked public class MongoAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { private static boolean PROJECT_REACTOR_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Mono", diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java index 0594f6176..b01827d8c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java @@ -35,6 +35,7 @@ import org.springframework.data.mongodb.core.convert.MongoCustomConversions; import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -52,7 +53,7 @@ public abstract class MongoConfigurationSupport { /** * Return the name of the database to connect to. * - * @return must not be {@literal null}. + * @return never {@literal null}. */ protected abstract String getDatabaseName(); @@ -76,7 +77,7 @@ public abstract class MongoConfigurationSupport { * Creates a {@link MongoMappingContext} equipped with entity classes scanned from the mapping base package. * * @see #getMappingBasePackages() - * @return + * @return never {@literal null}. */ @Bean public MongoMappingContext mongoMappingContext(MongoCustomConversions customConversions, @@ -172,8 +173,10 @@ public abstract class MongoConfigurationSupport { for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { - initialEntitySet - .add(ClassUtils.forName(candidate.getBeanClassName(), MongoConfigurationSupport.class.getClassLoader())); + String beanClassName = candidate.getBeanClassName(); + Assert.notNull(beanClassName, "BeanClassName cannot be null"); + + initialEntitySet.add(ClassUtils.forName(beanClassName, MongoConfigurationSupport.class.getClassLoader())); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java index b8f23a35a..93d778c86 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java @@ -26,7 +26,7 @@ import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java index 2e733cc79..2d3649c53 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java @@ -20,6 +20,8 @@ import static org.springframework.data.mongodb.config.MongoParsingUtils.*; import java.util.Set; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -31,7 +33,6 @@ import org.springframework.beans.factory.xml.ParserContext; import org.springframework.data.config.BeanComponentDefinitionBuilder; import org.springframework.data.mongodb.core.MongoClientFactoryBean; import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -47,6 +48,7 @@ import com.mongodb.ConnectionString; * @author Viktor Khoroshko * @author Mark Paluch */ +@NullUnmarked public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { private static final Set MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES = Set.of("id", "write-concern"); @@ -125,8 +127,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { * @param parserContext * @return {@literal null} in case no client-/uri defined. */ - @Nullable - private BeanDefinition getConnectionString(Element element, ParserContext parserContext) { + private @Nullable BeanDefinition getConnectionString(Element element, ParserContext parserContext) { String type = null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java index 95b56b58f..00e993fdc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoParsingUtils.java @@ -19,6 +19,7 @@ import static org.springframework.data.config.ParsingUtils.*; import java.util.Map; +import org.jspecify.annotations.NullUnmarked; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.CustomEditorConfigurer; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -40,6 +41,7 @@ import org.w3c.dom.Element; * @author Christoph Strobl * @author Mark Paluch */ +@NullUnmarked abstract class MongoParsingUtils { private MongoParsingUtils() {} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java index 1e1b11356..5053e540f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoTemplateParser.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.config; import static org.springframework.data.config.ParsingUtils.*; import static org.springframework.data.mongodb.config.MongoParsingUtils.*; +import org.jspecify.annotations.NullUnmarked; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; @@ -37,6 +38,7 @@ import org.w3c.dom.Element; * @author Martin Baumgartner * @author Oliver Gierke */ +@NullUnmarked class MongoTemplateParser extends AbstractBeanDefinitionParser { @Override diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java index 60bf126ae..3f5cb0ca6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadConcernPropertyEditor.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; import com.mongodb.ReadConcern; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java index 5ed9b6661..f24c43534 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ReadPreferencePropertyEditor.java @@ -17,10 +17,10 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; -import org.springframework.lang.Nullable; - import com.mongodb.ReadPreference; +import org.jspecify.annotations.Nullable; + /** * Parse a {@link String} to a {@link ReadPreference}. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java index 9c5190090..9ff59e5b2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ServerAddressPropertyEditor.java @@ -23,7 +23,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -80,11 +80,10 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport { * @param source * @return the */ - @Nullable - private ServerAddress parseServerAddress(String source) { + private @Nullable ServerAddress parseServerAddress(String source) { if (!StringUtils.hasText(source)) { - if(LOG.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source)); } return null; @@ -93,7 +92,7 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport { String[] hostAndPort = extractHostAddressAndPort(source.trim()); if (hostAndPort.length > 2) { - if(LOG.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "source", source)); } return null; @@ -105,11 +104,11 @@ public class ServerAddressPropertyEditor extends PropertyEditorSupport { return port == null ? new ServerAddress(hostAddress) : new ServerAddress(hostAddress, port); } catch (UnknownHostException e) { - if(LOG.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "host", hostAndPort[0])); } } catch (NumberFormatException e) { - if(LOG.isWarnEnabled()) { + if (LOG.isWarnEnabled()) { LOG.warn(String.format(COULD_NOT_PARSE_ADDRESS_MESSAGE, "port", hostAndPort[1])); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java index b77796996..23c15102a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/UUidRepresentationPropertyEditor.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; import org.bson.UuidRepresentation; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java index ee0d09e55..32c19e24c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/WriteConcernPropertyEditor.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; import com.mongodb.WriteConcern; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java index 5a1e5b725..555cc9f66 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/package-info.java @@ -1,6 +1,6 @@ /** * Spring XML namespace configuration for MongoDB specific repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.config; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java index a00d95a9a..ec7c368ea 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/AggregationUtil.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.List; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; @@ -30,7 +30,6 @@ import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; /** * Utility methods to map {@link org.springframework.data.mongodb.core.aggregation.Aggregation} pipeline definitions and diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java index 17b8835b7..8a74ace28 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java @@ -21,9 +21,9 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.bson.BsonTimestamp; import org.bson.BsonValue; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.messaging.Message; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -78,8 +78,7 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public ChangeStreamDocument getRaw() { + public @Nullable ChangeStreamDocument getRaw() { return raw; } @@ -88,10 +87,10 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public Instant getTimestamp() { + public @Nullable Instant getTimestamp() { - return getBsonTimestamp() != null ? converter.getConversionService().convert(raw.getClusterTime(), Instant.class) + return getBsonTimestamp() != null && raw != null + ? converter.getConversionService().convert(raw.getClusterTime(), Instant.class) : null; } @@ -111,8 +110,7 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public BsonValue getResumeToken() { + public @Nullable BsonValue getResumeToken() { return raw != null ? raw.getResumeToken() : null; } @@ -121,8 +119,7 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public OperationType getOperationType() { + public @Nullable OperationType getOperationType() { return raw != null ? raw.getOperationType() : null; } @@ -131,8 +128,7 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public String getDatabaseName() { + public @Nullable String getDatabaseName() { return raw != null ? raw.getNamespace().getDatabaseName() : null; } @@ -141,8 +137,7 @@ public class ChangeStreamEvent { * * @return can be {@literal null}. */ - @Nullable - public String getCollectionName() { + public @Nullable String getCollectionName() { return raw != null ? raw.getNamespace().getCollectionName() : null; } @@ -152,8 +147,7 @@ public class ChangeStreamEvent { * @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocument()} is * {@literal null}. */ - @Nullable - public T getBody() { + public @Nullable T getBody() { if (raw == null || raw.getFullDocument() == null) { return null; @@ -163,14 +157,14 @@ public class ChangeStreamEvent { } /** - * Get the potentially converted {@link ChangeStreamDocument#getFullDocumentBeforeChange() document} before being changed. + * Get the potentially converted {@link ChangeStreamDocument#getFullDocumentBeforeChange() document} before being + * changed. * * @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocumentBeforeChange()} is * {@literal null}. * @since 4.0 */ - @Nullable - public T getBodyBeforeChange() { + public @Nullable T getBodyBeforeChange() { if (raw == null || raw.getFullDocumentBeforeChange() == null) { return null; @@ -189,6 +183,7 @@ public class ChangeStreamEvent { return (T) doGetConverted(fullDocument, CONVERTED_FULL_DOCUMENT_UPDATER); } + @SuppressWarnings("NullAway") private Object doGetConverted(Document fullDocument, AtomicReferenceFieldUpdater updater) { Object result = updater.get(this); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java index aaee3b76a..9c99b0e01 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamOptions.java @@ -23,9 +23,10 @@ import org.bson.BsonDocument; import org.bson.BsonTimestamp; import org.bson.BsonValue; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -248,6 +249,7 @@ public class ChangeStreamOptions { * @param collation must not be {@literal null} nor {@literal empty}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder collation(Collation collation) { Assert.notNull(collation, "Collation must not be null nor empty"); @@ -257,14 +259,12 @@ public class ChangeStreamOptions { } /** - * Set the filter to apply. - *
+ * Set the filter to apply.
* Fields on aggregation expression root level are prefixed to map to fields contained in * {@link ChangeStreamDocument#getFullDocument() fullDocument}. However {@literal operationType}, {@literal ns}, * {@literal documentKey} and {@literal fullDocument} are reserved words that will be omitted, and therefore taken * as given, during the mapping procedure. You may want to have a look at the - * structure of Change Events. - *
+ * structure of Change Events.
* Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to ensure filter expressions are * mapped to domain type fields. * @@ -272,6 +272,7 @@ public class ChangeStreamOptions { * {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder filter(Aggregation filter) { Assert.notNull(filter, "Filter must not be null"); @@ -286,6 +287,7 @@ public class ChangeStreamOptions { * @param filter must not be {@literal null} nor contain {@literal null} values. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder filter(Document... filter) { Assert.noNullElements(filter, "Filter must not contain null values"); @@ -301,6 +303,7 @@ public class ChangeStreamOptions { * @param resumeToken must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder resumeToken(BsonValue resumeToken) { Assert.notNull(resumeToken, "ResumeToken must not be null"); @@ -330,6 +333,7 @@ public class ChangeStreamOptions { * @param lookup must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder fullDocumentLookup(FullDocument lookup) { Assert.notNull(lookup, "Lookup must not be null"); @@ -345,6 +349,7 @@ public class ChangeStreamOptions { * @return this. * @since 4.0 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) { Assert.notNull(lookup, "Lookup must not be null"); @@ -358,7 +363,7 @@ public class ChangeStreamOptions { * * @return this. * @since 4.0 - * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) + * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) */ public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() { return fullDocumentBeforeChangeLookup(FullDocumentBeforeChange.WHEN_AVAILABLE); @@ -370,6 +375,7 @@ public class ChangeStreamOptions { * @param resumeTimestamp must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder resumeAt(Instant resumeTimestamp) { Assert.notNull(resumeTimestamp, "ResumeTimestamp must not be null"); @@ -385,6 +391,7 @@ public class ChangeStreamOptions { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder resumeAt(BsonTimestamp resumeTimestamp) { Assert.notNull(resumeTimestamp, "ResumeTimestamp must not be null"); @@ -400,6 +407,7 @@ public class ChangeStreamOptions { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) { resumeToken(resumeToken); @@ -415,6 +423,7 @@ public class ChangeStreamOptions { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) { resumeToken(resumeToken); @@ -426,6 +435,7 @@ public class ChangeStreamOptions { /** * @return the built {@link ChangeStreamOptions} */ + @Contract("-> new") public ChangeStreamOptions build() { ChangeStreamOptions options = new ChangeStreamOptions(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java index c142aca17..bf8be5ba6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionCallback.java @@ -16,8 +16,8 @@ package org.springframework.data.mongodb.core; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessException; -import org.springframework.lang.Nullable; import com.mongodb.MongoException; import com.mongodb.client.MongoCollection; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java index 5df30e0b9..da1cdfa33 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java @@ -245,7 +245,7 @@ public class CollectionOptions { * @since 2.1 */ public CollectionOptions schema(MongoJsonSchema schema) { - return validator(Validator.schema(schema)); + return validator(schema != null ? Validator.schema(schema) : null); } /** @@ -574,6 +574,7 @@ public class CollectionOptions { * @param validator can be {@literal null}. * @return new instance of {@link ValidationOptions}. */ + @Contract("_ -> new") public ValidationOptions validator(@Nullable Validator validator) { return new ValidationOptions(validator, validationLevel, validationAction); } @@ -584,6 +585,7 @@ public class CollectionOptions { * @param validationLevel can be {@literal null}. * @return new instance of {@link ValidationOptions}. */ + @Contract("_ -> new") public ValidationOptions validationLevel(ValidationLevel validationLevel) { return new ValidationOptions(validator, validationLevel, validationAction); } @@ -594,6 +596,7 @@ public class CollectionOptions { * @param validationAction can be {@literal null}. * @return new instance of {@link ValidationOptions}. */ + @Contract("_ -> new") public ValidationOptions validationAction(ValidationAction validationAction) { return new ValidationOptions(validator, validationLevel, validationAction); } @@ -950,6 +953,7 @@ public class CollectionOptions { * @param metaField must not be {@literal null}. * @return new instance of {@link TimeSeriesOptions}. */ + @Contract("_ -> new") public TimeSeriesOptions metaField(String metaField) { return new TimeSeriesOptions(timeField, metaField, granularity, expireAfter); } @@ -961,6 +965,7 @@ public class CollectionOptions { * @return new instance of {@link TimeSeriesOptions}. * @see Granularity */ + @Contract("_ -> new") public TimeSeriesOptions granularity(GranularityDefinition granularity) { return new TimeSeriesOptions(timeField, metaField, granularity, expireAfter); } @@ -973,6 +978,7 @@ public class CollectionOptions { * @see com.mongodb.client.model.CreateCollectionOptions#expireAfter(long, java.util.concurrent.TimeUnit) * @since 4.4 */ + @Contract("_ -> new") public TimeSeriesOptions expireAfter(Duration ttl) { return new TimeSeriesOptions(timeField, metaField, granularity, ttl); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java index 644a3a54d..bdf0b90ee 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionPreparerSupport.java @@ -21,6 +21,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import org.bson.Document; +import org.jspecify.annotations.Nullable; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; @@ -84,7 +85,7 @@ class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { for (Object aware : sources) { if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) { @@ -108,7 +109,7 @@ class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { for (Object aware : sources) { if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java index 4fa6b3e97..11d9f09af 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CountQuery.java @@ -23,9 +23,9 @@ import java.util.List; import java.util.Map; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.query.MetricConversion; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -154,7 +154,7 @@ class CountQuery { * @param $and potentially existing {@code $and} condition. * @return the rewritten query {@link Document}. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "NullAway" }) private static Document createGeoWithin(String key, Document source, @Nullable Object $and) { boolean spheric = source.containsKey("$nearSphere"); @@ -233,6 +233,7 @@ class CountQuery { return source.containsKey("$minDistance"); } + @SuppressWarnings("NullAway") private static Object toCenterCoordinates(Object value) { if (ObjectUtils.isArray(value)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java index 9b7408b0c..3b53cef8d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CursorPreparer.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.function.Function; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import com.mongodb.ReadPreference; @@ -76,8 +76,7 @@ public interface CursorPreparer extends ReadPreferenceAware { * @since 2.2 */ @Override - @Nullable - default ReadPreference getReadPreference() { + default @Nullable ReadPreference getReadPreference() { return null; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java index 9d588ad16..f450bddb3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DbCallback.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessException; -import org.springframework.lang.Nullable; import com.mongodb.MongoException; import com.mongodb.client.MongoDatabase; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java index 52343522a..8bc5349e6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultBulkOperations.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.dao.DataIntegrityViolationException; @@ -40,7 +41,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.MongoBulkWriteException; @@ -115,6 +116,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations insert(Object document) { Assert.notNull(document, "Document must not be null"); @@ -127,6 +129,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations insert(List documents) { Assert.notNull(documents, "Documents must not be null"); @@ -137,6 +140,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_, _ -> this") public BulkOperations updateOne(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -146,6 +150,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations updateOne(List> updates) { Assert.notNull(updates, "Updates must not be null"); @@ -158,6 +163,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_, _ -> this") public BulkOperations updateMulti(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -169,6 +175,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations updateMulti(List> updates) { Assert.notNull(updates, "Updates must not be null"); @@ -181,11 +188,13 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_, _ -> this") public BulkOperations upsert(Query query, UpdateDefinition update) { return update(query, update, true, true); } @Override + @Contract("_ -> this") public BulkOperations upsert(List> updates) { for (Pair update : updates) { @@ -196,6 +205,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations remove(Query query) { Assert.notNull(query, "Query must not be null"); @@ -209,6 +219,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_ -> this") public BulkOperations remove(List removes) { Assert.notNull(removes, "Removals must not be null"); @@ -221,6 +232,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati } @Override + @Contract("_, _, _ -> this") public BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) { Assert.notNull(query, "Query must not be null"); @@ -412,7 +424,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati return eventPublisher == null; } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public T callback(Class callbackType, T entity, String collectionName) { if (skipEntityCallbacks()) { @@ -422,7 +434,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati return entityCallbacks.callback(callbackType, entity, collectionName); } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public T callback(Class callbackType, T entity, Document document, String collectionName) { @@ -433,6 +445,7 @@ class DefaultBulkOperations extends BulkOperationsSupport implements BulkOperati return entityCallbacks.callback(callbackType, entity, document, collectionName); } + @SuppressWarnings("NullAway") public void publishEvent(ApplicationEvent event) { if (skipEventPublishing()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java index 2057e2f04..24d22bd80 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperations.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessException; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.UncategorizedMongoDbException; @@ -28,7 +29,6 @@ import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; @@ -115,6 +115,7 @@ public class DefaultIndexOperations implements IndexOperations { } @Override + @SuppressWarnings("NullAway") public String ensureIndex(IndexDefinition indexDefinition) { return execute(collection -> { @@ -131,8 +132,7 @@ public class DefaultIndexOperations implements IndexOperations { }); } - @Nullable - private MongoPersistentEntity lookupPersistentEntity(@Nullable Class entityType, String collection) { + private @Nullable MongoPersistentEntity lookupPersistentEntity(@Nullable Class entityType, String collection) { if (entityType != null) { return mapper.getMappingContext().getRequiredPersistentEntity(entityType); @@ -160,6 +160,7 @@ public class DefaultIndexOperations implements IndexOperations { } @Override + @SuppressWarnings("NullAway") public void alterIndex(String name, org.springframework.data.mongodb.core.index.IndexOptions options) { Document indexOptions = new Document("name", name); @@ -180,6 +181,7 @@ public class DefaultIndexOperations implements IndexOperations { } @Override + @SuppressWarnings("NullAway") public List getIndexInfo() { return execute(new CollectionCallback>() { @@ -208,8 +210,7 @@ public class DefaultIndexOperations implements IndexOperations { }); } - @Nullable - public T execute(CollectionCallback callback) { + public @Nullable T execute(CollectionCallback callback) { Assert.notNull(callback, "CollectionCallback must not be null"); @@ -228,6 +229,7 @@ public class DefaultIndexOperations implements IndexOperations { mapper.getMappedSort((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity)); } + @SuppressWarnings("NullAway") private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops, @Nullable MongoPersistentEntity entity) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java index e2471dbb1..a34c1fb94 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultIndexOperationsProvider.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.IndexOperations; @@ -43,7 +44,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider { } @Override - public IndexOperations indexOps(String collectionName, Class type) { + public IndexOperations indexOps(String collectionName, @Nullable Class type) { return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper, type); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java index 59b7ccd63..92c6a957d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveBulkOperations.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import org.springframework.lang.Contract; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -24,6 +25,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.mapping.callback.EntityCallback; @@ -40,7 +42,6 @@ import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.WriteConcern; @@ -107,6 +108,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_ -> this") public ReactiveBulkOperations insert(Object document) { Assert.notNull(document, "Document must not be null"); @@ -120,6 +122,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_ -> this") public ReactiveBulkOperations insert(List documents) { Assert.notNull(documents, "Documents must not be null"); @@ -130,6 +133,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_, _, _ -> this") public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -140,6 +144,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_, _ -> this") public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -150,11 +155,13 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_, _ -> this") public ReactiveBulkOperations upsert(Query query, UpdateDefinition update) { return update(query, update, true, true); } @Override + @Contract("_ -> this") public ReactiveBulkOperations remove(Query query) { Assert.notNull(query, "Query must not be null"); @@ -169,6 +176,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_ -> this") public ReactiveBulkOperations remove(List removes) { Assert.notNull(removes, "Removals must not be null"); @@ -181,6 +189,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea } @Override + @Contract("_, _, _ -> this") public ReactiveBulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) { Assert.notNull(query, "Query must not be null"); @@ -359,7 +368,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea return eventPublisher == null; } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public Mono callback(Class callbackType, T entity, String collectionName) { if (skipEntityCallbacks()) { @@ -369,7 +378,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea return entityCallbacks.callback(callbackType, entity, collectionName); } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public Mono callback(Class callbackType, T entity, Document document, String collectionName) { @@ -380,6 +389,7 @@ class DefaultReactiveBulkOperations extends BulkOperationsSupport implements Rea return entityCallbacks.callback(callbackType, entity, document, collectionName); } + @SuppressWarnings("NullAway") public void publishEvent(ApplicationEvent event) { if (skipEventPublishing()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java index 8e78f421f..69ade2e16 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultReactiveIndexOperations.java @@ -19,16 +19,15 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Collection; -import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.ReactiveIndexOperations; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; @@ -48,7 +47,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { private final ReactiveMongoOperations mongoOperations; private final String collectionName; private final QueryMapper queryMapper; - private final Optional> type; + private final @Nullable Class type; /** * Creates a new {@link DefaultReactiveIndexOperations}. @@ -59,7 +58,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { */ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, QueryMapper queryMapper) { - this(mongoOperations, collectionName, queryMapper, Optional.empty()); + this(mongoOperations, collectionName, queryMapper, null); } /** @@ -71,12 +70,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { * @param type used for mapping potential partial index filter expression, must not be {@literal null}. */ public DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, - QueryMapper queryMapper, Class type) { - this(mongoOperations, collectionName, queryMapper, Optional.of(type)); - } - - private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, String collectionName, - QueryMapper queryMapper, Optional> type) { + QueryMapper queryMapper, @Nullable Class type) { Assert.notNull(mongoOperations, "ReactiveMongoOperations must not be null"); Assert.notNull(collectionName, "Collection must not be null"); @@ -89,13 +83,12 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { } @Override + @SuppressWarnings("NullAway") public Mono ensureIndex(IndexDefinition indexDefinition) { return mongoOperations.execute(collectionName, collection -> { - MongoPersistentEntity entity = type - .map(val -> (MongoPersistentEntity) queryMapper.getMappingContext().getRequiredPersistentEntity(val)) - .orElseGet(() -> lookupPersistentEntity(collectionName)); + MongoPersistentEntity entity = getConfiguredEntity(); IndexOptions indexOptions = IndexConverters.indexDefinitionToIndexOptionsConverter().convert(indexDefinition); @@ -124,8 +117,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { }).then(); } - @Nullable - private MongoPersistentEntity lookupPersistentEntity(String collection) { + private @Nullable MongoPersistentEntity lookupPersistentEntity(String collection) { Collection> entities = queryMapper.getMappingContext().getPersistentEntities(); @@ -152,6 +144,14 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { .map(IndexConverters.documentToIndexInfoConverter()::convert); } + private @Nullable MongoPersistentEntity getConfiguredEntity() { + + if (type != null) { + return queryMapper.getMappingContext().getRequiredPersistentEntity(type); + } + return lookupPersistentEntity(collectionName); + } + private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document sourceOptions, @Nullable MongoPersistentEntity entity) { @@ -164,6 +164,7 @@ public class DefaultReactiveIndexOperations implements ReactiveIndexOperations { queryMapper.getMappedObject((Document) sourceOptions.get(PARTIAL_FILTER_EXPRESSION_KEY), entity)); } + @SuppressWarnings("NullAway") private static IndexOptions addDefaultCollationIfRequired(IndexOptions ops, @Nullable MongoPersistentEntity entity) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java index b236b4df2..6dde79e0e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultScriptOperations.java @@ -15,9 +15,9 @@ */ package org.springframework.data.mongodb.core; -import static java.util.UUID.*; -import static org.springframework.data.mongodb.core.query.Criteria.*; -import static org.springframework.data.mongodb.core.query.Query.*; +import static java.util.UUID.randomUUID; +import static org.springframework.data.mongodb.core.query.Criteria.where; +import static org.springframework.data.mongodb.core.query.Query.query; import java.util.ArrayList; import java.util.Arrays; @@ -28,7 +28,8 @@ import java.util.Set; import org.bson.Document; import org.bson.types.ObjectId; -import org.springframework.dao.DataAccessException; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.script.ExecutableMongoScript; import org.springframework.data.mongodb.core.script.NamedMongoScript; @@ -38,8 +39,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import com.mongodb.BasicDBList; -import com.mongodb.MongoException; -import com.mongodb.client.MongoDatabase; /** * Default implementation of {@link ScriptOperations} capable of saving and executing {@link ExecutableMongoScript}. @@ -51,6 +50,7 @@ import com.mongodb.client.MongoDatabase; * @deprecated since 2.2. The {@code eval} command has been removed in MongoDB Server 4.2.0. */ @Deprecated +@NullUnmarked class DefaultScriptOperations implements ScriptOperations { private static final String SCRIPT_COLLECTION_NAME = "system.js"; @@ -85,38 +85,28 @@ class DefaultScriptOperations implements ScriptOperations { } @Override - public Object execute(ExecutableMongoScript script, Object... args) { + public @Nullable Object execute(ExecutableMongoScript script, Object... args) { Assert.notNull(script, "Script must not be null"); - return mongoOperations.execute(new DbCallback() { + return mongoOperations.execute(db -> { - @Override - public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException { - - Document command = new Document("$eval", script.getCode()); - BasicDBList commandArgs = new BasicDBList(); - commandArgs.addAll(Arrays.asList(convertScriptArgs(false, args))); - command.append("args", commandArgs); - return db.runCommand(command).get("retval"); - } + Document command = new Document("$eval", script.getCode()); + BasicDBList commandArgs = new BasicDBList(); + commandArgs.addAll(Arrays.asList(convertScriptArgs(false, args))); + command.append("args", commandArgs); + return db.runCommand(command).get("retval"); }); } @Override - public Object call(String scriptName, Object... args) { + public @Nullable Object call(String scriptName, Object... args) { Assert.hasText(scriptName, "ScriptName must not be null or empty"); - return mongoOperations.execute(new DbCallback() { - - @Override - public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException { - - return db.runCommand(new Document("eval", String.format("%s(%s)", scriptName, convertAndJoinScriptArgs(args)))) - .get("retval"); - } - }); + return mongoOperations.execute( + db -> db.runCommand(new Document("eval", String.format("%s(%s)", scriptName, convertAndJoinScriptArgs(args)))) + .get("retval")); } @Override diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java index 8b4de14e0..c445e06f8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/DefaultWriteConcernResolver.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; + import com.mongodb.WriteConcern; /** @@ -26,7 +28,7 @@ enum DefaultWriteConcernResolver implements WriteConcernResolver { INSTANCE; - public WriteConcern resolve(MongoAction action) { + public @Nullable WriteConcern resolve(MongoAction action) { return action.getDefaultWriteConcern(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java index 94352ad65..ad3c2b856 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityLifecycleEventDelegate.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.lang.Nullable; /** * Delegate class to encapsulate lifecycle event configuration and publishing. @@ -47,6 +47,7 @@ class EntityLifecycleEventDelegate { * * @param event the application event. */ + @SuppressWarnings("NullAway") public void publishEvent(Object event) { if (canPublishEvent()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java index 38269787c..132765635 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java @@ -26,9 +26,11 @@ import java.util.function.Predicate; import org.bson.BsonNull; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionService; import org.springframework.core.env.Environment; import org.springframework.core.env.EnvironmentCapable; +import org.springframework.core.env.StandardEnvironment; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.convert.CustomConversions; import org.springframework.data.expression.ValueEvaluationContext; @@ -65,7 +67,6 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.TargetAware; import org.springframework.data.util.Optionals; import org.springframework.expression.spel.support.SimpleEvaluationContext; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.LinkedMultiValueMap; @@ -423,6 +424,7 @@ class EntityOperations { * * @return */ + @Nullable Object getId(); /** @@ -528,10 +530,9 @@ class EntityOperations { * Populates the identifier of the backing entity if it has an identifier property and there's no identifier * currently present. * - * @param id must not be {@literal null}. + * @param id can be {@literal null}. * @return */ - @Nullable T populateIdIfNecessary(@Nullable Object id); /** @@ -574,12 +575,12 @@ class EntityOperations { } @Override - public Object getId() { + public @Nullable Object getId() { return getPropertyValue(ID_FIELD); } @Override - public Object getPropertyValue(String key) { + public @Nullable Object getPropertyValue(String key) { return map.get(key); } @@ -588,7 +589,6 @@ class EntityOperations { return Query.query(Criteria.where(ID_FIELD).is(map.get(ID_FIELD))); } - @Nullable @Override public T populateIdIfNecessary(@Nullable Object id) { @@ -615,8 +615,7 @@ class EntityOperations { } @Override - @Nullable - public Number getVersion() { + public @Nullable Number getVersion() { return null; } @@ -733,7 +732,7 @@ class EntityOperations { } @Override - public Object getPropertyValue(String key) { + public @Nullable Object getPropertyValue(String key) { return propertyAccessor.getProperty(entity.getRequiredPersistentProperty(key)); } @@ -800,8 +799,7 @@ class EntityOperations { } @Override - @Nullable - public Object getVersion() { + public @Nullable Object getVersion() { return propertyAccessor.getProperty(entity.getRequiredVersionProperty()); } @@ -849,7 +847,6 @@ class EntityOperations { return keyset; } - @Nullable private Object getNestedPropertyValue(String key) { String[] segments = key.split("\\."); @@ -862,6 +859,10 @@ class EntityOperations { currentValue = currentEntity.getPropertyValue(segment); if (i < segments.length - 1) { + if (currentValue == null) { + return BsonNull.VALUE; + } + currentEntity = entityOperations.forEntity(currentValue); } } @@ -898,7 +899,6 @@ class EntityOperations { new ConvertingPropertyAccessor<>(propertyAccessor, conversionService), entityOperations); } - @Nullable @Override public T populateIdIfNecessary(@Nullable Object id) { @@ -920,8 +920,7 @@ class EntityOperations { } @Override - @Nullable - public Number getVersion() { + public @Nullable Number getVersion() { MongoPersistentProperty versionProperty = entity.getRequiredVersionProperty(); @@ -1137,7 +1136,7 @@ class EntityOperations { @Override public String getIdKeyName() { - return entity.getIdProperty().getName(); + return entity.getIdProperty() != null ? entity.getIdProperty().getName() : ID_FIELD; } private String mappedNameOrDefault(String name) { @@ -1157,7 +1156,8 @@ class EntityOperations { return mongoEntity.getValueEvaluationContext(null); } - return ValueEvaluationContext.of(this.environment, SimpleEvaluationContext.forReadOnlyDataBinding().build()); + return ValueEvaluationContext.of(this.environment != null ? this.environment : new StandardEnvironment(), + SimpleEvaluationContext.forReadOnlyDataBinding().build()); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java index ca5aa7a51..d28afada0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.aggregation.TypedAggregation; @@ -55,11 +56,11 @@ class ExecutableAggregationOperationSupport implements ExecutableAggregationOper private final MongoTemplate template; private final Class domainType; - private final Aggregation aggregation; - private final String collection; + private final @Nullable Aggregation aggregation; + private final @Nullable String collection; - public ExecutableAggregationSupport(MongoTemplate template, Class domainType, Aggregation aggregation, - String collection) { + public ExecutableAggregationSupport(MongoTemplate template, Class domainType, @Nullable Aggregation aggregation, + @Nullable String collection) { this.template = template; this.domainType = domainType; this.aggregation = aggregation; @@ -84,21 +85,25 @@ class ExecutableAggregationOperationSupport implements ExecutableAggregationOper @Override public AggregationResults all() { + + Assert.notNull(aggregation, "Aggregation must be set first"); return template.aggregate(aggregation, getCollectionName(aggregation), domainType); } @Override public Stream stream() { + + Assert.notNull(aggregation, "Aggregation must be set first"); return template.aggregateStream(aggregation, getCollectionName(aggregation), domainType); } - private String getCollectionName(Aggregation aggregation) { + private String getCollectionName(@Nullable Aggregation aggregation) { if (StringUtils.hasText(collection)) { return collection; } - if (aggregation instanceof TypedAggregation typedAggregation) { + if (aggregation instanceof TypedAggregation typedAggregation) { if (typedAggregation.getInputType() != null) { return template.getCollectionName(typedAggregation.getInputType()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java index 3358ff2b1..21cb37ae8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessException; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.ScrollPosition; @@ -27,7 +28,6 @@ import org.springframework.data.geo.GeoResults; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; import com.mongodb.client.MongoCollection; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java index 4e6c3547c..af8c80903 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java @@ -16,18 +16,17 @@ package org.springframework.data.mongodb.core; import java.util.List; -import java.util.Optional; import java.util.stream.Stream; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Window; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.SerializationUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -53,6 +52,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override + @Contract("_ -> new") public ExecutableFind query(Class domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -84,6 +84,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override + @Contract("_ -> new") public FindWithProjection inCollection(String collection) { Assert.hasText(collection, "Collection name must not be null nor empty"); @@ -92,6 +93,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override + @Contract("_ -> new") public FindWithQuery as(Class returnType) { Assert.notNull(returnType, "ReturnType must not be null"); @@ -100,6 +102,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override + @Contract("_ -> new") public TerminatingFind matching(Query query) { Assert.notNull(query, "Query must not be null"); @@ -108,7 +111,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override - public T oneValue() { + public @Nullable T oneValue() { List result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(2)); @@ -124,7 +127,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override - public T firstValue() { + public @Nullable T firstValue() { List result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(1)); @@ -206,10 +209,10 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { * @author Christoph Strobl * @since 2.0 */ - static class DelegatingQueryCursorPreparer implements SortingQueryCursorPreparer { + static class DelegatingQueryCursorPreparer implements CursorPreparer { private final @Nullable CursorPreparer delegate; - private Optional limit = Optional.empty(); + private int limit = -1; DelegatingQueryCursorPreparer(@Nullable CursorPreparer delegate) { this.delegate = delegate; @@ -219,25 +222,22 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { public FindIterable prepare(FindIterable iterable) { FindIterable target = delegate != null ? delegate.prepare(iterable) : iterable; - return limit.map(target::limit).orElse(target); + if (limit >= 0) { + target.limit(limit); + } + return target; } + @Contract("_ -> this") CursorPreparer limit(int limit) { - this.limit = Optional.of(limit); + this.limit = limit; return this; } @Override - @Nullable - public ReadPreference getReadPreference() { - return delegate.getReadPreference(); - } - - @Override - @Nullable - public Document getSortObject() { - return delegate instanceof SortingQueryCursorPreparer sqcp ? sqcp.getSortObject() : null; + public @Nullable ReadPreference getReadPreference() { + return delegate != null ? delegate.getReadPreference() : null; } } @@ -258,6 +258,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { @Override @SuppressWarnings("unchecked") + @Contract("_ -> new") public TerminatingDistinct as(Class resultType) { Assert.notNull(resultType, "ResultType must not be null"); @@ -266,6 +267,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { } @Override + @Contract("_ -> new") public TerminatingDistinct matching(Query query) { Assert.notNull(query, "Query must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java index 47b7127de..599a91003 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java @@ -18,8 +18,9 @@ package org.springframework.data.mongodb.core; import java.util.ArrayList; import java.util.Collection; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.BulkOperations.BulkMode; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -41,6 +42,7 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { } @Override + @Contract("_ -> new") public ExecutableInsert insert(Class domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -56,10 +58,11 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { private final MongoTemplate template; private final Class domainType; - @Nullable private final String collection; - @Nullable private final BulkMode bulkMode; + private final @Nullable String collection; + private final @Nullable BulkMode bulkMode; - ExecutableInsertSupport(MongoTemplate template, Class domainType, String collection, BulkMode bulkMode) { + ExecutableInsertSupport(MongoTemplate template, Class domainType, @Nullable String collection, + @Nullable BulkMode bulkMode) { this.template = template; this.domainType = domainType; @@ -93,6 +96,7 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { } @Override + @Contract("_ -> new") public InsertWithBulkMode inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -101,6 +105,7 @@ class ExecutableInsertOperationSupport implements ExecutableInsertOperation { } @Override + @Contract("_ -> new") public TerminatingBulkInsert withBulkMode(BulkMode bulkMode) { Assert.notNull(bulkMode, "BulkMode must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java index 9f7869354..55864cbd8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableMapReduceOperationSupport.java @@ -17,9 +17,10 @@ package org.springframework.data.mongodb.core; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -46,6 +47,7 @@ class ExecutableMapReduceOperationSupport implements ExecutableMapReduceOperatio * @see in org.springframework.data.mongodb.core.ExecutableMapReduceOperation#mapReduce(java.lang.Class) */ @Override + @Contract("_ -> new") public ExecutableMapReduceSupport mapReduce(Class domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -89,6 +91,7 @@ class ExecutableMapReduceOperationSupport implements ExecutableMapReduceOperatio * @see in org.springframework.data.mongodb.core.ExecutableMapReduceOperation.TerminatingMapReduce#all() */ @Override + @SuppressWarnings("NullAway") public List all() { return template.mapReduce(query, domainType, getCollectionName(), mapFunction, reduceFunction, options, returnType); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java index 8e84aa7dd..e53e80b10 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java @@ -17,8 +17,9 @@ package org.springframework.data.mongodb.core; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -42,6 +43,7 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation { } @Override + @Contract("_ -> new") public ExecutableRemove remove(Class domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -60,7 +62,8 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation { private final Query query; @Nullable private final String collection; - public ExecutableRemoveSupport(MongoTemplate template, Class domainType, Query query, String collection) { + public ExecutableRemoveSupport(MongoTemplate template, Class domainType, Query query, + @Nullable String collection) { this.template = template; this.domainType = domainType; this.query = query; @@ -68,6 +71,7 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation { } @Override + @Contract("_ -> new") public RemoveWithQuery inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -76,6 +80,7 @@ class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation { } @Override + @Contract("_ -> new") public TerminatingRemove matching(Query query) { Assert.notNull(query, "Query must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java index a5c63e9b6..69365459b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java @@ -17,12 +17,12 @@ package org.springframework.data.mongodb.core; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AggregationUpdate; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.lang.Nullable; import com.mongodb.client.result.UpdateResult; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java index 593d863d3..75756c6f1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java @@ -15,9 +15,10 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -41,6 +42,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public ExecutableUpdate update(Class domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -52,6 +54,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { * @author Christoph Strobl * @since 2.0 */ + @SuppressWarnings("rawtypes") static class ExecutableUpdateSupport implements ExecutableUpdate, UpdateWithCollection, UpdateWithQuery, TerminatingUpdate, FindAndReplaceWithOptions, TerminatingFindAndReplace, FindAndReplaceWithProjection { @@ -66,9 +69,9 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { @Nullable private final Object replacement; private final Class targetType; - ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, UpdateDefinition update, - String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions, - Object replacement, Class targetType) { + ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, @Nullable UpdateDefinition update, + @Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, + @Nullable FindAndReplaceOptions findAndReplaceOptions, @Nullable Object replacement, Class targetType) { this.template = template; this.domainType = domainType; @@ -82,6 +85,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public TerminatingUpdate apply(UpdateDefinition update) { Assert.notNull(update, "Update must not be null"); @@ -91,6 +95,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public UpdateWithQuery inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -100,6 +105,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public TerminatingFindAndModify withOptions(FindAndModifyOptions options) { Assert.notNull(options, "Options must not be null"); @@ -109,6 +115,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public FindAndReplaceWithProjection replaceWith(T replacement) { Assert.notNull(replacement, "Replacement must not be null"); @@ -118,6 +125,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public FindAndReplaceWithProjection withOptions(FindAndReplaceOptions options) { Assert.notNull(options, "Options must not be null"); @@ -127,6 +135,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public TerminatingReplace withOptions(ReplaceOptions options) { FindAndReplaceOptions target = new FindAndReplaceOptions(); @@ -138,6 +147,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public UpdateWithUpdate matching(Query query) { Assert.notNull(query, "Query must not be null"); @@ -147,6 +157,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @Contract("_ -> new") public FindAndReplaceWithOptions as(Class resultType) { Assert.notNull(resultType, "ResultType must not be null"); @@ -171,6 +182,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @SuppressWarnings("NullAway") public @Nullable T findAndModifyValue() { return template.findAndModify(query, update, @@ -179,6 +191,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @SuppressWarnings({ "unchecked", "NullAway" }) public @Nullable T findAndReplaceValue() { return (T) template.findAndReplace(query, replacement, @@ -187,6 +200,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { } @Override + @SuppressWarnings({ "unchecked", "NullAway" }) public UpdateResult replaceFirst() { if (replacement != null) { @@ -198,6 +212,7 @@ class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation { findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName()); } + @SuppressWarnings("NullAway") private UpdateResult doUpdate(boolean multi, boolean upsert) { return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java index 51a2c5b86..6e9b77532 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java @@ -17,8 +17,9 @@ package org.springframework.data.mongodb.core; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * @author Mark Pollak @@ -99,16 +100,19 @@ public class FindAndModifyOptions { return options; } + @Contract("_ -> this") public FindAndModifyOptions returnNew(boolean returnNew) { this.returnNew = returnNew; return this; } + @Contract("_ -> this") public FindAndModifyOptions upsert(boolean upsert) { this.upsert = upsert; return this; } + @Contract("_ -> this") public FindAndModifyOptions remove(boolean remove) { this.remove = remove; return this; @@ -121,6 +125,7 @@ public class FindAndModifyOptions { * @return this. * @since 2.0 */ + @Contract("_ -> this") public FindAndModifyOptions collation(@Nullable Collation collation) { this.collation = collation; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java index 266a0742c..2005ba3c6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndReplaceOptions.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.springframework.lang.Contract; + /** * Options for * findOneAndReplace. @@ -95,6 +97,7 @@ public class FindAndReplaceOptions extends ReplaceOptions { * * @return this. */ + @Contract("-> this") public FindAndReplaceOptions returnNew() { this.returnNew = true; @@ -106,6 +109,7 @@ public class FindAndReplaceOptions extends ReplaceOptions { * * @return this. */ + @Contract("-> this") public FindAndReplaceOptions upsert() { super.upsert(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java index 625a85950..f04417325 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindPublisherPreparer.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.function.Function; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import com.mongodb.ReadPreference; @@ -76,8 +76,7 @@ public interface FindPublisherPreparer extends ReadPreferenceAware { * @since 2.2 */ @Override - @Nullable - default ReadPreference getReadPreference() { + default @Nullable ReadPreference getReadPreference() { return null; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java index 57abe9a52..043613122 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java @@ -18,9 +18,9 @@ package org.springframework.data.mongodb.core; import java.util.function.Function; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java index da4766343..cd9ba9045 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java @@ -20,6 +20,7 @@ import java.util.List; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; @@ -70,7 +71,7 @@ public class MappedDocument { return hasId() && document.get(ID_FIELD) != null; } - public Object getId() { + public @Nullable Object getId() { return document.get(ID_FIELD); } @@ -86,7 +87,7 @@ public class MappedDocument { return new Document(ID_FIELD, document.get(ID_FIELD)); } - public Object get(String key) { + public @Nullable Object get(String key) { return document.get(key); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java index bc26dfb68..396ae1ce8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java @@ -24,7 +24,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.convert.MongoConverter; @@ -46,6 +46,7 @@ import org.springframework.data.mongodb.core.schema.QueryCharacteristic; import org.springframework.data.mongodb.core.schema.QueryCharacteristics; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -94,6 +95,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { } @Override + @Contract("_ -> new") public MongoJsonSchemaCreator filter(Predicate filter) { return new MappingMongoJsonSchemaCreator(converter, mappingContext, filter, mergeProperties); } @@ -111,6 +113,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { * @return new instance of {@link MongoJsonSchemaCreator}. * @since 3.4 */ + @Contract("_, _ -> new") public MongoJsonSchemaCreator withTypesFor(String path, Class... types) { LinkedMultiValueMap> clone = mergeProperties.clone(); @@ -183,6 +186,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { return schemaProperties; } + @SuppressWarnings("NullAway") private JsonSchemaProperty computeSchemaForProperty(List path) { String stringPath = path.stream().map(MongoPersistentProperty::getName).collect(Collectors.joining(".")); @@ -373,7 +377,9 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { return schemaObject; } - private String computePropertyFieldName(PersistentProperty property) { + private String computePropertyFieldName(@Nullable PersistentProperty property) { + + Assert.notNull(property, "Property must not be null"); return property instanceof MongoPersistentProperty mongoPersistentProperty ? mongoPersistentProperty.getFieldName() : property.getName(); @@ -445,7 +451,8 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { } @Override - public MongoPersistentEntity resolveEntity(MongoPersistentProperty property) { + @SuppressWarnings("unchecked") + public @Nullable MongoPersistentEntity resolveEntity(MongoPersistentProperty property) { return (MongoPersistentEntity) mappingContext.getPersistentEntity(property); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java index fdfeaa81a..c827c5b8a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoAction.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import com.mongodb.WriteConcern; @@ -72,28 +72,23 @@ public class MongoAction { return collectionName; } - @Nullable - public WriteConcern getDefaultWriteConcern() { + public @Nullable WriteConcern getDefaultWriteConcern() { return defaultWriteConcern; } - @Nullable - public Class getEntityType() { + public @Nullable Class getEntityType() { return entityType; } - @Nullable - public MongoActionOperation getMongoActionOperation() { + public @Nullable MongoActionOperation getMongoActionOperation() { return mongoActionOperation; } - @Nullable - public Document getQuery() { + public @Nullable Document getQuery() { return query; } - @Nullable - public Document getDocument() { + public @Nullable Document getDocument() { return document; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java index c5fee9cf5..9210dd85e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientFactoryBean.java @@ -24,11 +24,11 @@ import java.util.function.Function; import java.util.stream.Collectors; import org.bson.UuidRepresentation; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.SpringDataMongoDB; -import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -78,7 +78,7 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp * * @param credential can be {@literal null}. */ - public void setCredential(@Nullable MongoCredential[] credential) { + public void setCredential(MongoCredential @Nullable[] credential) { this.credential = Arrays.asList(credential); } @@ -119,8 +119,7 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp } @Override - @Nullable - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) { return exceptionTranslator.translateExceptionIfPossible(ex); } @@ -316,13 +315,13 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp settingsBuilder.accept(value); } - private T computeSettingsValue(Function function, S defaultValueHolder, S settingsValueHolder, + private @Nullable T computeSettingsValue(Function function, S defaultValueHolder, S settingsValueHolder, @Nullable T connectionStringValue) { return computeSettingsValue(function.apply(defaultValueHolder), function.apply(settingsValueHolder), connectionStringValue); } - private T computeSettingsValue(T defaultValue, T fromSettings, T fromConnectionString) { + private @Nullable T computeSettingsValue(@Nullable T defaultValue, T fromSettings, @Nullable T fromConnectionString) { boolean fromSettingsIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromSettings); boolean fromConnectionStringIsDefault = ObjectUtils.nullSafeEquals(defaultValue, fromConnectionString); @@ -337,7 +336,7 @@ public class MongoClientFactoryBean extends AbstractFactoryBean imp return MongoClients.create(settings, SpringDataMongoDB.driverInformation()); } - private String getOrDefault(Object value, String defaultValue) { + private String getOrDefault(@Nullable Object value, String defaultValue) { if(value == null) { return defaultValue; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java index eab6b5d7f..0a62b7aa4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoDatabaseFactorySupport.java @@ -15,12 +15,13 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.aop.framework.ProxyFactory; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.SessionAwareMethodInterceptor; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -132,6 +133,7 @@ public abstract class MongoDatabaseFactorySupport implements MongoDatabaseFac } @Override + @Contract("_ -> new") public MongoDatabaseFactory withSession(ClientSession session) { return new MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session, this); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java index 7aef5a3a8..f361b19bb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoEncryptionSettingsFactoryBean.java @@ -19,8 +19,8 @@ import java.util.Collections; import java.util.Map; import org.bson.BsonDocument; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.FactoryBean; -import org.springframework.lang.Nullable; import com.mongodb.AutoEncryptionSettings; import com.mongodb.MongoClientSettings; @@ -34,11 +34,11 @@ import com.mongodb.MongoClientSettings; public class MongoEncryptionSettingsFactoryBean implements FactoryBean { private boolean bypassAutoEncryption; - private String keyVaultNamespace; - private Map extraOptions; - private MongoClientSettings keyVaultClientSettings; - private Map> kmsProviders; - private Map schemaMap; + private @Nullable String keyVaultNamespace; + private @Nullable Map extraOptions; + private @Nullable MongoClientSettings keyVaultClientSettings; + private @Nullable Map> kmsProviders; + private @Nullable Map schemaMap; /** * @param bypassAutoEncryption diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java index 1ec7d3ffc..2bde873c2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoExceptionTranslator.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.Set; import org.bson.BsonInvalidOperationException; - +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataIntegrityViolationException; @@ -31,7 +31,6 @@ import org.springframework.data.mongodb.ClientSessionException; import org.springframework.data.mongodb.TransientClientSessionException; import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.data.mongodb.util.MongoDbErrorCodes; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.MongoBulkWriteException; @@ -69,12 +68,12 @@ public class MongoExceptionTranslator implements PersistenceExceptionTranslator private static final Set SECURITY_EXCEPTIONS = Set.of("MongoCryptException"); @Override - @Nullable - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) { return doTranslateException(ex); } @Nullable + @SuppressWarnings("NullAway") DataAccessException doTranslateException(RuntimeException ex) { // Check for well-known MongoException subclasses. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java index 66b1cf209..84c395bf2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java @@ -139,8 +139,7 @@ public interface MongoJsonSchemaCreator { * @return {@literal null} if the property is not an entity. It is nevertheless recommend to check * {@link PersistentProperty#isEntity()} first. */ - @Nullable - MongoPersistentEntity resolveEntity(MongoPersistentProperty property); + @Nullable MongoPersistentEntity resolveEntity(MongoPersistentProperty property); } @@ -162,6 +161,7 @@ public interface MongoJsonSchemaCreator { return extracted(context.getProperty(), context); } + @SuppressWarnings("NullAway") private boolean extracted(MongoPersistentProperty property, JsonSchemaPropertyContext context) { if (property.isAnnotationPresent(Encrypted.class)) { return true; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index 65396bc7f..7fdd15752 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -24,6 +24,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Window; @@ -49,7 +50,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.util.Lock; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -196,7 +196,8 @@ public interface MongoOperations extends FluentMongoOperations { private @Nullable ClientSession session; @Override - public T execute(SessionCallback action, Consumer onComplete) { + @SuppressWarnings("NullAway") + public @Nullable T execute(SessionCallback action, Consumer onComplete) { lock.executeWithoutResult(() -> { @@ -733,8 +734,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param entityClass the parametrized type of the returned list. * @return the converted object. */ - @Nullable - T findOne(Query query, Class entityClass); + @Nullable T findOne(Query query, Class entityClass); /** * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified @@ -750,8 +750,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param collectionName name of the collection to retrieve the objects from. * @return the converted object. */ - @Nullable - T findOne(Query query, Class entityClass, String collectionName); + @Nullable T findOne(Query query, Class entityClass, String collectionName); /** * Determine result of given {@link Query} contains at least one element.
@@ -871,8 +870,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param entityClass the type the document shall be converted into. Must not be {@literal null}. * @return the document with the given id mapped onto the given target class. */ - @Nullable - T findById(Object id, Class entityClass); + @Nullable T findById(Object id, Class entityClass); /** * Returns the document with the given id from the given collection mapped onto the given target class. @@ -882,8 +880,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param collectionName the collection to query for the document. * @return he converted object or {@literal null} if document does not exist. */ - @Nullable - T findById(Object id, Class entityClass, String collectionName); + @Nullable T findById(Object id, Class entityClass, String collectionName); /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and @@ -960,8 +957,7 @@ public interface MongoOperations extends FluentMongoOperations { * @see Update * @see AggregationUpdate */ - @Nullable - T findAndModify(Query query, UpdateDefinition update, Class entityClass); + @Nullable T findAndModify(Query query, UpdateDefinition update, Class entityClass); /** * Triggers findAndModify @@ -980,8 +976,7 @@ public interface MongoOperations extends FluentMongoOperations { * @see Update * @see AggregationUpdate */ - @Nullable - T findAndModify(Query query, UpdateDefinition update, Class entityClass, String collectionName); + @Nullable T findAndModify(Query query, UpdateDefinition update, Class entityClass, String collectionName); /** * Triggers findAndModify @@ -1003,8 +998,7 @@ public interface MongoOperations extends FluentMongoOperations { * @see Update * @see AggregationUpdate */ - @Nullable - T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass); + @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass); /** * Triggers findAndModify @@ -1027,8 +1021,7 @@ public interface MongoOperations extends FluentMongoOperations { * @see Update * @see AggregationUpdate */ - @Nullable - T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass, + @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass, String collectionName); /** @@ -1048,8 +1041,7 @@ public interface MongoOperations extends FluentMongoOperations { * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, T replacement) { + default @Nullable T findAndReplace(Query query, T replacement) { return findAndReplace(query, replacement, FindAndReplaceOptions.empty()); } @@ -1068,8 +1060,7 @@ public interface MongoOperations extends FluentMongoOperations { * @return the converted object that was updated or {@literal null}, if not found. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, T replacement, String collectionName) { + default @Nullable T findAndReplace(Query query, T replacement, String collectionName) { return findAndReplace(query, replacement, FindAndReplaceOptions.empty(), collectionName); } @@ -1091,8 +1082,7 @@ public interface MongoOperations extends FluentMongoOperations { * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) { + default @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) { return findAndReplace(query, replacement, options, getCollectionName(ClassUtils.getUserClass(replacement))); } @@ -1112,8 +1102,7 @@ public interface MongoOperations extends FluentMongoOperations { * as it is after the update. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) { + default @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) { Assert.notNull(replacement, "Replacement must not be null"); return findAndReplace(query, replacement, options, (Class) ClassUtils.getUserClass(replacement), collectionName); @@ -1137,8 +1126,7 @@ public interface MongoOperations extends FluentMongoOperations { * as it is after the update. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class entityType, + default @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class entityType, String collectionName) { return findAndReplace(query, replacement, options, entityType, collectionName, entityType); @@ -1166,8 +1154,7 @@ public interface MongoOperations extends FluentMongoOperations { * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, + default @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, Class resultType) { return findAndReplace(query, replacement, options, entityType, @@ -1194,8 +1181,7 @@ public interface MongoOperations extends FluentMongoOperations { * as it is after the update. * @since 2.1 */ - @Nullable - T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, + @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, String collectionName, Class resultType); /** @@ -1211,8 +1197,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param entityClass the parametrized type of the returned list. * @return the converted object */ - @Nullable - T findAndRemove(Query query, Class entityClass); + @Nullable T findAndRemove(Query query, Class entityClass); /** * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified @@ -1229,8 +1214,7 @@ public interface MongoOperations extends FluentMongoOperations { * @param collectionName name of the collection to retrieve the objects from. * @return the converted object. */ - @Nullable - T findAndRemove(Query query, Class entityClass, String collectionName); + @Nullable T findAndRemove(Query query, Class entityClass, String collectionName); /** * Returns the number of documents for the given {@link Query} by querying the collection of the given entity class. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java index 37001faa4..574c0c893 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoServerApiFactoryBean.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.FactoryBean; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import com.mongodb.ServerApi; @@ -31,7 +31,7 @@ import com.mongodb.ServerApiVersion; */ public class MongoServerApiFactoryBean implements FactoryBean { - private String version; + private @Nullable String version; private @Nullable Boolean deprecationErrors; private @Nullable Boolean strict; @@ -59,9 +59,8 @@ public class MongoServerApiFactoryBean implements FactoryBean { this.strict = strict; } - @Nullable @Override - public ServerApi getObject() throws Exception { + public @Nullable ServerApi getObject() throws Exception { Builder builder = ServerApi.builder().version(version()); @@ -81,6 +80,11 @@ public class MongoServerApiFactoryBean implements FactoryBean { } private ServerApiVersion version() { + + if(version == null) { + return ServerApiVersion.V1; + } + try { // lookup by name eg. 'V1' return ObjectUtils.caseInsensitiveValueOf(ServerApiVersion.values(), version); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index e0442ab68..5c7df76cc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -15,12 +15,22 @@ */ package org.springframework.data.mongodb.core; -import static org.springframework.data.mongodb.core.query.SerializationUtils.*; +import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Scanner; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; import java.util.stream.Collectors; @@ -31,6 +41,7 @@ import org.apache.commons.logging.LogFactory; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -95,7 +106,18 @@ import org.springframework.data.mongodb.core.index.SearchIndexOperationsProvider import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.mapping.event.*; +import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; +import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent; +import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent; +import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent; +import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback; +import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; +import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; +import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; +import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent; +import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback; +import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; +import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.mapreduce.MapReduceResults; import org.springframework.data.mongodb.core.query.BasicQuery; @@ -110,7 +132,7 @@ import org.springframework.data.mongodb.core.validation.Validator; import org.springframework.data.projection.EntityProjection; import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.Optionals; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -133,7 +155,21 @@ import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoIterable; -import com.mongodb.client.model.*; +import com.mongodb.client.model.CountOptions; +import com.mongodb.client.model.CreateCollectionOptions; +import com.mongodb.client.model.CreateViewOptions; +import com.mongodb.client.model.DeleteOptions; +import com.mongodb.client.model.EstimatedDocumentCountOptions; +import com.mongodb.client.model.FindOneAndDeleteOptions; +import com.mongodb.client.model.FindOneAndReplaceOptions; +import com.mongodb.client.model.FindOneAndUpdateOptions; +import com.mongodb.client.model.ReturnDocument; +import com.mongodb.client.model.TimeSeriesGranularity; +import com.mongodb.client.model.TimeSeriesOptions; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.ValidationAction; +import com.mongodb.client.model.ValidationLevel; +import com.mongodb.client.model.ValidationOptions; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.UpdateResult; @@ -342,7 +378,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return this.readPreference; } @@ -478,7 +514,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doStream(query, entityType, collectionName, entityType); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected Stream doStream(Query query, Class entityType, String collectionName, Class returnType) { Assert.notNull(query, "Query must not be null"); @@ -511,7 +547,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(String jsonCommand) { Assert.hasText(jsonCommand, "JsonCommand must not be null nor empty"); @@ -520,7 +556,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(Document command) { Assert.notNull(command, "Command must not be null"); @@ -529,7 +565,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(Document command, @Nullable ReadPreference readPreference) { Assert.notNull(command, "Command must not be null"); @@ -576,7 +612,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - public T execute(DbCallback action) { + public @Nullable T execute(DbCallback action) { Assert.notNull(action, "DbCallback must not be null"); @@ -589,14 +625,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - public T execute(Class entityClass, CollectionCallback callback) { + public @Nullable T execute(Class entityClass, CollectionCallback callback) { Assert.notNull(entityClass, "EntityClass must not be null"); return execute(getCollectionName(entityClass), callback); } @Override - public T execute(String collectionName, CollectionCallback callback) { + public @Nullable T execute(String collectionName, CollectionCallback callback) { Assert.notNull(collectionName, "CollectionName must not be null"); Assert.notNull(callback, "CollectionCallback must not be null"); @@ -618,6 +654,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override + @Contract("_ -> new") public MongoTemplate withSession(ClientSession session) { Assert.notNull(session, "ClientSession must not be null"); @@ -691,6 +728,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doCreateView(name, source, aggregation.getAggregationPipeline(), options); } + @SuppressWarnings("NullAway") protected MongoCollection doCreateView(String name, String source, List pipeline, @Nullable ViewOptions options) { @@ -706,8 +744,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") - public MongoCollection getCollection(String collectionName) { + @SuppressWarnings({ "ConstantConditions", "NullAway" }) + @Contract("null -> fail") + public MongoCollection getCollection(@Nullable String collectionName) { Assert.notNull(collectionName, "CollectionName must not be null"); @@ -720,7 +759,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public boolean collectionExists(String collectionName) { Assert.notNull(collectionName, "CollectionName must not be null"); @@ -854,7 +893,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public boolean exists(Query query, @Nullable Class entityClass, String collectionName) { if (query == null) { @@ -956,7 +995,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "NullAway" }) public List findDistinct(Query query, String field, String collectionName, Class entityClass, Class resultClass) { @@ -1074,20 +1113,22 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @Nullable @Override - public T findAndModify(Query query, UpdateDefinition update, Class entityClass, String collectionName) { + public T findAndModify(Query query, UpdateDefinition update, Class entityClass, + String collectionName) { return findAndModify(query, update, new FindAndModifyOptions(), entityClass, collectionName); } @Nullable @Override - public T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass) { + public T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, + Class entityClass) { return findAndModify(query, update, options, entityClass, getCollectionName(entityClass)); } @Nullable @Override - public T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class entityClass, - String collectionName) { + public T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, + Class entityClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(update, "Update must not be null"); @@ -1111,8 +1152,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - public T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, - String collectionName, Class resultType) { + public @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, + Class entityType, String collectionName, Class resultType) { Assert.notNull(query, "Query must not be null"); Assert.notNull(replacement, "Replacement must not be null"); @@ -1223,6 +1264,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doEstimatedCount(CollectionPreparerDelegate.of(this), collectionName, new EstimatedDocumentCountOptions()); } + @SuppressWarnings("NullAway") protected long doEstimatedCount(CollectionPreparer> collectionPreparer, String collectionName, EstimatedDocumentCountOptions options) { return execute(collectionName, @@ -1240,6 +1282,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doExactCount(createDelegate(query), collectionName, mappedQuery, options); } + @SuppressWarnings("NullAway") protected long doExactCount(CollectionPreparer> collectionPreparer, String collectionName, Document filter, CountOptions options) { return execute(collectionName, collection -> collectionPreparer.prepare(collection) @@ -1543,7 +1586,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return maybeCallAfterSave(saved, dbDoc, collectionName); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected Object insertDocument(String collectionName, Document document, Class entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1597,6 +1640,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return MappedDocument.toIds(documents); } + @SuppressWarnings("NullAway") protected Object saveDocument(String collectionName, Document dbDoc, Class entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1695,7 +1739,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doUpdate(collectionName, query, update, entityClass, false, true); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefinition update, @Nullable Class entityClass, boolean upsert, boolean multi) { @@ -1805,7 +1849,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doRemove(collectionName, query, entityClass, true); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected DeleteResult doRemove(String collectionName, Query query, @Nullable Class entityClass, boolean multi) { @@ -2126,6 +2170,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @param entityClass * @return */ + @SuppressWarnings("NullAway") protected List doFindAndDelete(String collectionName, Query query, Class entityClass) { List result = find(query, entityClass, collectionName); @@ -2159,7 +2204,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return doAggregate(aggregation, collectionName, outputType, context.getAggregationOperationContext()); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected AggregationResults doAggregate(Aggregation aggregation, String collectionName, Class outputType, AggregationOperationContext context) { @@ -2242,7 +2287,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, }); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected Stream aggregateStream(Aggregation aggregation, String collectionName, Class outputType, @Nullable AggregationOperationContext context) { @@ -2357,7 +2402,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Set getCollectionNames() { return execute(db -> { Set result = new LinkedHashSet<>(); @@ -2441,7 +2486,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @return the collection that was created * @since 3.3.3 */ - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected MongoCollection doCreateCollection(String collectionName, CreateCollectionOptions collectionOptions) { @@ -2520,8 +2565,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @return the converted object or {@literal null} if none exists. */ @Nullable - protected T doFindOne(String collectionName, CollectionPreparer> collectionPreparer, - Document query, Document fields, Class entityClass) { + protected T doFindOne(String collectionName, + CollectionPreparer> collectionPreparer, Document query, Document fields, + Class entityClass) { return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass); } @@ -2540,8 +2586,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, */ @Nullable @SuppressWarnings("ConstantConditions") - protected T doFindOne(String collectionName, CollectionPreparer> collectionPreparer, - Document query, Document fields, CursorPreparer preparer, Class entityClass) { + protected T doFindOne(String collectionName, + CollectionPreparer> collectionPreparer, Document query, Document fields, + CursorPreparer preparer, Class entityClass) { MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); @@ -2717,8 +2764,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @return the List of converted objects. */ @SuppressWarnings("ConstantConditions") - protected T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, Document query, - Document fields, Document sort, @Nullable Collation collation, Class entityClass) { + protected @Nullable T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, + Document query, @Nullable Document fields, @Nullable Document sort, @Nullable Collation collation, + Class entityClass) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s", @@ -2733,8 +2781,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @SuppressWarnings("ConstantConditions") - protected T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, Document query, - Document fields, Document sort, Class entityClass, UpdateDefinition update, + protected @Nullable T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, + Document query, @Nullable Document fields, @Nullable Document sort, Class entityClass, UpdateDefinition update, @Nullable FindAndModifyOptions options) { if (options == null) { @@ -2779,9 +2827,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}. */ @Nullable - protected T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery, - Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, - Class entityType, Document replacement, FindAndReplaceOptions options, Class resultType) { + protected T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, + Document mappedQuery, Document mappedFields, Document mappedSort, + com.mongodb.client.model.@Nullable Collation collation, Class entityType, Document replacement, + FindAndReplaceOptions options, Class resultType) { EntityProjection projection = operations.introspectProjection(resultType, entityType); @@ -2821,9 +2870,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @since 3.4 */ @Nullable - private T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery, - Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, - Class entityType, Document replacement, FindAndReplaceOptions options, EntityProjection projection) { + private T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, + Document mappedQuery, Document mappedFields, Document mappedSort, + com.mongodb.client.model.@Nullable Collation collation, Class entityType, Document replacement, + FindAndReplaceOptions options, EntityProjection projection) { if (LOGGER.isDebugEnabled()) { LOGGER @@ -2839,6 +2889,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, collectionName); } + @SuppressWarnings("NullAway") private UpdateResult doReplace(ReplaceOptions options, Class entityType, String collectionName, UpdateContext updateContext, CollectionPreparer> collectionPreparer, Document replacement) { @@ -2999,13 +3050,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return getMappedSortObject(query.getSortObject(), type); } - @Nullable - private Document getMappedSortObject(Document sortObject, Class type) { + private @Nullable Document getMappedSortObject(@Nullable Document sortObject, Class type) { return getMappedSortObject(sortObject, mappingContext.getPersistentEntity(type)); } - @Nullable - private Document getMappedSortObject(Document sortObject, @Nullable MongoPersistentEntity entity) { + + private @Nullable Document getMappedSortObject(@Nullable Document sortObject, @Nullable MongoPersistentEntity entity) { if (ObjectUtils.isEmpty(sortObject)) { return null; @@ -3081,10 +3131,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private final CollectionPreparer> collectionPreparer; private final Document query; private final Document fields; - private final @Nullable com.mongodb.client.model.Collation collation; + private final com.mongodb.client.model.@Nullable Collation collation; public FindCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, @Nullable com.mongodb.client.model.Collation collation) { + Document fields, com.mongodb.client.model.@Nullable Collation collation) { Assert.notNull(query, "Query must not be null"); Assert.notNull(fields, "Fields must not be null"); @@ -3120,10 +3170,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private final CollectionPreparer collectionPreparer; private final Document mappedQuery; - private final com.mongodb.client.model.Collation collation; + private final com.mongodb.client.model.@Nullable Collation collation; ExistsCallback(CollectionPreparer collectionPreparer, Document mappedQuery, - com.mongodb.client.model.Collation collation) { + com.mongodb.client.model.@Nullable Collation collation) { this.collectionPreparer = collectionPreparer; this.mappedQuery = mappedQuery; @@ -3148,12 +3198,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private final CollectionPreparer> collectionPreparer; private final Document query; - private final Document fields; - private final Document sort; + private final @Nullable Document fields; + private final @Nullable Document sort; private final Optional collation; FindAndRemoveCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, @Nullable Collation collation) { + @Nullable Document fields, @Nullable Document sort, @Nullable Collation collation) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -3176,14 +3226,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private final CollectionPreparer> collectionPreparer; private final Document query; - private final Document fields; - private final Document sort; + private final @Nullable Document fields; + private final @Nullable Document sort; private final Object update; private final List arrayFilters; private final FindAndModifyOptions options; FindAndModifyCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, Object update, List arrayFilters, FindAndModifyOptions options) { + @Nullable Document fields, @Nullable Document sort, Object update, List arrayFilters, + FindAndModifyOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -3237,11 +3288,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, private final Document fields; private final Document sort; private final Document update; - private final @Nullable com.mongodb.client.model.Collation collation; + private final com.mongodb.client.model.@Nullable Collation collation; private final FindAndReplaceOptions options; FindAndReplaceCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, Document update, @Nullable com.mongodb.client.model.Collation collation, + Document fields, Document sort, Document update, com.mongodb.client.model.@Nullable Collation collation, FindAndReplaceOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -3347,10 +3398,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @SuppressWarnings("unchecked") public T doWith(Document document) { - if (document == null) { - return null; - } - maybeEmitEvent(new AfterLoadEvent<>(document, projection.getMappedType().getType(), collectionName)); Object entity = mongoConverter.project(projection, document); @@ -3595,8 +3642,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, throw potentiallyConvertRuntimeException(ex, exceptionTranslator); } finally { cursor = null; - exceptionTranslator = null; - objectReadCallback = null; } } } @@ -3628,7 +3673,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } @Override - public MongoCollection getCollection(String collectionName) { + public MongoCollection getCollection(@Nullable String collectionName) { // native MongoDB objects that offer methods with ClientSession must not be proxied. return delegate.getCollection(collectionName); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java index 28ca85fbd..4ae618eaa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java @@ -31,6 +31,7 @@ import org.bson.Document; import org.bson.codecs.Codec; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.data.mapping.context.MappingContext; @@ -62,7 +63,7 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter; import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.projection.EntityProjection; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import com.mongodb.client.model.CountOptions; @@ -283,6 +284,7 @@ class QueryOperations { * @param * @return the {@link MappedDocument} containing the changes. */ + @SuppressWarnings("NullAway") MappedDocument prepareId(@Nullable MongoPersistentEntity entity) { if (entity == null || source.hasId()) { @@ -361,6 +363,7 @@ class QueryOperations { return queryMapper.getMappedObject(getQueryObject(), entity); } + @SuppressWarnings("NullAway") Document getMappedFields(@Nullable MongoPersistentEntity entity, EntityProjection projection) { Document fields = evaluateFields(entity); @@ -888,6 +891,8 @@ class QueryOperations { */ List getUpdatePipeline(@Nullable Class domainType) { + Assert.isInstanceOf(AggregationUpdate.class, update); + Class type = domainType != null ? domainType : Object.class; AggregationOperationContext context = new RelaxedTypeBasedAggregationOperationContext(type, mappingContext, @@ -901,6 +906,7 @@ class QueryOperations { * @param entity * @return */ + @SuppressWarnings("NullAway") Document getMappedUpdate(@Nullable MongoPersistentEntity entity) { if (update != null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java index 954fd6171..978aa9634 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveAggregationOperationSupport.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import org.springframework.data.mongodb.core.aggregation.Aggregation; @@ -59,11 +60,11 @@ class ReactiveAggregationOperationSupport implements ReactiveAggregationOperatio private final ReactiveMongoTemplate template; private final Class domainType; - private final Aggregation aggregation; - private final String collection; + private final @Nullable Aggregation aggregation; + private final @Nullable String collection; - ReactiveAggregationSupport(ReactiveMongoTemplate template, Class domainType, Aggregation aggregation, - String collection) { + ReactiveAggregationSupport(ReactiveMongoTemplate template, Class domainType, @Nullable Aggregation aggregation, + @Nullable String collection) { this.template = template; this.domainType = domainType; @@ -89,6 +90,9 @@ class ReactiveAggregationOperationSupport implements ReactiveAggregationOperatio @Override public Flux all() { + + Assert.notNull(aggregation, "Aggregation must be set first"); + return template.aggregate(aggregation, getCollectionName(aggregation), domainType); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java index afeb6c5e0..589f264f1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveChangeStreamOperationSupport.java @@ -24,11 +24,11 @@ import java.util.function.Consumer; import org.bson.BsonTimestamp; import org.bson.BsonValue; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.MatchOperation; import org.springframework.data.mongodb.core.query.CriteriaDefinition; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java index d1aec8af3..9445dbdad 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java @@ -19,6 +19,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Window; import org.springframework.data.domain.ScrollPosition; @@ -26,7 +27,6 @@ import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveC import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.SerializationUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -67,10 +67,10 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation { private final ReactiveMongoTemplate template; private final Class domainType; private final Class returnType; - private final String collection; + private final @Nullable String collection; private final Query query; - ReactiveFindSupport(ReactiveMongoTemplate template, Class domainType, Class returnType, String collection, + ReactiveFindSupport(ReactiveMongoTemplate template, Class domainType, Class returnType, @Nullable String collection, Query query) { this.template = template; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java index 06d3c6eae..9d424c244 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveInsertOperationSupport.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -50,9 +51,9 @@ class ReactiveInsertOperationSupport implements ReactiveInsertOperation { private final ReactiveMongoTemplate template; private final Class domainType; - private final String collection; + private final @Nullable String collection; - ReactiveInsertSupport(ReactiveMongoTemplate template, Class domainType, String collection) { + ReactiveInsertSupport(ReactiveMongoTemplate template, Class domainType, @Nullable String collection) { this.template = template; this.domainType = domainType; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java index 4f0d39595..4e3379bad 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupport.java @@ -17,9 +17,9 @@ package org.springframework.data.mongodb.core; import reactor.core.publisher.Flux; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -89,8 +89,11 @@ class ReactiveMapReduceOperationSupport implements ReactiveMapReduceOperation { @Override public Flux all() { + Assert.notNull(mapFunction, "MapFunction must be set first"); + Assert.notNull(reduceFunction, "ReduceFunction must be set first"); + return template.mapReduce(query, domainType, getCollectionName(), returnType, mapFunction, reduceFunction, - options); + options != null ? options : MapReduceOptions.options()); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java index 89d1cd78a..89caf3273 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoClientFactoryBean.java @@ -16,10 +16,10 @@ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import com.mongodb.MongoClientSettings; @@ -89,7 +89,7 @@ public class ReactiveMongoClientFactoryBean extends AbstractFactoryBean collectionExists(String collectionName) { - return createMono(db -> Flux.from(db.listCollectionNames()) // - .filter(s -> s.equals(collectionName)) // - .map(s -> true) // - .single(false)); + return createMono( + db -> Flux.from(db.listCollectionNames()) // + .filter(s -> s.equals(collectionName)) // + .map(s -> true) // + .single(false)); } @Override @@ -898,7 +911,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati Mono> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), keysetPaginationQuery.query(), keysetPaginationQuery.fields(), sourceClass, new QueryFindPublisherPreparer(query, keysetPaginationQuery.sort(), limit, 0, sourceClass), callback) - .collectList(); + .collectList(); return result.map(it -> ScrollUtils.createWindow(query, it, sourceClass, operations)); } @@ -906,7 +919,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati Mono> result = doFind(collectionName, ReactiveCollectionPreparerDelegate.of(query), query.getQueryObject(), query.getFieldsObject(), sourceClass, new QueryFindPublisherPreparer(query, query.getSortObject(), limit, query.getSkip(), sourceClass), callback) - .collectList(); + .collectList(); return result.map( it -> ScrollUtils.createWindow(it, query.getLimit(), OffsetScrollPosition.positionFunction(query.getSkip()))); @@ -1145,6 +1158,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } @Override + @SuppressWarnings("NullAway") public Mono findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class entityType, String collectionName, Class resultType) { @@ -1350,6 +1364,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return doInsert(collectionName, objectToSave, this.mongoConverter); } + @SuppressWarnings("NullAway") protected Mono doInsert(String collectionName, T objectToSave, MongoWriter writer) { return Mono.just(PersistableEntityModel.of(objectToSave, collectionName)) // @@ -1400,6 +1415,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return Flux.from(objectsToSave).flatMapSequential(this::insertAll); } + @SuppressWarnings("NullAway") protected Flux doInsertAll(Collection listToSave, MongoWriter writer) { Map> elementsByCollection = new HashMap<>(); @@ -1416,6 +1432,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati .concatMap(collectionName -> doInsertBatch(collectionName, elementsByCollection.get(collectionName), writer)); } + @SuppressWarnings("NullAway") protected Flux doInsertBatch(String collectionName, Collection batchToSave, MongoWriter writer) { @@ -1536,6 +1553,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati }); } + @SuppressWarnings("NullAway") protected Mono doSave(String collectionName, T objectToSave, MongoWriter writer) { assertUpdateableIdIfNotSet(objectToSave); @@ -1629,6 +1647,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return collectionToUse; } + @SuppressWarnings("NullAway") protected Mono saveDocument(String collectionName, Document document, Class entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1732,7 +1751,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return doUpdate(collectionName, query, update, entityClass, false, true); } - protected Mono doUpdate(String collectionName, Query query, @Nullable UpdateDefinition update, + @SuppressWarnings("NullAway") + protected Mono doUpdate(String collectionName, Query query, UpdateDefinition update, @Nullable Class entityClass, boolean upsert, boolean multi) { MongoPersistentEntity entity = entityClass == null ? null : getPersistentEntity(entityClass); @@ -1814,7 +1834,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati Document updateObj = updateContext.getMappedUpdate(entity); if (containsVersionProperty(queryObj, entity)) - throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity %s to collection %s".formatted(entity.getName(), collectionName)); + throw new OptimisticLockingFailureException("Optimistic lock exception on saving entity %s to collection %s" + .formatted(entity.getName(), collectionName)); } } }); @@ -2012,18 +2033,18 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @Override public Flux tail(@Nullable Query query, Class entityClass, String collectionName) { - ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query); if (query == null) { LOGGER.debug(String.format("Tail for class: %s in collection: %s", entityClass, collectionName)); return executeFindMultiInternal( - collection -> new FindCallback(collectionPreparer, null).doInCollection(collection) + collection -> new FindCallback(CollectionPreparer.identity(), null).doInCollection(collection) .cursorType(CursorType.TailableAwait), FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName), collectionName); } + ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query); return doFind(collectionName, collectionPreparer, query.getQueryObject(), query.getFieldsObject(), entityClass, new TailingQueryFindPublisherPreparer(query, entityClass)); } @@ -2382,8 +2403,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName)); } - return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer, - objectCallback, collectionName); + return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), + preparer != null ? preparer : FindPublisherPreparer.NO_OP_PREPARER, objectCallback, collectionName); } CollectionPreparer> createCollectionPreparer(Query query) { @@ -2448,8 +2469,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return the List of converted objects. */ protected Mono doFindAndRemove(String collectionName, - CollectionPreparer> collectionPreparer, Document query, Document fields, Document sort, - @Nullable Collation collation, Class entityClass) { + CollectionPreparer> collectionPreparer, Document query, Document fields, + @Nullable Document sort, @Nullable Collation collation, Class entityClass) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s", @@ -2464,8 +2485,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } protected Mono doFindAndModify(String collectionName, - CollectionPreparer> collectionPreparer, Document query, Document fields, Document sort, - Class entityClass, UpdateDefinition update, FindAndModifyOptions options) { + CollectionPreparer> collectionPreparer, Document query, Document fields, + @Nullable Document sort, Class entityClass, UpdateDefinition update, FindAndModifyOptions options) { MongoPersistentEntity entity = mappingContext.getPersistentEntity(entityClass); UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false); @@ -2481,8 +2502,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati LOGGER.debug(String.format( "findAndModify using query: %s fields: %s sort: %s for class: %s and update: %s " + "in collection: %s", serializeToJsonSafely(mappedQuery), fields, serializeToJsonSafely(sort), entityClass, - serializeToJsonSafely(mappedUpdate), - collectionName)); + serializeToJsonSafely(mappedUpdate), collectionName)); } return executeFindOneInternal( @@ -2659,8 +2679,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @see #setWriteConcern(WriteConcern) * @see #setWriteConcernResolver(WriteConcernResolver) */ - @Nullable - protected WriteConcern prepareWriteConcern(MongoAction mongoAction) { + protected @Nullable WriteConcern prepareWriteConcern(MongoAction mongoAction) { WriteConcern wc = writeConcernResolver.resolve(mongoAction); return potentiallyForceAcknowledgedWrite(wc); @@ -2679,7 +2698,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati if (ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking)) { if (wc == null || wc.getWObject() == null - || (wc.getWObject()instanceof Number concern && concern.intValue() < 1)) { + || (wc.getWObject() instanceof Number concern && concern.intValue() < 1)) { return WriteConcern.ACKNOWLEDGED; } } @@ -2725,7 +2744,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return */ private Flux executeFindMultiInternal(ReactiveCollectionQueryCallback collectionCallback, - @Nullable FindPublisherPreparer preparer, DocumentCallback objectCallback, String collectionName) { + FindPublisherPreparer preparer, DocumentCallback objectCallback, String collectionName) { return createFlux(collectionName, collection -> { return Flux.from(preparer.initiateFind(collection, collectionCallback::doInCollection)) @@ -2764,8 +2783,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return resolved == null ? ex : resolved; } - @Nullable - private MongoPersistentEntity getPersistentEntity(@Nullable Class type) { + private @Nullable MongoPersistentEntity getPersistentEntity(@Nullable Class type) { return type == null ? null : mappingContext.getPersistentEntity(type); } @@ -2785,8 +2803,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return converter; } - @Nullable - private Document getMappedSortObject(Query query, Class type) { + @Contract("null, _ -> null") + private @Nullable Document getMappedSortObject(@Nullable Query query, Class type) { if (query == null) { return null; @@ -2795,8 +2813,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati return getMappedSortObject(query.getSortObject(), type); } - @Nullable - private Document getMappedSortObject(Document sortObject, Class type) { + @Contract("null, _ -> null") + private @Nullable Document getMappedSortObject(@Nullable Document sortObject, Class type) { if (ObjectUtils.isEmpty(sortObject)) { return null; @@ -2862,7 +2880,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati this(collectionPreparer, query, null); } - FindCallback(CollectionPreparer> collectionPreparer, Document query, Document fields) { + FindCallback(CollectionPreparer> collectionPreparer, @Nullable Document query, + @Nullable Document fields) { this.collectionPreparer = collectionPreparer; this.query = query; this.fields = fields; @@ -2898,11 +2917,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati private final CollectionPreparer> collectionPreparer; private final Document query; private final Document fields; - private final Document sort; + private final @Nullable Document sort; private final Optional collation; FindAndRemoveCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, @Nullable Collation collation) { + Document fields, @Nullable Document sort, @Nullable Collation collation) { this.collectionPreparer = collectionPreparer; this.query = query; this.fields = fields; @@ -2928,14 +2947,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati private final CollectionPreparer> collectionPreparer; private final Document query; - private final Document fields; - private final Document sort; + private final @Nullable Document fields; + private final @Nullable Document sort; private final Object update; private final List arrayFilters; private final FindAndModifyOptions options; FindAndModifyCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, Object update, List arrayFilters, FindAndModifyOptions options) { + @Nullable Document fields, @Nullable Document sort, Object update, List arrayFilters, + FindAndModifyOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -2973,7 +2993,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } private static FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options, - Document fields, Document sort, List arrayFilters) { + @Nullable Document fields, @Nullable Document sort, List arrayFilters) { FindOneAndUpdateOptions result = new FindOneAndUpdateOptions(); @@ -3009,11 +3029,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati private final Document fields; private final Document sort; private final Document update; - private final @Nullable com.mongodb.client.model.Collation collation; + private final com.mongodb.client.model.@Nullable Collation collation; private final FindAndReplaceOptions options; FindAndReplaceCallback(CollectionPreparer> collectionPreparer, Document query, - Document fields, Document sort, Document update, com.mongodb.client.model.Collation collation, + Document fields, Document sort, Document update, com.mongodb.client.model.@Nullable Collation collation, FindAndReplaceOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -3049,7 +3069,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati } } - private static FindOneAndDeleteOptions convertToFindOneAndDeleteOptions(Document fields, Document sort) { + private static FindOneAndDeleteOptions convertToFindOneAndDeleteOptions(@Nullable Document fields, + @Nullable Document sort) { FindOneAndDeleteOptions result = new FindOneAndDeleteOptions(); result = result.projection(fields).sort(sort); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java index 97c9cb0d0..5c935ec62 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveRemoveOperationSupport.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -54,9 +55,9 @@ class ReactiveRemoveOperationSupport implements ReactiveRemoveOperation { private final ReactiveMongoTemplate template; private final Class domainType; private final Query query; - private final String collection; + private final @Nullable String collection; - ReactiveRemoveSupport(ReactiveMongoTemplate template, Class domainType, Query query, String collection) { + ReactiveRemoveSupport(ReactiveMongoTemplate template, Class domainType, Query query, @Nullable String collection) { this.template = template; this.domainType = domainType; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java index 51cd99dc9..75bfeef31 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveUpdateOperationSupport.java @@ -17,9 +17,9 @@ package org.springframework.data.mongodb.core; import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -57,16 +57,16 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation { private final ReactiveMongoTemplate template; private final Class domainType; private final Query query; - private final org.springframework.data.mongodb.core.query.UpdateDefinition update; - @Nullable private final String collection; - @Nullable private final FindAndModifyOptions findAndModifyOptions; - @Nullable private final FindAndReplaceOptions findAndReplaceOptions; - @Nullable private final Object replacement; + private final org.springframework.data.mongodb.core.query.@Nullable UpdateDefinition update; + private final @Nullable String collection; + private final @Nullable FindAndModifyOptions findAndModifyOptions; + private final @Nullable FindAndReplaceOptions findAndReplaceOptions; + private final @Nullable Object replacement; private final Class targetType; - ReactiveUpdateSupport(ReactiveMongoTemplate template, Class domainType, Query query, UpdateDefinition update, - String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions, - Object replacement, Class targetType) { + ReactiveUpdateSupport(ReactiveMongoTemplate template, Class domainType, Query query, @Nullable UpdateDefinition update, + @Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, @Nullable FindAndReplaceOptions findAndReplaceOptions, + @Nullable Object replacement, Class targetType) { this.template = template; this.domainType = domainType; @@ -108,6 +108,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation { } @Override + @SuppressWarnings("NullAway") public Mono findAndModify() { String collectionName = getCollectionName(); @@ -118,7 +119,11 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation { } @Override + @SuppressWarnings({"unchecked","rawtypes"}) public Mono findAndReplace() { + + Assert.notNull(replacement, "Replacement must be set first"); + return template.findAndReplace(query, replacement, findAndReplaceOptions != null ? findAndReplaceOptions : FindAndReplaceOptions.none(), (Class) domainType, getCollectionName(), targetType); @@ -186,6 +191,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation { } @Override + @SuppressWarnings("NullAway") public Mono replaceFirst() { if (replacement != null) { @@ -197,6 +203,7 @@ class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation { findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName()); } + @SuppressWarnings("NullAway") private Mono doUpdate(boolean multi, boolean upsert) { return template.doUpdate(getCollectionName(), query, update, domainType, upsert, multi); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java index 00c5815fc..7a7e5fdfb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.ReadConcern; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java index 74bca9abe..e6f3fc0da 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadPreferenceAware.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.ReadPreference; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java index a2e2ba24c..a487cde66 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReplaceOptions.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.lang.Contract; /** * Options for {@link org.springframework.data.mongodb.core.MongoOperations#replace(Query, Object) replace operations}. Defaults to @@ -69,6 +70,7 @@ public class ReplaceOptions { * * @return this. */ + @Contract("-> this") public ReplaceOptions upsert() { this.upsert = true; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java index a01760368..2ec71b415 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScriptOperations.java @@ -17,9 +17,9 @@ package org.springframework.data.mongodb.core; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.script.ExecutableMongoScript; import org.springframework.data.mongodb.core.script.NamedMongoScript; -import org.springframework.lang.Nullable; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java index 85ddce765..62e6d6c51 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ScrollUtils.java @@ -29,6 +29,7 @@ import org.springframework.data.domain.ScrollPosition.Direction; import org.springframework.data.domain.Window; import org.springframework.data.mongodb.core.EntityOperations.Entity; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.util.Assert; /** * Utilities to run scroll queries and create {@link Window} results. @@ -48,7 +49,11 @@ class ScrollUtils { */ static KeysetScrollQuery createKeysetPaginationQuery(Query query, String idPropertyName) { + KeysetScrollPosition keyset = query.getKeyset(); + + Assert.notNull(keyset, "Query.keyset must not be null"); + KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection()); Document sortObject = director.getSortObject(idPropertyName, query); Document fieldsObject = director.getFieldsObject(query.getFieldsObject(), sortObject); @@ -61,6 +66,9 @@ class ScrollUtils { Document sortObject = query.getSortObject(); KeysetScrollPosition keyset = query.getKeyset(); + + Assert.notNull(keyset, "Query.keyset must not be null"); + Direction direction = keyset.getDirection(); KeysetScrollDirector director = KeysetScrollDirector.of(direction); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java index 55a87ecad..76a6d525f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionCallback.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; /** * Callback interface for executing operations within a {@link com.mongodb.session.ClientSession}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java index 33ad9d731..906d68268 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SessionScoped.java @@ -17,10 +17,10 @@ package org.springframework.data.mongodb.core; import java.util.function.Consumer; -import org.springframework.lang.Nullable; - import com.mongodb.client.ClientSession; +import org.jspecify.annotations.Nullable; + /** * Gateway interface to execute {@link ClientSession} bound operations against MongoDB via a {@link SessionCallback}. *
@@ -42,8 +42,7 @@ public interface SessionScoped { * @param return type. * @return a result object returned by the action. Can be {@literal null}. */ - @Nullable - default T execute(SessionCallback action) { + default @Nullable T execute(SessionCallback action) { return execute(action, session -> {}); } @@ -60,6 +59,5 @@ public interface SessionScoped { * @param return type. * @return a result object returned by the action. Can be {@literal null}. */ - @Nullable - T execute(SessionCallback action, Consumer doFinally); + @Nullable T execute(SessionCallback action, Consumer doFinally); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java index 84edf13d5..529f912e6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleReactiveMongoDatabaseFactory.java @@ -18,13 +18,13 @@ package org.springframework.data.mongodb.core; import reactor.core.publisher.Mono; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; import org.springframework.data.mongodb.SessionAwareMethodInterceptor; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java index e50e1088c..b4b525fc9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ViewOptions.java @@ -17,8 +17,9 @@ package org.springframework.data.mongodb.core; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * Immutable object holding additional options to be applied when creating a MongoDB @@ -59,6 +60,7 @@ public class ViewOptions { * @param collation the {@link Collation} to use for language-specific string comparison. * @return new instance of {@link ViewOptions}. */ + @Contract("_ -> new") public ViewOptions collation(Collation collation) { return new ViewOptions(collation); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java index d6e4119b2..bdc7de666 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernAware.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.WriteConcern; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java index 8df417184..a72c656e4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/WriteConcernResolver.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.WriteConcern; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java index d4cdece41..710b570ed 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java @@ -26,6 +26,7 @@ import java.util.Map; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; @@ -282,7 +283,7 @@ abstract class AbstractAggregationExpression implements AggregationExpression { * @since 2.1 */ @SuppressWarnings("unchecked") - protected T get(Object key) { + protected @Nullable T get(Object key) { Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java index cf6485c23..fa44656c9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; import org.bson.Document; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -60,8 +62,8 @@ public class AccumulatorOperators { */ public static class AccumulatorOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link AccumulatorOperatorFactory} for given {@literal fieldReference}. @@ -93,6 +95,7 @@ public class AccumulatorOperators { * * @return new instance of {@link Sum}. */ + @SuppressWarnings("NullAway") public Sum sum() { return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression); } @@ -103,6 +106,7 @@ public class AccumulatorOperators { * * @return new instance of {@link Avg}. */ + @SuppressWarnings("NullAway") public Avg avg() { return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression); } @@ -113,6 +117,7 @@ public class AccumulatorOperators { * * @return new instance of {@link Max}. */ + @SuppressWarnings("NullAway") public Max max() { return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression); } @@ -134,6 +139,7 @@ public class AccumulatorOperators { * * @return new instance of {@link Min}. */ + @SuppressWarnings("NullAway") public Min min() { return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression); } @@ -155,6 +161,7 @@ public class AccumulatorOperators { * * @return new instance of {@link StdDevPop}. */ + @SuppressWarnings("NullAway") public StdDevPop stdDevPop() { return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression); } @@ -165,6 +172,7 @@ public class AccumulatorOperators { * * @return new instance of {@link StdDevSamp}. */ + @SuppressWarnings("NullAway") public StdDevSamp stdDevSamp() { return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression); } @@ -193,6 +201,7 @@ public class AccumulatorOperators { return covariancePop().and(expression); } + @SuppressWarnings("NullAway") private CovariancePop covariancePop() { return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression); } @@ -221,6 +230,7 @@ public class AccumulatorOperators { return covarianceSamp().and(expression); } + @SuppressWarnings("NullAway") private CovarianceSamp covarianceSamp() { return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference) : CovarianceSamp.covarianceSampOf(expression); @@ -233,6 +243,7 @@ public class AccumulatorOperators { * @return new instance of {@link ExpMovingAvg}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ExpMovingAvgBuilder expMovingAvg() { ExpMovingAvg expMovingAvg = usesFieldRef() ? ExpMovingAvg.expMovingAvgOf(fieldReference) @@ -252,13 +263,14 @@ public class AccumulatorOperators { } /** - * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the - * associated numeric value expression. + * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the associated numeric + * value expression. * * @return new instance of {@link Percentile}. * @param percentages must not be {@literal null}. * @since 4.2 */ + @SuppressWarnings("NullAway") public Percentile percentile(Double... percentages) { Percentile percentile = usesFieldRef() ? Percentile.percentileOf(fieldReference) : Percentile.percentileOf(expression); @@ -271,6 +283,7 @@ public class AccumulatorOperators { * @return new instance of {@link Median}. * @since 4.2 */ + @SuppressWarnings("NullAway") public Median median() { return usesFieldRef() ? Median.medianOf(fieldReference) : Median.medianOf(expression); } @@ -339,6 +352,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Sum}. */ + @Contract("_ -> new") public static Sum sumOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -352,6 +366,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Sum}. */ + @Contract("_ -> new") public Sum and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -365,6 +380,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Sum}. */ + @Contract("_ -> new") public Sum and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -379,6 +395,7 @@ public class AccumulatorOperators { * @return new instance of {@link Sum}. * @since 2.2 */ + @Contract("_ -> new") public Sum and(Number value) { Assert.notNull(value, "Value must not be null"); @@ -386,7 +403,6 @@ public class AccumulatorOperators { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List list && list.size() == 1) { @@ -444,6 +460,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Avg}. */ + @Contract("_ -> new") public Avg and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -457,6 +474,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Avg}. */ + @Contract("_ -> new") public Avg and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -464,7 +482,6 @@ public class AccumulatorOperators { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List list && list.size() == 1) { @@ -522,6 +539,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Max}. */ + @Contract("_ -> new") public Max and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -535,6 +553,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Max}. */ + @Contract("_ -> new") public Max and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -548,11 +567,13 @@ public class AccumulatorOperators { * @param numberOfResults * @return new instance of {@link Max}. */ + @Contract("_ -> new") public Max limit(int numberOfResults) { return new Max(append("n", numberOfResults)); } @Override + @SuppressWarnings("NullAway") public Document toDocument(AggregationOperationContext context) { if (get("n") == null) { return toDocument(get("input"), context); @@ -619,6 +640,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Min}. */ + @Contract("_ -> new") public Min and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -632,6 +654,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Min}. */ + @Contract("_ -> new") public Min and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -645,11 +668,13 @@ public class AccumulatorOperators { * @param numberOfResults * @return new instance of {@link Min}. */ + @Contract("_ -> new") public Min limit(int numberOfResults) { return new Min(append("n", numberOfResults)); } @Override + @SuppressWarnings("NullAway") public Document toDocument(AggregationOperationContext context) { if (get("n") == null) { @@ -659,7 +684,6 @@ public class AccumulatorOperators { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List list && list.size() == 1) { @@ -717,6 +741,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link StdDevPop}. */ + @Contract("_ -> new") public StdDevPop and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -730,6 +755,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link StdDevPop}. */ + @Contract("_ -> new") public StdDevPop and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -737,7 +763,6 @@ public class AccumulatorOperators { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List list && list.size() == 1) { @@ -795,6 +820,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link StdDevSamp}. */ + @Contract("_ -> new") public StdDevSamp and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -808,6 +834,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link StdDevSamp}. */ + @Contract("_ -> new") public StdDevSamp and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -815,7 +842,6 @@ public class AccumulatorOperators { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List list && list.size() == 1) { @@ -866,6 +892,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link CovariancePop}. */ + @Contract("_ -> new") public CovariancePop and(String fieldReference) { return new CovariancePop(append(asFields(fieldReference))); } @@ -876,6 +903,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link CovariancePop}. */ + @Contract("_ -> new") public CovariancePop and(AggregationExpression expression) { return new CovariancePop(append(expression)); } @@ -926,6 +954,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link CovarianceSamp}. */ + @Contract("_ -> new") public CovarianceSamp and(String fieldReference) { return new CovarianceSamp(append(asFields(fieldReference))); } @@ -936,6 +965,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link CovarianceSamp}. */ + @Contract("_ -> new") public CovarianceSamp and(AggregationExpression expression) { return new CovarianceSamp(append(expression)); } @@ -986,6 +1016,7 @@ public class AccumulatorOperators { * @param numberOfHistoricalDocuments * @return new instance of {@link ExpMovingAvg}. */ + @Contract("_ -> new") public ExpMovingAvg n/*umber of historical documents*/(int numberOfHistoricalDocuments) { return new ExpMovingAvg(append("N", numberOfHistoricalDocuments)); } @@ -997,6 +1028,7 @@ public class AccumulatorOperators { * @param exponentialDecayValue * @return new instance of {@link ExpMovingAvg}. */ + @Contract("_ -> new") public ExpMovingAvg alpha(double exponentialDecayValue) { return new ExpMovingAvg(append("alpha", exponentialDecayValue)); } @@ -1055,6 +1087,7 @@ public class AccumulatorOperators { * @param percentages must not be {@literal null}. * @return new instance of {@link Percentile}. */ + @Contract("_ -> new") public Percentile percentages(Double... percentages) { Assert.notEmpty(percentages, "Percentages must not be null or empty"); @@ -1068,6 +1101,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Percentile}. */ + @Contract("_ -> new") public Percentile and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1081,6 +1115,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Percentile}. */ + @Contract("_ -> new") public Percentile and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1142,6 +1177,7 @@ public class AccumulatorOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Median}. */ + @Contract("_ -> new") public Median and(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1155,6 +1191,7 @@ public class AccumulatorOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Median}. */ + @Contract("_ -> new") public Median and(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java index b79d978b8..3cb75d305 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java @@ -19,8 +19,9 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder.ValueAppender; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * Adds new fields to documents. {@code $addFields} outputs documents that contain all existing fields from the input @@ -83,6 +84,7 @@ public class AddFieldsOperation extends DocumentEnhancingOperation { * @param value the value to assign. * @return new instance of {@link AddFieldsOperation}. */ + @Contract("_ -> new") public AddFieldsOperation addField(Object field, Object value) { LinkedHashMap target = new LinkedHashMap<>(getValueMap()); @@ -96,6 +98,7 @@ public class AddFieldsOperation extends DocumentEnhancingOperation { * * @return new instance of {@link AddFieldsOperationBuilder}. */ + @Contract("-> new") public AddFieldsOperationBuilder and() { return new AddFieldsOperationBuilder(getValueMap()); } @@ -140,7 +143,7 @@ public class AddFieldsOperation extends DocumentEnhancingOperation { return new ValueAppender() { @Override - public AddFieldsOperationBuilder withValue(Object value) { + public AddFieldsOperationBuilder withValue(@Nullable Object value) { valueMap.put(field, value); return AddFieldsOperationBuilder.this; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java index 00db38329..e33c565d1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressionTransformer.java @@ -16,12 +16,12 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AggregationExpressionTransformer.AggregationExpressionTransformationContext; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; import org.springframework.data.mongodb.core.spel.ExpressionNode; import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport; import org.springframework.data.mongodb.core.spel.ExpressionTransformer; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java index a49c7e46d..502732846 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java @@ -21,10 +21,10 @@ import java.util.Arrays; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java index fd5f7ed97..6437ec981 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationRenderer.java @@ -19,12 +19,12 @@ import java.util.ArrayList; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField; import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; -import org.springframework.lang.Nullable; /** * Rendering support for {@link AggregationOperation} into a {@link List} of {@link org.bson.Document}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java index 327d40b8c..278da408c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOptions.java @@ -19,11 +19,12 @@ import java.time.Duration; import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.ReadConcernAware; import org.springframework.data.mongodb.core.ReadPreferenceAware; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.ReadConcern; @@ -299,7 +300,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return readConcern.orElse(null); } @@ -309,7 +310,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return readPreference.orElse(null); } @@ -426,7 +427,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware */ public static class Builder { - private Boolean allowDiskUse; + private @Nullable Boolean allowDiskUse; private boolean explain; private @Nullable Document cursor; private @Nullable Collation collation; @@ -444,6 +445,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @param allowDiskUse use {@literal true} to allow disk use during the aggregation. * @return this. */ + @Contract("_ -> this") public Builder allowDiskUse(boolean allowDiskUse) { this.allowDiskUse = allowDiskUse; @@ -456,6 +458,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @param explain use {@literal true} to enable explain feature. * @return this. */ + @Contract("_ -> this") public Builder explain(boolean explain) { this.explain = explain; @@ -468,6 +471,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @param cursor must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Builder cursor(Document cursor) { this.cursor = cursor; @@ -481,6 +485,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 2.0 */ + @Contract("_ -> this") public Builder cursorBatchSize(int batchSize) { this.cursor = createCursor(batchSize); @@ -494,6 +499,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 2.0 */ + @Contract("_ -> this") public Builder collation(@Nullable Collation collation) { this.collation = collation; @@ -507,6 +513,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 2.2 */ + @Contract("_ -> this") public Builder comment(@Nullable String comment) { this.comment = comment; @@ -520,6 +527,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.1 */ + @Contract("_ -> this") public Builder hint(@Nullable Document hint) { this.hint = hint; @@ -533,6 +541,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder hint(@Nullable String indexName) { this.hint = indexName; @@ -546,6 +555,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder readConcern(@Nullable ReadConcern readConcern) { this.readConcern = readConcern; @@ -559,6 +569,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder readPreference(@Nullable ReadPreference readPreference) { this.readPreference = readPreference; @@ -573,6 +584,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.0 */ + @Contract("_ -> this") public Builder maxTime(@Nullable Duration maxTime) { this.maxTime = maxTime; @@ -587,6 +599,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.0.2 */ + @Contract("-> this") public Builder skipOutput() { this.resultOptions = ResultOptions.SKIP; @@ -600,6 +613,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.2 */ + @Contract("-> this") public Builder strictMapping() { this.domainTypeMapping = DomainTypeMapping.STRICT; @@ -613,6 +627,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.2 */ + @Contract("-> this") public Builder relaxedMapping() { this.domainTypeMapping = DomainTypeMapping.RELAXED; @@ -625,6 +640,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * @return this. * @since 3.2 */ + @Contract("-> this") public Builder noMapping() { this.domainTypeMapping = DomainTypeMapping.NONE; @@ -636,6 +652,7 @@ public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware * * @return new instance of {@link AggregationOptions}. */ + @Contract("-> new") public AggregationOptions build() { AggregationOptions options = new AggregationOptions(allowDiskUse, explain, cursor, collation, comment, hint); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java index 68662ec0d..40966bcf3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationPipeline.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.function.Predicate; import org.bson.Document; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -63,6 +64,7 @@ public class AggregationPipeline { * @param aggregationOperation must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public AggregationPipeline add(AggregationOperation aggregationOperation) { Assert.notNull(aggregationOperation, "AggregationOperation must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java index 438eb9e49..f5a861cdd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationResults.java @@ -20,7 +20,7 @@ import java.util.Iterator; import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** @@ -71,8 +71,7 @@ public class AggregationResults implements Iterable { * @return the single already mapped result object or raise an error if more than one found. * @throws IllegalArgumentException in case more than one result is available. */ - @Nullable - public T getUniqueMappedResult() { + public @Nullable T getUniqueMappedResult() { Assert.isTrue(mappedResults.size() < 2, "Expected unique result or null, but got more than one"); return mappedResults.size() == 1 ? mappedResults.get(0) : null; } @@ -101,8 +100,7 @@ public class AggregationResults implements Iterable { return rawResults; } - @Nullable - private String parseServerUsed() { + private @Nullable String parseServerUsed() { Object object = rawResults.get("serverUsed"); return object instanceof String stringValue ? stringValue : null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java index 1626d672b..c5b53ef0c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java @@ -66,6 +66,8 @@ public class AggregationSpELExpression implements AggregationExpression { @Override public Document toDocument(AggregationOperationContext context) { - return (Document) TRANSFORMER.transform(rawExpression, context, parameters); + + Document doc = (Document) TRANSFORMER.transform(rawExpression, context, parameters); + return doc != null ? doc : new Document(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java index 15d700309..9e8564c03 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationUpdate.java @@ -25,11 +25,11 @@ import java.util.StringJoiner; import java.util.stream.Collectors; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.SerializationUtils; import org.springframework.data.mongodb.core.query.UpdateDefinition; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -129,6 +129,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @return this. * @see $set Aggregation Reference */ + @Contract("_ -> this") public AggregationUpdate set(SetOperation setOperation) { Assert.notNull(setOperation, "SetOperation must not be null"); @@ -148,6 +149,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @see $unset Aggregation * Reference */ + @Contract("_ -> this") public AggregationUpdate unset(UnsetOperation unsetOperation) { Assert.notNull(unsetOperation, "UnsetOperation must not be null"); @@ -166,6 +168,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @see $replaceWith Aggregation * Reference */ + @Contract("_ -> this") public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) { Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null"); @@ -179,6 +182,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @param value must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public AggregationUpdate replaceWith(Object value) { Assert.notNull(value, "Value must not be null"); @@ -193,6 +197,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @return new instance of {@link SetValueAppender}. * @see #set(SetOperation) */ + @Contract("_ -> new") public SetValueAppender set(String key) { Assert.notNull(key, "Key must not be null"); @@ -219,6 +224,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * @param keys the fields to remove. * @return this. */ + @Contract("_ -> this") public AggregationUpdate unset(String... keys) { Assert.notNull(keys, "Keys must not be null"); @@ -234,6 +240,7 @@ public class AggregationUpdate extends Aggregation implements UpdateDefinition { * * @return never {@literal null}. */ + @Contract("-> this") public AggregationUpdate isolated() { isolated = true; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java index ed7920234..522dd5eae 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationVariable.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.aggregation; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java index e2c31c634..c7787b382 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Locale; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Avg; import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovariancePop; import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovarianceSamp; @@ -32,7 +33,7 @@ import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.St import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; import org.springframework.data.mongodb.core.aggregation.SetWindowFieldsOperation.WindowUnit; import org.springframework.data.mongodb.core.aggregation.SetWindowFieldsOperation.WindowUnits; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -84,8 +85,8 @@ public class ArithmeticOperators { */ public static class ArithmeticOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}. @@ -116,6 +117,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Abs}. */ + @SuppressWarnings("NullAway") public Abs abs() { return usesFieldRef() ? Abs.absoluteValueOf(fieldReference) : Abs.absoluteValueOf(expression); } @@ -158,6 +160,7 @@ public class ArithmeticOperators { return createAdd().add(value); } + @SuppressWarnings("NullAway") private Add createAdd() { return usesFieldRef() ? Add.valueOf(fieldReference) : Add.valueOf(expression); } @@ -168,6 +171,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Ceil}. */ + @SuppressWarnings("NullAway") public Ceil ceil() { return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression); } @@ -205,6 +209,7 @@ public class ArithmeticOperators { * @return new instance of {@link Derivative}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Derivative derivative(@Nullable String unit) { Derivative derivative = usesFieldRef() ? Derivative.derivativeOf(fieldReference) @@ -250,6 +255,7 @@ public class ArithmeticOperators { return createDivide().divideBy(value); } + @SuppressWarnings("NullAway") private Divide createDivide() { return usesFieldRef() ? Divide.valueOf(fieldReference) : Divide.valueOf(expression); } @@ -259,6 +265,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Exp}. */ + @SuppressWarnings("NullAway") public Exp exp() { return usesFieldRef() ? Exp.expValueOf(fieldReference) : Exp.expValueOf(expression); } @@ -269,6 +276,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Floor}. */ + @SuppressWarnings("NullAway") public Floor floor() { return usesFieldRef() ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression); } @@ -279,6 +287,7 @@ public class ArithmeticOperators { * @return new instance of {@link Integral}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Integral integral() { return usesFieldRef() ? Integral.integralOf(fieldReference) : Integral.integralOf(expression); } @@ -318,6 +327,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Ln}. */ + @SuppressWarnings("NullAway") public Ln ln() { return usesFieldRef() ? Ln.lnValueOf(fieldReference) : Ln.lnValueOf(expression); } @@ -345,7 +355,7 @@ public class ArithmeticOperators { public Log log(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); - return createLog().log(fieldReference); + return createLog().log(expression); } /** @@ -361,6 +371,7 @@ public class ArithmeticOperators { return createLog().log(base); } + @SuppressWarnings("NullAway") private Log createLog() { return usesFieldRef() ? Log.valueOf(fieldReference) : Log.valueOf(expression); } @@ -370,6 +381,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Log10}. */ + @SuppressWarnings("NullAway") public Log10 log10() { return usesFieldRef() ? Log10.log10ValueOf(fieldReference) : Log10.log10ValueOf(expression); } @@ -413,6 +425,7 @@ public class ArithmeticOperators { return createMod().mod(value); } + @SuppressWarnings("NullAway") private Mod createMod() { return usesFieldRef() ? Mod.valueOf(fieldReference) : Mod.valueOf(expression); } @@ -453,6 +466,7 @@ public class ArithmeticOperators { return createMultiply().multiplyBy(value); } + @SuppressWarnings("NullAway") private Multiply createMultiply() { return usesFieldRef() ? Multiply.valueOf(fieldReference) : Multiply.valueOf(expression); } @@ -493,6 +507,7 @@ public class ArithmeticOperators { return createPow().pow(value); } + @SuppressWarnings("NullAway") private Pow createPow() { return usesFieldRef() ? Pow.valueOf(fieldReference) : Pow.valueOf(expression); } @@ -502,6 +517,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Sqrt}. */ + @SuppressWarnings("NullAway") public Sqrt sqrt() { return usesFieldRef() ? Sqrt.sqrtOf(fieldReference) : Sqrt.sqrtOf(expression); } @@ -542,6 +558,7 @@ public class ArithmeticOperators { return createSubtract().subtract(value); } + @SuppressWarnings("NullAway") private Subtract createSubtract() { return usesFieldRef() ? Subtract.valueOf(fieldReference) : Subtract.valueOf(expression); } @@ -551,6 +568,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Trunc}. */ + @SuppressWarnings("NullAway") public Trunc trunc() { return usesFieldRef() ? Trunc.truncValueOf(fieldReference) : Trunc.truncValueOf(expression); } @@ -560,6 +578,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Sum}. */ + @SuppressWarnings("NullAway") public Sum sum() { return usesFieldRef() ? AccumulatorOperators.Sum.sumOf(fieldReference) : AccumulatorOperators.Sum.sumOf(expression); @@ -570,6 +589,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Avg}. */ + @SuppressWarnings("NullAway") public Avg avg() { return usesFieldRef() ? AccumulatorOperators.Avg.avgOf(fieldReference) : AccumulatorOperators.Avg.avgOf(expression); @@ -580,6 +600,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Max}. */ + @SuppressWarnings("NullAway") public Max max() { return usesFieldRef() ? AccumulatorOperators.Max.maxOf(fieldReference) : AccumulatorOperators.Max.maxOf(expression); @@ -590,6 +611,7 @@ public class ArithmeticOperators { * * @return new instance of {@link Min}. */ + @SuppressWarnings("NullAway") public Min min() { return usesFieldRef() ? AccumulatorOperators.Min.minOf(fieldReference) : AccumulatorOperators.Min.minOf(expression); @@ -600,6 +622,7 @@ public class ArithmeticOperators { * * @return new instance of {@link StdDevPop}. */ + @SuppressWarnings("NullAway") public StdDevPop stdDevPop() { return usesFieldRef() ? AccumulatorOperators.StdDevPop.stdDevPopOf(fieldReference) : AccumulatorOperators.StdDevPop.stdDevPopOf(expression); @@ -610,6 +633,7 @@ public class ArithmeticOperators { * * @return new instance of {@link StdDevSamp}. */ + @SuppressWarnings("NullAway") public StdDevSamp stdDevSamp() { return usesFieldRef() ? AccumulatorOperators.StdDevSamp.stdDevSampOf(fieldReference) : AccumulatorOperators.StdDevSamp.stdDevSampOf(expression); @@ -639,6 +663,7 @@ public class ArithmeticOperators { return covariancePop().and(expression); } + @SuppressWarnings("NullAway") private CovariancePop covariancePop() { return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression); } @@ -667,6 +692,7 @@ public class ArithmeticOperators { return covarianceSamp().and(expression); } + @SuppressWarnings("NullAway") private CovarianceSamp covarianceSamp() { return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference) : CovarianceSamp.covarianceSampOf(expression); @@ -679,6 +705,7 @@ public class ArithmeticOperators { * @return new instance of {@link Round}. * @since 3.0 */ + @SuppressWarnings("NullAway") public Round round() { return usesFieldRef() ? Round.roundValueOf(fieldReference) : Round.roundValueOf(expression); } @@ -712,6 +739,7 @@ public class ArithmeticOperators { * @return new instance of {@link Sin}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Sin sin(AngularUnit unit) { return usesFieldRef() ? Sin.sinOf(fieldReference, unit) : Sin.sinOf(expression, unit); } @@ -734,6 +762,7 @@ public class ArithmeticOperators { * @return new instance of {@link Sinh}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Sinh sinh(AngularUnit unit) { return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit); } @@ -744,6 +773,7 @@ public class ArithmeticOperators { * @return new instance of {@link ASin}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ASin asin() { return usesFieldRef() ? ASin.asinOf(fieldReference) : ASin.asinOf(expression); } @@ -754,6 +784,7 @@ public class ArithmeticOperators { * @return new instance of {@link ASinh}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ASinh asinh() { return usesFieldRef() ? ASinh.asinhOf(fieldReference) : ASinh.asinhOf(expression); } @@ -777,6 +808,7 @@ public class ArithmeticOperators { * @return new instance of {@link Cos}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Cos cos(AngularUnit unit) { return usesFieldRef() ? Cos.cosOf(fieldReference, unit) : Cos.cosOf(expression, unit); } @@ -799,6 +831,7 @@ public class ArithmeticOperators { * @return new instance of {@link Cosh}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Cosh cosh(AngularUnit unit) { return usesFieldRef() ? Cosh.coshOf(fieldReference, unit) : Cosh.coshOf(expression, unit); } @@ -809,6 +842,7 @@ public class ArithmeticOperators { * @return new instance of {@link ACos}. * @since 3.4 */ + @SuppressWarnings("NullAway") public ACos acos() { return usesFieldRef() ? ACos.acosOf(fieldReference) : ACos.acosOf(expression); } @@ -819,6 +853,7 @@ public class ArithmeticOperators { * @return new instance of {@link ACosh}. * @since 3.4 */ + @SuppressWarnings("NullAway") public ACosh acosh() { return usesFieldRef() ? ACosh.acoshOf(fieldReference) : ACosh.acoshOf(expression); } @@ -840,6 +875,7 @@ public class ArithmeticOperators { * @return new instance of {@link ATan}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ATan atan() { return usesFieldRef() ? ATan.atanOf(fieldReference) : ATan.atanOf(expression); } @@ -852,6 +888,7 @@ public class ArithmeticOperators { * @return new instance of {@link ATan2}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ATan2 atan2(Number value) { Assert.notNull(value, "Value must not be null"); @@ -886,8 +923,8 @@ public class ArithmeticOperators { return createATan2().atan2of(expression); } + @SuppressWarnings("NullAway") private ATan2 createATan2() { - return usesFieldRef() ? ATan2.valueOf(fieldReference) : ATan2.valueOf(expression); } @@ -897,6 +934,7 @@ public class ArithmeticOperators { * @return new instance of {@link ATanh}. * @since 3.3 */ + @SuppressWarnings("NullAway") public ATanh atanh() { return usesFieldRef() ? ATanh.atanhOf(fieldReference) : ATanh.atanhOf(expression); } @@ -909,6 +947,7 @@ public class ArithmeticOperators { * @return new instance of {@link Tan}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Tan tan(AngularUnit unit) { return usesFieldRef() ? Tan.tanOf(fieldReference, unit) : Tan.tanOf(expression, unit); } @@ -931,18 +970,19 @@ public class ArithmeticOperators { * @return new instance of {@link Tanh}. * @since 3.3 */ + @SuppressWarnings("NullAway") public Tanh tanh(AngularUnit unit) { return usesFieldRef() ? Tanh.tanhOf(fieldReference, unit) : Tanh.tanhOf(expression, unit); } /** - * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the - * numeric value. + * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the numeric value. * * @return new instance of {@link Percentile}. * @param percentages must not be {@literal null}. * @since 4.2 */ + @SuppressWarnings("NullAway") public Percentile percentile(Double... percentages) { Percentile percentile = usesFieldRef() ? AccumulatorOperators.Percentile.percentileOf(fieldReference) : AccumulatorOperators.Percentile.percentileOf(expression); @@ -950,12 +990,12 @@ public class ArithmeticOperators { } /** - * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the - * numeric value. + * Creates new {@link AggregationExpression} that calculates the requested percentile(s) of the numeric value. * * @return new instance of {@link Median}. * @since 4.2 */ + @SuppressWarnings("NullAway") public Median median() { return usesFieldRef() ? AccumulatorOperators.Median.medianOf(fieldReference) : AccumulatorOperators.Median.medianOf(expression); @@ -1077,6 +1117,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Add}. */ + @Contract("_ -> new") public Add add(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1089,6 +1130,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Add}. */ + @Contract("_ -> new") public Add add(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1101,6 +1143,7 @@ public class ArithmeticOperators { * @param value must not be {@literal null}. * @return new instance of {@link Add}. */ + @Contract("_ -> new") public Add add(Number value) { return new Add(append(value)); } @@ -1217,6 +1260,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Divide}. */ + @Contract("_ -> new") public Divide divideBy(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1229,6 +1273,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Divide}. */ + @Contract("_ -> new") public Divide divideBy(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1241,6 +1286,7 @@ public class ArithmeticOperators { * @param value must not be {@literal null}. * @return new instance of {@link Divide}. */ + @Contract("_ -> new") public Divide divideBy(Number value) { return new Divide(append(value)); } @@ -1463,6 +1509,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Log}. */ + @Contract("_ -> new") public Log log(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1475,6 +1522,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Log}. */ + @Contract("_ -> new") public Log log(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1487,6 +1535,7 @@ public class ArithmeticOperators { * @param base must not be {@literal null}. * @return new instance of {@link Log}. */ + @Contract("_ -> new") public Log log(Number base) { return new Log(append(base)); } @@ -1603,6 +1652,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Mod}. */ + @Contract("_ -> new") public Mod mod(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1615,6 +1665,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Mod}. */ + @Contract("_ -> new") public Mod mod(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1627,6 +1678,7 @@ public class ArithmeticOperators { * @param base must not be {@literal null}. * @return new instance of {@link Mod}. */ + @Contract("_ -> new") public Mod mod(Number base) { return new Mod(append(base)); } @@ -1690,6 +1742,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Multiply}. */ + @Contract("_ -> new") public Multiply multiplyBy(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1702,6 +1755,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Multiply}. */ + @Contract("_ -> new") public Multiply multiplyBy(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1714,6 +1768,7 @@ public class ArithmeticOperators { * @param value must not be {@literal null}. * @return new instance of {@link Multiply}. */ + @Contract("_ -> new") public Multiply multiplyBy(Number value) { return new Multiply(append(value)); } @@ -1777,6 +1832,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Pow pow(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1789,6 +1845,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Pow pow(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1801,6 +1858,7 @@ public class ArithmeticOperators { * @param value must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Pow pow(Number value) { return new Pow(append(value)); } @@ -1917,6 +1975,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Subtract subtract(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1929,6 +1988,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Subtract subtract(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1941,6 +2001,7 @@ public class ArithmeticOperators { * @param value must not be {@literal null}. * @return new instance of {@link Pow}. */ + @Contract("_ -> new") public Subtract subtract(Number value) { return new Subtract(append(value)); } @@ -2060,6 +2121,7 @@ public class ArithmeticOperators { * @param place value between -20 and 100, exclusive. * @return new instance of {@link Round}. */ + @Contract("_ -> new") public Round place(int place) { return new Round(append(place)); } @@ -2070,6 +2132,7 @@ public class ArithmeticOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Round}. */ + @Contract("_ -> new") public Round placeOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2083,6 +2146,7 @@ public class ArithmeticOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Round}. */ + @Contract("_ -> new") public Round placeOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -2133,6 +2197,7 @@ public class ArithmeticOperators { return new Derivative(Collections.singletonMap("input", value)); } + @Contract("_ -> new") public Derivative unit(String unit) { return new Derivative(append("unit", unit)); } @@ -2183,6 +2248,7 @@ public class ArithmeticOperators { * @param unit the unit of measure. * @return new instance of {@link Integral}. */ + @Contract("_ -> new") public Integral unit(String unit) { return new Integral(append("unit", unit)); } @@ -2217,8 +2283,7 @@ public class ArithmeticOperators { /** * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - *
+ * {@link AngularUnit#RADIANS radians}.
* Use {@code sinhOf("angle", DEGREES)} as shortcut for * *
@@ -2330,8 +2395,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * 
+ * the given {@link AngularUnit unit}.
* Use {@code sinhOf("angle", DEGREES)} as shortcut for * *
@@ -2350,8 +2414,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * 
+ * {@link AngularUnit#RADIANS}.
* Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -2434,8 +2497,7 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value. - *
+ * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value.
* * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ASin}. @@ -2484,8 +2546,7 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value. - *
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
* * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ASinh}. @@ -2525,8 +2586,7 @@ public class ArithmeticOperators { /** * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - *
+ * {@link AngularUnit#RADIANS radians}.
* Use {@code cosOf("angle", DEGREES)} as shortcut for * *
@@ -2636,8 +2696,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * 
+ * the given {@link AngularUnit unit}.
* Use {@code coshOf("angle", DEGREES)} as shortcut for * *
@@ -2654,8 +2713,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * 
+ * {@link AngularUnit#RADIANS}.
* Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -2738,8 +2796,7 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value. - *
+ * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value.
* * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ACos}. @@ -2788,8 +2845,7 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value. - *
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value.
* * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ACosh}. @@ -2829,8 +2885,7 @@ public class ArithmeticOperators { /** * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - *
+ * {@link AngularUnit#RADIANS radians}.
* Use {@code tanOf("angle", DEGREES)} as shortcut for * *
@@ -3008,8 +3063,8 @@ public class ArithmeticOperators {
 		 * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
 		 * the first and second values passed to the expression respectively.
 		 *
-		 * @param fieldReference anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
-		 *          numeric value.
+		 * @param fieldReference anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves
+		 *          to a numeric value.
 		 * @return new instance of {@link ATan2}.
 		 */
 		public ATan2 atan2of(String fieldReference) {
@@ -3022,8 +3077,8 @@ public class ArithmeticOperators {
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
 		 * {@link AngularUnit#RADIANS}.
 		 *
-		 * @param expression anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
-		 *          numeric value.
+		 * @param expression anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to
+		 *          a numeric value.
 		 * @return new instance of {@link ATan2}.
 		 */
 		public ATan2 atan2of(AggregationExpression expression) {
@@ -3075,8 +3130,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
-		 * the given {@link AngularUnit unit}.
-		 * 
+ * the given {@link AngularUnit unit}.
* Use {@code tanhOf("angle", DEGREES)} as shortcut for * *
@@ -3093,8 +3147,7 @@ public class ArithmeticOperators {
 
 		/**
 		 * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
-		 * {@link AngularUnit#RADIANS}.
-		 * 
+ * {@link AngularUnit#RADIANS}.
* Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -3165,11 +3218,9 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse - * hyperbolic tangent of a value. + * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. * - * @param fieldReference the name of the {@link Field field} that resolves to a - * numeric value. + * @param fieldReference the name of the {@link Field field} that resolves to a numeric value. * @return new instance of {@link ATanh}. */ public static ATanh atanhOf(String fieldReference) { @@ -3177,8 +3228,7 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. - *
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value.
* * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ATanh}. @@ -3188,11 +3238,10 @@ public class ArithmeticOperators { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse - * hyperbolic tangent of a value. + * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. * - * @param value anything ({@link Field field}, {@link AggregationExpression - * expression}, ...) that resolves to a numeric value. + * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a + * numeric value. * @return new instance of {@link ATanh}. */ public static ATanh atanhOf(Object value) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java index a8cb58d17..02b805d5e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java @@ -22,13 +22,14 @@ import java.util.Collections; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -159,6 +160,7 @@ public class ArrayOperators { return createArrayElemAt().elementAt(fieldReference); } + @SuppressWarnings("NullAway") private ArrayElemAt createArrayElemAt() { if (usesFieldRef()) { @@ -194,6 +196,7 @@ public class ArrayOperators { return createConcatArrays().concat(expression); } + @SuppressWarnings("NullAway") private ConcatArrays createConcatArrays() { if (usesFieldRef()) { @@ -209,6 +212,7 @@ public class ArrayOperators { * * @return new instance of {@link AsBuilder} to create a {@link Filter}. */ + @SuppressWarnings("NullAway") public AsBuilder filter() { if (usesFieldRef()) { @@ -228,6 +232,7 @@ public class ArrayOperators { * * @return new instance of {@link IsArray}. */ + @SuppressWarnings("NullAway") public IsArray isArray() { Assert.state(values == null, "Does it make sense to call isArray on an array; Maybe just skip it"); @@ -240,6 +245,7 @@ public class ArrayOperators { * * @return new instance of {@link Size}. */ + @SuppressWarnings("NullAway") public Size length() { if (usesFieldRef()) { @@ -254,6 +260,7 @@ public class ArrayOperators { * * @return new instance of {@link Slice}. */ + @SuppressWarnings("NullAway") public Slice slice() { if (usesFieldRef()) { @@ -270,6 +277,7 @@ public class ArrayOperators { * @param value must not be {@literal null}. * @return new instance of {@link IndexOfArray}. */ + @SuppressWarnings("NullAway") public IndexOfArray indexOf(Object value) { if (usesFieldRef()) { @@ -285,6 +293,7 @@ public class ArrayOperators { * * @return new instance of {@link ReverseArray}. */ + @SuppressWarnings("NullAway") public ReverseArray reverse() { if (usesFieldRef()) { @@ -302,6 +311,7 @@ public class ArrayOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}. */ + @SuppressWarnings("NullAway") public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(AggregationExpression expression) { return initialValue -> (usesFieldRef() ? Reduce.arrayOf(fieldReference) @@ -315,6 +325,7 @@ public class ArrayOperators { * @param expressions must not be {@literal null}. * @return new instance of {@link ReduceInitialValueBuilder} to create {@link Reduce}. */ + @SuppressWarnings("NullAway") public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression... expressions) { return initialValue -> (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression)) @@ -328,6 +339,7 @@ public class ArrayOperators { * @return new instance of {@link SortArray}. * @since 4.0 */ + @SuppressWarnings("NullAway") public SortArray sort(Sort sort) { if (usesFieldRef()) { @@ -361,6 +373,7 @@ public class ArrayOperators { * @param arrays must not be {@literal null}. * @return new instance of {@link Zip}. */ + @SuppressWarnings("NullAway") public Zip zipWith(Object... arrays) { if (usesFieldRef()) { @@ -377,6 +390,7 @@ public class ArrayOperators { * @param value must not be {@literal null}. * @return new instance of {@link In}. */ + @SuppressWarnings("NullAway") public In containsValue(Object value) { if (usesFieldRef()) { @@ -393,6 +407,7 @@ public class ArrayOperators { * @return new instance of {@link ArrayToObject}. * @since 2.1 */ + @SuppressWarnings("NullAway") public ArrayToObject toObject() { if (usesFieldRef()) { @@ -409,6 +424,7 @@ public class ArrayOperators { * @return new instance of {@link First}. * @since 3.4 */ + @SuppressWarnings("NullAway") public First first() { if (usesFieldRef()) { @@ -425,6 +441,7 @@ public class ArrayOperators { * @return new instance of {@link Last}. * @since 3.4 */ + @SuppressWarnings("NullAway") public Last last() { if (usesFieldRef()) { @@ -523,6 +540,7 @@ public class ArrayOperators { * @param index the index number * @return new instance of {@link ArrayElemAt}. */ + @Contract("_ -> new") public ArrayElemAt elementAt(int index) { return new ArrayElemAt(append(index)); } @@ -533,6 +551,7 @@ public class ArrayOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ArrayElemAt}. */ + @Contract("_ -> new") public ArrayElemAt elementAt(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -545,6 +564,7 @@ public class ArrayOperators { * @param arrayFieldReference the field name. * @return new instance of {@link ArrayElemAt}. */ + @Contract("_ -> new") public ArrayElemAt elementAt(String arrayFieldReference) { Assert.notNull(arrayFieldReference, "ArrayReference must not be null"); @@ -611,6 +631,7 @@ public class ArrayOperators { * @param arrayFieldReference must not be {@literal null}. * @return new instance of {@link ConcatArrays}. */ + @Contract("_ -> new") public ConcatArrays concat(String arrayFieldReference) { Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null"); @@ -623,6 +644,7 @@ public class ArrayOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ConcatArrays}. */ + @Contract("_ -> new") public ConcatArrays concat(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -698,9 +720,12 @@ public class ArrayOperators { @Override public Document toDocument(final AggregationOperationContext context) { + + Assert.notNull(as, "As must be set first"); return toFilter(ExposedFields.from(as), context); } + @SuppressWarnings("NullAway") private Document toFilter(ExposedFields exposedFields, AggregationOperationContext context) { Document filterExpression = new Document(); @@ -714,7 +739,7 @@ public class ArrayOperators { return new Document("$filter", filterExpression); } - private Object getMappedInput(AggregationOperationContext context) { + private @Nullable Object getMappedInput(AggregationOperationContext context) { if (input instanceof Field field) { return context.getReference(field).toString(); @@ -727,7 +752,7 @@ public class ArrayOperators { return input; } - private Object getMappedCondition(AggregationOperationContext context) { + private @Nullable Object getMappedCondition(AggregationOperationContext context) { if (!(condition instanceof AggregationExpression aggregationExpression)) { return condition; @@ -834,6 +859,7 @@ public class ArrayOperators { } @Override + @Contract("_ -> this") public AsBuilder filter(List array) { Assert.notNull(array, "Array must not be null"); @@ -842,6 +868,7 @@ public class ArrayOperators { } @Override + @Contract("_ -> this") public AsBuilder filter(Field field) { Assert.notNull(field, "Field must not be null"); @@ -850,6 +877,7 @@ public class ArrayOperators { } @Override + @Contract("_ -> this") public AsBuilder filter(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -858,6 +886,7 @@ public class ArrayOperators { } @Override + @Contract("_ -> this") public ConditionBuilder as(String variableName) { Assert.notNull(variableName, "Variable name must not be null"); @@ -1045,6 +1074,7 @@ public class ArrayOperators { * @param count number of elements to slice. * @return new instance of {@link Slice}. */ + @Contract("_ -> new") public Slice itemCount(int count) { return new Slice(append(count)); } @@ -1057,6 +1087,7 @@ public class ArrayOperators { * @return new instance of {@link Slice}. * @since 4.5 */ + @Contract("_ -> new") public Slice itemCount(AggregationExpression count) { return new Slice(append(count)); } @@ -1175,6 +1206,7 @@ public class ArrayOperators { * @param range the lookup range. * @return new instance of {@link IndexOfArray}. */ + @Contract("_ -> new") public IndexOfArray within(Range range) { return new IndexOfArray(append(AggregationUtils.toRangeValues(range))); } @@ -1250,6 +1282,7 @@ public class ArrayOperators { return new RangeOperatorBuilder(value); } + @Contract("_ -> new") public RangeOperator withStepSize(long stepSize) { return new RangeOperator(append(stepSize)); } @@ -1382,6 +1415,7 @@ public class ArrayOperators { return new Document("$reduce", document); } + @SuppressWarnings("NullAway") private Object getMappedValue(Object value, AggregationOperationContext context) { if (value instanceof Document) { @@ -1701,6 +1735,7 @@ public class ArrayOperators { * * @return new instance of {@link Zip}. */ + @Contract("-> new") public Zip useLongestLength() { return new Zip(append("useLongestLength", true)); } @@ -1711,6 +1746,7 @@ public class ArrayOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Zip}. */ + @Contract("_ -> new") public Zip defaultTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1723,6 +1759,7 @@ public class ArrayOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Zip}. */ + @Contract("_ -> new") public Zip defaultTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1735,6 +1772,7 @@ public class ArrayOperators { * @param array must not be {@literal null}. * @return new instance of {@link Zip}. */ + @Contract("_ -> new") public Zip defaultTo(Object[] array) { Assert.notNull(array, "Array must not be null"); @@ -2064,6 +2102,7 @@ public class ArrayOperators { * @param sort must not be {@literal null}. * @return new instance of {@link SortArray}. */ + @Contract("_ -> new") public SortArray by(Sort sort) { return new SortArray(append("sortBy", sort)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java index 69689908c..f3ffdb7ad 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java @@ -19,6 +19,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -77,8 +79,8 @@ public class BooleanOperators { */ public static class BooleanOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}. @@ -130,6 +132,7 @@ public class BooleanOperators { return createAnd().andField(fieldReference); } + @SuppressWarnings("NullAway") private And createAnd() { return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression); } @@ -160,6 +163,7 @@ public class BooleanOperators { return createOr().orField(fieldReference); } + @SuppressWarnings("NullAway") private Or createOr() { return usesFieldRef() ? Or.or(Fields.field(fieldReference)) : Or.or(expression); } @@ -169,6 +173,7 @@ public class BooleanOperators { * * @return new instance of {@link Not}. */ + @SuppressWarnings("NullAway") public Not not() { return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression); } @@ -211,6 +216,7 @@ public class BooleanOperators { * @param expression must not be {@literal null}. * @return new instance of {@link And}. */ + @Contract("_ -> new") public And andExpression(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -223,6 +229,7 @@ public class BooleanOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link And}. */ + @Contract("_ -> new") public And andField(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -235,6 +242,7 @@ public class BooleanOperators { * @param value must not be {@literal null}. * @return new instance of {@link And}. */ + @Contract("_ -> new") public And andValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -277,6 +285,7 @@ public class BooleanOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Or}. */ + @Contract("_ -> new") public Or orExpression(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -289,6 +298,7 @@ public class BooleanOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Or}. */ + @Contract("_ -> new") public Or orField(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -301,6 +311,7 @@ public class BooleanOperators { * @param value must not be {@literal null}. * @return new instance of {@link Or}. */ + @Contract("_ -> new") public Or orValue(Object value) { Assert.notNull(value, "Value must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java index 36492e2a8..16eca4ec2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketAutoOperation.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.BucketAutoOperationOutputBuilder; import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder; import org.springframework.util.Assert; @@ -38,7 +39,7 @@ public class BucketAutoOperation extends BucketOperationSupport boundaries; - private final Object defaultBucket; + private final @Nullable Object defaultBucket; /** * Creates a new {@link BucketOperation} given a {@link Field group-by field}. @@ -76,7 +78,7 @@ public class BucketOperation extends BucketOperationSupport boundaries, Object defaultBucket) { + private BucketOperation(BucketOperation bucketOperation, List boundaries, @Nullable Object defaultBucket) { super(bucketOperation); @@ -111,6 +113,7 @@ public class BucketOperation extends BucketOperationSupport new") public BucketOperation withDefaultBucket(Object literal) { Assert.notNull(literal, "Default bucket literal must not be null"); @@ -124,6 +127,7 @@ public class BucketOperation extends BucketOperationSupport new") public BucketOperation withBoundaries(Object... boundaries) { Assert.notNull(boundaries, "Boundaries must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java index e19ad59a3..3d5ded05c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperationSupport.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder; @@ -40,8 +41,8 @@ import org.springframework.util.Assert; public abstract class BucketOperationSupport, B extends OutputBuilder> implements FieldsExposingAggregationOperation { - private final Field groupByField; - private final AggregationExpression groupByExpression; + private final @Nullable Field groupByField; + private final @Nullable AggregationExpression groupByExpression; private final Outputs outputs; /** @@ -142,12 +143,17 @@ public abstract class BucketOperationSupport new") public Cmp compareTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -396,6 +406,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Cmp}. */ + @Contract("_ -> new") public Cmp compareTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -408,6 +419,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Cmp}. */ + @Contract("_ -> new") public Cmp compareToValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -461,6 +473,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Eq}. */ + @Contract("_ -> new") public Eq equalTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -473,6 +486,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Eq}. */ + @Contract("_ -> new") public Eq equalTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -485,6 +499,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Eq}. */ + @Contract("_ -> new") public Eq equalToValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -538,6 +553,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Gt}. */ + @Contract("_ -> new") public Gt greaterThan(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -550,6 +566,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Gt}. */ + @Contract("_ -> new") public Gt greaterThan(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -562,6 +579,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Gt}. */ + @Contract("_ -> new") public Gt greaterThanValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -615,6 +633,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Lt}. */ + @Contract("_ -> new") public Lt lessThan(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -627,6 +646,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Lt}. */ + @Contract("_ -> new") public Lt lessThan(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -639,6 +659,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Lt}. */ + @Contract("_ -> new") public Lt lessThanValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -692,6 +713,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Gte}. */ + @Contract("_ -> new") public Gte greaterThanEqualTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -704,6 +726,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Gte}. */ + @Contract("_ -> new") public Gte greaterThanEqualTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -716,6 +739,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Gte}. */ + @Contract("_ -> new") public Gte greaterThanEqualToValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -769,6 +793,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Lte}. */ + @Contract("_ -> new") public Lte lessThanEqualTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -781,6 +806,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Lte}. */ + @Contract("_ -> new") public Lte lessThanEqualTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -793,6 +819,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Lte}. */ + @Contract("_ -> new") public Lte lessThanEqualToValue(Object value) { Assert.notNull(value, "Value must not be null"); @@ -846,6 +873,7 @@ public class ComparisonOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Ne}. */ + @Contract("_ -> new") public Ne notEqualTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -858,6 +886,7 @@ public class ComparisonOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Ne}. */ + @Contract("_ -> new") public Ne notEqualTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -870,6 +899,7 @@ public class ComparisonOperators { * @param value must not be {@literal null}. * @return new instance of {@link Ne}. */ + @Contract("_ -> new") public Ne notEqualToValue(Object value) { Assert.notNull(value, "Value must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java index 323a11895..462d94d6f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java @@ -22,12 +22,13 @@ import java.util.Collections; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; import org.springframework.data.mongodb.core.query.CriteriaDefinition; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -211,6 +212,7 @@ public class ConditionalOperators { return createThenBuilder().thenValueOf(fieldReference); } + @SuppressWarnings("NullAway") private ThenBuilder createThenBuilder() { if (usesFieldRef()) { @@ -303,6 +305,7 @@ public class ConditionalOperators { } } + @SuppressWarnings("NullAway") private Object resolve(Object value, AggregationOperationContext context) { if (value instanceof Field field) { @@ -389,7 +392,7 @@ public class ConditionalOperators { */ static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder { - private @Nullable List conditions; + private List conditions; private IfNullOperatorBuilder() { conditions = new ArrayList<>(); @@ -404,6 +407,7 @@ public class ConditionalOperators { return new IfNullOperatorBuilder(); } + @Contract("_ -> this") public ThenBuilder ifNull(String fieldReference) { Assert.hasText(fieldReference, "FieldReference name must not be null or empty"); @@ -412,6 +416,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ThenBuilder ifNull(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression name must not be null or empty"); @@ -420,25 +425,30 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ThenBuilder orIfNull(String fieldReference) { return ifNull(fieldReference); } @Override + @Contract("_ -> this") public ThenBuilder orIfNull(AggregationExpression expression) { return ifNull(expression); } + @Contract("_ -> new") public IfNull then(Object value) { return new IfNull(conditions, value); } + @Contract("_ -> new") public IfNull thenValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); return new IfNull(conditions, Fields.field(fieldReference)); } + @Contract("_ -> new") public IfNull thenValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -491,6 +501,7 @@ public class ConditionalOperators { * @param value must not be {@literal null}. * @return new instance of {@link Switch}. */ + @Contract("_ -> new") public Switch defaultTo(Object value) { return new Switch(append("default", value)); } @@ -623,6 +634,7 @@ public class ConditionalOperators { return new Document("$cond", condObject); } + @SuppressWarnings("NullAway") private Object resolveValue(AggregationOperationContext context, Object value) { if (value instanceof Document || value instanceof Field) { @@ -886,6 +898,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ConditionalExpressionBuilder when(Document booleanExpression) { Assert.notNull(booleanExpression, "'Boolean expression' must not be null"); @@ -895,6 +908,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ThenBuilder when(CriteriaDefinition criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -903,6 +917,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ThenBuilder when(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression field must not be null"); @@ -911,6 +926,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public ThenBuilder when(String booleanField) { Assert.hasText(booleanField, "Boolean field name must not be null or empty"); @@ -919,6 +935,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public OtherwiseBuilder then(Object thenValue) { Assert.notNull(thenValue, "Then-value must not be null"); @@ -927,6 +944,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public OtherwiseBuilder thenValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -935,6 +953,7 @@ public class ConditionalOperators { } @Override + @Contract("_ -> this") public OtherwiseBuilder thenValueOf(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression must not be null"); @@ -943,23 +962,32 @@ public class ConditionalOperators { } @Override + @Contract("_ -> new") public Cond otherwise(Object otherwiseValue) { Assert.notNull(otherwiseValue, "Value must not be null"); + Assert.notNull(condition, "Condition value needs to be set first"); + Assert.notNull(thenValue, "Then value needs to be set first"); return new Cond(condition, thenValue, otherwiseValue); } @Override + @Contract("_ -> new") public Cond otherwiseValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); + Assert.notNull(condition, "Condition value needs to be set first"); + Assert.notNull(thenValue, "Then value needs to be set first"); return new Cond(condition, thenValue, Fields.field(fieldReference)); } @Override + @Contract("_ -> new") public Cond otherwiseValueOf(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression must not be null"); + Assert.notNull(condition, "Condition value needs to be set first"); + Assert.notNull(thenValue, "Then value needs to be set first"); return new Cond(condition, thenValue, expression); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java index aa085b2a2..35a6ad061 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java @@ -17,8 +17,8 @@ package org.springframework.data.mongodb.core.aggregation; import java.util.Collections; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -242,10 +242,12 @@ public class ConvertOperators { return DegreesToRadians.degreesToRadians(valueObject()); } + @SuppressWarnings("NullAway") private Convert createConvert() { return usesFieldRef() ? Convert.convertValueOf(fieldReference) : Convert.convertValueOf(expression); } + @SuppressWarnings("NullAway") private Object valueObject() { return usesFieldRef() ? Fields.field(fieldReference) : expression; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java index ff6ed7e98..7bf8a231f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java @@ -26,7 +26,8 @@ import java.util.Map; import java.util.TimeZone; import java.util.concurrent.TimeUnit; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -848,6 +849,7 @@ public class DateOperators { return TsSecond.tsSecond(dateReference()); } + @SuppressWarnings("NullAway") private Object dateReference() { if (usesFieldRef()) { @@ -1076,6 +1078,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfYear withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1148,6 +1151,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfMonth withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1220,6 +1224,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1292,6 +1297,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Year withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1364,6 +1370,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Month withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1436,6 +1443,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Week withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1508,6 +1516,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Hour withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1580,6 +1589,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Minute withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1652,6 +1662,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Second withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1724,6 +1735,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public Millisecond withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1810,6 +1822,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public DateToString withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1824,6 +1837,7 @@ public class DateOperators { * @return new instance of {@link DateToString}. * @since 2.1 */ + @Contract("_ -> new") public DateToString onNullReturn(Object value) { return new DateToString(append("onNull", value)); } @@ -1836,6 +1850,7 @@ public class DateOperators { * @return new instance of {@link DateToString}. * @since 2.1 */ + @Contract("_ -> new") public DateToString onNullReturnValueOf(String fieldReference) { return onNullReturn(Fields.field(fieldReference)); } @@ -1848,6 +1863,7 @@ public class DateOperators { * @return new instance of {@link DateToString}. * @since 2.1 */ + @Contract("_ -> new") public DateToString onNullReturnValueOf(AggregationExpression expression) { return onNullReturn(expression); } @@ -1973,6 +1989,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoDayOfWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2045,6 +2062,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2117,6 +2135,7 @@ public class DateOperators { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoWeekYear withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2301,6 +2320,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal month} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts month(Object month) { return new DateFromParts(append("month", month)); } @@ -2312,6 +2332,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts monthOf(String fieldReference) { return month(Fields.field(fieldReference)); } @@ -2323,6 +2344,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal expression} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts monthOf(AggregationExpression expression) { return month(expression); } @@ -2335,6 +2357,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal day} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts day(Object day) { return new DateFromParts(append("day", day)); } @@ -2346,6 +2369,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts dayOf(String fieldReference) { return day(Fields.field(fieldReference)); } @@ -2357,26 +2381,31 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal expression} is {@literal null}. */ + @Contract("_ -> new") public DateFromParts dayOf(AggregationExpression expression) { return day(expression); } @Override + @Contract("_ -> new") public DateFromParts hour(Object hour) { return new DateFromParts(append("hour", hour)); } @Override + @Contract("_ -> new") public DateFromParts minute(Object minute) { return new DateFromParts(append("minute", minute)); } @Override + @Contract("_ -> new") public DateFromParts second(Object second) { return new DateFromParts(append("second", second)); } @Override + @Contract("_ -> new") public DateFromParts millisecond(Object millisecond) { return new DateFromParts(append("millisecond", millisecond)); } @@ -2390,6 +2419,7 @@ public class DateOperators { * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}. */ @Override + @Contract("_ -> new") public DateFromParts withTimezone(Timezone timezone) { return new DateFromParts(appendTimezone(argumentMap(), timezone)); } @@ -2477,6 +2507,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal isoWeek} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoWeek(Object isoWeek) { return new IsoDateFromParts(append("isoWeek", isoWeek)); } @@ -2488,6 +2519,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoWeekOf(String fieldReference) { return isoWeek(Fields.field(fieldReference)); } @@ -2499,6 +2531,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal expression} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoWeekOf(AggregationExpression expression) { return isoWeek(expression); } @@ -2511,6 +2544,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal isoWeek} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoDayOfWeek(Object day) { return new IsoDateFromParts(append("isoDayOfWeek", day)); } @@ -2522,6 +2556,7 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoDayOfWeekOf(String fieldReference) { return isoDayOfWeek(Fields.field(fieldReference)); } @@ -2533,26 +2568,31 @@ public class DateOperators { * @return new instance. * @throws IllegalArgumentException if given {@literal expression} is {@literal null}. */ + @Contract("_ -> new") public IsoDateFromParts isoDayOfWeekOf(AggregationExpression expression) { return isoDayOfWeek(expression); } @Override + @Contract("_ -> new") public IsoDateFromParts hour(Object hour) { return new IsoDateFromParts(append("hour", hour)); } @Override + @Contract("_ -> new") public IsoDateFromParts minute(Object minute) { return new IsoDateFromParts(append("minute", minute)); } @Override + @Contract("_ -> new") public IsoDateFromParts second(Object second) { return new IsoDateFromParts(append("second", second)); } @Override + @Contract("_ -> new") public IsoDateFromParts millisecond(Object millisecond) { return new IsoDateFromParts(append("millisecond", millisecond)); } @@ -2566,6 +2606,7 @@ public class DateOperators { * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}. */ @Override + @Contract("_ -> new") public IsoDateFromParts withTimezone(Timezone timezone) { return new IsoDateFromParts(appendTimezone(argumentMap(), timezone)); } @@ -2676,6 +2717,7 @@ public class DateOperators { * * @return new instance of {@link DateToParts}. */ + @Contract("_ -> new") public DateToParts iso8601() { return new DateToParts(append("iso8601", true)); } @@ -2689,6 +2731,7 @@ public class DateOperators { * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}. */ @Override + @Contract("_ -> new") public DateToParts withTimezone(Timezone timezone) { return new DateToParts(appendTimezone(argumentMap(), timezone)); } @@ -2733,6 +2776,7 @@ public class DateOperators { * @return new instance of {@link DateFromString}. * @throws IllegalArgumentException if given {@literal fieldReference} is {@literal null}. */ + @Contract("_ -> new") public static DateFromString fromStringOf(String fieldReference) { return fromString(Fields.field(fieldReference)); } @@ -2744,6 +2788,7 @@ public class DateOperators { * @return new instance of {@link DateFromString}. * @throws IllegalArgumentException if given {@literal expression} is {@literal null}. */ + @Contract("_ -> new") public static DateFromString fromStringOf(AggregationExpression expression) { return fromString(expression); } @@ -2757,6 +2802,7 @@ public class DateOperators { * @throws IllegalArgumentException if given {@literal timezone} is {@literal null}. */ @Override + @Contract("_ -> new") public DateFromString withTimezone(Timezone timezone) { return new DateFromString(appendTimezone(argumentMap(), timezone)); } @@ -2769,6 +2815,7 @@ public class DateOperators { * @return new instance of {@link DateFromString}. * @throws IllegalArgumentException if given {@literal format} is {@literal null}. */ + @Contract("_ -> new") public DateFromString withFormat(String format) { Assert.notNull(format, "Format must not be null"); @@ -2838,6 +2885,7 @@ public class DateOperators { * @param expression must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateAdd toDateOf(AggregationExpression expression) { return toDate(expression); } @@ -2848,6 +2896,7 @@ public class DateOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateAdd toDateOf(String fieldReference) { return toDate(Fields.field(fieldReference)); } @@ -2858,6 +2907,7 @@ public class DateOperators { * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateAdd toDate(Object dateExpression) { return new DateAdd(append("startDate", dateExpression)); } @@ -2868,6 +2918,7 @@ public class DateOperators { * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateAdd withTimezone(Timezone timezone) { return new DateAdd(appendTimezone(argumentMap(), timezone)); } @@ -2935,6 +2986,7 @@ public class DateOperators { * @param expression must not be {@literal null}. * @return new instance of {@link DateSubtract}. */ + @Contract("_ -> new") public DateSubtract fromDateOf(AggregationExpression expression) { return fromDate(expression); } @@ -2945,6 +2997,7 @@ public class DateOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link DateSubtract}. */ + @Contract("_ -> new") public DateSubtract fromDateOf(String fieldReference) { return fromDate(Fields.field(fieldReference)); } @@ -2955,6 +3008,7 @@ public class DateOperators { * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}. * @return new instance of {@link DateSubtract}. */ + @Contract("_ -> new") public DateSubtract fromDate(Object dateExpression) { return new DateSubtract(append("startDate", dateExpression)); } @@ -2965,6 +3019,7 @@ public class DateOperators { * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead. * @return new instance of {@link DateSubtract}. */ + @Contract("_ -> new") public DateSubtract withTimezone(Timezone timezone) { return new DateSubtract(appendTimezone(argumentMap(), timezone)); } @@ -3032,6 +3087,7 @@ public class DateOperators { * @param expression must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateDiff toDateOf(AggregationExpression expression) { return toDate(expression); } @@ -3042,6 +3098,7 @@ public class DateOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateDiff toDateOf(String fieldReference) { return toDate(Fields.field(fieldReference)); } @@ -3052,6 +3109,7 @@ public class DateOperators { * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateDiff toDate(Object dateExpression) { return new DateDiff(append("startDate", dateExpression)); } @@ -3062,6 +3120,7 @@ public class DateOperators { * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead. * @return new instance of {@link DateAdd}. */ + @Contract("_ -> new") public DateDiff withTimezone(Timezone timezone) { return new DateDiff(appendTimezone(argumentMap(), timezone)); } @@ -3073,6 +3132,7 @@ public class DateOperators { * @param day must not be {@literal null}. * @return new instance of {@link DateDiff}. */ + @Contract("_ -> new") public DateDiff startOfWeek(Object day) { return new DateDiff(append("startOfWeek", day)); } @@ -3132,6 +3192,7 @@ public class DateOperators { * @param unit must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc to(String unit) { return new DateTrunc(append("unit", unit)); } @@ -3142,6 +3203,7 @@ public class DateOperators { * @param unit must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc to(AggregationExpression unit) { return new DateTrunc(append("unit", unit)); } @@ -3152,6 +3214,7 @@ public class DateOperators { * @param day must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc startOfWeek(java.time.DayOfWeek day) { return startOfWeek(day.name().toLowerCase(Locale.US)); } @@ -3162,6 +3225,7 @@ public class DateOperators { * @param day must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc startOfWeek(String day) { return new DateTrunc(append("startOfWeek", day)); } @@ -3172,6 +3236,7 @@ public class DateOperators { * @param binSize must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc binSize(int binSize) { return binSize((Object) binSize); } @@ -3182,6 +3247,7 @@ public class DateOperators { * @param expression must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc binSize(AggregationExpression expression) { return binSize((Object) expression); } @@ -3192,6 +3258,7 @@ public class DateOperators { * @param binSize must not be {@literal null}. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc binSize(Object binSize) { return new DateTrunc(append("binSize", binSize)); } @@ -3202,6 +3269,7 @@ public class DateOperators { * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead. * @return new instance of {@link DateTrunc}. */ + @Contract("_ -> new") public DateTrunc withTimezone(Timezone timezone) { return new DateTrunc(appendTimezone(argumentMap(), timezone)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java index 0da9343dd..1a559fd26 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DensifyOperation.java @@ -25,7 +25,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -60,6 +61,9 @@ public class DensifyOperation implements AggregationOperation { @Override public Document toDocument(AggregationOperationContext context) { + Assert.notNull(field, "Field must be set first"); + Assert.notNull(range, "Range must be set first"); + Document densify = new Document(); densify.put("field", context.getReference(field).getRaw()); if (!ObjectUtils.isEmpty(partitionBy)) { @@ -149,9 +153,9 @@ public class DensifyOperation implements AggregationOperation { public static abstract class DensifyRange implements Range { private @Nullable DensifyUnit unit; - private Number step; + private @Nullable Number step; - public DensifyRange(DensifyUnit unit) { + public DensifyRange(@Nullable DensifyUnit unit) { this.unit = unit; } @@ -172,6 +176,7 @@ public class DensifyOperation implements AggregationOperation { * @param step must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public DensifyRange incrementBy(Number step) { this.step = step; return this; @@ -183,6 +188,7 @@ public class DensifyOperation implements AggregationOperation { * @param step must not be {@literal null}. * @return this. */ + @Contract("_, _ -> this") public DensifyRange incrementBy(Number step, DensifyUnit unit) { this.step = step; return unit(unit); @@ -194,6 +200,7 @@ public class DensifyOperation implements AggregationOperation { * @param unit * @return this. */ + @Contract("_ -> this") public DensifyRange unit(DensifyUnit unit) { this.unit = unit; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java index 7f260c378..431215e85 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentEnhancingOperation.java @@ -22,6 +22,7 @@ import java.util.Map.Entry; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; import org.springframework.util.Assert; @@ -105,7 +106,11 @@ abstract class DocumentEnhancingOperation implements InheritsFieldsAggregationOp return new Document(field, value); } - private static Object computeValue(Object value, AggregationOperationContext context) { + private static @Nullable Object computeValue(@Nullable Object value, AggregationOperationContext context) { + + if(value == null) { + return value; + } if (value instanceof Field field) { return context.getReference(field).toString(); @@ -154,7 +159,7 @@ abstract class DocumentEnhancingOperation implements InheritsFieldsAggregationOp this.params = parameters.clone(); } - Object toExpression(AggregationOperationContext context) { + @Nullable Object toExpression(AggregationOperationContext context) { return TRANSFORMER.transform(expression, context, params); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java index ff63ad834..a0cd1d056 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.aggregation; import java.util.Collections; import org.bson.Document; +import org.springframework.lang.Contract; /** * Gateway to {@literal document expressions} such as {@literal $rank, $documentNumber, etc.} @@ -190,6 +191,7 @@ public class DocumentOperators { * @param shiftBy value to add to the current position. * @return new instance of {@link Shift}. */ + @Contract("_ -> new") public Shift by(int shiftBy) { return new Shift(append("by", shiftBy)); } @@ -200,6 +202,7 @@ public class DocumentOperators { * @param value must not be {@literal null}. * @return new instance of {@link Shift}. */ + @Contract("_ -> new") public Shift defaultTo(Object value) { return new Shift(append("default", value)); } @@ -210,6 +213,7 @@ public class DocumentOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Shift}. */ + @Contract("_ -> new") public Shift defaultToValueOf(AggregationExpression expression) { return defaultTo(expression); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java index 56f20dde1..dfdc2d620 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.util.Assert; @@ -50,8 +51,8 @@ public class EvaluationOperators { public static class EvaluationOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link EvaluationOperatorFactory} for given {@literal fieldReference}. @@ -82,6 +83,7 @@ public class EvaluationOperators { * * @return new instance of {@link Expr}. */ + @SuppressWarnings("NullAway") public Expr expr() { return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression); } @@ -91,6 +93,7 @@ public class EvaluationOperators { * * @return new instance of {@link Expr}. */ + @SuppressWarnings("NullAway") public LastObservationCarriedForward locf() { return usesFieldRef() ? LastObservationCarriedForward.locfValueOf(fieldReference) : LastObservationCarriedForward.locfValueOf(expression); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java index 458bc4343..703d5d5f0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFields.java @@ -21,8 +21,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CompositeIterator; import org.springframework.util.ObjectUtils; @@ -154,8 +154,7 @@ public final class ExposedFields implements Iterable { * @param name must not be {@literal null}. * @return can be {@literal null}. */ - @Nullable - public ExposedField getField(String name) { + public @Nullable ExposedField getField(String name) { for (ExposedField field : this) { if (field.canBeReferredToBy(name)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java index 131fa8a84..1639a54d4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java @@ -17,11 +17,10 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -119,8 +118,7 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo * @param name must not be {@literal null}. * @return the resolved reference or {@literal null}. */ - @Nullable - protected FieldReference resolveExposedField(@Nullable Field field, String name) { + protected @Nullable FieldReference resolveExposedField(@Nullable Field field, String name) { ExposedField exposedField = exposedFields.getField(name); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java index f5c73dd09..d1ca95f65 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/FacetOperation.java @@ -23,6 +23,7 @@ import java.util.List; import org.bson.Document; import org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Output; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java index 83fc7c2b8..7f574a850 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java @@ -23,8 +23,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -145,14 +146,17 @@ public final class Fields implements Iterable { * @param name must not be {@literal null}. * @return */ + @Contract("_ -> new") public Fields and(String name) { return and(new AggregationField(name)); } + @Contract("_ -> new") public Fields and(String name, String target) { return and(new AggregationField(name, target)); } + @Contract("_ -> new") public Fields and(Field field) { return new Fields(this, field); } @@ -172,8 +176,7 @@ public final class Fields implements Iterable { return fields.size(); } - @Nullable - public Field getField(String name) { + public @Nullable Field getField(String name) { for (Field field : fields) { if (field.getName().equals(name)) { @@ -206,7 +209,7 @@ public final class Fields implements Iterable { private final String raw; private final String name; - private final String target; + private final @Nullable String target; /** * Creates an aggregation field with the given {@code name}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java index f4a5fb449..bcfc64f2b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperation.java @@ -19,8 +19,9 @@ import java.util.ArrayList; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -80,6 +81,7 @@ public class GeoNearOperation implements AggregationOperation { * @return new instance of {@link GeoNearOperation}. * @since 2.1 */ + @Contract("_ -> new") public GeoNearOperation useIndex(String key) { return new GeoNearOperation(nearQuery, distanceField, key); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java index 72a917c59..ad1f8ae64 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperation.java @@ -21,10 +21,11 @@ import java.util.List; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; import org.springframework.data.mongodb.core.query.CriteriaDefinition; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -35,14 +36,16 @@ import org.springframework.util.ClassUtils; * We recommend to use the static factory method {@link Aggregation#graphLookup(String)} instead of creating instances * of this class directly. * - * @see https://docs.mongodb.org/manual/reference/aggregation/graphLookup/ + * @see https://docs.mongodb.org/manual/reference/aggregation/graphLookup/ * @author Mark Paluch * @author Christoph Strobl * @since 1.10 */ public class GraphLookupOperation implements InheritsFieldsAggregationOperation { - private static final Set> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class, Field.class, Document.class); + private static final Set> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class, + Field.class, Document.class); private final String from; private final List startWith; @@ -126,7 +129,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation List fields = new ArrayList<>(2); fields.add(new ExposedField(as, true)); - if(depthField != null) { + if (depthField != null) { fields.add(new ExposedField(depthField, true)); } return ExposedFields.from(fields.toArray(new ExposedField[0])); @@ -217,10 +220,11 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation implements FromBuilder, StartWithBuilder, ConnectFromBuilder, ConnectToBuilder { private @Nullable String from; - private @Nullable List startWith; + private @Nullable List startWith; private @Nullable String connectFrom; @Override + @Contract("_ -> this") public StartWithBuilder from(String collectionName) { Assert.hasText(collectionName, "CollectionName must not be null or empty"); @@ -230,6 +234,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(String... fieldReferences) { Assert.notNull(fieldReferences, "FieldReferences must not be null"); @@ -246,6 +251,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(AggregationExpression... expressions) { Assert.notNull(expressions, "AggregationExpressions must not be null"); @@ -256,6 +262,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(Object... expressions) { Assert.notNull(expressions, "Expressions must not be null"); @@ -297,6 +304,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation } @Override + @Contract("_ -> this") public ConnectToBuilder connectFrom(String fieldName) { Assert.hasText(fieldName, "ConnectFrom must not be null or empty"); @@ -306,10 +314,14 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation } @Override + @Contract("_ -> new") public GraphLookupOperationBuilder connectTo(String fieldName) { Assert.hasText(fieldName, "ConnectTo must not be null or empty"); + Assert.notNull(from, "From must not be null"); + Assert.notNull(startWith, "startWith must ne set first"); + Assert.notNull(connectFrom, "ConnectFrom must be set first"); return new GraphLookupOperationBuilder(from, startWith, connectFrom, fieldName); } } @@ -327,8 +339,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation private @Nullable Field depthField; private @Nullable CriteriaDefinition restrictSearchWithMatch; - private GraphLookupOperationBuilder(String from, List startWith, String connectFrom, - String connectTo) { + private GraphLookupOperationBuilder(String from, List startWith, String connectFrom, String connectTo) { this.from = from; this.startWith = new ArrayList<>(startWith); @@ -342,6 +353,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation * @param numberOfRecursions must be greater or equal to zero. * @return this. */ + @Contract("_ -> this") public GraphLookupOperationBuilder maxDepth(long numberOfRecursions) { Assert.isTrue(numberOfRecursions >= 0, "Max depth must be >= 0"); @@ -356,6 +368,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation * @param fieldName must not be {@literal null} or empty. * @return this. */ + @Contract("_ -> this") public GraphLookupOperationBuilder depthField(String fieldName) { Assert.hasText(fieldName, "Depth field name must not be null or empty"); @@ -370,6 +383,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation * @param criteriaDefinition must not be {@literal null}. * @return */ + @Contract("_ -> this") public GraphLookupOperationBuilder restrict(CriteriaDefinition criteriaDefinition) { Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null"); @@ -385,6 +399,7 @@ public class GraphLookupOperation implements InheritsFieldsAggregationOperation * @param fieldName must not be {@literal null} or empty. * @return the final {@link GraphLookupOperation}. */ + @Contract("_ -> new") public GraphLookupOperation as(String fieldName) { Assert.hasText(fieldName, "As field name must not be null or empty"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java index 10d58a768..b6d36f1ba 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/GroupOperation.java @@ -20,10 +20,10 @@ import java.util.Collections; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -497,6 +497,7 @@ public class GroupOperation implements FieldsExposingAggregationOperation { } public ExposedField asField() { + Assert.notNull(key, "Key must be set first"); return new ExposedField(key, true); } @@ -506,10 +507,12 @@ public class GroupOperation implements FieldsExposingAggregationOperation { if(op == null && value instanceof Document) { return new Document(key, value); } + + Assert.notNull(op, "Operation keyword must be set"); return new Document(key, new Document(op.toString(), value)); } - public Object getValue(AggregationOperationContext context) { + public @Nullable Object getValue(AggregationOperationContext context) { if (reference == null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java index ca6a2e275..739b7c52a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/InheritingExposedFieldsAggregationOperationContext.java @@ -16,8 +16,8 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; -import org.springframework.lang.Nullable; /** * {@link ExposedFieldsAggregationOperationContext} that inherits fields from its parent diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java index 282ffbd9e..a0e0cc03c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LookupOperation.java @@ -22,6 +22,7 @@ import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedFi import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; +import org.springframework.lang.Contract; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -281,6 +282,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> this") public LocalFieldBuilder from(String name) { Assert.hasText(name, "'From' must not be null or empty"); @@ -289,6 +291,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> this") public AsBuilder foreignField(String name) { Assert.hasText(name, "'ForeignField' must not be null or empty"); @@ -297,6 +300,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> this") public ForeignFieldBuilder localField(String name) { Assert.hasText(name, "'LocalField' must not be null or empty"); @@ -305,6 +309,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> this") public PipelineBuilder let(Let let) { Assert.notNull(let, "Let must not be null"); @@ -313,6 +318,7 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> this") public AsBuilder pipeline(AggregationPipeline pipeline) { Assert.notNull(pipeline, "Pipeline must not be null"); @@ -321,9 +327,11 @@ public class LookupOperation implements FieldsExposingAggregationOperation, Inhe } @Override + @Contract("_ -> new") public LookupOperation as(String name) { Assert.hasText(name, "'As' must not be null or empty"); + Assert.notNull(from, "From must be set first"); as = new ExposedField(Fields.field(name), true); return new LookupOperation(from, localField, foreignField, let, pipeline, as); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java index da1dbfc02..5f736b55a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.util.Assert; @@ -37,8 +38,8 @@ import org.springframework.util.Assert; */ public class MatchOperation implements AggregationOperation { - private final CriteriaDefinition criteriaDefinition; - private final AggregationExpression expression; + private final @Nullable CriteriaDefinition criteriaDefinition; + private final @Nullable AggregationExpression expression; /** * Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}. @@ -68,6 +69,7 @@ public class MatchOperation implements AggregationOperation { } @Override + @SuppressWarnings("NullAway") public Document toDocument(AggregationOperationContext context) { return new Document(getOperator(), diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java index 314f83fc7..bda9a3330 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MergeOperation.java @@ -22,10 +22,11 @@ import java.util.List; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -415,7 +416,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher */ public static class MergeOperationBuilder { - private String collection; + private @Nullable String collection; private @Nullable String database; private UniqueMergeId id = UniqueMergeId.id(); private @Nullable Let let; @@ -430,6 +431,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param collection must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder intoCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -444,6 +446,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param database must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder inDatabase(String database) { this.database = database; @@ -456,6 +459,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param into must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder into(MergeOperationTarget into) { this.database = into.database; @@ -469,6 +473,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param target must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder target(MergeOperationTarget target) { return into(target); } @@ -482,6 +487,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param fields must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder on(String... fields) { return id(UniqueMergeId.ofIdFields(fields)); } @@ -493,6 +499,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param id must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder id(UniqueMergeId id) { this.id = id; @@ -506,6 +513,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param let the variable expressions * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder let(Let let) { this.let = let; @@ -519,6 +527,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param let the variable expressions * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder exposeVariablesOf(Let let) { return let(let); } @@ -529,6 +538,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param whenMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenMatched(WhenDocumentsMatch whenMatched) { this.whenMatched = whenMatched; @@ -541,6 +551,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param whenMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenDocumentsMatch(WhenDocumentsMatch whenMatched) { return whenMatched(whenMatched); } @@ -551,6 +562,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param aggregation must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenDocumentsMatchApply(Aggregation aggregation) { return whenMatched(WhenDocumentsMatch.updateWith(aggregation)); } @@ -561,6 +573,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param whenNotMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenNotMatched(WhenDocumentsDontMatch whenNotMatched) { this.whenNotMatched = whenNotMatched; @@ -573,6 +586,7 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher * @param whenNotMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenDocumentsDontMatch(WhenDocumentsDontMatch whenNotMatched) { return whenNotMatched(whenNotMatched); } @@ -580,7 +594,10 @@ public class MergeOperation implements FieldsExposingAggregationOperation, Inher /** * @return new instance of {@link MergeOperation}. */ + @Contract("-> new") public MergeOperation build() { + + Assert.notNull(collection, "Collection must not be null"); return new MergeOperation(new MergeOperationTarget(database, collection), id, let, whenMatched, whenNotMatched); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java index c553a7be0..a5124320f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java @@ -20,6 +20,7 @@ import java.util.Collection; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExpressionFieldReference; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; import org.springframework.util.Assert; @@ -57,7 +58,7 @@ class NestedDelegatingExpressionAggregationOperationContext implements Aggregati } @Override - public Document getMappedObject(Document document, Class type) { + public Document getMappedObject(Document document, @Nullable Class type) { return delegate.getMappedObject(document, type); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java index 25189241b..4e21ab7bd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ObjectOperators.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.Collections; import org.bson.Document; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -277,7 +278,7 @@ public class ObjectOperators { return super.toDocument(potentiallyExtractSingleValue(value), context); } - @SuppressWarnings("unchecked") + @SuppressWarnings("NullAway") private Object potentiallyExtractSingleValue(Object value) { if (value instanceof Collection collection && collection.size() == 1) { @@ -385,6 +386,7 @@ public class ObjectOperators { * @param fieldRef must not be {@literal null}. * @return new instance of {@link GetField}. */ + @Contract("_ -> new") public GetField of(String fieldRef) { return of(Fields.field(fieldRef)); } @@ -396,6 +398,7 @@ public class ObjectOperators { * @param expression must not be {@literal null}. * @return new instance of {@link GetField}. */ + @Contract("_ -> new") public GetField of(AggregationExpression expression) { return of((Object) expression); } @@ -459,6 +462,7 @@ public class ObjectOperators { * @param fieldRef must not be {@literal null}. * @return new instance of {@link GetField}. */ + @Contract("_ -> new") public SetField input(String fieldRef) { return input(Fields.field(fieldRef)); } @@ -470,6 +474,7 @@ public class ObjectOperators { * @param expression must not be {@literal null}. * @return new instance of {@link SetField}. */ + @Contract("_ -> new") public SetField input(AggregationExpression expression) { return input((Object) expression); } @@ -481,6 +486,7 @@ public class ObjectOperators { * @param fieldRef must not be {@literal null}. * @return new instance of {@link SetField}. */ + @Contract("_ -> new") private SetField input(Object fieldRef) { return new SetField(append("input", fieldRef)); } @@ -491,6 +497,7 @@ public class ObjectOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link SetField}. */ + @Contract("_ -> new") public SetField toValueOf(String fieldReference) { return toValue(Fields.field(fieldReference)); } @@ -502,6 +509,7 @@ public class ObjectOperators { * @param expression must not be {@literal null}. * @return new instance of {@link SetField}. */ + @Contract("_ -> new") public SetField toValueOf(AggregationExpression expression) { return toValue(expression); } @@ -512,6 +520,7 @@ public class ObjectOperators { * @param value * @return new instance of {@link SetField}. */ + @Contract("_ -> new") public SetField toValue(Object value) { return new SetField(append("value", value)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java index 51520f086..7dbed3a85 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/OutOperation.java @@ -16,8 +16,9 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -73,6 +74,7 @@ public class OutOperation implements AggregationOperation { * @return new instance of {@link OutOperation}. * @since 2.2 */ + @Contract("_ -> new") public OutOperation in(@Nullable String database) { return new OutOperation(database, collectionName, uniqueKey, mode); } @@ -102,6 +104,7 @@ public class OutOperation implements AggregationOperation { * @return new instance of {@link OutOperation}. * @since 2.2 */ + @Contract("_ -> new") public OutOperation uniqueKey(@Nullable String key) { Document uniqueKey = key == null ? null : BsonUtils.toDocumentOrElse(key, it -> new Document(it, 1)); @@ -126,6 +129,7 @@ public class OutOperation implements AggregationOperation { * @return new instance of {@link OutOperation}. * @since 2.2 */ + @Contract("_ -> new") public OutOperation uniqueKeyOf(Iterable fields) { Assert.notNull(fields, "Fields must not be null"); @@ -144,6 +148,7 @@ public class OutOperation implements AggregationOperation { * @return new instance of {@link OutOperation}. * @since 2.2 */ + @Contract("_ -> new") public OutOperation mode(OutMode mode) { Assert.notNull(mode, "Mode must not be null"); @@ -158,6 +163,7 @@ public class OutOperation implements AggregationOperation { * @see OutMode#REPLACE_COLLECTION * @since 2.2 */ + @Contract("-> new") public OutOperation replaceCollection() { return mode(OutMode.REPLACE_COLLECTION); } @@ -170,6 +176,7 @@ public class OutOperation implements AggregationOperation { * @see OutMode#REPLACE * @since 2.2 */ + @Contract("-> new") public OutOperation replaceDocuments() { return mode(OutMode.REPLACE); } @@ -182,6 +189,7 @@ public class OutOperation implements AggregationOperation { * @see OutMode#INSERT * @since 2.2 */ + @Contract("-> new") public OutOperation insertDocuments() { return mode(OutMode.INSERT); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java index 9524171fe..54ed40b03 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java @@ -25,8 +25,8 @@ import java.util.Set; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; -import org.springframework.lang.Nullable; /** * {@link AggregationOperationContext} implementation prefixing non-command keys on root level with the given prefix. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java index 35db2214f..af7cf5bfb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java @@ -23,13 +23,14 @@ import java.util.List; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -149,6 +150,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param fieldNames must not be {@literal null}. * @return */ + @Contract("_ -> new") public ProjectionOperation andExclude(String... fieldNames) { List excludeProjections = FieldProjection.from(Fields.fields(fieldNames), false); @@ -161,6 +163,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param fieldNames must not be {@literal null}. * @return */ + @Contract("_ -> new") public ProjectionOperation andInclude(String... fieldNames) { List projections = FieldProjection.from(Fields.fields(fieldNames), true); @@ -173,6 +176,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param fields must not be {@literal null}. * @return */ + @Contract("_ -> new") public ProjectionOperation andInclude(Fields fields) { return new ProjectionOperation(this.projections, FieldProjection.from(fields, true)); } @@ -185,6 +189,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @return new instance of {@link ProjectionOperation}. * @since 2.2 */ + @Contract("_ -> new") public ProjectionOperation asArray(String name) { return new ProjectionOperation(Collections.emptyList(), @@ -402,7 +407,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { return new Document(getExposedField().getName(), toMongoExpression(context, expression, params)); } - protected static Object toMongoExpression(AggregationOperationContext context, String expression, + protected static @Nullable Object toMongoExpression(AggregationOperationContext context, String expression, Object[] params) { return TRANSFORMER.transform(expression, context, params); } @@ -1780,6 +1785,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param expression * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression must not be null"); @@ -1794,6 +1800,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param field * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(Field field) { Assert.notNull(field, "Field must not be null"); @@ -1808,6 +1815,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param value * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(Object value) { this.projections.add(value); @@ -1820,6 +1828,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { * @param name The target property name. Must not be {@literal null}. * @return new instance of {@link ArrayProjectionOperationBuilder}. */ + @Contract("_ -> new") public ProjectionOperation as(String name) { return new ProjectionOperation(target.projections, diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java index a37001635..5f16fcfc1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/RedactOperation.java @@ -16,8 +16,10 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder; import org.springframework.data.mongodb.core.query.CriteriaDefinition; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -33,7 +35,8 @@ import org.springframework.util.Assert; * * * @author Christoph Strobl - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/redact/ + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/redact/ * @since 3.0 */ public class RedactOperation implements AggregationOperation { @@ -94,9 +97,9 @@ public class RedactOperation implements AggregationOperation { */ public static class RedactOperationBuilder { - private Object when; - private Object then; - private Object otherwise; + private @Nullable Object when; + private @Nullable Object then; + private @Nullable Object otherwise; private RedactOperationBuilder() { @@ -108,6 +111,7 @@ public class RedactOperation implements AggregationOperation { * @param criteria must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(CriteriaDefinition criteria) { this.when = criteria; @@ -120,6 +124,7 @@ public class RedactOperation implements AggregationOperation { * @param condition must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(AggregationExpression condition) { this.when = condition; @@ -132,6 +137,7 @@ public class RedactOperation implements AggregationOperation { * @param condition must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(Document condition) { this.when = condition; @@ -143,6 +149,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenDescend() { return then(DESCEND); } @@ -152,6 +159,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenKeep() { return then(KEEP); } @@ -161,6 +169,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenPrune() { return then(PRUNE); } @@ -172,6 +181,7 @@ public class RedactOperation implements AggregationOperation { * @param then must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder then(Object then) { this.then = then; @@ -183,6 +193,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwiseDescend() { return otherwise(DESCEND); } @@ -192,6 +203,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwiseKeep() { return otherwise(KEEP); } @@ -201,6 +213,7 @@ public class RedactOperation implements AggregationOperation { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwisePrune() { return otherwise(PRUNE); } @@ -212,6 +225,7 @@ public class RedactOperation implements AggregationOperation { * @param otherwise must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder otherwise(Object otherwise) { this.otherwise = otherwise; return this; @@ -220,7 +234,12 @@ public class RedactOperation implements AggregationOperation { /** * @return new instance of {@link RedactOperation}. */ + @Contract("-> new") public RedactOperation build() { + + Assert.notNull(then, "Then must be set first"); + Assert.notNull(otherwise, "Otherwise must be set first"); + return new RedactOperation(when().then(then).otherwise(otherwise)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java index 130182a00..ec306eb6c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java @@ -23,6 +23,7 @@ import java.util.List; import org.bson.Document; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.expression.spel.ast.Projection; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -457,6 +458,7 @@ public class ReplaceRootOperation implements FieldsExposingAggregationOperation } @Override + @SuppressWarnings("NullAway") public Document toDocument(AggregationOperationContext context) { Document document = new Document("$set", value); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java index 9eab041e8..ed615d986 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ScriptOperators.java @@ -22,15 +22,15 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorBuilder; import org.springframework.data.mongodb.core.aggregation.ScriptOperators.Accumulator.AccumulatorInitBuilder; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** - * Gateway to {@literal $function} and {@literal $accumulator} aggregation operations. - *
+ * Gateway to {@literal $function} and {@literal $accumulator} aggregation operations.
* Using {@link ScriptOperators} as part of the {@link Aggregation} requires MongoDB server to have * server-side JavaScript execution * enabled. @@ -53,8 +53,8 @@ public class ScriptOperators { } /** - * Create a custom $accumulator operator - * in Javascript. + * Create a custom $accumulator + * operator in Javascript. * * @return new instance of {@link AccumulatorInitBuilder}. */ @@ -74,8 +74,7 @@ public class ScriptOperators { * lang: "js" * } * } - * - *
+ *
* {@link Function} cannot be used as part of {@link org.springframework.data.mongodb.core.schema.MongoJsonSchema * schema} validation query expression.
* NOTE: Server-Side JavaScript @@ -150,10 +149,12 @@ public class ScriptOperators { return get(Fields.ARGS.toString()); } + @Nullable String getBody() { return get(Fields.BODY.toString()); } + @Nullable String getLang() { return get(Fields.LANG.toString()); } @@ -178,8 +179,7 @@ public class ScriptOperators { * {@link Accumulator} defines a custom aggregation * $accumulator operator, * one that maintains its state (e.g. totals, maximums, minimums, and related data) as documents progress through the - * pipeline, in JavaScript. - *
+ * pipeline, in JavaScript.
* * { * $accumulator: { @@ -192,8 +192,7 @@ public class ScriptOperators { * lang: "js" * } * } - * - *
+ *
* {@link Accumulator} can be used as part of {@link GroupOperation $group}, {@link BucketOperation $bucket} and * {@link BucketAutoOperation $bucketAuto} pipeline stages.
* NOTE: Server-Side JavaScript @@ -240,8 +239,7 @@ public class ScriptOperators { /** * Define the {@code init} {@link Function} for the {@link Accumulator accumulators} initial state. The function - * receives its arguments from the {@link Function#args(Object...) initArgs} array expression. - *
+ * receives its arguments from the {@link Function#args(Object...) initArgs} array expression.
* * function(initArg1, initArg2, ...) { * ... @@ -253,13 +251,16 @@ public class ScriptOperators { * @return this. */ default AccumulatorAccumulateBuilder init(Function function) { - return init(function.getBody()).initArgs(function.getArgs()); + + Assert.notNull(function.getBody(), "Function body must not be null"); + + List args = function.getArgs(); + return init(function.getBody()).initArgs(args != null ? args : List.of()); } /** * Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives - * its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression. - *
+ * its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression.
* * function(initArg1, initArg2, ...) { * ... @@ -307,8 +308,7 @@ public class ScriptOperators { /** * Set the {@code accumulate} {@link Function} that updates the state for each document. The functions first * argument is the current {@code state}, additional arguments can be defined via {@link Function#args(Object...) - * accumulateArgs}. - *
+ * accumulateArgs}.
* * function(state, accumArg1, accumArg2, ...) { * ... @@ -320,14 +320,17 @@ public class ScriptOperators { * @return this. */ default AccumulatorMergeBuilder accumulate(Function function) { - return accumulate(function.getBody()).accumulateArgs(function.getArgs()); + + Assert.notNull(function.getBody(), "Function body must not be null"); + + List args = function.getArgs(); + return accumulate(function.getBody()).accumulateArgs(args != null ? args : List.of()); } /** * Set the {@code accumulate} function that updates the state for each document. The functions first argument is * the current {@code state}, additional arguments can be defined via - * {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}. - *
+ * {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}.
* * function(state, accumArg1, accumArg2, ...) { * ... @@ -369,8 +372,7 @@ public class ScriptOperators { /** * Set the {@code merge} function used to merge two internal states.
* This might be required because the operation is run on a sharded cluster or when the operator exceeds its - * memory limit. - *
+ * memory limit.
* * function(state1, state2) { * ... @@ -388,8 +390,7 @@ public class ScriptOperators { /** * Set the {@code finalize} function used to update the result of the accumulation when all documents have been - * processed. - *
+ * processed.
* * function(state) { * ... @@ -414,18 +415,17 @@ public class ScriptOperators { implements AccumulatorInitBuilder, AccumulatorInitArgsBuilder, AccumulatorAccumulateBuilder, AccumulatorAccumulateArgsBuilder, AccumulatorMergeBuilder, AccumulatorFinalizeBuilder { - private List initArgs; - private String initFunction; - private List accumulateArgs; - private String accumulateFunction; - private String mergeFunction; - private String finalizeFunction; + private @Nullable List initArgs; + private @Nullable String initFunction; + private @Nullable List accumulateArgs; + private @Nullable String accumulateFunction; + private @Nullable String mergeFunction; + private @Nullable String finalizeFunction; private String lang = "js"; /** * Define the {@code init} function for the {@link Accumulator accumulators} initial state. The function receives - * its arguments from the {@link #initArgs(Object...)} array expression. - *
+ * its arguments from the {@link #initArgs(Object...)} array expression.
* * function(initArg1, initArg2, ...) { * ... @@ -437,6 +437,7 @@ public class ScriptOperators { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder init(String function) { this.initFunction = function; @@ -450,6 +451,7 @@ public class ScriptOperators { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder initArgs(List args) { Assert.notNull(args, "Args must not be null"); @@ -460,8 +462,7 @@ public class ScriptOperators { /** * Set the {@code accumulate} function that updates the state for each document. The functions first argument is - * the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}. - *
+ * the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}.
* * function(state, accumArg1, accumArg2, ...) { * ... @@ -473,6 +474,7 @@ public class ScriptOperators { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder accumulate(String function) { Assert.notNull(function, "Accumulate function must not be null"); @@ -488,6 +490,7 @@ public class ScriptOperators { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder accumulateArgs(List args) { Assert.notNull(args, "Args must not be null"); @@ -499,8 +502,7 @@ public class ScriptOperators { /** * Set the {@code merge} function used to merge two internal states.
* This might be required because the operation is run on a sharded cluster or when the operator exceeds its - * memory limit. - *
+ * memory limit.
* * function(state1, state2) { * ... @@ -512,6 +514,7 @@ public class ScriptOperators { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder merge(String function) { Assert.notNull(function, "Merge function must not be null"); @@ -526,6 +529,7 @@ public class ScriptOperators { * @param lang must not be {@literal null}. Default is {@literal js}. * @return this. */ + @Contract("_ -> this") public AccumulatorBuilder lang(String lang) { Assert.hasText(lang, "Lang must not be null nor empty; The default would be 'js'"); @@ -536,8 +540,7 @@ public class ScriptOperators { /** * Set the {@code finalize} function used to update the result of the accumulation when all documents have been - * processed. - *
+ * processed.
* * function(state) { * ... @@ -549,6 +552,7 @@ public class ScriptOperators { * @return new instance of {@link Accumulator}. */ @Override + @Contract("_ -> new") public Accumulator finalize(String function) { Assert.notNull(function, "Finalize function must not be null"); @@ -562,6 +566,7 @@ public class ScriptOperators { } @Override + @Contract("-> new") public Accumulator build() { return new Accumulator(createArgumentMap()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java index 9da80c466..4db0181d3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.Collections; import org.springframework.data.domain.Sort; +import org.springframework.lang.Contract; /** * Gateway to {@literal selection operators} such as {@literal $bottom}. @@ -69,6 +70,7 @@ public class SelectionOperators { * @param numberOfResults * @return new instance of {@link Bottom}. */ + @Contract("_ -> new") public Bottom limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -80,10 +82,12 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Bottom}. */ + @Contract("_ -> new") public Bottom limit(AggregationExpression expression) { return limit((Object) expression); } + @Contract("_ -> new") private Bottom limit(Object value) { return new Bottom(append("n", value)); } @@ -94,6 +98,7 @@ public class SelectionOperators { * @param sort must not be {@literal null}. * @return new instance of {@link Bottom}. */ + @Contract("_ -> new") public Bottom sortBy(Sort sort) { return new Bottom(append("sortBy", sort)); } @@ -104,6 +109,7 @@ public class SelectionOperators { * @param out must not be {@literal null}. * @return new instance of {@link Bottom}. */ + @Contract("_ -> new") public Bottom output(Fields out) { return new Bottom(append("output", out)); } @@ -115,6 +121,7 @@ public class SelectionOperators { * @return new instance of {@link Bottom}. * @see #output(Fields) */ + @Contract("_ -> new") public Bottom output(String... fieldNames) { return output(Fields.fields(fieldNames)); } @@ -126,6 +133,7 @@ public class SelectionOperators { * @return new instance of {@link Bottom}. * @see #output(Fields) */ + @Contract("_ -> new") public Bottom output(AggregationExpression... out) { return new Bottom(append("output", Arrays.asList(out))); } @@ -172,6 +180,7 @@ public class SelectionOperators { * @param numberOfResults * @return new instance of {@link Top}. */ + @Contract("_ -> new") public Top limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -183,6 +192,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Top}. */ + @Contract("_ -> new") public Top limit(AggregationExpression expression) { return limit((Object) expression); } @@ -197,6 +207,7 @@ public class SelectionOperators { * @param sort must not be {@literal null}. * @return new instance of {@link Top}. */ + @Contract("_ -> new") public Top sortBy(Sort sort) { return new Top(append("sortBy", sort)); } @@ -207,6 +218,7 @@ public class SelectionOperators { * @param out must not be {@literal null}. * @return new instance of {@link Top}. */ + @Contract("_ -> new") public Top output(Fields out) { return new Top(append("output", out)); } @@ -218,6 +230,7 @@ public class SelectionOperators { * @return new instance of {@link Top}. * @see #output(Fields) */ + @Contract("_ -> new") public Top output(String... fieldNames) { return output(Fields.fields(fieldNames)); } @@ -229,6 +242,7 @@ public class SelectionOperators { * @return new instance of {@link Top}. * @see #output(Fields) */ + @Contract("_ -> new") public Top output(AggregationExpression... out) { return new Top(append("output", Arrays.asList(out))); } @@ -263,6 +277,7 @@ public class SelectionOperators { * @param numberOfResults * @return new instance of {@link First}. */ + @Contract("_ -> new") public First limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -274,6 +289,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link First}. */ + @Contract("_ -> new") public First limit(AggregationExpression expression) { return limit((Object) expression); } @@ -288,6 +304,7 @@ public class SelectionOperators { * @param fieldName must not be {@literal null}. * @return new instance of {@link First}. */ + @Contract("_ -> new") public First of(String fieldName) { return input(fieldName); } @@ -298,6 +315,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link First}. */ + @Contract("_ -> new") public First of(AggregationExpression expression) { return input(expression); } @@ -308,6 +326,7 @@ public class SelectionOperators { * @param fieldName must not be {@literal null}. * @return new instance of {@link First}. */ + @Contract("_ -> new") public First input(String fieldName) { return new First(append("input", Fields.field(fieldName))); } @@ -318,6 +337,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link First}. */ + @Contract("_ -> new") public First input(AggregationExpression expression) { return new First(append("input", expression)); } @@ -357,6 +377,7 @@ public class SelectionOperators { * @param numberOfResults * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -368,6 +389,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last limit(AggregationExpression expression) { return limit((Object) expression); } @@ -382,6 +404,7 @@ public class SelectionOperators { * @param fieldName must not be {@literal null}. * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last of(String fieldName) { return input(fieldName); } @@ -392,6 +415,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last of(AggregationExpression expression) { return input(expression); } @@ -402,6 +426,7 @@ public class SelectionOperators { * @param fieldName must not be {@literal null}. * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last input(String fieldName) { return new Last(append("input", Fields.field(fieldName))); } @@ -412,6 +437,7 @@ public class SelectionOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last input(AggregationExpression expression) { return new Last(append("input", expression)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java index b188b16b5..800d8b37d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java @@ -19,8 +19,9 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.SetOperation.FieldAppender.ValueAppender; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * Adds new fields to documents. {@code $set} outputs documents that contain all existing fields from the input @@ -82,6 +83,7 @@ public class SetOperation extends DocumentEnhancingOperation { * @param value the value to assign. * @return new instance of {@link SetOperation}. */ + @Contract("_, _ -> new") public SetOperation set(Object field, Object value) { LinkedHashMap target = new LinkedHashMap<>(getValueMap()); @@ -131,7 +133,7 @@ public class SetOperation extends DocumentEnhancingOperation { return new ValueAppender() { @Override - public SetOperation toValue(Object value) { + public SetOperation toValue(@Nullable Object value) { valueMap.put(field, value); return FieldAppender.this.build(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java index 094ef7365..a99c0926f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java @@ -19,7 +19,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -55,8 +57,8 @@ public class SetOperators { */ public static class SetOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link SetOperatorFactory} for given {@literal fieldReference}. @@ -104,6 +106,7 @@ public class SetOperators { return createSetEquals().isEqualTo(expressions); } + @SuppressWarnings("NullAway") private SetEquals createSetEquals() { return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression); } @@ -130,6 +133,7 @@ public class SetOperators { return createSetIntersection().intersects(expressions); } + @SuppressWarnings("NullAway") private SetIntersection createSetIntersection() { return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression); } @@ -156,6 +160,7 @@ public class SetOperators { return createSetUnion().union(expressions); } + @SuppressWarnings("NullAway") private SetUnion createSetUnion() { return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression); } @@ -182,6 +187,7 @@ public class SetOperators { return createSetDifference().differenceTo(expression); } + @SuppressWarnings("NullAway") private SetDifference createSetDifference() { return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression); } @@ -208,6 +214,7 @@ public class SetOperators { return createSetIsSubset().isSubsetOf(expression); } + @SuppressWarnings("NullAway") private SetIsSubset createSetIsSubset() { return usesFieldRef() ? SetIsSubset.arrayAsSet(fieldReference) : SetIsSubset.arrayAsSet(expression); } @@ -218,6 +225,7 @@ public class SetOperators { * * @return new instance of {@link AnyElementTrue}. */ + @SuppressWarnings("NullAway") public AnyElementTrue anyElementTrue() { return usesFieldRef() ? AnyElementTrue.arrayAsSet(fieldReference) : AnyElementTrue.arrayAsSet(expression); } @@ -228,6 +236,7 @@ public class SetOperators { * * @return new instance of {@link AllElementsTrue}. */ + @SuppressWarnings("NullAway") public AllElementsTrue allElementsTrue() { return usesFieldRef() ? AllElementsTrue.arrayAsSet(fieldReference) : AllElementsTrue.arrayAsSet(expression); } @@ -283,6 +292,7 @@ public class SetOperators { * @param arrayReferences must not be {@literal null}. * @return new instance of {@link SetEquals}. */ + @Contract("_ -> new") public SetEquals isEqualTo(String... arrayReferences) { Assert.notNull(arrayReferences, "ArrayReferences must not be null"); @@ -295,6 +305,7 @@ public class SetOperators { * @param expressions must not be {@literal null}. * @return new instance of {@link SetEquals}. */ + @Contract("_ -> new") public SetEquals isEqualTo(AggregationExpression... expressions) { Assert.notNull(expressions, "Expressions must not be null"); @@ -307,6 +318,7 @@ public class SetOperators { * @param array must not be {@literal null}. * @return new instance of {@link SetEquals}. */ + @Contract("_ -> new") public SetEquals isEqualTo(Object[] array) { Assert.notNull(array, "Array must not be null"); @@ -360,6 +372,7 @@ public class SetOperators { * @param arrayReferences must not be {@literal null}. * @return new instance of {@link SetIntersection}. */ + @Contract("_ -> new") public SetIntersection intersects(String... arrayReferences) { Assert.notNull(arrayReferences, "ArrayReferences must not be null"); @@ -372,6 +385,7 @@ public class SetOperators { * @param expressions must not be {@literal null}. * @return new instance of {@link SetIntersection}. */ + @Contract("_ -> new") public SetIntersection intersects(AggregationExpression... expressions) { Assert.notNull(expressions, "Expressions must not be null"); @@ -425,6 +439,7 @@ public class SetOperators { * @param arrayReferences must not be {@literal null}. * @return new instance of {@link SetUnion}. */ + @Contract("_ -> new") public SetUnion union(String... arrayReferences) { Assert.notNull(arrayReferences, "ArrayReferences must not be null"); @@ -437,6 +452,7 @@ public class SetOperators { * @param expressions must not be {@literal null}. * @return new instance of {@link SetUnion}. */ + @Contract("_ -> new") public SetUnion union(AggregationExpression... expressions) { Assert.notNull(expressions, "Expressions must not be null"); @@ -490,6 +506,7 @@ public class SetOperators { * @param arrayReference must not be {@literal null}. * @return new instance of {@link SetDifference}. */ + @Contract("_ -> new") public SetDifference differenceTo(String arrayReference) { Assert.notNull(arrayReference, "ArrayReference must not be null"); @@ -502,6 +519,7 @@ public class SetOperators { * @param expression must not be {@literal null}. * @return new instance of {@link SetDifference}. */ + @Contract("_ -> new") public SetDifference differenceTo(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -555,6 +573,7 @@ public class SetOperators { * @param arrayReference must not be {@literal null}. * @return new instance of {@link SetIsSubset}. */ + @Contract("_ -> new") public SetIsSubset isSubsetOf(String arrayReference) { Assert.notNull(arrayReference, "ArrayReference must not be null"); @@ -567,6 +586,7 @@ public class SetOperators { * @param expression must not be {@literal null}. * @return new instance of {@link SetIsSubset}. */ + @Contract("_ -> new") public SetIsSubset isSubsetOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -614,6 +634,7 @@ public class SetOperators { return new AnyElementTrue(Collections.singletonList(expression)); } + @Contract("-> this") public AnyElementTrue anyElementTrue() { return this; } @@ -659,6 +680,7 @@ public class SetOperators { return new AllElementsTrue(Collections.singletonList(expression)); } + @Contract("-> this") public AllElementsTrue allElementsTrue() { return this; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java index 2b8df539e..e1fec1781 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java @@ -22,8 +22,9 @@ import java.util.List; import java.util.concurrent.TimeUnit; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -31,7 +32,8 @@ import org.springframework.util.Assert; * * @author Christoph Strobl * @since 3.3 - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/ + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/ */ public class SetWindowFieldsOperation implements AggregationOperation, FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation { @@ -137,6 +139,7 @@ public class SetWindowFieldsOperation * @param field must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public WindowOutput append(ComputedField field) { Assert.notNull(field, "Field must not be null"); @@ -152,6 +155,7 @@ public class SetWindowFieldsOperation * @return new instance of {@link ComputedFieldAppender}. * @see #append(ComputedField) */ + @Contract("_ -> new") public ComputedFieldAppender append(AggregationExpression expression) { return new ComputedFieldAppender() { @@ -249,8 +253,7 @@ public class SetWindowFieldsOperation return windowOperator; } - @Nullable - public Window getWindow() { + public @Nullable Window getWindow() { return window; } } @@ -360,6 +363,7 @@ public class SetWindowFieldsOperation * @param lower eg. {@literal current} or {@literal unbounded}. * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder from(String lower) { this.lower = lower; @@ -372,6 +376,7 @@ public class SetWindowFieldsOperation * @param upper eg. {@literal current} or {@literal unbounded}. * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder to(String upper) { this.upper = upper; @@ -386,6 +391,7 @@ public class SetWindowFieldsOperation * @param lower * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder from(Number lower) { this.lower = lower; @@ -400,6 +406,7 @@ public class SetWindowFieldsOperation * @param upper * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder to(Number upper) { this.upper = upper; @@ -411,6 +418,7 @@ public class SetWindowFieldsOperation * * @return this. */ + @Contract("-> this") public RangeWindowBuilder fromCurrent() { return from(CURRENT); } @@ -420,6 +428,7 @@ public class SetWindowFieldsOperation * * @return this. */ + @Contract("-> this") public RangeWindowBuilder fromUnbounded() { return from(UNBOUNDED); } @@ -429,6 +438,7 @@ public class SetWindowFieldsOperation * * @return this. */ + @Contract("-> this") public RangeWindowBuilder toCurrent() { return to(CURRENT); } @@ -438,6 +448,7 @@ public class SetWindowFieldsOperation * * @return this. */ + @Contract("-> this") public RangeWindowBuilder toUnbounded() { return to(UNBOUNDED); } @@ -448,6 +459,7 @@ public class SetWindowFieldsOperation * @param windowUnit must not be {@literal null}. Can be on of {@link Windows}. * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder unit(WindowUnit windowUnit) { Assert.notNull(windowUnit, "WindowUnit must not be null"); @@ -460,6 +472,7 @@ public class SetWindowFieldsOperation * * @return new instance of {@link RangeWindow}. */ + @Contract("-> new") public RangeWindow build() { Assert.notNull(lower, "Lower bound must not be null"); @@ -488,20 +501,24 @@ public class SetWindowFieldsOperation * @param lower * @return this. */ + @Contract("_ -> this") public DocumentWindowBuilder from(Number lower) { this.lower = lower; return this; } + @Contract("-> this") public DocumentWindowBuilder fromCurrent() { return from(CURRENT); } + @Contract("-> this") public DocumentWindowBuilder fromUnbounded() { return from(UNBOUNDED); } + @Contract("-> this") public DocumentWindowBuilder to(String upper) { this.upper = upper; @@ -514,6 +531,7 @@ public class SetWindowFieldsOperation * @param lower eg. {@literal current} or {@literal unbounded}. * @return this. */ + @Contract("_ -> this") public DocumentWindowBuilder from(String lower) { this.lower = lower; @@ -528,20 +546,24 @@ public class SetWindowFieldsOperation * @param upper * @return this. */ + @Contract("_ -> this") public DocumentWindowBuilder to(Number upper) { this.upper = upper; return this; } + @Contract("-> this") public DocumentWindowBuilder toCurrent() { return to(CURRENT); } + @Contract("-> this") public DocumentWindowBuilder toUnbounded() { return to(UNBOUNDED); } + @Contract("-> new") public DocumentWindow build() { Assert.notNull(lower, "Lower bound must not be null"); @@ -689,9 +711,9 @@ public class SetWindowFieldsOperation */ public static class SetWindowFieldsOperationBuilder { - private Object partitionBy; - private SortOperation sortOperation; - private WindowOutput output; + private @Nullable Object partitionBy; + private @Nullable SortOperation sortOperation; + private @Nullable WindowOutput output; /** * Specify the field to group by. @@ -699,6 +721,7 @@ public class SetWindowFieldsOperation * @param fieldName must not be {@literal null} or null. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder partitionByField(String fieldName) { Assert.hasText(fieldName, "Field name must not be empty or null"); @@ -711,6 +734,7 @@ public class SetWindowFieldsOperation * @param expression must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder partitionByExpression(AggregationExpression expression) { return partitionBy(expression); } @@ -721,6 +745,7 @@ public class SetWindowFieldsOperation * @param fields must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder sortBy(String... fields) { return sortBy(Sort.by(fields)); } @@ -731,6 +756,7 @@ public class SetWindowFieldsOperation * @param sort must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder sortBy(Sort sort) { return sortBy(new SortOperation(sort)); } @@ -741,6 +767,7 @@ public class SetWindowFieldsOperation * @param sort must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder sortBy(SortOperation sort) { Assert.notNull(sort, "SortOperation must not be null"); @@ -755,6 +782,7 @@ public class SetWindowFieldsOperation * @param output must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder output(WindowOutput output) { Assert.notNull(output, "WindowOutput must not be null"); @@ -769,6 +797,7 @@ public class SetWindowFieldsOperation * @param expression must not be {@literal null}. * @return new instance of {@link WindowChoice}. */ + @Contract("_ -> new") public WindowChoice output(AggregationExpression expression) { return new WindowChoice() { @@ -837,6 +866,7 @@ public class SetWindowFieldsOperation * @param value must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder partitionBy(Object value) { Assert.notNull(value, "Partition By must not be null"); @@ -850,7 +880,10 @@ public class SetWindowFieldsOperation * * @return new instance of {@link SetWindowFieldsOperation}. */ + @Contract("-> new") public SetWindowFieldsOperation build() { + + Assert.notNull(output, "Output must be set first"); return new SetWindowFieldsOperation(partitionBy, sortOperation, output); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java index ffc0aa065..e6a9a23d3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SortByCountOperation.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** @@ -67,6 +67,7 @@ public class SortByCountOperation implements AggregationOperation { } @Override + @SuppressWarnings("NullAway") public Document toDocument(AggregationOperationContext context) { return new Document(getOperator(), groupByExpression == null ? context.getReference(groupByField).toString() diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java index 3119e2729..ade4f5328 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.core.GenericTypeResolver; import org.springframework.data.mongodb.core.spel.ExpressionNode; import org.springframework.data.mongodb.core.spel.ExpressionTransformationContextSupport; @@ -42,7 +43,6 @@ import org.springframework.expression.spel.ast.PropertyOrFieldReference; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; @@ -83,7 +83,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { * @param params must not be {@literal null} * @return */ - public Object transform(String expression, AggregationOperationContext context, Object... params) { + public @Nullable Object transform(String expression, AggregationOperationContext context, Object... params) { Assert.notNull(expression, "Expression must not be null"); Assert.notNull(context, "AggregationOperationContext must not be null"); @@ -96,7 +96,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { return transform(new AggregationExpressionTransformationContext<>(node, null, null, context)); } - public Object transform(AggregationExpressionTransformationContext context) { + public @Nullable Object transform(AggregationExpressionTransformationContext context) { return lookupConversionFor(context.getCurrentNode()).convert(context); } @@ -137,7 +137,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { * * @param transformer must not be {@literal null}. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "NullAway"}) public ExpressionNodeConversion(AggregationExpressionTransformer transformer) { Assert.notNull(transformer, "Transformer must not be null"); @@ -165,7 +165,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { * @param context must not be {@literal null}. * @return */ - protected Object transform(ExpressionNode node, AggregationExpressionTransformationContext context) { + protected @Nullable Object transform(ExpressionNode node, AggregationExpressionTransformationContext context) { Assert.notNull(node, "ExpressionNode must not be null"); Assert.notNull(context, "AggregationExpressionTransformationContext must not be null"); @@ -183,7 +183,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { * @param context must not be {@literal null}. * @return */ - protected Object transform(ExpressionNode node, @Nullable ExpressionNode parent, @Nullable Document operation, + protected @Nullable Object transform(ExpressionNode node, @Nullable ExpressionNode parent, @Nullable Document operation, AggregationExpressionTransformationContext context) { Assert.notNull(node, "ExpressionNode must not be null"); @@ -194,7 +194,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - public Object transform(AggregationExpressionTransformationContext context) { + public @Nullable Object transform(AggregationExpressionTransformationContext context) { return transformer.transform(context); } @@ -204,7 +204,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { * @param context * @return */ - protected abstract Object convert(AggregationExpressionTransformationContext context); + protected abstract @Nullable Object convert(AggregationExpressionTransformationContext context); } /** @@ -247,6 +247,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { return operationObject; } + @SuppressWarnings("NullAway") private Document createOperationObjectAndAddToPreviousArgumentsIfNecessary( AggregationExpressionTransformationContext context, OperatorNode currentNode) { @@ -301,7 +302,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { return context.addToPreviousOrReturn(context.getCurrentNode().getValue()); } @@ -322,9 +323,8 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { super(transformer); } - @Nullable @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { ExpressionNode currentNode = context.getCurrentNode(); @@ -355,7 +355,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { String fieldReference = context.getFieldReference().toString(); return context.addToPreviousOrReturn(fieldReference); @@ -381,14 +381,14 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { @Override @SuppressWarnings("unchecked") - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { LiteralNode node = context.getCurrentNode(); Object value = node.getValue(); if (context.hasPreviousOperation()) { - if (node.isUnaryMinus(context.getParentNode())) { + if (node.isUnaryMinus(context.getParentNode()) && value != null) { // unary minus operator return NumberUtils.convertNumberToTargetClass(((Number) value).doubleValue() * -1, (Class) value.getClass()); // retain type, e.g. int to -int @@ -419,7 +419,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { MethodReferenceNode node = context.getCurrentNode(); AggregationMethodReference methodReference = node.getMethodReference(); @@ -469,7 +469,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { ExpressionNode currentNode = context.getCurrentNode(); @@ -503,7 +503,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { NotOperatorNode node = context.getCurrentNode(); List args = new ArrayList<>(); @@ -537,7 +537,7 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { } @Override - protected Object convert(AggregationExpressionTransformationContext context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext context) { Object value = context.getCurrentNode().getValue(); return ObjectUtils.isArray(value) ? Arrays.asList(ObjectUtils.toObjectArray(value)) : value; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java index 978849760..0f3447a47 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java @@ -21,8 +21,10 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.mongodb.util.RegexFlags; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -60,8 +62,8 @@ public class StringOperators { */ public static class StringOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link StringOperatorFactory} for given {@literal fieldReference}. @@ -126,6 +128,7 @@ public class StringOperators { return createConcat().concat(value); } + @SuppressWarnings("NullAway") private Concat createConcat() { return usesFieldRef() ? Concat.valueOf(fieldReference) : Concat.valueOf(expression); } @@ -153,6 +156,7 @@ public class StringOperators { return createSubstr().substring(start, nrOfChars); } + @SuppressWarnings("NullAway") private Substr createSubstr() { return usesFieldRef() ? Substr.valueOf(fieldReference) : Substr.valueOf(expression); } @@ -162,6 +166,7 @@ public class StringOperators { * * @return new instance of {@link ToLower}. */ + @SuppressWarnings("NullAway") public ToLower toLower() { return usesFieldRef() ? ToLower.lowerValueOf(fieldReference) : ToLower.lowerValueOf(expression); } @@ -171,6 +176,7 @@ public class StringOperators { * * @return new instance of {@link ToUpper}. */ + @SuppressWarnings("NullAway") public ToUpper toUpper() { return usesFieldRef() ? ToUpper.upperValueOf(fieldReference) : ToUpper.upperValueOf(expression); } @@ -214,6 +220,7 @@ public class StringOperators { return createStrCaseCmp().strcasecmpValueOf(expression); } + @SuppressWarnings("NullAway") private StrCaseCmp createStrCaseCmp() { return usesFieldRef() ? StrCaseCmp.valueOf(fieldReference) : StrCaseCmp.valueOf(expression); } @@ -260,6 +267,7 @@ public class StringOperators { return createIndexOfBytesSubstringBuilder().indexOf(expression); } + @SuppressWarnings("NullAway") private IndexOfBytes.SubstringBuilder createIndexOfBytesSubstringBuilder() { return usesFieldRef() ? IndexOfBytes.valueOf(fieldReference) : IndexOfBytes.valueOf(expression); } @@ -306,6 +314,7 @@ public class StringOperators { return createIndexOfCPSubstringBuilder().indexOf(expression); } + @SuppressWarnings("NullAway") private IndexOfCP.SubstringBuilder createIndexOfCPSubstringBuilder() { return usesFieldRef() ? IndexOfCP.valueOf(fieldReference) : IndexOfCP.valueOf(expression); } @@ -343,6 +352,7 @@ public class StringOperators { return createSplit().split(expression); } + @SuppressWarnings("NullAway") private Split createSplit() { return usesFieldRef() ? Split.valueOf(fieldReference) : Split.valueOf(expression); } @@ -353,6 +363,7 @@ public class StringOperators { * * @return new instance of {@link StrLenBytes}. */ + @SuppressWarnings("NullAway") public StrLenBytes length() { return usesFieldRef() ? StrLenBytes.stringLengthOf(fieldReference) : StrLenBytes.stringLengthOf(expression); } @@ -363,6 +374,7 @@ public class StringOperators { * * @return new instance of {@link StrLenCP}. */ + @SuppressWarnings("NullAway") public StrLenCP lengthCP() { return usesFieldRef() ? StrLenCP.stringLengthOfCP(fieldReference) : StrLenCP.stringLengthOfCP(expression); } @@ -390,6 +402,7 @@ public class StringOperators { return createSubstrCP().substringCP(codePointStart, nrOfCodePoints); } + @SuppressWarnings("NullAway") private SubstrCP createSubstrCP() { return usesFieldRef() ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression); } @@ -432,6 +445,7 @@ public class StringOperators { return trim().charsOf(expression); } + @SuppressWarnings("NullAway") private Trim createTrim() { return usesFieldRef() ? Trim.valueOf(fieldReference) : Trim.valueOf(expression); } @@ -474,6 +488,7 @@ public class StringOperators { return ltrim().charsOf(expression); } + @SuppressWarnings("NullAway") private LTrim createLTrim() { return usesFieldRef() ? LTrim.valueOf(fieldReference) : LTrim.valueOf(expression); } @@ -516,6 +531,7 @@ public class StringOperators { return rtrim().charsOf(expression); } + @SuppressWarnings("NullAway") private RTrim createRTrim() { return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression); } @@ -572,6 +588,7 @@ public class StringOperators { return createRegexFind().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexFind createRegexFind() { return usesFieldRef() ? RegexFind.valueOf(fieldReference) : RegexFind.valueOf(expression); } @@ -628,6 +645,7 @@ public class StringOperators { return createRegexFindAll().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexFindAll createRegexFindAll() { return usesFieldRef() ? RegexFindAll.valueOf(fieldReference) : RegexFindAll.valueOf(expression); } @@ -683,6 +701,7 @@ public class StringOperators { return createRegexMatch().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexMatch createRegexMatch() { return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression); } @@ -713,6 +732,7 @@ public class StringOperators { return createReplaceOne().findValueOf(search).replacement(replacement); } + @SuppressWarnings("NullAway") private ReplaceOne createReplaceOne() { return usesFieldRef() ? ReplaceOne.valueOf(fieldReference) : ReplaceOne.valueOf(expression); } @@ -743,6 +763,7 @@ public class StringOperators { return createReplaceAll().findValueOf(search).replacement(replacement); } + @SuppressWarnings("NullAway") private ReplaceAll createReplaceAll() { return usesFieldRef() ? ReplaceAll.valueOf(fieldReference) : ReplaceAll.valueOf(expression); } @@ -810,6 +831,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Concat}. */ + @Contract("_ -> new") public Concat concatValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -822,6 +844,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Concat}. */ + @Contract("_ -> new") public Concat concatValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -834,6 +857,7 @@ public class StringOperators { * @param value must not be {@literal null}. * @return new instance of {@link Concat}. */ + @Contract("_ -> new") public Concat concat(String value) { return new Concat(append(value)); } @@ -883,6 +907,7 @@ public class StringOperators { * @param start start index (including) * @return new instance of {@link Substr}. */ + @Contract("_ -> new") public Substr substring(int start) { return substring(start, -1); } @@ -892,6 +917,7 @@ public class StringOperators { * @param nrOfChars * @return new instance of {@link Substr}. */ + @Contract("_, _ -> new") public Substr substring(int start, int nrOfChars) { return new Substr(append(Arrays.asList(start, nrOfChars))); } @@ -1055,16 +1081,19 @@ public class StringOperators { return new StrCaseCmp(Collections.singletonList(value)); } + @Contract("_ -> new") public StrCaseCmp strcasecmp(String value) { return new StrCaseCmp(append(value)); } + @Contract("_ -> new") public StrCaseCmp strcasecmpValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); return new StrCaseCmp(append(Fields.field(fieldReference))); } + @Contract("_ -> new") public StrCaseCmp strcasecmpValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1118,6 +1147,7 @@ public class StringOperators { * @param range must not be {@literal null}. * @return new instance of {@link IndexOfBytes}. */ + @Contract("_ -> new") public IndexOfBytes within(Range range) { return new IndexOfBytes(append(AggregationUtils.toRangeValues(range))); } @@ -1208,6 +1238,7 @@ public class StringOperators { * @param range must not be {@literal null}. * @return new instance of {@link IndexOfCP}. */ + @Contract("_ -> new") public IndexOfCP within(Range range) { return new IndexOfCP(append(AggregationUtils.toRangeValues(range))); } @@ -1298,6 +1329,7 @@ public class StringOperators { * @param delimiter must not be {@literal null}. * @return new instance of {@link Split}. */ + @Contract("_ -> new") public Split split(String delimiter) { Assert.notNull(delimiter, "Delimiter must not be null"); @@ -1310,6 +1342,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Split}. */ + @Contract("_ -> new") public Split split(Field fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1322,6 +1355,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Split}. */ + @Contract("_ -> new") public Split split(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1447,10 +1481,12 @@ public class StringOperators { return new SubstrCP(Collections.singletonList(expression)); } + @Contract("_ -> new") public SubstrCP substringCP(int start) { return substringCP(start, -1); } + @Contract("_, _ -> new") public SubstrCP substringCP(int start, int nrOfChars) { return new SubstrCP(append(Arrays.asList(start, nrOfChars))); } @@ -1501,6 +1537,7 @@ public class StringOperators { * @param chars must not be {@literal null}. * @return new instance of {@link Trim}. */ + @Contract("_ -> new") public Trim chars(String chars) { Assert.notNull(chars, "Chars must not be null"); @@ -1514,6 +1551,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Trim}. */ + @Contract("_ -> new") public Trim charsOf(String fieldReference) { return new Trim(append("chars", Fields.field(fieldReference))); } @@ -1525,6 +1563,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link Trim}. */ + @Contract("_ -> new") public Trim charsOf(AggregationExpression expression) { return new Trim(append("chars", expression)); } @@ -1598,6 +1637,7 @@ public class StringOperators { * @param chars must not be {@literal null}. * @return new instance of {@link LTrim}. */ + @Contract("_ -> new") public LTrim chars(String chars) { Assert.notNull(chars, "Chars must not be null"); @@ -1611,6 +1651,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link LTrim}. */ + @Contract("_ -> new") public LTrim charsOf(String fieldReference) { return new LTrim(append("chars", Fields.field(fieldReference))); } @@ -1622,6 +1663,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link LTrim}. */ + @Contract("_ -> new") public LTrim charsOf(AggregationExpression expression) { return new LTrim(append("chars", expression)); } @@ -1677,6 +1719,7 @@ public class StringOperators { * @param chars must not be {@literal null}. * @return new instance of {@link RTrim}. */ + @Contract("_ -> new") public RTrim chars(String chars) { Assert.notNull(chars, "Chars must not be null"); @@ -1689,6 +1732,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RTrim}. */ + @Contract("_ -> new") public RTrim charsOf(String fieldReference) { return new RTrim(append("chars", Fields.field(fieldReference))); } @@ -1699,6 +1743,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RTrim}. */ + @Contract("_ -> new") public RTrim charsOf(AggregationExpression expression) { return new RTrim(append("chars", expression)); } @@ -1757,6 +1802,7 @@ public class StringOperators { * @param options must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind options(String options) { Assert.notNull(options, "Options must not be null"); @@ -1771,6 +1817,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind optionsOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -1785,6 +1832,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind optionsOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1798,6 +1846,7 @@ public class StringOperators { * @param regex must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind regex(String regex) { Assert.notNull(regex, "Regex must not be null"); @@ -1811,6 +1860,7 @@ public class StringOperators { * @param pattern must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind pattern(Pattern pattern) { Assert.notNull(pattern, "Pattern must not be null"); @@ -1827,6 +1877,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind regexOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -1840,6 +1891,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexFind}. */ + @Contract("_ -> new") public RegexFind regexOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1899,6 +1951,7 @@ public class StringOperators { * @param options must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll options(String options) { Assert.notNull(options, "Options must not be null"); @@ -1913,6 +1966,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll optionsOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -1927,6 +1981,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll optionsOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -1940,6 +1995,7 @@ public class StringOperators { * @param pattern must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll pattern(Pattern pattern) { Assert.notNull(pattern, "Pattern must not be null"); @@ -1956,6 +2012,7 @@ public class StringOperators { * @param regex must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll regex(String regex) { Assert.notNull(regex, "Regex must not be null"); @@ -1969,6 +2026,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll regexOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -1982,6 +2040,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexFindAll}. */ + @Contract("_ -> new") public RegexFindAll regexOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2043,6 +2102,7 @@ public class StringOperators { * @param options must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch options(String options) { Assert.notNull(options, "Options must not be null"); @@ -2057,6 +2117,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch optionsOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -2071,6 +2132,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch optionsOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2084,6 +2146,7 @@ public class StringOperators { * @param pattern must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch pattern(Pattern pattern) { Assert.notNull(pattern, "Pattern must not be null"); @@ -2100,6 +2163,7 @@ public class StringOperators { * @param regex must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch regex(String regex) { Assert.notNull(regex, "Regex must not be null"); @@ -2113,6 +2177,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch regexOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -2126,6 +2191,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link RegexMatch}. */ + @Contract("_ -> new") public RegexMatch regexOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2201,6 +2267,7 @@ public class StringOperators { * @param replacement must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne replacement(String replacement) { Assert.notNull(replacement, "Replacement must not be null"); @@ -2215,6 +2282,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne replacementOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -2229,6 +2297,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne replacementOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2242,6 +2311,7 @@ public class StringOperators { * @param value must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne find(String value) { Assert.notNull(value, "Search string must not be null"); @@ -2255,6 +2325,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne findValueOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -2269,6 +2340,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ReplaceOne}. */ + @Contract("_ -> new") public ReplaceOne findValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2344,6 +2416,7 @@ public class StringOperators { * @param replacement must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll replacement(String replacement) { Assert.notNull(replacement, "Replacement must not be null"); @@ -2358,6 +2431,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll replacementValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -2372,6 +2446,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll replacementValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -2385,6 +2460,7 @@ public class StringOperators { * @param value must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll find(String value) { Assert.notNull(value, "Search string must not be null"); @@ -2398,6 +2474,7 @@ public class StringOperators { * @param fieldReference must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll findValueOf(String fieldReference) { Assert.notNull(fieldReference, "fieldReference must not be null"); @@ -2411,6 +2488,7 @@ public class StringOperators { * @param expression must not be {@literal null}. * @return new instance of {@link ReplaceAll}. */ + @Contract("_ -> new") public ReplaceAll findValueOf(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java index 1fcf87d2a..cc0296c90 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SystemVariable.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.aggregation; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Describes the system variables available in MongoDB aggregation framework pipeline expressions. @@ -116,8 +116,7 @@ public enum SystemVariable implements AggregationVariable { return toString(); } - @Nullable - static String variableNameFrom(@Nullable String fieldRef) { + static @Nullable String variableNameFrom(@Nullable String fieldRef) { if (fieldRef == null || !fieldRef.startsWith(PREFIX) || fieldRef.length() <= 2) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java index f30ebf394..d2d49abf7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java @@ -22,7 +22,7 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.context.MappingContext; @@ -33,7 +33,6 @@ import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java index 057ada12d..c93c1bad9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperation.java @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java index ff765c37f..0bcc192de 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnsetOperation.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; import org.bson.Document; import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -67,6 +68,7 @@ public class UnsetOperation implements InheritsFieldsAggregationOperation { * @param fields must not be {@literal null}. * @return new instance of {@link UnsetOperation}. */ + @Contract("_ -> new") public UnsetOperation and(String... fields) { List target = new ArrayList<>(this.fields); @@ -80,6 +82,7 @@ public class UnsetOperation implements InheritsFieldsAggregationOperation { * @param fields must not be {@literal null}. * @return new instance of {@link UnsetOperation}. */ + @Contract("_ -> new") public UnsetOperation and(Field... fields) { List target = new ArrayList<>(this.fields); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java index d59ae01b1..cc0552cd1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/UnwindOperation.java @@ -16,8 +16,9 @@ package org.springframework.data.mongodb.core.aggregation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -201,6 +202,8 @@ public class UnwindOperation @Override public UnwindOperation preserveNullAndEmptyArrays() { + Assert.notNull(field, "Path needs to be set first"); + if (arrayIndex != null) { return new UnwindOperation(field, arrayIndex, true); } @@ -211,6 +214,8 @@ public class UnwindOperation @Override public UnwindOperation skipNullAndEmptyArrays() { + Assert.notNull(field, "Path needs to be set first"); + if (arrayIndex != null) { return new UnwindOperation(field, arrayIndex, false); } @@ -219,6 +224,7 @@ public class UnwindOperation } @Override + @Contract("_ -> this") public EmptyArraysBuilder arrayIndex(String field) { Assert.hasText(field, "'ArrayIndex' must not be null or empty"); @@ -227,6 +233,7 @@ public class UnwindOperation } @Override + @Contract("-> this") public EmptyArraysBuilder noArrayIndex() { arrayIndex = null; @@ -234,6 +241,7 @@ public class UnwindOperation } @Override + @Contract("_ -> this") public UnwindOperationBuilder path(String path) { Assert.hasText(path, "'Path' must not be null or empty"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java index 8e676c72b..b5a9ca0f2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java @@ -22,8 +22,8 @@ import java.util.List; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -223,8 +223,7 @@ public class VariableOperators { private final List vars; - @Nullable // - private final AggregationExpression expression; + private final @Nullable AggregationExpression expression; private Let(List vars, @Nullable AggregationExpression expression) { @@ -333,6 +332,7 @@ public class VariableOperators { return new Document(var.variableName, var.expression); } + @SuppressWarnings("NullAway") private Object getMappedIn(AggregationOperationContext context) { return expression.toDocument(new NestedDelegatingExpressionAggregationOperationContext(context, this.vars.stream().map(var -> Fields.field(var.variableName)).collect(Collectors.toList()))); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VectorSearchOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VectorSearchOperation.java index dd14ef20c..de266a306 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VectorSearchOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VectorSearchOperation.java @@ -24,13 +24,14 @@ import java.util.stream.Collectors; import org.bson.BinaryVector; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Limit; import org.springframework.data.domain.Vector; import org.springframework.data.mongodb.core.mapping.MongoVector; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.lang.Contract; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -55,12 +56,12 @@ public class VectorSearchOperation implements AggregationOperation { private final @Nullable Integer numCandidates; private final QueryPaths path; private final Vector vector; - private final String score; - private final Consumer scoreCriteria; + private final @Nullable String score; + private final @Nullable Consumer scoreCriteria; private VectorSearchOperation(SearchType searchType, @Nullable CriteriaDefinition filter, String indexName, Limit limit, @Nullable Integer numCandidates, QueryPaths path, Vector vector, @Nullable String searchScore, - Consumer scoreCriteria) { + @Nullable Consumer scoreCriteria) { this.searchType = searchType; this.filter = filter; @@ -299,9 +300,9 @@ public class VectorSearchOperation implements AggregationOperation { */ private static class VectorSearchBuilder implements PathContributor, VectorContributor, LimitContributor { - String index; - QueryPath paths; - Vector vector; + @Nullable String index; + @Nullable QueryPath paths; + @Nullable Vector vector; PathContributor index(String index) { this.index = index; @@ -317,6 +318,11 @@ public class VectorSearchOperation implements AggregationOperation { @Override public VectorSearchOperation limit(Limit limit) { + + Assert.notNull(index, "Index must be set first"); + Assert.notNull(paths, "Path must be set first"); + Assert.notNull(vector, "Vector must be set first"); + return new VectorSearchOperation(index, QueryPaths.of(paths), limit, vector); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java index 0e30b8b85..2769990ca 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java @@ -3,6 +3,6 @@ * * @since 1.3 */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.aggregation; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java index 3e08dc101..9ada2014a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/annotation/package-info.java @@ -1,6 +1,6 @@ /** * Core Spring Data MongoDB annotations not limited to a special use case (like Query,...). */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.annotation; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java index 7a0167793..9b1c744be 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/AbstractMongoConverter.java @@ -20,6 +20,7 @@ import java.util.Date; import org.bson.types.Code; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; @@ -31,7 +32,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverters.BigIntegerT import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToBigIntegerConverter; import org.springframework.data.mongodb.core.convert.MongoConverters.ObjectIdToStringConverter; import org.springframework.data.mongodb.core.convert.MongoConverters.StringToObjectIdConverter; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java index 40afbb8c1..3b4dd99d4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefProxyHandler.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import com.mongodb.DBRef; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java index 023569403..ee1f56849 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java @@ -18,10 +18,10 @@ package org.springframework.data.mongodb.core.convert; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import com.mongodb.DBRef; @@ -60,7 +60,7 @@ public interface DbRefResolver extends ReferenceResolver { * @param id will never be {@literal null}. * @return new instance of {@link DBRef}. */ - default DBRef createDbRef(@Nullable org.springframework.data.mongodb.core.mapping.DBRef annotation, + default DBRef createDbRef(org.springframework.data.mongodb.core.mapping.@Nullable DBRef annotation, MongoPersistentEntity entity, Object id) { if (annotation != null && StringUtils.hasText(annotation.db())) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java index bf6b88237..fd8002911 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolverCallback.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; /** @@ -31,5 +32,5 @@ public interface DbRefResolverCallback { * @param property will never be {@literal null}. * @return */ - Object resolve(MongoPersistentProperty property); + @Nullable Object resolve(MongoPersistentProperty property); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java index 22b1ce798..13c0198aa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefProxyHandler.java @@ -18,13 +18,12 @@ package org.springframework.data.mongodb.core.convert; import java.util.function.Function; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import com.mongodb.DBRef; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java index de66c3ea9..c72bc4b88 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java @@ -25,6 +25,7 @@ import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.MongoDatabaseUtils; @@ -32,8 +33,8 @@ import org.springframework.data.mongodb.core.convert.ReferenceLoader.DocumentRef import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import com.mongodb.DBRef; @@ -71,7 +72,7 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db } @Override - public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback, + public @Nullable Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback, DbRefProxyHandler handler) { Assert.notNull(property, "Property must not be null"); @@ -86,7 +87,7 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db } @Override - public Document fetch(DBRef dbRef) { + public @Nullable Document fetch(DBRef dbRef) { return getReferenceLoader().fetchOne( DocumentReferenceQuery.forSingleDocument(Filters.eq(FieldName.ID.name(), dbRef.getId())), ReferenceCollection.fromDBRef(dbRef)); @@ -171,7 +172,7 @@ public class DefaultDbRefResolver extends DefaultReferenceResolver implements Db private static Stream documentWithId(Object identifier, Collection documents) { return documents.stream() // - .filter(it -> it.get(BasicMongoPersistentProperty.ID_FIELD_NAME).equals(identifier)) // + .filter(it -> ObjectUtils.nullSafeEquals(it.get(BasicMongoPersistentProperty.ID_FIELD_NAME), identifier)) // .limit(1); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java index 82e5c9d0e..376e0dd8c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolverCallback.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.convert; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; @@ -53,7 +54,7 @@ class DefaultDbRefResolverCallback implements DbRefResolverCallback { } @Override - public Object resolve(MongoPersistentProperty property) { + public @Nullable Object resolve(MongoPersistentProperty property) { return resolver.getValueInternal(property, surroundingObject, evaluator, path); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java index 2c2b52afd..f5db41f00 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultMongoTypeMapper.java @@ -23,6 +23,7 @@ import java.util.function.UnaryOperator; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.DefaultTypeMapper; import org.springframework.data.convert.SimpleTypeInformationMapper; @@ -32,7 +33,6 @@ import org.springframework.data.mapping.Alias; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import com.mongodb.BasicDBList; @@ -114,7 +114,7 @@ public class DefaultMongoTypeMapper extends DefaultTypeMapper implements M } private DefaultMongoTypeMapper(@Nullable String typeKey, TypeAliasAccessor accessor, - MappingContext, ?> mappingContext, + @Nullable MappingContext, ?> mappingContext, List mappers) { super(accessor, mappingContext, mappers); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java index a7b3d6f21..4df7c02f9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java @@ -20,6 +20,7 @@ import static org.springframework.data.mongodb.core.convert.ReferenceLookupDeleg import java.util.Collections; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.DocumentReference; @@ -63,7 +64,8 @@ public class DefaultReferenceResolver implements ReferenceResolver { } @Override - public Object resolveReference(MongoPersistentProperty property, Object source, + @SuppressWarnings("NullAway") + public @Nullable Object resolveReference(MongoPersistentProperty property, Object source, ReferenceLookupDelegate referenceLookupDelegate, MongoEntityReader entityReader) { LookupFunction lookupFunction = (property.isCollectionLike() || property.isMap()) ? collectionLookupFunction @@ -84,6 +86,7 @@ public class DefaultReferenceResolver implements ReferenceResolver { * @see DBRef#lazy() * @see DocumentReference#lazy() */ + @SuppressWarnings("NullAway") protected boolean isLazyReference(MongoPersistentProperty property) { if (property.isDocumentReference()) { @@ -106,6 +109,7 @@ public class DefaultReferenceResolver implements ReferenceResolver { return proxyFactory; } + @SuppressWarnings("NullAway") private Object createLazyLoadingProxy(MongoPersistentProperty property, Object source, ReferenceLookupDelegate referenceLookupDelegate, LookupFunction lookupFunction, MongoEntityReader entityReader) { return proxyFactory.createLazyLoadingProxy(property, diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java index c795add9c..ff50dd5df 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java @@ -21,11 +21,11 @@ import java.util.Map; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.DBObject; @@ -119,8 +119,7 @@ class DocumentAccessor { * @param property must not be {@literal null}. * @return can be {@literal null}. */ - @Nullable - public Object get(MongoPersistentProperty property) { + public @Nullable Object get(MongoPersistentProperty property) { return BsonUtils.resolveValue(document, getFieldName(property)); } @@ -131,8 +130,7 @@ class DocumentAccessor { * @param entity must not be {@literal null}. * @return */ - @Nullable - public Object getRawId(MongoPersistentEntity entity) { + public @Nullable Object getRawId(MongoPersistentEntity entity) { return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.get(document, FieldName.ID.name()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java index 8429584a6..e03d21508 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java @@ -70,6 +70,7 @@ class DocumentPointerFactory { this.cache = new WeakHashMap<>(); } + @SuppressWarnings("NullAway") DocumentPointer computePointer( MappingContext, MongoPersistentProperty> mappingContext, MongoPersistentProperty property, Object value, Class typeHint) { @@ -87,7 +88,7 @@ class DocumentPointerFactory { if (usesDefaultLookup(property)) { - MongoPersistentProperty idProperty = persistentEntity.getIdProperty(); + MongoPersistentProperty idProperty = persistentEntity.getRequiredIdProperty(); Object idValue = persistentEntity.getIdentifierAccessor(value).getIdentifier(); if (idProperty.hasExplicitWriteTarget() @@ -114,6 +115,7 @@ class DocumentPointerFactory { .getDocumentPointer(mappingContext, persistentEntity, propertyAccessor); } + @SuppressWarnings("NullAway") private boolean usesDefaultLookup(MongoPersistentProperty property) { if (property.isDocumentReference()) { @@ -216,9 +218,16 @@ class DocumentPointerFactory { MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(entry.getKey()); if (persistentProperty != null && persistentProperty.isEntity()) { - MongoPersistentEntity nestedEntity = mappingContext.getPersistentEntity(persistentProperty.getType()); - target.put(entry.getKey(), updatePlaceholders(document, new Document(), mappingContext, - nestedEntity, nestedEntity.getPropertyAccessor(propertyAccessor.getProperty(persistentProperty)))); + MongoPersistentEntity nestedEntity = mappingContext.getRequiredPersistentEntity(persistentProperty.getType()); + Object propertyValue = propertyAccessor.getProperty(persistentProperty); + + if(propertyValue == null) { + target.put(entry.getKey(), propertyValue); + } else { + PersistentPropertyAccessor nestedAccessor = nestedEntity.getPropertyAccessor(propertyValue); + target.put(entry.getKey(), updatePlaceholders(document, new Document(), mappingContext, + nestedEntity, nestedAccessor)); + } } else { target.put(entry.getKey(), updatePlaceholders((Document) entry.getValue(), new Document(), mappingContext, persistentEntity, propertyAccessor)); @@ -236,7 +245,7 @@ class DocumentPointerFactory { String fieldName = entry.getKey().equals(FieldName.ID.name()) ? "id" : entry.getKey(); if (!fieldName.contains(".")) { - Object targetValue = propertyAccessor.getProperty(persistentEntity.getPersistentProperty(fieldName)); + Object targetValue = propertyAccessor.getProperty(persistentEntity.getRequiredPersistentProperty(fieldName)); target.put(attribute, targetValue); continue; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java index ea5ce01b4..a41e17c0e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPropertyAccessor.java @@ -18,11 +18,11 @@ package org.springframework.data.mongodb.core.convert; import java.util.Map; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; -import org.springframework.lang.Nullable; /** * {@link PropertyAccessor} to allow entity based field access to {@link Document}s. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java index bf2178105..b1e894efe 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java @@ -15,7 +15,8 @@ */ package org.springframework.data.mongodb.core.convert; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; /** * The source object to resolve document references upon. Encapsulates the actual source and the reference specific @@ -56,8 +57,7 @@ public class DocumentReferenceSource { * * @return can be {@literal null}. */ - @Nullable - public Object getTargetSource() { + public @Nullable Object getTargetSource() { return targetSource; } @@ -67,8 +67,7 @@ public class DocumentReferenceSource { * @param source * @return */ - @Nullable - static Object getTargetSource(Object source) { + static @Nullable Object getTargetSource(@Nullable Object source) { return source instanceof DocumentReferenceSource referenceSource ? referenceSource.getTargetSource() : source; } @@ -78,7 +77,8 @@ public class DocumentReferenceSource { * @param self * @return */ - static Object getSelf(Object self) { + @Contract("null -> null") + static @Nullable Object getSelf(@Nullable Object self) { return self instanceof DocumentReferenceSource referenceSource ? referenceSource.getSelf() : self; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java index 2bca260b7..ae73ab68b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java @@ -25,6 +25,7 @@ import java.util.TreeMap; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; @@ -45,6 +46,7 @@ import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; import org.springframework.data.mongodb.core.geo.Sphere; import org.springframework.data.mongodb.core.query.GeoCommand; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; @@ -130,7 +132,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Point convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable Point convert(@Nullable Document source) { if (source == null) { return null; @@ -157,7 +160,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(Point source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable Point source) { return source == null ? null : new Document("x", source.getX()).append("y", source.getY()); } } @@ -174,7 +178,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(Box source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable Box source) { if (source == null) { return null; @@ -199,7 +204,9 @@ abstract class GeoConverters { INSTANCE; @Override - public Box convert(Document source) { + @Contract("null -> null; !null -> !null") + @SuppressWarnings("NullAway") + public @Nullable Box convert(@Nullable Document source) { if (source == null) { return null; @@ -223,7 +230,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(Circle source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable Circle source) { if (source == null) { return null; @@ -249,7 +257,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Circle convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable Circle convert(@Nullable Document source) { if (source == null) { return null; @@ -286,7 +295,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(Sphere source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable Sphere source) { if (source == null) { return null; @@ -312,7 +322,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Sphere convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable Sphere convert(@Nullable Document source) { if (source == null) { return null; @@ -349,7 +360,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(Polygon source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable Polygon source) { if (source == null) { return null; @@ -381,18 +393,20 @@ abstract class GeoConverters { @Override @SuppressWarnings({ "unchecked" }) - public Polygon convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable Polygon convert(@Nullable Document source) { if (source == null) { return null; } List points = (List) source.get("points"); - List newPoints = new ArrayList<>(points.size()); + Assert.notNull(points, "Points elements of polygon must not be null"); + List newPoints = new ArrayList<>(points.size()); for (Document element : points) { - Assert.notNull(element, "Point elements of polygon must not be null"); + Assert.notNull(element, "Point elements of polygon must not contain null"); newPoints.add(DocumentToPointConverter.INSTANCE.convert(element)); } @@ -412,7 +426,8 @@ abstract class GeoConverters { @Override @SuppressWarnings("rawtypes") - public Document convert(GeoCommand source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable GeoCommand source) { if (source == null) { return null; @@ -463,7 +478,8 @@ abstract class GeoConverters { INSTANCE; @Override - public Document convert(GeoJson source) { + @Contract("null -> null; !null -> !null") + public @Nullable Document convert(@Nullable GeoJson source) { if (source == null) { return null; @@ -490,7 +506,7 @@ abstract class GeoConverters { private Object convertIfNecessary(Object candidate) { - if (candidate instanceof GeoJson geoJson) { + if (candidate instanceof GeoJson geoJson) { return convertIfNecessary(geoJson.getCoordinates()); } @@ -551,7 +567,8 @@ abstract class GeoConverters { @Override @SuppressWarnings("unchecked") - public GeoJsonPoint convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonPoint convert(@Nullable Document source) { if (source == null) { return null; @@ -560,7 +577,10 @@ abstract class GeoConverters { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"), String.format("Cannot convert type '%s' to Point", source.get("type"))); - List dbl = (List) source.get("coordinates"); + if(!(source.get("coordinates") instanceof List sourceCoordinates)) { + throw new IllegalArgumentException("Coordinates need to be present"); + } + List dbl = (List) sourceCoordinates; return new GeoJsonPoint(toPrimitiveDoubleValue(dbl.get(0)), toPrimitiveDoubleValue(dbl.get(1))); } } @@ -574,7 +594,8 @@ abstract class GeoConverters { INSTANCE; @Override - public GeoJsonPolygon convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonPolygon convert(@Nullable Document source) { if (source == null) { return null; @@ -596,7 +617,8 @@ abstract class GeoConverters { INSTANCE; @Override - public GeoJsonMultiPolygon convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonMultiPolygon convert(@Nullable Document source) { if (source == null) { return null; @@ -606,8 +628,9 @@ abstract class GeoConverters { String.format("Cannot convert type '%s' to MultiPolygon", source.get("type"))); List dbl = (List) source.get("coordinates"); - List polygones = new ArrayList<>(); + Assert.notNull(dbl, "Source needs to contain coordinates"); + List polygones = new ArrayList<>(dbl.size()); for (Object polygon : dbl) { polygones.add(toGeoJsonPolygon((List) polygon)); } @@ -625,7 +648,8 @@ abstract class GeoConverters { INSTANCE; @Override - public GeoJsonLineString convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonLineString convert(@Nullable Document source) { if (source == null) { return null; @@ -649,7 +673,8 @@ abstract class GeoConverters { INSTANCE; @Override - public GeoJsonMultiPoint convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonMultiPoint convert(@Nullable Document source) { if (source == null) { return null; @@ -673,7 +698,8 @@ abstract class GeoConverters { INSTANCE; @Override - public GeoJsonMultiLineString convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonMultiLineString convert(@Nullable Document source) { if (source == null) { return null; @@ -682,10 +708,13 @@ abstract class GeoConverters { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiLineString"), String.format("Cannot convert type '%s' to MultiLineString", source.get("type"))); - List lines = new ArrayList<>(); - List cords = (List) source.get("coordinates"); + if(!(source.get("coordinates") instanceof List coordinates)) { + throw new IllegalArgumentException("coordinates need to be present"); + } + + List lines = new ArrayList<>(coordinates.size()); - for (Object line : cords) { + for (Object line : coordinates) { lines.add(new GeoJsonLineString(toListOfPoint((List) line))); } return new GeoJsonMultiLineString(lines); @@ -700,9 +729,9 @@ abstract class GeoConverters { INSTANCE; - @SuppressWarnings("rawtypes") @Override - public GeoJsonGeometryCollection convert(Document source) { + @Contract("null -> null; !null -> !null") + public @Nullable GeoJsonGeometryCollection convert(@Nullable Document source) { if (source == null) { return null; @@ -711,8 +740,12 @@ abstract class GeoConverters { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"), String.format("Cannot convert type '%s' to GeometryCollection", source.get("type"))); - List> geometries = new ArrayList<>(); - for (Object o : (List) source.get("geometries")) { + if(!(source.get("geometries") instanceof List sourceGeometries)) { + throw new IllegalArgumentException("Geometries need to be present"); + } + + List> geometries = new ArrayList<>(sourceGeometries.size()); + for (Object o : sourceGeometries) { geometries.add(toGenericGeoJson((Document) o)); } @@ -732,7 +765,10 @@ abstract class GeoConverters { * @since 1.7 */ @SuppressWarnings("unchecked") - static List toListOfPoint(List listOfCoordinatePairs) { + @Contract("null -> fail") + static List toListOfPoint(@Nullable List listOfCoordinatePairs) { + + Assert.notNull(listOfCoordinatePairs, "ListOfCoordinatePairs must not be null"); List points = new ArrayList<>(listOfCoordinatePairs.size()); @@ -755,7 +791,10 @@ abstract class GeoConverters { * @return never {@literal null}. * @since 1.7 */ - static GeoJsonPolygon toGeoJsonPolygon(List dbList) { + @Contract("null -> fail") + static GeoJsonPolygon toGeoJsonPolygon(@Nullable List dbList) { + + Assert.notNull(dbList, "DbList must not be null"); GeoJsonPolygon polygon = new GeoJsonPolygon(toListOfPoint((List) dbList.get(0))); return dbList.size() > 1 ? polygon.withInnerRing(toListOfPoint((List) dbList.get(1))) : polygon; @@ -794,7 +833,8 @@ abstract class GeoConverters { throw new IllegalArgumentException(String.format("No converter found capable of converting GeoJson type %s", type)); } - private static double toPrimitiveDoubleValue(Object value) { + @Contract("null -> fail") + private static double toPrimitiveDoubleValue(@Nullable Object value) { Assert.isInstanceOf(Number.class, value, "Argument must be a Number"); return NumberUtils.convertNumberToTargetClass((Number) value, Double.class); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java index 77aac5581..6329d74d4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxy.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.convert; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.DBRef; @@ -53,8 +53,7 @@ public interface LazyLoadingProxy { * @return can be {@literal null}. * @since 3.3 */ - @Nullable - default Object getSource() { + default @Nullable Object getSource() { return toDBRef(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java index 76539ea43..eff58e7bd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/LazyLoadingProxyFactory.java @@ -30,6 +30,8 @@ import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.aop.framework.ProxyFactory; import org.springframework.cglib.core.SpringNamingPolicy; import org.springframework.cglib.proxy.Callback; @@ -43,7 +45,6 @@ import org.springframework.data.mongodb.LazyLoadingException; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.Lock; import org.springframework.data.util.Lock.AcquiredLock; -import org.springframework.lang.Nullable; import org.springframework.objenesis.SpringObjenesis; import org.springframework.util.ReflectionUtils; @@ -124,7 +125,7 @@ public final class LazyLoadingProxyFactory { } public Object createLazyLoadingProxy(MongoPersistentProperty property, DbRefResolverCallback callback, - Object source) { + @Nullable Object source) { Class propertyType = property.getType(); LazyLoadingInterceptor interceptor = new LazyLoadingInterceptor(property, callback, source, exceptionTranslator); @@ -160,6 +161,7 @@ public final class LazyLoadingProxyFactory { return enhancer.createClass(); } + @NullUnmarked public static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable { @@ -180,10 +182,10 @@ public final class LazyLoadingProxyFactory { private final Lock readLock = Lock.of(rwLock.readLock()); private final Lock writeLock = Lock.of(rwLock.writeLock()); - private final MongoPersistentProperty property; - private final DbRefResolverCallback callback; - private final Object source; - private final PersistenceExceptionTranslator exceptionTranslator; + private final @Nullable MongoPersistentProperty property; + private final @Nullable DbRefResolverCallback callback; + private final @Nullable Object source; + private final @Nullable PersistenceExceptionTranslator exceptionTranslator; private volatile boolean resolved; private @Nullable Object result; @@ -191,18 +193,17 @@ public final class LazyLoadingProxyFactory { * @return a {@link LazyLoadingInterceptor} that just continues with the invocation. * @since 4.0 */ + @SuppressWarnings("NullAway") public static LazyLoadingInterceptor none() { return new LazyLoadingInterceptor(null, null, null, null) { - @Nullable @Override - public Object invoke(MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null); } - @Nullable @Override - public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable { + public @Nullable Object intercept(Object o, Method method, @Nullable Object @Nullable[] args, @Nullable MethodProxy proxy) throws Throwable { ReflectionUtils.makeAccessible(method); return method.invoke(o, args); @@ -210,8 +211,8 @@ public final class LazyLoadingProxyFactory { }; } - public LazyLoadingInterceptor(MongoPersistentProperty property, DbRefResolverCallback callback, Object source, - PersistenceExceptionTranslator exceptionTranslator) { + public LazyLoadingInterceptor(@Nullable MongoPersistentProperty property, @Nullable DbRefResolverCallback callback, @Nullable Object source, + @Nullable PersistenceExceptionTranslator exceptionTranslator) { this.property = property; this.callback = callback; @@ -219,15 +220,13 @@ public final class LazyLoadingProxyFactory { this.exceptionTranslator = exceptionTranslator; } - @Nullable @Override - public Object invoke(MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { return intercept(invocation.getThis(), invocation.getMethod(), invocation.getArguments(), null); } - @Nullable @Override - public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable { + public @Nullable Object intercept(Object o, Method method, @Nullable Object @Nullable[] args, @Nullable MethodProxy proxy) throws Throwable { if (INITIALIZE_METHOD.equals(method)) { return ensureResolved(); @@ -247,7 +246,7 @@ public final class LazyLoadingProxyFactory { return proxyToString(source); } - if (ReflectionUtils.isEqualsMethod(method)) { + if (ReflectionUtils.isEqualsMethod(method) && args != null) { return proxyEquals(o, args[0]); } @@ -347,8 +346,8 @@ public final class LazyLoadingProxyFactory { } } - @Nullable - private Object resolve() { + @SuppressWarnings("NullAway") + private @Nullable Object resolve() { try (AcquiredLock l = readLock.lock()) { if (resolved) { @@ -370,7 +369,7 @@ public final class LazyLoadingProxyFactory { return writeLock.execute(() -> callback.resolve(property)); } catch (RuntimeException ex) { - DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(ex); + DataAccessException translatedException = exceptionTranslator != null ? exceptionTranslator.translateExceptionIfPossible(ex) : null; if (translatedException instanceof ClientSessionException) { throw new LazyLoadingException("Unable to lazily resolve DBRef; Invalid session state", ex); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 1d40573b8..2d073869a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -39,7 +39,7 @@ import org.bson.codecs.configuration.CodecRegistry; import org.bson.conversions.Bson; import org.bson.json.JsonReader; import org.bson.types.ObjectId; - +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.context.ApplicationContext; @@ -53,6 +53,7 @@ import org.springframework.core.env.EnvironmentCapable; import org.springframework.core.env.StandardEnvironment; import org.springframework.data.annotation.Reference; import org.springframework.data.convert.CustomConversions; +import org.springframework.data.convert.PropertyValueConversions; import org.springframework.data.convert.PropertyValueConverter; import org.springframework.data.convert.TypeMapper; import org.springframework.data.convert.ValueConversionContext; @@ -94,7 +95,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.util.Predicates; import org.springframework.data.util.TypeInformation; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -157,7 +158,7 @@ public class MappingMongoConverter extends AbstractMongoConverter protected @Nullable ApplicationContext applicationContext; protected @Nullable Environment environment; - protected MongoTypeMapper typeMapper; + protected @Nullable MongoTypeMapper typeMapper; protected @Nullable String mapKeyDotReplacement = null; protected @Nullable CodecRegistryProvider codecRegistryProvider; @@ -307,7 +308,9 @@ public class MappingMongoConverter extends AbstractMongoConverter this.environment = applicationContext.getEnvironment(); this.spELContext = new SpELContext(this.spELContext, applicationContext); this.projectionFactory.setBeanFactory(applicationContext); - this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader()); + if(applicationContext.getClassLoader() != null) { + this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader()); + } if (entityCallbacks == null) { setEntityCallbacks(EntityCallbacks.create(applicationContext)); @@ -418,7 +421,7 @@ public class MappingMongoConverter extends AbstractMongoConverter return accessor.getBean(); } - private Object doReadOrProject(ConversionContext context, Bson source, TypeInformation typeHint, + private Object doReadOrProject(ConversionContext context, @Nullable Bson source, TypeInformation typeHint, EntityProjection typeDescriptor) { if (typeDescriptor.isProjection()) { @@ -433,12 +436,12 @@ public class MappingMongoConverter extends AbstractMongoConverter Map map = new LinkedHashMap<>(); @Override - public void setProperty(PersistentProperty persistentProperty, Object o) { + public void setProperty(PersistentProperty persistentProperty, @Nullable Object o) { map.put(persistentProperty.getName(), o); } @Override - public Object getProperty(PersistentProperty persistentProperty) { + public @Nullable Object getProperty(PersistentProperty persistentProperty) { return map.get(persistentProperty.getName()); } @@ -466,10 +469,14 @@ public class MappingMongoConverter extends AbstractMongoConverter * @return the converted object, will never be {@literal null}. * @since 3.2 */ - @SuppressWarnings("unchecked") - protected S readDocument(ConversionContext context, Bson bson, + @SuppressWarnings({"unchecked","NullAway"}) + protected S readDocument(ConversionContext context, @Nullable Bson bson, TypeInformation typeHint) { + if(bson == null) { + bson = new Document(); + } + Document document = bson instanceof BasicDBObject dbObject ? new Document(dbObject) : (Document) bson; TypeInformation typeToRead = getTypeMapper().readType(document, typeHint); Class rawType = typeToRead.getType(); @@ -545,7 +552,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public T evaluate(String expression) { + public @Nullable T evaluate(String expression) { return expressionEvaluatorFactory.create(getDocument()).evaluate(expression); } } @@ -599,8 +606,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * Reads the identifier from either the bean backing the {@link PersistentPropertyAccessor} or the source document in * case the identifier has not be populated yet. In this case the identifier is set on the bean for further reference. */ - @Nullable - private Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor accessor, + private @Nullable Object readAndPopulateIdentifier(ConversionContext context, PersistentPropertyAccessor accessor, DocumentAccessor document, MongoPersistentEntity entity, ValueExpressionEvaluator evaluator) { Object rawId = document.getRawId(entity); @@ -684,8 +690,7 @@ public class MappingMongoConverter extends AbstractMongoConverter (prop, bson, e, path) -> MappingMongoConverter.this.getValueInternal(context, prop, bson, e)); } - @Nullable - private Object readAssociation(Association association, DocumentAccessor documentAccessor, + private @Nullable Object readAssociation(Association association, DocumentAccessor documentAccessor, DbRefProxyHandler handler, DbRefResolverCallback callback, ConversionContext context) { MongoPersistentProperty property = association.getInverse(); @@ -745,8 +750,8 @@ public class MappingMongoConverter extends AbstractMongoConverter } } - @Nullable - private Object readUnwrapped(ConversionContext context, DocumentAccessor documentAccessor, + @SuppressWarnings("NullAway") + private @Nullable Object readUnwrapped(ConversionContext context, DocumentAccessor documentAccessor, MongoPersistentProperty prop, MongoPersistentEntity unwrappedEntity) { if (prop.findAnnotation(Unwrapped.class).onEmpty().equals(OnEmpty.USE_EMPTY)) { @@ -762,13 +767,14 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override + @SuppressWarnings("NullAway") public DBRef toDBRef(Object object, @Nullable MongoPersistentProperty referringProperty) { org.springframework.data.mongodb.core.mapping.DBRef annotation; if (referringProperty != null) { annotation = referringProperty.getDBRef(); - Assert.isTrue(annotation != null, "The referenced property has to be mapped with @DBRef"); + Assert.notNull(annotation, "The referenced property has to be mapped with @DBRef"); } // DATAMONGO-913 @@ -780,6 +786,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override + @SuppressWarnings("NullAway") public DocumentPointer toDocumentPointer(Object source, @Nullable MongoPersistentProperty referringProperty) { if (source instanceof LazyLoadingProxy proxy) { @@ -799,6 +806,7 @@ public class MappingMongoConverter extends AbstractMongoConverter throw new IllegalArgumentException("The referringProperty is neither a DBRef nor a document reference"); } + @SuppressWarnings("NullAway") DocumentPointer createDocumentPointer(Object source, @Nullable MongoPersistentProperty referringProperty) { if (referringProperty == null) { @@ -863,7 +871,7 @@ public class MappingMongoConverter extends AbstractMongoConverter /** * Internal write conversion method which should be used for nested invocations. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","NullAway"}) protected void writeInternal(@Nullable Object obj, Bson bson, @Nullable TypeInformation typeHint) { if (null == obj) { @@ -1267,6 +1275,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * * @param key */ + @SuppressWarnings("NullAway") private String potentiallyConvertMapKey(Object key) { if (key instanceof String stringValue) { @@ -1332,20 +1341,23 @@ public class MappingMongoConverter extends AbstractMongoConverter property.hasExplicitWriteTarget() ? property.getFieldType() : Object.class)); } - @Nullable @SuppressWarnings("unchecked") - private Object applyPropertyConversion(@Nullable Object value, MongoPersistentProperty property, + private @Nullable Object applyPropertyConversion(@Nullable Object value, MongoPersistentProperty property, PersistentPropertyAccessor persistentPropertyAccessor) { MongoConversionContext context = new MongoConversionContext(new PropertyValueProvider<>() { - @Nullable @Override - public T getPropertyValue(MongoPersistentProperty property) { + public @Nullable T getPropertyValue(MongoPersistentProperty property) { return (T) persistentPropertyAccessor.getProperty(property); } }, property, this, spELContext); - PropertyValueConverter> valueConverter = conversions - .getPropertyValueConversions().getValueConverter(property); + + PropertyValueConversions propertyValueConversions = conversions.getPropertyValueConversions(); + if(propertyValueConversions == null) { + return value; + } + + PropertyValueConverter> valueConverter = propertyValueConversions.getValueConverter(property); return value != null ? valueConverter.write(value, context) : valueConverter.writeNull(context); } @@ -1353,8 +1365,9 @@ public class MappingMongoConverter extends AbstractMongoConverter * Checks whether we have a custom conversion registered for the given value into an arbitrary simple Mongo type. * Returns the converted value if so. If not, we perform special enum handling or simply return the value as is. */ - @Nullable - private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nullable Class typeHint) { + @Contract("null, _-> null") + @SuppressWarnings("NullAway") + private @Nullable Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nullable Class typeHint) { if (value == null) { return null; @@ -1390,7 +1403,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * * @since 3.2 */ - protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation target) { + protected @Nullable Object getPotentiallyConvertedSimpleRead(@Nullable Object value, TypeInformation target) { return getPotentiallyConvertedSimpleRead(value, target.getType()); } @@ -1398,10 +1411,11 @@ public class MappingMongoConverter extends AbstractMongoConverter * Checks whether we have a custom conversion for the given simple object. Converts the given value if so, applies * {@link Enum} handling or returns the value as is. */ + @Contract("null, _ -> null; _, null -> param1") @SuppressWarnings({ "rawtypes", "unchecked" }) - private Object getPotentiallyConvertedSimpleRead(Object value, @Nullable Class target) { + private @Nullable Object getPotentiallyConvertedSimpleRead(@Nullable Object value, @Nullable Class target) { - if (target == null) { + if (target == null || value == null) { return value; } @@ -1420,6 +1434,7 @@ public class MappingMongoConverter extends AbstractMongoConverter return doConvert(value, target); } + @SuppressWarnings("NullAway") protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty property) { Assert.notNull(target, "Target object must not be null"); @@ -1455,8 +1470,7 @@ public class MappingMongoConverter extends AbstractMongoConverter throw new MappingException("No id property found on class " + entity.getType()); } - @Nullable - private Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson, + private @Nullable Object getValueInternal(ConversionContext context, MongoPersistentProperty prop, Bson bson, ValueExpressionEvaluator evaluator) { return new MongoDbPropertyValueProvider(context, bson, evaluator).getPropertyValue(prop); } @@ -1471,11 +1485,12 @@ public class MappingMongoConverter extends AbstractMongoConverter * @since 3.2 * @return the converted {@link Collection} or array, will never be {@literal null}. */ - @SuppressWarnings("unchecked") - protected Object readCollectionOrArray(ConversionContext context, Collection source, + @SuppressWarnings({"unchecked","NullAway"}) + protected @Nullable Object readCollectionOrArray(ConversionContext context, @Nullable Collection source, TypeInformation targetType) { Assert.notNull(targetType, "Target type must not be null"); + Assert.notNull(source, "Source must not be null"); Class collectionType = targetType.isSubTypeOf(Collection.class) // ? targetType.getType() // @@ -1517,7 +1532,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * @return the converted {@link Map}, will never be {@literal null}. * @since 3.2 */ - protected Map readMap(ConversionContext context, Bson bson, TypeInformation targetType) { + protected @Nullable Map readMap(ConversionContext context, @Nullable Bson bson, TypeInformation targetType) { Assert.notNull(bson, "Document must not be null"); Assert.notNull(targetType, "TypeInformation must not be null"); @@ -1714,9 +1729,8 @@ public class MappingMongoConverter extends AbstractMongoConverter return document; } - @Nullable @SuppressWarnings("unchecked") - T readValue(ConversionContext context, @Nullable Object value, TypeInformation type) { + @Nullable T readValue(ConversionContext context, @Nullable Object value, TypeInformation type) { if (value == null) { return null; @@ -1735,8 +1749,7 @@ public class MappingMongoConverter extends AbstractMongoConverter return (T) context.convert(value, type); } - @Nullable - private Object readDBRef(ConversionContext context, @Nullable DBRef dbref, TypeInformation type) { + private @Nullable Object readDBRef(ConversionContext context, @Nullable DBRef dbref, TypeInformation type) { if (type.getType().equals(DBRef.class)) { return dbref; @@ -1801,6 +1814,7 @@ public class MappingMongoConverter extends AbstractMongoConverter return targetList; } + @SuppressWarnings("NullAway") private void maybeEmitEvent(MongoMappingEvent event) { if (canPublishEvent()) { @@ -1880,12 +1894,12 @@ public class MappingMongoConverter extends AbstractMongoConverter return target; } - private T doConvert(Object value, Class target) { + private @Nullable T doConvert(Object value, Class target) { return doConvert(value, target, null); } @SuppressWarnings("ConstantConditions") - private T doConvert(Object value, Class target, + private @Nullable T doConvert(Object value, Class target, @Nullable Class fallback) { if (conversionService.canConvert(value.getClass(), target) || fallback == null) { @@ -1939,7 +1953,7 @@ public class MappingMongoConverter extends AbstractMongoConverter final ConversionContext context; final DocumentAccessor accessor; final ValueExpressionEvaluator evaluator; - final SpELContext spELContext; + final @Nullable SpELContext spELContext; /** * Creates a new {@link MongoDbPropertyValueProvider} for the given source, {@link ValueExpressionEvaluator} and @@ -1962,7 +1976,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * @param evaluator must not be {@literal null}. */ MongoDbPropertyValueProvider(ConversionContext context, DocumentAccessor accessor, - ValueExpressionEvaluator evaluator, SpELContext spELContext) { + ValueExpressionEvaluator evaluator, @Nullable SpELContext spELContext) { this.context = context; this.accessor = accessor; @@ -1971,9 +1985,8 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - @Nullable - @SuppressWarnings("unchecked") - public T getPropertyValue(MongoPersistentProperty property) { + @SuppressWarnings({"unchecked", "NullAway"}) + public @Nullable T getPropertyValue(MongoPersistentProperty property) { String expression = property.getSpelExpression(); Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property); @@ -2109,7 +2122,7 @@ public class MappingMongoConverter extends AbstractMongoConverter INSTANCE; @Override - public T getParameterValue(Parameter parameter) { + public @Nullable T getParameterValue(Parameter parameter) { return null; } } @@ -2137,7 +2150,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public org.springframework.data.util.TypeInformation getProperty(String property) { + public org.springframework.data.util.@Nullable TypeInformation getProperty(String property) { return delegate.getProperty(property); } @@ -2172,7 +2185,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public org.springframework.data.util.TypeInformation getActualType() { + public org.springframework.data.util.@Nullable TypeInformation getActualType() { return delegate.getActualType(); } @@ -2187,7 +2200,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public org.springframework.data.util.TypeInformation getSuperTypeInformation(Class superType) { + public org.springframework.data.util.@Nullable TypeInformation getSuperTypeInformation(Class superType) { return delegate.getSuperTypeInformation(superType); } @@ -2279,8 +2292,7 @@ public class MappingMongoConverter extends AbstractMongoConverter * @return * @param */ - @Nullable - default S findContextualEntity(MongoPersistentEntity entity, Document document) { + default @Nullable S findContextualEntity(MongoPersistentEntity entity, Document document) { return null; } @@ -2314,7 +2326,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public S findContextualEntity(MongoPersistentEntity entity, Document document) { + public @Nullable S findContextualEntity(MongoPersistentEntity entity, Document document) { Object identifier = document.get(BasicMongoPersistentProperty.ID_FIELD_NAME); @@ -2351,15 +2363,15 @@ public class MappingMongoConverter extends AbstractMongoConverter final ObjectPath path; final ContainerValueConverter documentConverter; final ContainerValueConverter> collectionConverter; - final ContainerValueConverter mapConverter; - final ContainerValueConverter dbRefConverter; - final ValueConverter elementConverter; + final ContainerValueConverter<@Nullable Bson> mapConverter; + final ContainerValueConverter<@Nullable DBRef> dbRefConverter; + final ValueConverter<@Nullable Object> elementConverter; DefaultConversionContext(MongoConverter sourceConverter, org.springframework.data.convert.CustomConversions customConversions, ObjectPath path, - ContainerValueConverter documentConverter, ContainerValueConverter> collectionConverter, - ContainerValueConverter mapConverter, ContainerValueConverter dbRefConverter, - ValueConverter elementConverter) { + ContainerValueConverter<@Nullable Bson> documentConverter, ContainerValueConverter<@Nullable Collection> collectionConverter, + ContainerValueConverter<@Nullable Bson> mapConverter, ContainerValueConverter<@Nullable DBRef> dbRefConverter, + ValueConverter<@Nullable Object> elementConverter) { this.sourceConverter = sourceConverter; this.conversions = customConversions; @@ -2371,8 +2383,8 @@ public class MappingMongoConverter extends AbstractMongoConverter this.elementConverter = elementConverter; } - @SuppressWarnings("unchecked") @Override + @SuppressWarnings({"unchecked", "NullAway"}) public S convert(Object source, TypeInformation typeHint, ConversionContext context) { @@ -2453,7 +2465,7 @@ public class MappingMongoConverter extends AbstractMongoConverter */ interface ValueConverter { - Object convert(T source, TypeInformation typeHint); + @Nullable Object convert(@Nullable T source, TypeInformation typeHint); } @@ -2465,7 +2477,7 @@ public class MappingMongoConverter extends AbstractMongoConverter */ interface ContainerValueConverter { - Object convert(ConversionContext context, T source, TypeInformation typeHint); + @Nullable Object convert(ConversionContext context, @Nullable T source, TypeInformation typeHint); } @@ -2480,7 +2492,7 @@ public class MappingMongoConverter extends AbstractMongoConverter ProjectingConversionContext(MongoConverter sourceConverter, CustomConversions customConversions, ObjectPath path, ContainerValueConverter> collectionConverter, ContainerValueConverter mapConverter, - ContainerValueConverter dbRefConverter, ValueConverter elementConverter, + ContainerValueConverter<@Nullable DBRef> dbRefConverter, ValueConverter<@Nullable Object> elementConverter, EntityProjection projection) { super(sourceConverter, customConversions, path, (context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection), @@ -2532,7 +2544,7 @@ public class MappingMongoConverter extends AbstractMongoConverter } @Override - public Object getProperty(PersistentProperty property) { + public @Nullable Object getProperty(PersistentProperty property) { return delegate.getProperty(translate(property)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java index da106715d..0cc687c81 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java @@ -16,13 +16,13 @@ package org.springframework.data.mongodb.core.convert; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.convert.ValueConversionContext; import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; import org.springframework.lang.CheckReturnValue; -import org.springframework.lang.Nullable; /** * {@link ValueConversionContext} that allows to delegate read/write to an underlying {@link MongoConverter}. @@ -95,12 +95,12 @@ public class MongoConversionContext implements ValueConversionContext T write(@Nullable Object value, TypeInformation target) { + public @Nullable T write(@Nullable Object value, TypeInformation target) { return (T) mongoConverter.convertToMongoType(value, target); } @Override - public T read(@Nullable Object value, TypeInformation target) { + public @Nullable T read(@Nullable Object value, TypeInformation target) { return value instanceof Bson bson ? mongoConverter.read(target.getType(), bson) : ValueConversionContext.super.read(value, target); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java index 3676e74c8..e147d64cc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java @@ -21,6 +21,7 @@ import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionException; import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.EntityConverter; @@ -33,7 +34,6 @@ import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -101,9 +101,8 @@ public interface MongoConverter * @throws IllegalArgumentException if {@literal targetType} is {@literal null}. * @since 2.1 */ - @SuppressWarnings("unchecked") - @Nullable - default T mapValueToTargetType(S source, Class targetType, DbRefResolver dbRefResolver) { + @SuppressWarnings({"unchecked","NullAway"}) + default @Nullable T mapValueToTargetType(S source, Class targetType, DbRefResolver dbRefResolver) { Assert.notNull(targetType, "TargetType must not be null"); Assert.notNull(dbRefResolver, "DbRefResolver must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java index f9a67d73a..1fd45e196 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverters.java @@ -47,6 +47,7 @@ import org.bson.types.Binary; import org.bson.types.Code; import org.bson.types.Decimal128; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.TypeDescriptor; @@ -142,7 +143,7 @@ abstract class MongoConverters { enum StringToObjectIdConverter implements Converter { INSTANCE; - public ObjectId convert(String source) { + public @Nullable ObjectId convert(String source) { return StringUtils.hasText(source) ? new ObjectId(source) : null; } } @@ -206,7 +207,7 @@ abstract class MongoConverters { enum StringToBigDecimalConverter implements Converter { INSTANCE; - public BigDecimal convert(String source) { + public @Nullable BigDecimal convert(String source) { return StringUtils.hasText(source) ? new BigDecimal(source) : null; } } @@ -235,7 +236,7 @@ abstract class MongoConverters { enum StringToBigIntegerConverter implements Converter { INSTANCE; - public BigInteger convert(String source) { + public @Nullable BigInteger convert(String source) { return StringUtils.hasText(source) ? new BigInteger(source) : null; } } @@ -312,20 +313,25 @@ abstract class MongoConverters { * @author Christoph Strobl * @since 1.7 */ + @SuppressWarnings("NullAway") enum DocumentToNamedMongoScriptConverter implements Converter { INSTANCE; @Override - public NamedMongoScript convert(Document source) { + public @Nullable NamedMongoScript convert(Document source) { if (source.isEmpty()) { return null; } String id = source.get(FieldName.ID.name()).toString(); + Assert.notNull(id, "Script id must not be null"); + Object rawValue = source.get("value"); + Assert.isInstanceOf(Code.class, rawValue); + return new NamedMongoScript(id, ((Code) rawValue).getCode()); } } @@ -379,7 +385,7 @@ abstract class MongoConverters { INSTANCE; @Override - public Currency convert(String source) { + public @Nullable Currency convert(String source) { return StringUtils.hasText(source) ? Currency.getInstance(source) : null; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java index 050c3bd27..8dccced38 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java @@ -32,6 +32,7 @@ import java.util.Locale; import java.util.Set; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; @@ -51,7 +52,7 @@ import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBig import org.springframework.data.mongodb.core.convert.MongoConverters.StringToBigIntegerConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -137,7 +138,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus return new HashSet<>(Arrays.asList(localeToString, booleanToString)); } - public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + public @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return source != null ? source.toString() : null; } } @@ -188,6 +189,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @param converter must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter registerConverter(Converter converter) { Assert.notNull(converter, "Converter must not be null"); @@ -202,6 +204,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @param converters must not be {@literal null} nor contain {@literal null} values. * @return this. */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter registerConverters(Collection converters) { Assert.notNull(converters, "Converters must not be null"); @@ -217,6 +220,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @param converterFactory must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter registerConverterFactory(ConverterFactory converterFactory) { Assert.notNull(converterFactory, "ConverterFactory must not be null"); @@ -232,6 +236,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @return this. * @since 3.4 */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter registerPropertyValueConverterFactory( PropertyValueConverterFactory converterFactory) { @@ -249,6 +254,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @return this. * @since 3.4 */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter configurePropertyConversions( Consumer> configurationAdapter) { @@ -271,6 +277,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @param useNativeDriverJavaTimeCodecs * @return this. */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs(boolean useNativeDriverJavaTimeCodecs) { this.useNativeDriverJavaTimeCodecs = useNativeDriverJavaTimeCodecs; @@ -285,6 +292,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @return this. * @see #useNativeDriverJavaTimeCodecs(boolean) */ + @Contract("-> this") public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs() { return useNativeDriverJavaTimeCodecs(true); } @@ -299,6 +307,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @return this. * @see #useNativeDriverJavaTimeCodecs(boolean) */ + @Contract("-> this") public MongoConverterConfigurationAdapter useSpringDataJavaTimeCodecs() { return useNativeDriverJavaTimeCodecs(false); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java index 0316251dc..67f9d5ec4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java @@ -28,6 +28,7 @@ import java.util.Stack; import java.util.regex.Pattern; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher.NullHandler; import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer; @@ -98,13 +99,17 @@ public class MongoExampleMapper { * @param entity must not be {@literal null}. * @return */ - public Document getMappedExample(Example example, MongoPersistentEntity entity) { + @SuppressWarnings("NullAway") + public Document getMappedExample(Example example, @Nullable MongoPersistentEntity entity) { Assert.notNull(example, "Example must not be null"); - Assert.notNull(entity, "MongoPersistentEntity must not be null"); Document reference = (Document) converter.convertToMongoType(example.getProbe()); + if(entity != null) { + entity = mappingContext.getRequiredPersistentEntity(example.getProbeType()); + } + if (entity.getIdProperty() != null && ClassUtils.isAssignable(entity.getType(), example.getProbeType())) { Object identifier = entity.getIdentifierAccessor(example.getProbe()).getIdentifier(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java index 8d199083e..a5d329045 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoJsonSchemaMapper.java @@ -21,12 +21,12 @@ import java.util.List; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java index 867a6213d..8aeb576c6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java @@ -16,12 +16,12 @@ package org.springframework.data.mongodb.core.convert; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.convert.EntityWriter; import org.springframework.data.mongodb.core.mapping.DocumentPointer; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import com.mongodb.DBRef; @@ -43,8 +43,7 @@ public interface MongoWriter extends EntityWriter { * @param obj can be {@literal null}. * @return */ - @Nullable - default Object convertToMongoType(@Nullable Object obj) { + default @Nullable Object convertToMongoType(@Nullable Object obj) { return convertToMongoType(obj, (TypeInformation) null); } @@ -59,7 +58,7 @@ public interface MongoWriter extends EntityWriter { @Nullable Object convertToMongoType(@Nullable Object obj, @Nullable TypeInformation typeInformation); - default Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity entity) { + default @Nullable Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity entity) { return convertToMongoType(obj, entity.getTypeInformation()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java index 265257af5..68578f32b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/NoOpDbRefResolver.java @@ -18,9 +18,8 @@ package org.springframework.data.mongodb.core.convert; import java.util.List; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import com.mongodb.DBRef; @@ -37,16 +36,14 @@ public enum NoOpDbRefResolver implements DbRefResolver { INSTANCE; @Override - @Nullable - public Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback, + public @Nullable Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbRefResolverCallback callback, DbRefProxyHandler proxyHandler) { return handle(); } @Override - @Nullable - public Document fetch(DBRef dbRef) { + public @Nullable Document fetch(DBRef dbRef) { return handle(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java index 5fefd472c..d5f034eb1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ObjectPath.java @@ -18,9 +18,9 @@ package org.springframework.data.mongodb.core.convert; import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -99,8 +99,7 @@ public class ObjectPath { * @return {@literal null} when no match found. * @since 2.0 */ - @Nullable - T getPathItem(Object id, String collection, Class type) { + @Nullable T getPathItem(Object id, String collection, Class type) { Assert.notNull(id, "Id must not be null"); Assert.hasText(collection, "Collection name must not be null"); @@ -133,13 +132,11 @@ public class ObjectPath { return getObject(); } - @Nullable - private Object getObject() { + private @Nullable Object getObject() { return object; } - @Nullable - private Object getIdValue() { + private @Nullable Object getIdValue() { return idValue; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index debaf2f12..11ed30aed 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -37,6 +37,8 @@ import org.bson.BsonValue; import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Reference; @@ -67,7 +69,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.mongodb.util.DotPath; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -137,6 +139,7 @@ public class QueryMapper { * @param entity can be {@literal null}. * @return */ + @SuppressWarnings("NullAway") public Document getMappedObject(Bson query, @Nullable MongoPersistentEntity entity) { if (isNestedKeyword(query)) { @@ -277,6 +280,7 @@ public class QueryMapper { return mapMetaAttributes(source, entity, MetaMapping.FORCE); } + @SuppressWarnings("NullAway") private Document mapMetaAttributes(Document source, @Nullable MongoPersistentEntity entity, MetaMapping metaMapping) { @@ -349,7 +353,7 @@ public class QueryMapper { * @param rawValue * @return */ - protected Entry getMappedObjectForField(Field field, Object rawValue) { + protected Entry getMappedObjectForField(Field field, @Nullable Object rawValue) { String key = field.getMappedKey(); Object value; @@ -413,7 +417,9 @@ public class QueryMapper { } if (keyword.isSample()) { - return exampleMapper.getMappedExample(keyword.getValue(), entity); + + Example example = keyword.getValue(); + return exampleMapper.getMappedExample(example, entity != null ? entity : mappingContext.getRequiredPersistentEntity(example.getProbeType())); } if (keyword.isJsonSchema()) { @@ -455,8 +461,8 @@ public class QueryMapper { * @return */ @Nullable - @SuppressWarnings("unchecked") - protected Object getMappedValue(Field documentField, Object sourceValue) { + @SuppressWarnings("NullAway") + protected Object getMappedValue(Field documentField, @Nullable Object sourceValue) { Object value = applyFieldTargetTypeHintToValue(documentField, sourceValue); @@ -493,6 +499,7 @@ public class QueryMapper { && documentField.getProperty().getOwner().isIdProperty(documentField.getProperty()); } + @SuppressWarnings("NullAway") private Class getIdTypeForField(Field documentField) { return isIdField(documentField) ? documentField.getProperty().getFieldType() : ObjectId.class; } @@ -531,7 +538,7 @@ public class QueryMapper { } MongoPersistentEntity entity = documentField.getPropertyEntity(); - return entity.hasIdProperty() + return entity != null && entity.hasIdProperty() && (type.equals(DBRef.class) || entity.getRequiredIdProperty().getActualType().isAssignableFrom(type)); } @@ -667,14 +674,14 @@ public class QueryMapper { return createReferenceFor(source, property); } - @Nullable - private Object convertValue(Field documentField, Object sourceValue, Object value, + private @Nullable Object convertValue(Field documentField, @Nullable Object sourceValue, @Nullable Object value, PropertyValueConverter> valueConverter) { MongoPersistentProperty property = documentField.getProperty(); OperatorContext criteriaContext = new QueryOperatorContext( - isKeyword(documentField.name) ? documentField.name : "$eq", property.getFieldName()); + isKeyword(documentField.name) ? documentField.name : "$eq", property != null ? property.getFieldName() : documentField.name); + MongoConversionContext conversionContext; if (valueConverter instanceof MongoConversionContext mcc) { conversionContext = mcc.forOperator(criteriaContext); @@ -686,8 +693,8 @@ public class QueryMapper { return convertValueWithConversionContext(documentField, sourceValue, value, valueConverter, conversionContext); } - @Nullable - protected Object convertValueWithConversionContext(Field documentField, Object sourceValue, Object value, + @SuppressWarnings("NullAway") + protected @Nullable Object convertValueWithConversionContext(Field documentField, @Nullable Object sourceValue, @Nullable Object value, PropertyValueConverter> valueConverter, MongoConversionContext conversionContext) { @@ -707,7 +714,7 @@ public class QueryMapper { return converted; } - if (property != null && !documentField.getProperty().isMap() && sourceValue instanceof Document document) { + if (property != null && !property.isMap() && sourceValue instanceof Document document) { return BsonUtils.mapValues(document, (key, val) -> { if (isKeyword(key)) { @@ -718,11 +725,11 @@ public class QueryMapper { }); } - return valueConverter.write(value, conversionContext); + return value != null ? valueConverter.write(value, conversionContext) : value; } @Nullable - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "NullAway"}) private Object convertIdField(Field documentField, Object source) { Object value = source; @@ -856,6 +863,7 @@ public class QueryMapper { * @param candidate * @return */ + @Contract("null -> false") protected boolean isNestedKeyword(@Nullable Object candidate) { if (!(candidate instanceof Document)) { @@ -890,8 +898,8 @@ public class QueryMapper { * @param candidate * @return */ - protected boolean isKeyword(String candidate) { - return candidate.startsWith("$"); + protected boolean isKeyword(@Nullable String candidate) { + return candidate != null && candidate.startsWith("$"); } /** @@ -936,6 +944,7 @@ public class QueryMapper { * @author Oliver Gierke * @author Christoph Strobl */ + @SuppressWarnings("NullAway") static class Keyword { private static final Set NON_DBREF_CONVERTING_KEYWORDS = Set.of("$", "$size", "$slice", "$gt", "$lt"); @@ -1184,6 +1193,7 @@ public class QueryMapper { * @param context must not be {@literal null}. * @param property may be {@literal null}. */ + @SuppressWarnings("NullAway") public MetadataBackedField(String name, MongoPersistentEntity entity, MappingContext, MongoPersistentProperty> context, @Nullable MongoPersistentProperty property) { @@ -1227,7 +1237,7 @@ public class QueryMapper { } @Override - public MongoPersistentEntity getPropertyEntity() { + public @Nullable MongoPersistentEntity getPropertyEntity() { MongoPersistentProperty property = getProperty(); return property == null ? null : mappingContext.getPersistentEntity(property); } @@ -1244,7 +1254,7 @@ public class QueryMapper { } @Override - public Association getAssociation() { + public @Nullable Association getAssociation() { return association; } @@ -1447,6 +1457,7 @@ public class QueryMapper { * @return * @since 1.7 */ + @SuppressWarnings("NullAway") protected Converter getAssociationConverter() { return new AssociationConverter(name, getAssociation()); } @@ -1598,7 +1609,7 @@ public class QueryMapper { } @Override - public String convert(MongoPersistentProperty source) { + public @Nullable String convert(MongoPersistentProperty source) { if (associationFound) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java index 5a1adf911..cd7d55311 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLoader.java @@ -20,9 +20,9 @@ import java.util.Iterator; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.ReferenceResolver.ReferenceCollection; import org.springframework.data.mongodb.core.mapping.FieldName; -import org.springframework.lang.Nullable; import com.mongodb.client.MongoCollection; @@ -42,8 +42,7 @@ public interface ReferenceLoader { * @param context must not be {@literal null}. * @return the matching {@link Document} or {@literal null} if none found. */ - @Nullable - default Document fetchOne(DocumentReferenceQuery referenceQuery, ReferenceCollection context) { + default @Nullable Document fetchOne(DocumentReferenceQuery referenceQuery, ReferenceCollection context) { Iterator it = fetchMany(referenceQuery, context).iterator(); return it.hasNext() ? it.next() : null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java index b912cfb54..a0e5a6f2b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java @@ -31,6 +31,7 @@ import java.util.stream.Collectors; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.SpELContext; import org.springframework.data.mongodb.core.convert.ReferenceLoader.DocumentReferenceQuery; @@ -47,7 +48,6 @@ import org.springframework.data.mongodb.util.json.ValueProvider; import org.springframework.data.mongodb.util.spel.ExpressionUtils; import org.springframework.data.util.Streamable; import org.springframework.expression.EvaluationContext; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -98,8 +98,7 @@ public final class ReferenceLookupDelegate { * {@literal null}. * @return can be {@literal null}. */ - @Nullable - public Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction, + public @Nullable Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction, MongoEntityReader entityReader) { Object value = source instanceof DocumentReferenceSource documentReferenceSource @@ -126,7 +125,7 @@ public final class ReferenceLookupDelegate { @Nullable private Iterable retrieveRawDocuments(MongoPersistentProperty property, Object source, - LookupFunction lookupFunction, Object value) { + LookupFunction lookupFunction, @Nullable Object value) { DocumentReferenceQuery filter = computeFilter(property, source, spELContext); if (filter instanceof NoResultsFilter) { @@ -137,7 +136,8 @@ public final class ReferenceLookupDelegate { return lookupFunction.apply(filter, referenceCollection); } - private ReferenceCollection computeReferenceContext(MongoPersistentProperty property, Object value, + @SuppressWarnings("NullAway") + private ReferenceCollection computeReferenceContext(MongoPersistentProperty property, @Nullable Object value, SpELContext spELContext) { // Use the first value as a reference for others in case of collection like @@ -195,7 +195,7 @@ public final class ReferenceLookupDelegate { * @return can be {@literal null}. */ @SuppressWarnings("unchecked") - private T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier defaultValue) { + private T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier<@Nullable T> defaultValue) { if (!StringUtils.hasText(value)) { return defaultValue.get(); @@ -220,7 +220,7 @@ public final class ReferenceLookupDelegate { return evaluated != null ? evaluated : defaultValue.get(); } - ParameterBindingContext bindingContext(MongoPersistentProperty property, Object source, SpELContext spELContext) { + ParameterBindingContext bindingContext(MongoPersistentProperty property, @Nullable Object source, SpELContext spELContext) { ValueProvider valueProvider = valueProviderFor(DocumentReferenceSource.getTargetSource(source)); @@ -228,7 +228,7 @@ public final class ReferenceLookupDelegate { () -> evaluationContextFor(property, source, spELContext)); } - ValueProvider valueProviderFor(Object source) { + ValueProvider valueProviderFor(@Nullable Object source) { return index -> { if (source instanceof Document document) { @@ -238,7 +238,7 @@ public final class ReferenceLookupDelegate { }; } - EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object source, SpELContext spELContext) { + EvaluationContext evaluationContextFor(MongoPersistentProperty property, @Nullable Object source, SpELContext spELContext) { Object target = source instanceof DocumentReferenceSource documentReferenceSource ? documentReferenceSource.getTargetSource() @@ -264,7 +264,7 @@ public final class ReferenceLookupDelegate { * @param spELContext must not be {@literal null}. * @return never {@literal null}. */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","NullAway"}) DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object source, SpELContext spELContext) { DocumentReference documentReference = property.isDocumentReference() ? property.getDocumentReference() diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java index 715327d18..0698b08bf 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceResolver.java @@ -15,11 +15,11 @@ */ package org.springframework.data.mongodb.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.DBRef; @@ -54,8 +54,7 @@ public interface ReferenceResolver { */ class ReferenceCollection { - @Nullable // - private final String database; + private final @Nullable String database; private final String collection; /** @@ -95,8 +94,7 @@ public interface ReferenceResolver { * * @return can be {@literal null}. */ - @Nullable - public String getDatabase() { + public @Nullable String getDatabase() { return database; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java index 805bafe97..bff72427f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java @@ -23,6 +23,7 @@ import java.util.Map.Entry; import org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.PropertyValueConverter; import org.springframework.data.convert.ValueConversionContext; @@ -37,7 +38,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update.Modifier; import org.springframework.data.mongodb.core.query.Update.Modifiers; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -132,7 +132,7 @@ public class UpdateMapper extends QueryMapper { * org.springframework.data.mongodb.core.mapping.MongoPersistentEntity) */ @Override - protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity entity) { + protected @Nullable Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity entity) { if (entity != null && entity.isUnwrapped()) { return converter.convertToMongoType(source, entity); @@ -143,7 +143,8 @@ public class UpdateMapper extends QueryMapper { } @Override - protected Entry getMappedObjectForField(Field field, Object rawValue) { + @SuppressWarnings("NullAway") + protected Entry getMappedObjectForField(Field field, @Nullable Object rawValue) { if (isDocument(rawValue)) { @@ -163,7 +164,7 @@ public class UpdateMapper extends QueryMapper { return super.getMappedObjectForField(field, rawValue); } - protected Object convertValueWithConversionContext(Field documentField, Object sourceValue, Object value, + protected @Nullable Object convertValueWithConversionContext(Field documentField, @Nullable Object sourceValue, @Nullable Object value, PropertyValueConverter> valueConverter, MongoConversionContext conversionContext) { @@ -206,11 +207,12 @@ public class UpdateMapper extends QueryMapper { return value instanceof Query; } - private Document getMappedValue(@Nullable Field field, Modifier modifier) { + private @Nullable Document getMappedValue(@Nullable Field field, Modifier modifier) { return new Document(modifier.getKey(), getMappedModifier(field, modifier)); } - private Object getMappedModifier(@Nullable Field field, Modifier modifier) { + @SuppressWarnings("NullAway") + private @Nullable Object getMappedModifier(@Nullable Field field, Modifier modifier) { Object value = modifier.getValue(); @@ -221,7 +223,7 @@ public class UpdateMapper extends QueryMapper { : getMappedSort(sortObject, field.getPropertyEntity()); } - if (isAssociationConversionNecessary(field, value)) { + if (field != null && isAssociationConversionNecessary(field, value)) { if (ObjectUtils.isArray(value) || value instanceof Collection) { List targetPointers = new ArrayList<>(); for (Object val : converter.getConversionService().convert(value, List.class)) { @@ -239,7 +241,7 @@ public class UpdateMapper extends QueryMapper { private TypeInformation getTypeHintForEntity(@Nullable Object source, MongoPersistentEntity entity) { TypeInformation info = entity.getTypeInformation(); - Class type = info.getActualType().getType(); + Class type = info.getRequiredActualType().getType(); if (source == null || type.isInterface() || java.lang.reflect.Modifier.isAbstract(type.getModifiers())) { return info; @@ -257,7 +259,7 @@ public class UpdateMapper extends QueryMapper { } @Override - protected Field createPropertyField(MongoPersistentEntity entity, String key, + protected Field createPropertyField(@Nullable MongoPersistentEntity entity, String key, MappingContext, MongoPersistentProperty> mappingContext) { return entity == null ? super.createPropertyField(entity, key, mappingContext) @@ -316,6 +318,7 @@ public class UpdateMapper extends QueryMapper { } @Override + @SuppressWarnings("NullAway") protected Converter getAssociationConverter() { return new UpdateAssociationConverter(getMappingContext(), getAssociation(), key); } @@ -343,7 +346,7 @@ public class UpdateMapper extends QueryMapper { } @Override - public String convert(MongoPersistentProperty source) { + public @Nullable String convert(MongoPersistentProperty source) { return super.convert(source) == null ? null : mapper.mapPropertyName(source); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java index 0a96cc867..8eed053a0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ValueResolver.java @@ -17,10 +17,9 @@ package org.springframework.data.mongodb.core.convert; import org.bson.Document; import org.bson.conversions.Bson; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; /** * Internal API to trigger the resolution of properties. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java index 4097be770..b31d8a2b7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/EncryptingConverter.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.core.convert.encryption; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.MongoConversionContext; import org.springframework.data.mongodb.core.convert.MongoValueConverter; import org.springframework.data.mongodb.core.encryption.EncryptionContext; @@ -28,7 +29,7 @@ import org.springframework.data.mongodb.core.encryption.EncryptionContext; public interface EncryptingConverter extends MongoValueConverter { @Override - default S read(Object value, MongoConversionContext context) { + default @Nullable S read(Object value, MongoConversionContext context) { return decrypt(value, buildEncryptionContext(context)); } @@ -39,10 +40,10 @@ public interface EncryptingConverter extends MongoValueConverter { * @param context the context to operate in. * @return never {@literal null}. */ - S decrypt(Object encryptedValue, EncryptionContext context); + @Nullable S decrypt(Object encryptedValue, EncryptionContext context); @Override - default T write(Object value, MongoConversionContext context) { + default @Nullable T write(Object value, MongoConversionContext context) { return encrypt(value, buildEncryptionContext(context)); } @@ -53,7 +54,7 @@ public interface EncryptingConverter extends MongoValueConverter { * @param context the context to operate in. * @return never {@literal null}. */ - T encrypt(Object value, EncryptionContext context); + T encrypt(@Nullable Object value, EncryptionContext context); /** * Obtain the {@link EncryptionContext} for a given {@link MongoConversionContext value conversion context}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java index 67c30fcf9..0431cf11d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/ExplicitEncryptionContext.java @@ -15,13 +15,13 @@ */ package org.springframework.data.mongodb.core.convert.encryption; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.MongoConversionContext; import org.springframework.data.mongodb.core.convert.MongoConversionContext.OperatorContext; import org.springframework.data.mongodb.core.encryption.EncryptionContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; -import org.springframework.lang.Nullable; /** * Default {@link EncryptionContext} implementation. @@ -43,29 +43,33 @@ class ExplicitEncryptionContext implements EncryptionContext { return conversionContext.getProperty(); } - @Nullable @Override - public Object lookupValue(String path) { + public @Nullable Object lookupValue(String path) { return conversionContext.getValue(path); } @Override - public Object convertToMongoType(Object value) { + public @Nullable Object convertToMongoType(Object value) { return conversionContext.write(value); } @Override - public EvaluationContext getEvaluationContext(Object source) { - return conversionContext.getSpELContext().getEvaluationContext(source); + public EvaluationContext getEvaluationContext(@Nullable Object source) { + + if(conversionContext.getSpELContext() != null) { + return conversionContext.getSpELContext().getEvaluationContext(source); + } + + throw new IllegalStateException("SpEL context not present"); } @Override - public T read(@Nullable Object value, TypeInformation target) { + public @Nullable T read(@Nullable Object value, TypeInformation target) { return conversionContext.read(value, target); } @Override - public T write(@Nullable Object value, TypeInformation target) { + public @Nullable T write(@Nullable Object value, TypeInformation target) { return conversionContext.write(value, target); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java index 8d29847aa..5bc100c48 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java @@ -81,7 +81,7 @@ public class MongoEncryptionConverter implements EncryptingConverterexplicit encryption * mechanism of Client-Side Field Level Encryption. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.convert.encryption; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java index cfa07fa8f..dbef5cbb9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/package-info.java @@ -1,6 +1,6 @@ /** * Spring Data MongoDB specific converter infrastructure. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.convert; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java index 5f5e29578..45e83ed7c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/EncryptionContext.java @@ -15,12 +15,12 @@ */ package org.springframework.data.mongodb.core.encryption; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mongodb.core.convert.MongoConversionContext.OperatorContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; -import org.springframework.lang.Nullable; /** * Context to encapsulate encryption for a specific {@link MongoPersistentProperty}. @@ -45,7 +45,7 @@ public interface EncryptionContext { * @param value * @return */ - Object convertToMongoType(Object value); + @Nullable Object convertToMongoType(Object value); /** * Reads the value as an instance of the {@link PersistentProperty#getTypeInformation() property type}. @@ -54,7 +54,7 @@ public interface EncryptionContext { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}. */ - default T read(@Nullable Object value) { + default @Nullable T read(@Nullable Object value) { return (T) read(value, getProperty().getTypeInformation()); } @@ -66,7 +66,7 @@ public interface EncryptionContext { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}. */ - default T read(@Nullable Object value, Class target) { + default @Nullable T read(@Nullable Object value, Class target) { return read(value, TypeInformation.of(target)); } @@ -78,7 +78,7 @@ public interface EncryptionContext { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}. */ - T read(@Nullable Object value, TypeInformation target); + @Nullable T read(@Nullable Object value, TypeInformation target); /** * Write the value as an instance of the {@link PersistentProperty#getTypeInformation() property type}. @@ -90,8 +90,7 @@ public interface EncryptionContext { * @see PersistentProperty#getTypeInformation() * @see #write(Object, TypeInformation) */ - @Nullable - default T write(@Nullable Object value) { + default @Nullable T write(@Nullable Object value) { return (T) write(value, getProperty().getTypeInformation()); } @@ -103,8 +102,7 @@ public interface EncryptionContext { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}. */ - @Nullable - default T write(@Nullable Object value, Class target) { + default @Nullable T write(@Nullable Object value, Class target) { return write(value, TypeInformation.of(target)); } @@ -116,8 +114,7 @@ public interface EncryptionContext { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}. */ - @Nullable - T write(@Nullable Object value, TypeInformation target); + @Nullable T write(@Nullable Object value, TypeInformation target); /** * Lookup the value for a given path within the current context. @@ -128,7 +125,7 @@ public interface EncryptionContext { @Nullable Object lookupValue(String path); - EvaluationContext getEvaluationContext(Object source); + EvaluationContext getEvaluationContext(@Nullable Object source); /** * The field name and field query operator diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java index f3906d89d..90a3ab872 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/package-info.java @@ -2,5 +2,5 @@ * Infrastructure for explicit * encryption mechanism of Client-Side Field Level Encryption. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.encryption; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java index 2372700ae..74f36e319 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java @@ -19,7 +19,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java index bc74a56df..cfae6761a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonModule.java @@ -19,9 +19,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; - +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; -import org.springframework.lang.Nullable; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.Version; @@ -139,9 +138,8 @@ public class GeoJsonModule extends SimpleModule { */ private static abstract class GeoJsonDeserializer> extends JsonDeserializer { - @Nullable @Override - public T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException { + public @Nullable T deserialize(JsonParser jp, @Nullable DeserializationContext ctxt) throws IOException { JsonNode node = jp.readValueAsTree(); JsonNode coordinates = node.get("coordinates"); @@ -158,8 +156,7 @@ public class GeoJsonModule extends SimpleModule { * @param coordinates * @return */ - @Nullable - protected abstract T doDeserialize(ArrayNode coordinates); + protected abstract @Nullable T doDeserialize(ArrayNode coordinates); /** * Get the {@link GeoJsonPoint} representation of given {@link ArrayNode} assuming {@code node.[0]} represents @@ -168,8 +165,7 @@ public class GeoJsonModule extends SimpleModule { * @param node can be {@literal null}. * @return {@literal null} when given a {@code null} value. */ - @Nullable - protected GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) { + protected @Nullable GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) { if (node == null) { return null; @@ -185,8 +181,7 @@ public class GeoJsonModule extends SimpleModule { * @param node can be {@literal null}. * @return {@literal null} when given a {@code null} value. */ - @Nullable - protected Point toPoint(@Nullable ArrayNode node) { + protected @Nullable Point toPoint(@Nullable ArrayNode node) { if (node == null) { return null; @@ -236,9 +231,8 @@ public class GeoJsonModule extends SimpleModule { */ private static class GeoJsonPointDeserializer extends GeoJsonDeserializer { - @Nullable @Override - protected GeoJsonPoint doDeserialize(ArrayNode coordinates) { + protected @Nullable GeoJsonPoint doDeserialize(ArrayNode coordinates) { return toGeoJsonPoint(coordinates); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java index 8dafe9ea0..833a1dd9f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java @@ -19,8 +19,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java index bcb4c3e79..e30ed5d6e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java @@ -20,8 +20,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java index 12b9de9da..a7e6306b4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java @@ -19,7 +19,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java index 166a10df0..990be290c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java @@ -21,9 +21,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; import org.springframework.data.geo.Polygon; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -78,6 +79,7 @@ public class GeoJsonPolygon extends Polygon implements GeoJson new") public GeoJsonPolygon withInnerRing(Point first, Point second, Point third, Point fourth, Point... others) { return withInnerRing(asList(first, second, third, fourth, others)); } @@ -88,6 +90,7 @@ public class GeoJsonPolygon extends Polygon implements GeoJson new") public GeoJsonPolygon withInnerRing(List points) { return withInnerRing(new GeoJsonLineString(points)); } @@ -99,6 +102,7 @@ public class GeoJsonPolygon extends Polygon implements GeoJson new") public GeoJsonPolygon withInnerRing(GeoJsonLineString lineString) { Assert.notNull(lineString, "LineString must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java index a482c136e..47be64586 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/Sphere.java @@ -18,12 +18,12 @@ package org.springframework.data.mongodb.core.geo; import java.util.Arrays; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Point; import org.springframework.data.geo.Shape; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java index 6cc77f832..e5adfb26f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/package-info.java @@ -1,6 +1,6 @@ /** * Support for MongoDB geo-spatial queries. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.geo; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DefaultSearchIndexOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DefaultSearchIndexOperations.java index 225bb41ac..b4b7b8430 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DefaultSearchIndexOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DefaultSearchIndexOperations.java @@ -20,12 +20,12 @@ import java.util.List; import org.bson.BsonString; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; import com.mongodb.client.model.SearchIndexModel; @@ -40,7 +40,7 @@ public class DefaultSearchIndexOperations implements SearchIndexOperations { private final MongoOperations mongoOperations; private final String collectionName; - private final TypeInformation entityTypeInformation; + private final @Nullable TypeInformation entityTypeInformation; public DefaultSearchIndexOperations(MongoOperations mongoOperations, Class type) { this(mongoOperations, mongoOperations.getCollectionName(type), type); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java index 1c5a8ca6b..b88acb06a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java @@ -20,6 +20,7 @@ import java.util.Optional; import org.bson.Document; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -60,6 +61,7 @@ public class GeospatialIndex implements IndexDefinition { * @param name must not be {@literal null} or empty. * @return this. */ + @Contract("_ -> this") public GeospatialIndex named(String name) { this.name = name; @@ -70,6 +72,7 @@ public class GeospatialIndex implements IndexDefinition { * @param min * @return this. */ + @Contract("_ -> this") public GeospatialIndex withMin(int min) { this.min = min; return this; @@ -79,6 +82,7 @@ public class GeospatialIndex implements IndexDefinition { * @param max * @return this. */ + @Contract("_ -> this") public GeospatialIndex withMax(int max) { this.max = max; return this; @@ -88,6 +92,7 @@ public class GeospatialIndex implements IndexDefinition { * @param bits * @return this. */ + @Contract("_ -> this") public GeospatialIndex withBits(int bits) { this.bits = bits; return this; @@ -97,6 +102,7 @@ public class GeospatialIndex implements IndexDefinition { * @param type must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GeospatialIndex typed(GeoSpatialIndexType type) { Assert.notNull(type, "Type must not be null"); @@ -109,6 +115,7 @@ public class GeospatialIndex implements IndexDefinition { * @param fieldName * @return this. */ + @Contract("_ -> this") public GeospatialIndex withAdditionalField(String fieldName) { this.additionalField = fieldName; return this; @@ -123,6 +130,7 @@ public class GeospatialIndex implements IndexDefinition { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/ * @since 1.10 */ + @Contract("_ -> this") public GeospatialIndex partial(@Nullable IndexFilter filter) { this.filter = Optional.ofNullable(filter); @@ -139,6 +147,7 @@ public class GeospatialIndex implements IndexDefinition { * @return this. * @since 2.0 */ + @Contract("_ -> this") public GeospatialIndex collation(@Nullable Collation collation) { this.collation = Optional.ofNullable(collation); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java index 95f4226e2..91195a40f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java @@ -23,10 +23,11 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.core.index.IndexOptions.Unique; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -52,11 +53,13 @@ public class Index implements IndexDefinition { fieldSpec.put(key, direction); } + @Contract("_, _ -> this") public Index on(String key, Direction direction) { fieldSpec.put(key, direction); return this; } + @Contract("_ -> this") public Index named(String name) { this.name = name; return this; @@ -69,6 +72,7 @@ public class Index implements IndexDefinition { * @see https://docs.mongodb.org/manual/core/index-unique/ */ + @Contract("-> this") public Index unique() { this.options.setUnique(Unique.YES); @@ -82,6 +86,7 @@ public class Index implements IndexDefinition { * @see https://docs.mongodb.org/manual/core/index-sparse/ */ + @Contract("-> this") public Index sparse() { this.sparse = true; return this; @@ -92,7 +97,7 @@ public class Index implements IndexDefinition { * * @return this. * @since 1.5 - */ + */@Contract("-> this") public Index background() { this.background = true; @@ -107,6 +112,7 @@ public class Index implements IndexDefinition { * "https://www.mongodb.com/docs/manual/core/index-hidden/">https://www.mongodb.com/docs/manual/core/index-hidden/ * @since 4.1 */ + @Contract("-> this") public Index hidden() { options.setHidden(true); @@ -120,6 +126,7 @@ public class Index implements IndexDefinition { * @return this. * @since 1.5 */ + @Contract("_ -> this") public Index expire(long value) { return expire(value, TimeUnit.SECONDS); } @@ -132,6 +139,7 @@ public class Index implements IndexDefinition { * @throws IllegalArgumentException if given {@literal timeout} is {@literal null}. * @since 2.2 */ + @Contract("_ -> this") public Index expire(Duration timeout) { Assert.notNull(timeout, "Timeout must not be null"); @@ -146,6 +154,7 @@ public class Index implements IndexDefinition { * @return this. * @since 1.5 */ + @Contract("_, _ -> this") public Index expire(long value, TimeUnit unit) { Assert.notNull(unit, "TimeUnit for expiration must not be null"); @@ -162,6 +171,7 @@ public class Index implements IndexDefinition { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/ * @since 1.10 */ + @Contract("_ -> this") public Index partial(@Nullable IndexFilter filter) { this.filter = Optional.ofNullable(filter); @@ -178,6 +188,7 @@ public class Index implements IndexDefinition { * @return this. * @since 2.0 */ + @Contract("_ -> this") public Index collation(@Nullable Collation collation) { this.collation = Optional.ofNullable(collation); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java index a5cbf6c89..2e7268699 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexField.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.core.index; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort.Direction; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -139,8 +139,7 @@ public final class IndexField { * * @return the direction */ - @Nullable - public Direction getDirection() { + public @Nullable Direction getDirection() { return direction; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java index de7153bfb..e9817746c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexInfo.java @@ -27,11 +27,12 @@ import java.util.Optional; import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Index information for a MongoDB index. @@ -89,7 +90,7 @@ public class IndexInfo { */ public static IndexInfo indexInfoOf(Document sourceDocument) { - Document keyDbObject = (Document) sourceDocument.get("key"); + Document keyDbObject = sourceDocument.get("key", new Document()); int numberOfElements = keyDbObject.keySet().size(); List indexFields = new ArrayList(numberOfElements); @@ -105,9 +106,10 @@ public class IndexInfo { } else if ("text".equals(value)) { Document weights = (Document) sourceDocument.get("weights"); - - for (String fieldName : weights.keySet()) { - indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString()))); + if(weights != null) { + for (String fieldName : weights.keySet()) { + indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString()))); + } } } else { @@ -129,7 +131,7 @@ public class IndexInfo { } } - String name = sourceDocument.get("name").toString(); + String name = ObjectUtils.nullSafeToString(sourceDocument.get("name")); boolean unique = sourceDocument.get("unique", false); boolean sparse = sourceDocument.get("sparse", false); @@ -161,8 +163,7 @@ public class IndexInfo { * @return the {@link String} representation of the partial filter {@link Document}. * @since 2.1.11 */ - @Nullable - private static String extractPartialFilterString(Document sourceDocument) { + private static @Nullable String extractPartialFilterString(Document sourceDocument) { if (!sourceDocument.containsKey("partialFilterExpression")) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java index ca3d951c9..aec1ba817 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOperationsProvider.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.index; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Provider interface to obtain {@link IndexOperations} by MongoDB collection name or entity type. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java index 887542cb0..a390d1eb3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexOptions.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.index; import java.time.Duration; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Changeable properties of an index. Can be used for index creation and modification. @@ -28,14 +28,11 @@ import org.springframework.lang.Nullable; */ public class IndexOptions { - @Nullable - private Duration expire; + private @Nullable Duration expire; - @Nullable - private Boolean hidden; + private @Nullable Boolean hidden; - @Nullable - private Unique unique; + private @Nullable Unique unique; public enum Unique { @@ -108,8 +105,7 @@ public class IndexOptions { /** * @return {@literal true} if hidden, {@literal null} if not set. */ - @Nullable - public Boolean isHidden() { + public @Nullable Boolean isHidden() { return hidden; } @@ -123,8 +119,7 @@ public class IndexOptions { /** * @return the unique property value, {@literal null} if not set. */ - @Nullable - public Unique getUnique() { + public @Nullable Unique getUnique() { return unique; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java index 362247725..3bb3fdbd0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/IndexPredicate.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.index; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Jon Brisbin @@ -26,8 +26,7 @@ public abstract class IndexPredicate { private IndexDirection direction = IndexDirection.ASCENDING; private boolean unique = false; - @Nullable - public String getName() { + public @Nullable String getName() { return name; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java index e20b0704c..f1550d150 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexCreator.java @@ -21,7 +21,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationListener; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.mapping.PersistentEntity; @@ -33,7 +33,6 @@ import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.util.MongoDbErrorCodes; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -183,8 +182,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener entity) { + private PartialIndexFilter evaluatePartialFilter(String filterExpression, @Nullable PersistentEntity entity) { Object result = ExpressionUtils.evaluate(filterExpression, () -> getEvaluationContextForProperty(entity)); @@ -586,7 +585,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return PartialIndexFilter.of(BsonUtils.parse(filterExpression, null)); } - private org.bson.Document evaluateWildcardProjection(String projectionExpression, PersistentEntity entity) { + private org.bson.Document evaluateWildcardProjection(String projectionExpression, @Nullable PersistentEntity entity) { Object result = ExpressionUtils.evaluate(projectionExpression, () -> getEvaluationContextForProperty(entity)); @@ -597,7 +596,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return BsonUtils.parse(projectionExpression, null); } - private Collation evaluateCollation(String collationExpression, PersistentEntity entity) { + private Collation evaluateCollation(String collationExpression, @Nullable PersistentEntity entity) { Object result = ExpressionUtils.evaluate(collationExpression, () -> getEvaluationContextForProperty(entity)); if (result instanceof org.bson.Document document) { @@ -690,8 +689,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { * @param persistentProperty * @return */ - @Nullable - protected IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection, + protected @Nullable IndexDefinitionHolder createGeoSpatialIndexDefinition(String dotPath, String collection, MongoPersistentProperty persistentProperty) { GeoSpatialIndexed index = persistentProperty.findAnnotation(GeoSpatialIndexed.class); @@ -793,8 +791,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { * @return the collation present on either the annotation or the entity as a fallback. Might be {@literal null}. * @since 4.0 */ - @Nullable - private Collation resolveCollation(Annotation annotation, @Nullable PersistentEntity entity) { + private @Nullable Collation resolveCollation(Annotation annotation, @Nullable PersistentEntity entity) { return MergedAnnotation.from(annotation).getValue("collation", String.class).filter(StringUtils::hasText) .map(it -> evaluateCollation(it, entity)).orElseGet(() -> { @@ -1119,8 +1116,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return strategy; } - @Nullable - public TextIndexedFieldSpec getParentFieldSpec() { + public @Nullable TextIndexedFieldSpec getParentFieldSpec() { return parentFieldSpec; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java index a87b15de4..0b473388f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/TextIndexDefinition.java @@ -20,9 +20,10 @@ import java.util.LinkedHashSet; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -235,6 +236,7 @@ public class TextIndexDefinition implements IndexDefinition { * @param name * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder named(String name) { this.instance.name = name; return this; @@ -246,6 +248,7 @@ public class TextIndexDefinition implements IndexDefinition { * * @return */ + @Contract("-> this") public TextIndexDefinitionBuilder onAllFields() { if (!instance.fieldSpecs.isEmpty()) { @@ -262,6 +265,7 @@ public class TextIndexDefinition implements IndexDefinition { * @param fieldnames * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder onFields(String... fieldnames) { for (String fieldname : fieldnames) { @@ -276,6 +280,7 @@ public class TextIndexDefinition implements IndexDefinition { * @param fieldname * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder onField(String fieldname) { return onField(fieldname, 1F); } @@ -286,6 +291,7 @@ public class TextIndexDefinition implements IndexDefinition { * @param fieldname * @return */ + @Contract("_, _ -> this") public TextIndexDefinitionBuilder onField(String fieldname, Float weight) { if (this.instance.fieldSpecs.contains(ALL_FIELDS)) { @@ -305,6 +311,7 @@ public class TextIndexDefinition implements IndexDefinition { * @see https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index */ + @Contract("_ -> this") public TextIndexDefinitionBuilder withDefaultLanguage(String language) { this.instance.defaultLanguage = language; @@ -317,6 +324,7 @@ public class TextIndexDefinition implements IndexDefinition { * @param fieldname * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) { if (StringUtils.hasText(this.instance.languageOverride)) { @@ -338,6 +346,7 @@ public class TextIndexDefinition implements IndexDefinition { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/ * @since 1.10 */ + @Contract("_ -> this") public TextIndexDefinitionBuilder partial(@Nullable IndexFilter filter) { this.instance.filter = filter; @@ -349,12 +358,14 @@ public class TextIndexDefinition implements IndexDefinition { * * @since 2.2 */ + @Contract("-> this") public TextIndexDefinitionBuilder withSimpleCollation() { this.instance.collation = Collation.simple(); return this; } + @Contract("-> new") public TextIndexDefinition build() { return this.instance; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/VectorIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/VectorIndex.java index b46dbf4d0..d56801c52 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/VectorIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/VectorIndex.java @@ -28,6 +28,7 @@ import org.springframework.data.util.TypeInformation; import org.springframework.lang.Contract; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -149,7 +150,8 @@ public class VectorIndex implements SearchIndexDefinition { for (Object entry : definition.get("fields", List.class)) { if (entry instanceof Document field) { - if (field.get("type").equals("vector")) { + Object fieldType = field.get("type"); + if (ObjectUtils.nullSafeEquals(fieldType, "vector")) { index.addField(new VectorIndexField(field.getString("path"), "vector", field.getInteger("numDimensions"), field.getString("similarity"), field.getString("quantization"))); } else { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java index dcd2b7c02..ff0a92ada 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java @@ -21,8 +21,9 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -76,6 +77,7 @@ public class WildcardIndex extends Index { * * @return this. */ + @Contract("-> this") public WildcardIndex includeId() { wildcardProjection.put(FieldName.ID.name(), 1); @@ -89,6 +91,7 @@ public class WildcardIndex extends Index { * @return this. */ @Override + @Contract("_ -> this") public WildcardIndex named(String name) { super.named(name); @@ -101,6 +104,7 @@ public class WildcardIndex extends Index { * @throws UnsupportedOperationException not supported for wildcard indexes. */ @Override + @Contract("-> fail") public Index unique() { throw new UnsupportedOperationException("Wildcard Index does not support 'unique'"); } @@ -111,6 +115,7 @@ public class WildcardIndex extends Index { * @throws UnsupportedOperationException not supported for wildcard indexes. */ @Override + @Contract("-> fail") public Index expire(long seconds) { throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'"); } @@ -121,6 +126,7 @@ public class WildcardIndex extends Index { * @throws UnsupportedOperationException not supported for wildcard indexes. */ @Override + @Contract("_, _ -> fail") public Index expire(long value, TimeUnit timeUnit) { throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'"); } @@ -131,6 +137,7 @@ public class WildcardIndex extends Index { * @throws UnsupportedOperationException not supported for wildcard indexes. */ @Override + @Contract("_ -> fail") public Index expire(Duration duration) { throw new UnsupportedOperationException("Wildcard Index does not support 'ttl'"); } @@ -142,6 +149,7 @@ public class WildcardIndex extends Index { * @param paths must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public WildcardIndex wildcardProjectionInclude(String... paths) { for (String path : paths) { @@ -157,6 +165,7 @@ public class WildcardIndex extends Index { * @param paths must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public WildcardIndex wildcardProjectionExclude(String... paths) { for (String path : paths) { @@ -172,6 +181,7 @@ public class WildcardIndex extends Index { * @param includeExclude must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public WildcardIndex wildcardProjection(Map includeExclude) { wildcardProjection.putAll(includeExclude); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java index c49f501d8..8524ee62f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/package-info.java @@ -1,6 +1,6 @@ /** * Support for MongoDB document indexing. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.index; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java index 3d68dbaac..e86500931 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Id; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.expression.ValueExpression; @@ -35,6 +36,7 @@ import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.mongodb.MongoCollectionUtils; +import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.util.encryption.EncryptionUtils; import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.util.Lazy; @@ -42,7 +44,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -139,9 +140,8 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity extends BasicPersistentEntity getEncryptionKeyIds() { + public @Nullable Collection getEncryptionKeyIds() { Encrypted encrypted = findAnnotation(Encrypted.class); if (encrypted == null) { @@ -405,6 +408,7 @@ public class BasicMongoPersistentEntity extends BasicPersistentEntity getEncryptionKeyIds() { Encrypted encrypted = findAnnotation(Encrypted.class); @@ -282,6 +280,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope return target; } + @SuppressWarnings("NullAway") protected MongoField doGetMongoField() { MongoFieldBuilder builder = MongoField.builder(); @@ -295,6 +294,7 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope return builder.build(); } + @SuppressWarnings("NullAway") private String doGetFieldName() { if (isIdProperty()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java index 105c38b28..eb8d08db6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/CachingMongoPersistentProperty.java @@ -15,11 +15,11 @@ */ package org.springframework.data.mongodb.core.mapping; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; /** * {@link MongoPersistentProperty} caching access to {@link #isIdProperty()} and {@link #getFieldName()}. @@ -121,12 +121,12 @@ public class CachingMongoPersistentProperty extends BasicMongoPersistentProperty } @Override - public DBRef getDBRef() { + public @Nullable DBRef getDBRef() { return dbref.getNullable(); } @Override - public DocumentReference getDocumentReference() { + public @Nullable DocumentReference getDocumentReference() { return documentReference.getNullable(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java index 6f0e1ae4c..881d741ee 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoField.java @@ -15,7 +15,9 @@ */ package org.springframework.data.mongodb.core.mapping; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.FieldName.Type; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -139,7 +141,7 @@ public class MongoField { */ public static class MongoFieldBuilder { - private String name; + private @Nullable String name; private Type nameType = Type.PATH; private FieldType type = FieldType.IMPLICIT; private int order = Integer.MAX_VALUE; @@ -150,6 +152,7 @@ public class MongoField { * @param fieldType * @return */ + @Contract("_ -> this") public MongoFieldBuilder fieldType(FieldType fieldType) { this.type = fieldType; @@ -163,6 +166,7 @@ public class MongoField { * @param fieldName * @return */ + @Contract("_ -> this") public MongoFieldBuilder name(String fieldName) { Assert.hasText(fieldName, "Field name must not be empty"); @@ -178,6 +182,7 @@ public class MongoField { * @param path * @return */ + @Contract("_ -> this") public MongoFieldBuilder path(String path) { Assert.hasText(path, "Field path (name) must not be empty"); @@ -193,6 +198,7 @@ public class MongoField { * @param order * @return */ + @Contract("_ -> this") public MongoFieldBuilder order(int order) { this.order = order; @@ -204,7 +210,10 @@ public class MongoField { * * @return a new {@link MongoField}. */ + @Contract("-> new") public MongoField build() { + + Assert.notNull(name, "Name of Field must not be null"); return new MongoField(new FieldName(name, nameType), type, order); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java index 76c026986..454049312 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoMappingContext.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.mapping; import java.util.AbstractMap; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -28,7 +29,6 @@ import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.NullableWrapperConverters; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; /** * Default implementation of a {@link MappingContext} for MongoDB using {@link BasicMongoPersistentEntity} and @@ -46,8 +46,7 @@ public class MongoMappingContext extends AbstractMappingContext getPersistentEntity(MongoPersistentProperty persistentProperty) { + public @Nullable MongoPersistentEntity getPersistentEntity(MongoPersistentProperty persistentProperty) { MongoPersistentEntity entity = super.getPersistentEntity(persistentProperty); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java index e02bd00c8..f1d67e4ae 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentEntity.java @@ -17,9 +17,10 @@ package org.springframework.data.mongodb.core.mapping; import java.util.Collection; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.MutablePersistentEntity; -import org.springframework.lang.Nullable; +import org.springframework.data.mongodb.core.query.Collation; /** * MongoDB specific {@link PersistentEntity} abstraction. @@ -68,8 +69,7 @@ public interface MongoPersistentEntity extends MutablePersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public MongoPersistentProperty getTextScoreProperty() { + public @Nullable MongoPersistentProperty getTextScoreProperty() { return delegate.getTextScoreProperty(); } @@ -73,8 +73,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public Collation getCollation() { + public @Nullable Collation getCollation() { return delegate.getCollation(); } @@ -120,8 +119,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public MongoPersistentProperty getIdProperty() { + public @Nullable MongoPersistentProperty getIdProperty() { return delegate.getIdProperty(); } @@ -131,8 +129,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public MongoPersistentProperty getVersionProperty() { + public @Nullable MongoPersistentProperty getVersionProperty() { return delegate.getVersionProperty(); } @@ -142,8 +139,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public MongoPersistentProperty getPersistentProperty(String name) { + public @Nullable MongoPersistentProperty getPersistentProperty(String name) { return wrap(delegate.getPersistentProperty(name)); } @@ -159,8 +155,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public MongoPersistentProperty getPersistentProperty(Class annotationType) { + public @Nullable MongoPersistentProperty getPersistentProperty(Class annotationType) { return wrap(delegate.getPersistentProperty(annotationType)); } @@ -226,8 +221,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - @Nullable - public A findAnnotation(Class annotationType) { + public @Nullable A findAnnotation(Class annotationType) { return delegate.findAnnotation(annotationType); } @@ -289,7 +283,9 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { return delegate.spliterator(); } - private MongoPersistentProperty wrap(MongoPersistentProperty source) { + @Contract("null -> null; !null -> !null") + private @Nullable MongoPersistentProperty wrap(@Nullable MongoPersistentProperty source) { + if (source == null) { return source; } @@ -332,7 +328,7 @@ class UnwrappedMongoPersistentEntity implements MongoPersistentEntity { } @Override - public Collection getEncryptionKeyIds() { + public @Nullable Collection getEncryptionKeyIds() { return delegate.getEncryptionKeyIds(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java index 1d4877478..ac7f24a55 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentProperty.java @@ -20,11 +20,11 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -47,6 +47,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override + @SuppressWarnings("NullAway") public String getFieldName() { if (!context.getProperty().isUnwrapped()) { @@ -57,6 +58,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override + @SuppressWarnings("NullAway") public boolean hasExplicitFieldName() { return delegate.hasExplicitFieldName() || !ObjectUtils.isEmpty(context.getProperty().findAnnotation(Unwrapped.class).prefix()); @@ -108,14 +110,12 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public DBRef getDBRef() { + public @Nullable DBRef getDBRef() { return delegate.getDBRef(); } @Override - @Nullable - public DocumentReference getDocumentReference() { + public @Nullable DocumentReference getDocumentReference() { return delegate.getDocumentReference(); } @@ -145,6 +145,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override + @SuppressWarnings("NullAway") public MongoField getMongoField() { if (!context.getProperty().isUnwrapped()) { @@ -165,8 +166,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Method getGetter() { + public @Nullable Method getGetter() { return delegate.getGetter(); } @@ -176,8 +176,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Method getSetter() { + public @Nullable Method getSetter() { return delegate.getSetter(); } @@ -187,8 +186,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Method getWither() { + public @Nullable Method getWither() { return delegate.getWither(); } @@ -198,8 +196,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Field getField() { + public @Nullable Field getField() { return delegate.getField(); } @@ -209,14 +206,12 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public String getSpelExpression() { + public @Nullable String getSpelExpression() { return delegate.getSpelExpression(); } @Override - @Nullable - public Association getAssociation() { + public @Nullable Association getAssociation() { return delegate.getAssociation(); } @@ -291,8 +286,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Class getComponentType() { + public @Nullable Class getComponentType() { return delegate.getComponentType(); } @@ -302,8 +296,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Class getMapValueType() { + public @Nullable Class getMapValueType() { return delegate.getMapValueType(); } @@ -313,8 +306,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public A findAnnotation(Class annotationType) { + public @Nullable A findAnnotation(Class annotationType) { return delegate.findAnnotation(annotationType); } @@ -324,8 +316,7 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public A findPropertyOrOwnerAnnotation(Class annotationType) { + public @Nullable A findPropertyOrOwnerAnnotation(Class annotationType) { return delegate.findPropertyOrOwnerAnnotation(annotationType); } @@ -340,13 +331,12 @@ class UnwrappedMongoPersistentProperty implements MongoPersistentProperty { } @Override - @Nullable - public Class getAssociationTargetType() { + public @Nullable Class getAssociationTargetType() { return delegate.getAssociationTargetType(); } @Override - public TypeInformation getAssociationTargetTypeInformation() { + public @Nullable TypeInformation getAssociationTargetTypeInformation() { return delegate.getAssociationTargetTypeInformation(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java index 73f4890de..a8e2c9377 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.mapping.event; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Base class for delete events. @@ -49,8 +49,7 @@ public abstract class AbstractDeleteEvent extends MongoMappingEvent * * @return can be {@literal null}. */ - @Nullable - public Class getType() { + public @Nullable Class getType() { return type; } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java index 55ccaa5f3..10f4cdbbb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.mapping.event; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Event being thrown after a single or a set of documents has/have been deleted. The {@link Document} held in the event diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java index 49d509fb4..c826cadb4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.mapping.event; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Event being thrown before a document is deleted. The {@link Document} held in the event will represent the query diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java index eec9a3edf..bec198672 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/MongoMappingEvent.java @@ -18,8 +18,8 @@ package org.springframework.data.mongodb.core.mapping.event; import java.util.function.Function; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEvent; -import org.springframework.lang.Nullable; /** * Base {@link ApplicationEvent} triggered by Spring Data MongoDB. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java index 0cc9d071a..71ed503b2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/package-info.java @@ -1,6 +1,6 @@ /** * Mapping event callback infrastructure for the MongoDB document-to-object mapping subsystem. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.mapping.event; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java index 0a513f1a1..f5c917d7d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/package-info.java @@ -1,6 +1,6 @@ /** * Infrastructure for the MongoDB document-to-object mapping subsystem. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.mapping; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java index 32a9ed511..ed9c148a1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceCounts.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.mapreduce; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Value object to encapsulate results of a map-reduce count. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java index e9ee146be..2b8c9d1eb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceOptions.java @@ -20,10 +20,11 @@ import java.util.Map; import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; import com.mongodb.client.model.MapReduceAction; +import org.springframework.lang.Contract; /** * @author Mark Pollack @@ -64,6 +65,7 @@ public class MapReduceOptions { * @param limit Limit the number of objects to process * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions limit(int limit) { this.limit = limit; @@ -77,6 +79,7 @@ public class MapReduceOptions { * @param collectionName The name of the collection where the results of the map-reduce operation will be stored. * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions outputCollection(String collectionName) { this.outputCollection = collectionName; @@ -90,6 +93,7 @@ public class MapReduceOptions { * @param outputDatabase The name of the database where the results of the map-reduce operation will be stored. * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions outputDatabase(@Nullable String outputDatabase) { this.outputDatabase = Optional.ofNullable(outputDatabase); @@ -104,6 +108,7 @@ public class MapReduceOptions { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionInline() { this.mapReduceAction = null; @@ -118,6 +123,7 @@ public class MapReduceOptions { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionMerge() { this.mapReduceAction = MapReduceAction.MERGE; @@ -132,6 +138,7 @@ public class MapReduceOptions { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionReduce() { this.mapReduceAction = MapReduceAction.REDUCE; @@ -145,6 +152,7 @@ public class MapReduceOptions { * @return MapReduceOptions so that methods can be chained in a fluent API style * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionReplace() { this.mapReduceAction = MapReduceAction.REPLACE; @@ -157,6 +165,7 @@ public class MapReduceOptions { * @param finalizeFunction The finalize function. Can be a JSON string or a Spring Resource URL * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions finalizeFunction(@Nullable String finalizeFunction) { this.finalizeFunction = Optional.ofNullable(finalizeFunction); @@ -170,6 +179,7 @@ public class MapReduceOptions { * @param scopeVariables variables that can be accessed from map, reduce, and finalize scripts * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions scopeVariables(Map scopeVariables) { this.scopeVariables = scopeVariables; @@ -183,6 +193,7 @@ public class MapReduceOptions { * @param javaScriptMode if true, have the execution of map-reduce stay in JavaScript * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions javaScriptMode(boolean javaScriptMode) { this.jsMode = javaScriptMode; @@ -194,6 +205,7 @@ public class MapReduceOptions { * * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions verbose(boolean verbose) { this.verbose = verbose; @@ -207,6 +219,7 @@ public class MapReduceOptions { * @return * @since 2.0 */ + @Contract("_ -> this") public MapReduceOptions collation(@Nullable Collation collation) { this.collation = Optional.ofNullable(collation); @@ -217,13 +230,11 @@ public class MapReduceOptions { return this.finalizeFunction; } - @Nullable - public Boolean getJavaScriptMode() { + public @Nullable Boolean getJavaScriptMode() { return this.jsMode; } - @Nullable - public String getOutputCollection() { + public @Nullable String getOutputCollection() { return this.outputCollection; } @@ -261,8 +272,7 @@ public class MapReduceOptions { * @return the mapped action or {@literal null} if the action maps to inline output. * @since 2.0.10 */ - @Nullable - public MapReduceAction getMapReduceAction() { + public @Nullable MapReduceAction getMapReduceAction() { return mapReduceAction; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java index 865a4e943..1d4f644bd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceResults.java @@ -19,7 +19,7 @@ import java.util.Iterator; import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** @@ -71,13 +71,11 @@ public class MapReduceResults implements Iterable { return mapReduceCounts; } - @Nullable - public String getOutputCollection() { + public @Nullable String getOutputCollection() { return outputCollection; } - @Nullable - public Document getRawResults() { + public @Nullable Document getRawResults() { return rawResults; } @@ -147,7 +145,8 @@ public class MapReduceResults implements Iterable { return null; } - return resultField instanceof Document document ? document.get("collection").toString() + return resultField instanceof Document document && document.containsKey("collection") + ? document.get("collection").toString() : resultField.toString(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java index 28de7fe85..d99f6d923 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/MapReduceTiming.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.mapreduce; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @deprecated since 3.4 in favor of {@link org.springframework.data.mongodb.core.aggregation}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java index 65522d861..c5f5840e6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapreduce/package-info.java @@ -3,6 +3,6 @@ * @deprecated since MongoDB server version 5.0 */ @Deprecated -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.mapreduce; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java index fec7fa60e..e1da0b33c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamRequest.java @@ -20,12 +20,13 @@ import java.time.Instant; import org.bson.BsonValue; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.ChangeStreamOptions; import org.springframework.data.mongodb.core.ChangeStreamOptions.ChangeStreamOptionsBuilder; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest.ChangeStreamRequestOptions; import org.springframework.data.mongodb.core.query.Collation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.client.model.changestream.ChangeStreamDocument; @@ -215,12 +216,12 @@ public class ChangeStreamRequest } @Override - public String getCollectionName() { + public @Nullable String getCollectionName() { return collectionName; } @Override - public String getDatabaseName() { + public @Nullable String getDatabaseName() { return databaseName; } @@ -253,6 +254,7 @@ public class ChangeStreamRequest * @param databaseName must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder database(String databaseName) { Assert.hasText(databaseName, "DatabaseName must not be null"); @@ -267,6 +269,7 @@ public class ChangeStreamRequest * @param collectionName must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder collection(String collectionName) { Assert.hasText(collectionName, "CollectionName must not be null"); @@ -281,6 +284,7 @@ public class ChangeStreamRequest * @param messageListener must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder publishTo( MessageListener, ? super T> messageListener) { @@ -308,6 +312,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getFilter() * @see ChangeStreamOptionsBuilder#filter(Aggregation) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder filter(Aggregation aggregation) { Assert.notNull(aggregation, "Aggregation must not be null"); @@ -323,6 +328,7 @@ public class ChangeStreamRequest * @return this. * @see ChangeStreamOptions#getFilter() */ + @Contract("_ -> this") public ChangeStreamRequestBuilder filter(Document... pipeline) { Assert.notNull(pipeline, "Aggregation pipeline must not be null"); @@ -340,6 +346,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getCollation() * @see ChangeStreamOptionsBuilder#collation(Collation) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder collation(Collation collation) { Assert.notNull(collation, "Collation must not be null"); @@ -357,6 +364,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getResumeToken() * @see ChangeStreamOptionsBuilder#resumeToken(org.bson.BsonValue) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder resumeToken(BsonValue resumeToken) { Assert.notNull(resumeToken, "Resume token not be null"); @@ -373,6 +381,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getResumeTimestamp() * @see ChangeStreamOptionsBuilder#resumeAt(java.time.Instant) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder resumeAt(Instant clusterTime) { Assert.notNull(clusterTime, "ClusterTime must not be null"); @@ -388,6 +397,7 @@ public class ChangeStreamRequest * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder resumeAfter(BsonValue resumeToken) { Assert.notNull(resumeToken, "ResumeToken must not be null"); @@ -403,6 +413,7 @@ public class ChangeStreamRequest * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder startAfter(BsonValue resumeToken) { Assert.notNull(resumeToken, "ResumeToken must not be null"); @@ -418,6 +429,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getFullDocumentLookup() * @see ChangeStreamOptionsBuilder#fullDocumentLookup(FullDocument) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder fullDocumentLookup(FullDocument lookup) { Assert.notNull(lookup, "FullDocument not be null"); @@ -434,6 +446,7 @@ public class ChangeStreamRequest * @see ChangeStreamOptions#getFullDocumentBeforeChangeLookup() * @see ChangeStreamOptionsBuilder#fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) { Assert.notNull(lookup, "FullDocumentBeforeChange not be null"); @@ -448,6 +461,7 @@ public class ChangeStreamRequest * @param timeout must not be {@literal null}. * @since 3.0 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder maxAwaitTime(Duration timeout) { Assert.notNull(timeout, "timeout not be null"); @@ -459,6 +473,7 @@ public class ChangeStreamRequest /** * @return the build {@link ChangeStreamRequest}. */ + @Contract("-> new") public ChangeStreamRequest build() { Assert.notNull(listener, "MessageListener must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java index fc8372613..cc4d3f0bd 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/ChangeStreamTask.java @@ -27,6 +27,7 @@ import org.bson.BsonDocument; import org.bson.BsonTimestamp; import org.bson.BsonValue; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.ChangeStreamEvent; import org.springframework.data.mongodb.core.ChangeStreamOptions; import org.springframework.data.mongodb.core.MongoTemplate; @@ -39,7 +40,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.messaging.Message.MessageProperties; import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ErrorHandler; import org.springframework.util.StringUtils; @@ -224,21 +224,18 @@ class ChangeStreamTask extends CursorReadingTask, this.messageProperties = messageProperties; } - @Nullable @Override - public ChangeStreamDocument getRaw() { + public @Nullable ChangeStreamDocument getRaw() { return delegate.getRaw(); } - @Nullable @Override - public T getBody() { + public @Nullable T getBody() { return delegate.getBody(); } - @Nullable @Override - public T getBodyBeforeChange() { + public @Nullable T getBodyBeforeChange() { return delegate.getBodyBeforeChange(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java index 41b5fed4f..662960284 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/CursorReadingTask.java @@ -21,12 +21,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.messaging.Message.MessageProperties; import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; import org.springframework.data.util.Lock; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; @@ -51,7 +51,7 @@ abstract class CursorReadingTask implements Task { private State state = State.CREATED; - private MongoCursor cursor; + private @Nullable MongoCursor cursor; /** * @param template must not be {@literal null}. @@ -109,6 +109,7 @@ abstract class CursorReadingTask implements Task { * is immediately {@link MongoCursor#close() closed} and a new {@link MongoCursor} is requested until a valid one is * retrieved or the {@link #state} changes. */ + @SuppressWarnings("NullAway") private void start() { lock.executeWithoutResult(() -> { @@ -188,6 +189,7 @@ abstract class CursorReadingTask implements Task { return awaitStart.await(timeout.toNanos(), TimeUnit.NANOSECONDS); } + @SuppressWarnings("NullAway") protected Message createMessage(T source, Class targetType, RequestOptions options) { SimpleMessage message = new SimpleMessage<>(source, source, MessageProperties.builder() @@ -209,11 +211,10 @@ abstract class CursorReadingTask implements Task { } } - @Nullable - private T getNext() { + private @Nullable T getNext() { return lock.execute(() -> { - if (State.RUNNING.equals(state)) { + if (cursor != null && State.RUNNING.equals(state)) { return cursor.tryNext(); } throw new IllegalStateException(String.format("Cursor %s is not longer open", cursor)); @@ -239,8 +240,7 @@ abstract class CursorReadingTask implements Task { * @return can be {@literal null}. * @throws RuntimeException The potentially translated exception. */ - @Nullable - private V execute(Supplier callback) { + private @Nullable V execute(Supplier callback) { try { return callback.get(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java index 546f3fdd3..1b24e67e0 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/DefaultMessageListenerContainer.java @@ -25,12 +25,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; import org.springframework.data.util.Lock; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java index 1c934e830..f9a9c4131 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/LazyMappingDelegatingMessage.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.core.messaging; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.util.ClassUtils; @@ -38,12 +39,12 @@ class LazyMappingDelegatingMessage implements Message { } @Override - public S getRaw() { + public @Nullable S getRaw() { return delegate.getRaw(); } @Override - public T getBody() { + public @Nullable T getBody() { if (delegate.getBody() == null || targetType.equals(delegate.getBody().getClass())) { return targetType.cast(delegate.getBody()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java index 46db06809..e7aa5b036 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/Message.java @@ -15,7 +15,8 @@ */ package org.springframework.data.mongodb.core.messaging; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -59,8 +60,7 @@ public interface Message { * @return can be {@literal null}. * @since 4.0 */ - @Nullable - default T getBodyBeforeChange() { + default @Nullable T getBodyBeforeChange() { return null; } @@ -87,8 +87,7 @@ public interface Message { * * @return can be {@literal null}. */ - @Nullable - public String getDatabaseName() { + public @Nullable String getDatabaseName() { return databaseName; } @@ -97,8 +96,7 @@ public interface Message { * * @return can be {@literal null}. */ - @Nullable - public String getCollectionName() { + public @Nullable String getCollectionName() { return collectionName; } @@ -162,6 +160,7 @@ public interface Message { * @param dbName must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MessagePropertiesBuilder databaseName(String dbName) { Assert.notNull(dbName, "Database name must not be null"); @@ -174,6 +173,7 @@ public interface Message { * @param collectionName must not be {@literal null}. * @return this */ + @Contract("_ -> this") public MessagePropertiesBuilder collectionName(String collectionName) { Assert.notNull(collectionName, "Collection name must not be null"); @@ -185,6 +185,7 @@ public interface Message { /** * @return the built {@link MessageProperties}. */ + @Contract("-> new") public MessageProperties build() { MessageProperties properties = new MessageProperties(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java index be5308e3c..acb7bfd8a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SimpleMessage.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.messaging; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -46,12 +46,12 @@ class SimpleMessage implements Message { } @Override - public S getRaw() { + public @Nullable S getRaw() { return raw; } @Override - public T getBody() { + public @Nullable T getBody() { return body; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java index 287ba293b..7b914f16f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/SubscriptionRequest.java @@ -17,9 +17,9 @@ package org.springframework.data.mongodb.core.messaging; import java.time.Duration; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -61,8 +61,7 @@ public interface SubscriptionRequest { * @return the name of the database to subscribe to. Can be {@literal null} in which case the default * {@link MongoDatabaseFactory#getMongoDatabase() database} is used. */ - @Nullable - default String getDatabaseName() { + default @Nullable String getDatabaseName() { return null; } @@ -106,7 +105,7 @@ public interface SubscriptionRequest { return new RequestOptions() { @Override - public String getCollectionName() { + public @Nullable String getCollectionName() { return null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java index c6caef12f..92e23ff84 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/TailableCursorRequest.java @@ -18,10 +18,11 @@ package org.springframework.data.mongodb.core.messaging; import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.messaging.SubscriptionRequest.RequestOptions; import org.springframework.data.mongodb.core.messaging.TailableCursorRequest.TailableCursorRequestOptions.TailableCursorRequestOptionsBuilder; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -121,6 +122,7 @@ public class TailableCursorRequest implements SubscriptionRequest implements SubscriptionRequest implements SubscriptionRequest this") public TailableCursorRequestOptionsBuilder collection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -177,6 +180,7 @@ public class TailableCursorRequest implements SubscriptionRequest this") public TailableCursorRequestOptionsBuilder filter(Query filter) { Assert.notNull(filter, "Filter must not be null"); @@ -188,6 +192,7 @@ public class TailableCursorRequest implements SubscriptionRequest new") public TailableCursorRequestOptions build() { TailableCursorRequestOptions options = new TailableCursorRequestOptions(); @@ -220,6 +225,7 @@ public class TailableCursorRequest implements SubscriptionRequest this") public TailableCursorRequestBuilder collection(String collectionName) { Assert.hasText(collectionName, "CollectionName must not be null"); @@ -234,6 +240,7 @@ public class TailableCursorRequest implements SubscriptionRequest this") public TailableCursorRequestBuilder publishTo(MessageListener messageListener) { Assert.notNull(messageListener, "MessageListener must not be null"); @@ -248,6 +255,7 @@ public class TailableCursorRequest implements SubscriptionRequest this") public TailableCursorRequestBuilder filter(Query filter) { Assert.notNull(filter, "Filter must not be null"); @@ -259,6 +267,7 @@ public class TailableCursorRequest implements SubscriptionRequest new") public TailableCursorRequest build() { Assert.notNull(listener, "MessageListener must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java index 35be8f2ef..aa879cc3c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/messaging/package-info.java @@ -2,5 +2,5 @@ * MongoDB specific messaging support for listening to eg. * Change Streams. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.messaging; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java index e2f9169d0..cae1d3df4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/package-info.java @@ -1,6 +1,6 @@ /** * MongoDB core support. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java index 8b1620b32..fd8103027 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java @@ -18,7 +18,8 @@ package org.springframework.data.mongodb.core.query; import static org.springframework.util.ObjectUtils.*; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -91,6 +92,7 @@ public class BasicQuery extends Query { * @param query the query to copy. * @since 4.4 */ + @SuppressWarnings("NullAway") public BasicQuery(Query query) { super(query); @@ -101,6 +103,7 @@ public class BasicQuery extends Query { } @Override + @Contract("_ -> this") public Query addCriteria(CriteriaDefinition criteria) { this.queryObject.putAll(criteria.getCriteriaObject()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java index 12843ce62..3d89f1e1b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicUpdate.java @@ -23,13 +23,11 @@ import java.util.Map; import java.util.function.BiFunction; import org.bson.Document; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.ClassUtils; /** - * {@link Document}-based {@link Update} variant. - * * @author Thomas Risberg * @author John Brisbin * @author Oliver Gierke @@ -49,48 +47,56 @@ public class BasicUpdate extends Update { } @Override + @Contract("_, _ -> this") public Update set(String key, @Nullable Object value) { setOperationValue("$set", key, value); return this; } @Override + @Contract("_ -> this") public Update unset(String key) { setOperationValue("$unset", key, 1); return this; } @Override + @Contract("_, _ -> this") public Update inc(String key, Number inc) { setOperationValue("$inc", key, inc); return this; } @Override + @Contract("_, _ -> this") public Update push(String key, @Nullable Object value) { setOperationValue("$push", key, value); return this; } @Override + @Contract("_, _ -> this") public Update addToSet(String key, @Nullable Object value) { setOperationValue("$addToSet", key, value); return this; } @Override + @Contract("_, _ -> this") public Update pop(String key, Position pos) { setOperationValue("$pop", key, (pos == Position.FIRST ? -1 : 1)); return this; } @Override + @Contract("_, _ -> this") public Update pull(String key, @Nullable Object value) { setOperationValue("$pull", key, value); return this; } @Override + @Contract("_, _ -> this") public Update pullAll(String key, Object[] values) { setOperationValue("$pullAll", key, List.of(values), (o, o2) -> { @@ -107,6 +113,7 @@ public class BasicUpdate extends Update { } @Override + @Contract("_, _ -> this") public Update rename(String oldName, String newName) { setOperationValue("$rename", oldName, newName); return this; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java index de24c0511..217e66988 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java @@ -19,8 +19,9 @@ import java.util.Locale; import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -180,6 +181,7 @@ public class Collation { * @param strength comparison level. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation strength(int strength) { ComparisonLevel current = this.strength.orElseGet(() -> new ICUComparisonLevel(strength)); @@ -192,6 +194,7 @@ public class Collation { * @param comparisonLevel must not be {@literal null}. * @return new {@link Collation} */ + @Contract("_ -> new") public Collation strength(ComparisonLevel comparisonLevel) { Collation newInstance = copy(); @@ -205,6 +208,7 @@ public class Collation { * @param caseLevel use {@literal true} to enable {@code caseLevel} comparison. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation caseLevel(boolean caseLevel) { ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::primary); @@ -218,6 +222,7 @@ public class Collation { * @param caseFirst must not be {@literal null}. * @return new instance of {@link Collation}. */ + @Contract("_ -> new") public Collation caseFirst(String caseFirst) { return caseFirst(new CaseFirst(caseFirst)); } @@ -228,6 +233,7 @@ public class Collation { * @param sort must not be {@literal null}. * @return new instance of {@link Collation}. */ + @Contract("_ -> new") public Collation caseFirst(CaseFirst sort) { ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::tertiary); @@ -239,6 +245,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation numericOrderingEnabled() { return numericOrdering(true); } @@ -248,6 +255,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation numericOrderingDisabled() { return numericOrdering(false); } @@ -257,6 +265,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation numericOrdering(boolean flag) { Collation newInstance = copy(); @@ -271,6 +280,7 @@ public class Collation { * @param alternate must not be {@literal null}. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation alternate(String alternate) { Alternate instance = this.alternate.orElseGet(() -> new Alternate(alternate, Optional.empty())); @@ -284,6 +294,7 @@ public class Collation { * @param alternate must not be {@literal null}. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation alternate(Alternate alternate) { Collation newInstance = copy(); @@ -296,6 +307,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation backwardDiacriticSort() { return backwards(true); } @@ -305,6 +317,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation forwardDiacriticSort() { return backwards(false); } @@ -315,6 +328,7 @@ public class Collation { * @param backwards must not be {@literal null}. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation backwards(boolean backwards) { Collation newInstance = copy(); @@ -327,6 +341,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation normalizationEnabled() { return normalization(true); } @@ -336,6 +351,7 @@ public class Collation { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation normalizationDisabled() { return normalization(false); } @@ -346,6 +362,7 @@ public class Collation { * @param normalization must not be {@literal null}. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation normalization(boolean normalization) { Collation newInstance = copy(); @@ -359,6 +376,7 @@ public class Collation { * @param maxVariable must not be {@literal null}. * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation maxVariable(String maxVariable) { Alternate alternateValue = alternate.orElseGet(Alternate::shifted); @@ -370,6 +388,7 @@ public class Collation { * * @return the native MongoDB {@link Document} representation of the {@link Collation}. */ + @SuppressWarnings("NullAway") public Document toDocument() { return map(toMongoDocumentConverter()); } @@ -379,7 +398,7 @@ public class Collation { * * @return he native MongoDB representation of the {@link Collation}. */ - public com.mongodb.client.model.Collation toMongoCollation() { + public com.mongodb.client.model.@Nullable Collation toMongoCollation() { return map(toMongoCollationConverter()); } @@ -390,7 +409,7 @@ public class Collation { * @param * @return the converted result. */ - public R map(Converter mapper) { + public @Nullable R map(Converter mapper) { return mapper.convert(this); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index 8d4cb703b..547c6965c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -33,6 +33,7 @@ import org.bson.BsonRegularExpression; import org.bson.BsonType; import org.bson.Document; import org.bson.types.Binary; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Example; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Point; @@ -45,7 +46,7 @@ import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type; import org.springframework.data.mongodb.core.schema.JsonSchemaProperty; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; import org.springframework.data.mongodb.util.RegexFlags; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; @@ -184,6 +185,7 @@ public class Criteria implements CriteriaDefinition { * * @return new instance of {@link Criteria}. */ + @Contract("_ -> new") public Criteria and(String key) { return new Criteria(this.criteriaChain, key); } @@ -194,6 +196,7 @@ public class Criteria implements CriteriaDefinition { * @param value can be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria is(@Nullable Object value) { if (!NOT_SET.equals(isValue)) { @@ -221,6 +224,7 @@ public class Criteria implements CriteriaDefinition { * Missing Fields: Equality Filter * @since 3.3 */ + @Contract("_ -> this") public Criteria isNull() { return is(null); } @@ -237,6 +241,7 @@ public class Criteria implements CriteriaDefinition { * Fields: Type Check * @since 3.3 */ + @Contract("_ -> this") public Criteria isNullValue() { criteria.put("$type", BsonType.NULL.getValue()); @@ -254,6 +259,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $ne */ + @Contract("_ -> this") public Criteria ne(@Nullable Object value) { criteria.put("$ne", value); return this; @@ -266,6 +272,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $lt */ + @Contract("_ -> this") public Criteria lt(Object value) { criteria.put("$lt", value); return this; @@ -278,6 +285,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $lte */ + @Contract("_ -> this") public Criteria lte(Object value) { criteria.put("$lte", value); return this; @@ -290,6 +298,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $gt */ + @Contract("_ -> this") public Criteria gt(Object value) { criteria.put("$gt", value); return this; @@ -302,6 +311,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $gte */ + @Contract("_ -> this") public Criteria gte(Object value) { criteria.put("$gte", value); return this; @@ -314,7 +324,8 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $in */ - public Criteria in(Object... values) { + @Contract("_ -> this") + public Criteria in(@Nullable Object ... values) { if (values.length > 1 && values[1] instanceof Collection) { throw new InvalidMongoDbApiUsageException( "You can only pass in one argument of type " + values[1].getClass().getName()); @@ -330,6 +341,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $in */ + @Contract("_ -> this") public Criteria in(Collection values) { criteria.put("$in", values); return this; @@ -342,6 +354,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $nin */ + @Contract("_ -> this") public Criteria nin(Object... values) { return nin(Arrays.asList(values)); } @@ -353,6 +366,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $nin */ + @Contract("_ -> this") public Criteria nin(Collection values) { criteria.put("$nin", values); return this; @@ -366,6 +380,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $mod */ + @Contract("_ -> this") public Criteria mod(Number value, Number remainder) { List l = new ArrayList<>(2); l.add(value); @@ -381,6 +396,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $all */ + @Contract("_ -> this") public Criteria all(Object... values) { return all(Arrays.asList(values)); } @@ -392,6 +408,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $all */ + @Contract("_ -> this") public Criteria all(Collection values) { criteria.put("$all", values); return this; @@ -404,6 +421,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $size */ + @Contract("_ -> this") public Criteria size(int size) { criteria.put("$size", size); return this; @@ -416,6 +434,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $exists */ + @Contract("_ -> this") public Criteria exists(boolean value) { criteria.put("$exists", value); return this; @@ -431,6 +450,7 @@ public class Criteria implements CriteriaDefinition { * $sampleRate * @since 3.3 */ + @Contract("_ -> this") public Criteria sampleRate(double sampleRate) { Assert.isTrue(sampleRate >= 0, "The sample rate must be greater than zero"); @@ -447,6 +467,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $type */ + @Contract("_ -> this") public Criteria type(int typeNumber) { criteria.put("$type", typeNumber); return this; @@ -460,6 +481,7 @@ public class Criteria implements CriteriaDefinition { * @since 2.1 * @see MongoDB Query operator: $type */ + @Contract("_ -> this") public Criteria type(Type... types) { Assert.notNull(types, "Types must not be null"); @@ -476,6 +498,7 @@ public class Criteria implements CriteriaDefinition { * @since 3.2 * @see MongoDB Query operator: $type */ + @Contract("_ -> this") public Criteria type(Collection types) { Assert.notNull(types, "Types must not be null"); @@ -490,6 +513,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $not */ + @Contract("-> this") public Criteria not() { return not(null); } @@ -501,6 +525,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $not */ + @Contract("_ -> this") private Criteria not(@Nullable Object value) { criteria.put("$not", value); return this; @@ -513,6 +538,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $regex */ + @Contract("_ -> this") public Criteria regex(String regex) { return regex(regex, null); } @@ -525,6 +551,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $regex */ + @Contract("_, _ -> this") public Criteria regex(String regex, @Nullable String options) { return regex(toPattern(regex, options)); } @@ -535,6 +562,7 @@ public class Criteria implements CriteriaDefinition { * @param pattern must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria regex(Pattern pattern) { Assert.notNull(pattern, "Pattern must not be null"); @@ -553,6 +581,7 @@ public class Criteria implements CriteriaDefinition { * @param regex must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria regex(BsonRegularExpression regex) { if (lastOperatorWasNot()) { @@ -581,6 +610,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $centerSphere */ + @Contract("_ -> this") public Criteria withinSphere(Circle circle) { Assert.notNull(circle, "Circle must not be null"); @@ -597,6 +627,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $geoWithin */ + @Contract("_ -> this") public Criteria within(Shape shape) { Assert.notNull(shape, "Shape must not be null"); @@ -612,6 +643,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @see MongoDB Query operator: $near */ + @Contract("_ -> this") public Criteria near(Point point) { Assert.notNull(point, "Point must not be null"); @@ -629,6 +661,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $nearSphere */ + @Contract("_ -> this") public Criteria nearSphere(Point point) { Assert.notNull(point, "Point must not be null"); @@ -646,6 +679,7 @@ public class Criteria implements CriteriaDefinition { * @since 1.8 */ @SuppressWarnings("rawtypes") + @Contract("_ -> this") public Criteria intersects(GeoJson geoJson) { Assert.notNull(geoJson, "GeoJson must not be null"); @@ -665,6 +699,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $maxDistance */ + @Contract("_ -> this") public Criteria maxDistance(double maxDistance) { if (createNearCriteriaForCommand("$near", "$maxDistance", maxDistance) @@ -687,6 +722,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @since 1.7 */ + @Contract("_ -> this") public Criteria minDistance(double minDistance) { if (createNearCriteriaForCommand("$near", "$minDistance", minDistance) @@ -706,6 +742,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $elemMatch */ + @Contract("_ -> this") public Criteria elemMatch(Criteria criteria) { this.criteria.put("$elemMatch", criteria.getCriteriaObject()); return this; @@ -718,6 +755,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @since 1.8 */ + @Contract("_ -> this") public Criteria alike(Example sample) { if (StringUtils.hasText(this.getKey())) { @@ -745,6 +783,7 @@ public class Criteria implements CriteriaDefinition { * @see MongoDB Query operator: * $jsonSchema */ + @Contract("_ -> this") public Criteria andDocumentStructureMatches(MongoJsonSchema schema) { Assert.notNull(schema, "Schema must not be null"); @@ -776,6 +815,7 @@ public class Criteria implements CriteriaDefinition { * @param criteria must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria orOperator(Criteria... criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -793,6 +833,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria orOperator(Collection criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -810,6 +851,7 @@ public class Criteria implements CriteriaDefinition { * @param criteria must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria norOperator(Criteria... criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -827,6 +869,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria norOperator(Collection criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -844,6 +887,7 @@ public class Criteria implements CriteriaDefinition { * @param criteria must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria andOperator(Criteria... criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -861,6 +905,7 @@ public class Criteria implements CriteriaDefinition { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria andOperator(Collection criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -884,8 +929,7 @@ public class Criteria implements CriteriaDefinition { * @see org.springframework.data.mongodb.core.query.CriteriaDefinition#getKey() */ @Override - @Nullable - public String getKey() { + public @Nullable String getKey() { return this.key; } @@ -1095,7 +1139,7 @@ public class Criteria implements CriteriaDefinition { if (Collection.class.isAssignableFrom(left.getClass())) { - if (!Collection.class.isAssignableFrom(right.getClass())) { + if (right == null || !Collection.class.isAssignableFrom(right.getClass())) { return false; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java index c00b1d4b8..c75f709ab 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/CriteriaDefinition.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.query; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Oliver Gierke diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java index 3540a5a83..9775fefdb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java @@ -22,8 +22,9 @@ import java.util.Map; import java.util.Map.Entry; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.MongoExpression; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -52,6 +53,7 @@ public class Field { * @param field the document field name to be included. * @return {@code this} field projection instance. */ + @Contract("_ -> this") public Field include(String field) { Assert.notNull(field, "Key must not be null"); @@ -111,6 +113,7 @@ public class Field { * @return new instance of {@link FieldProjectionExpression}. * @since 3.2 */ + @Contract("_, _ -> this") public Field projectAs(MongoExpression expression, String field) { criteria.put(field, expression); @@ -124,6 +127,7 @@ public class Field { * @return {@code this} field projection instance. * @since 3.1 */ + @Contract("_ -> this") public Field include(String... fields) { return include(Arrays.asList(fields)); } @@ -135,6 +139,7 @@ public class Field { * @return {@code this} field projection instance. * @since 4.4 */ + @Contract("_ -> this") public Field include(Collection fields) { Assert.notNull(fields, "Keys must not be null"); @@ -149,6 +154,7 @@ public class Field { * @param field the document field name to be excluded. * @return {@code this} field projection instance. */ + @Contract("_ -> this") public Field exclude(String field) { Assert.notNull(field, "Key must not be null"); @@ -165,6 +171,7 @@ public class Field { * @return {@code this} field projection instance. * @since 3.1 */ + @Contract("_ -> this") public Field exclude(String... fields) { return exclude(Arrays.asList(fields)); } @@ -176,6 +183,7 @@ public class Field { * @return {@code this} field projection instance. * @since 4.4 */ + @Contract("_ -> this") public Field exclude(Collection fields) { Assert.notNull(fields, "Keys must not be null"); @@ -191,6 +199,7 @@ public class Field { * @param size the number of elements to include. * @return {@code this} field projection instance. */ + @Contract("_, _ -> this") public Field slice(String field, int size) { Assert.notNull(field, "Key must not be null"); @@ -209,12 +218,14 @@ public class Field { * @param size the number of elements to include. * @return {@code this} field projection instance. */ + @Contract("_, _, _ -> this") public Field slice(String field, int offset, int size) { slices.put(field, Arrays.asList(offset, size)); return this; } + @Contract("_, _ -> this") public Field elemMatch(String field, Criteria elemMatchCriteria) { elemMatches.put(field, elemMatchCriteria); @@ -229,6 +240,7 @@ public class Field { * @param value * @return {@code this} field projection instance. */ + @Contract("_, _ -> this") public Field position(String field, int value) { Assert.hasText(field, "DocumentField must not be null or empty"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java index 83417c720..19ecd94e2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java @@ -17,12 +17,12 @@ package org.springframework.data.mongodb.core.query; import static org.springframework.util.ObjectUtils.*; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Box; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Polygon; import org.springframework.data.geo.Shape; import org.springframework.data.mongodb.core.geo.Sphere; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java index 5757aa94a..5ec4af398 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java @@ -23,7 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -50,8 +50,8 @@ public class Meta { private Map values = Collections.emptyMap(); private Set flags = Collections.emptySet(); - private Integer cursorBatchSize; - private Boolean allowDiskUse; + private @Nullable Integer cursorBatchSize; + private @Nullable Boolean allowDiskUse; public Meta() {} @@ -85,8 +85,7 @@ public class Meta { /** * @return {@literal null} if not set. */ - @Nullable - public Long getMaxTimeMsec() { + public @Nullable Long getMaxTimeMsec() { return getValue(MetaKey.MAX_TIME_MS.key); } @@ -181,8 +180,7 @@ public class Meta { * @return {@literal null} if not set. * @since 2.1 */ - @Nullable - public Integer getCursorBatchSize() { + public @Nullable Integer getCursorBatchSize() { return cursorBatchSize; } @@ -285,9 +283,8 @@ public class Meta { this.values.put(key, value); } - @Nullable @SuppressWarnings("unchecked") - private T getValue(String key) { + private @Nullable T getValue(String key) { return (T) this.values.get(key); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java index 571bbd275..5625de5e9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MetricConversion.java @@ -20,9 +20,11 @@ import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Metric; import org.springframework.data.geo.Metrics; +import org.springframework.util.Assert; /** * {@link Metric} and {@link Distance} conversions using the metric system. @@ -151,8 +153,8 @@ public class MetricConversion { */ private static class ConversionMultiplierBuilder { - private Number from; - private Number to; + private @Nullable Number from; + private @Nullable Number to; ConversionMultiplierBuilder() {} @@ -177,6 +179,9 @@ public class MetricConversion { } ConversionMultiplier build() { + + Assert.notNull(from, "[From] must be set first"); + Assert.notNull(to, "[To] must be set first"); return new ConversionMultiplier(this.from, this.to); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java index e26a61c61..b37c08898 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.query; import java.util.regex.Pattern; import org.bson.BsonRegularExpression; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Christoph Strobl @@ -80,8 +80,7 @@ public enum MongoRegexCreator { * @param matcherType the type of matching to perform * @return {@literal source} when {@literal source} or {@literal matcherType} is {@literal null}. */ - @Nullable - public String toRegularExpression(@Nullable String source, @Nullable MatchMode matcherType) { + public @Nullable String toRegularExpression(@Nullable String source, @Nullable MatchMode matcherType) { if (matcherType == null || source == null) { return source; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java index f0f3b0a4d..6dad07b8c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.query; import java.util.Arrays; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.geo.CustomMetric; import org.springframework.data.geo.Distance; @@ -27,7 +28,7 @@ import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.ReadConcernAware; import org.springframework.data.mongodb.core.ReadPreferenceAware; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -278,6 +279,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return * @since 2.2 */ + @Contract("_ -> this") public NearQuery limit(long limit) { this.limit = limit; return this; @@ -289,6 +291,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param skip * @return */ + @Contract("_ -> this") public NearQuery skip(long skip) { this.skip = skip; return this; @@ -300,6 +303,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param pageable must not be {@literal null} * @return */ + @Contract("_ -> this") public NearQuery with(Pageable pageable) { Assert.notNull(pageable, "Pageable must not be 'null'"); @@ -323,6 +327,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param maxDistance * @return */ + @Contract("_ -> this") public NearQuery maxDistance(double maxDistance) { return maxDistance(new Distance(maxDistance, getMetric())); } @@ -335,6 +340,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param metric must not be {@literal null}. * @return */ + @Contract("_, _ -> this") public NearQuery maxDistance(double maxDistance, Metric metric) { Assert.notNull(metric, "Metric must not be null"); @@ -349,6 +355,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param distance must not be {@literal null}. * @return */ + @Contract("_ -> this") public NearQuery maxDistance(Distance distance) { Assert.notNull(distance, "Distance must not be null"); @@ -379,6 +386,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return * @since 1.7 */ + @Contract("_ -> this") public NearQuery minDistance(double minDistance) { return minDistance(new Distance(minDistance, getMetric())); } @@ -392,6 +400,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return * @since 1.7 */ + @Contract("_, _ -> this") public NearQuery minDistance(double minDistance, Metric metric) { Assert.notNull(metric, "Metric must not be null"); @@ -407,6 +416,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return * @since 1.7 */ + @Contract("_ -> this") public NearQuery minDistance(Distance distance) { Assert.notNull(distance, "Distance must not be null"); @@ -428,8 +438,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * * @return */ - @Nullable - public Distance getMaxDistance() { + public @Nullable Distance getMaxDistance() { return this.maxDistance; } @@ -439,8 +448,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return * @since 1.7 */ - @Nullable - public Distance getMinDistance() { + public @Nullable Distance getMinDistance() { return this.minDistance; } @@ -450,6 +458,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param distanceMultiplier * @return */ + @Contract("_ -> this") public NearQuery distanceMultiplier(double distanceMultiplier) { this.metric = new CustomMetric(distanceMultiplier); @@ -462,6 +471,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param spherical * @return */ + @Contract("_ -> this") public NearQuery spherical(boolean spherical) { this.spherical = spherical; return this; @@ -482,6 +492,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * * @return */ + @Contract("-> this") public NearQuery inKilometers() { return adaptMetric(Metrics.KILOMETERS); } @@ -492,6 +503,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * * @return */ + @Contract("-> this") public NearQuery inMiles() { return adaptMetric(Metrics.MILES); } @@ -504,6 +516,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * passed. * @return */ + @Contract("_ -> this") public NearQuery in(@Nullable Metric metric) { return adaptMetric(metric == null ? Metrics.NEUTRAL : metric); } @@ -514,6 +527,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * * @param metric */ + @Contract("_ -> this") private NearQuery adaptMetric(Metric metric) { if (metric != Metrics.NEUTRAL) { @@ -530,6 +544,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @param query must not be {@literal null}. * @return */ + @Contract("_ -> this") public NearQuery query(Query query) { Assert.notNull(query, "Cannot apply 'null' query on NearQuery"); @@ -546,8 +561,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { /** * @return the number of elements to skip. */ - @Nullable - public Long getSkip() { + public @Nullable Long getSkip() { return skip; } @@ -557,8 +571,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return the {@link Collation} if set. {@literal null} otherwise. * @since 2.2 */ - @Nullable - public Collation getCollation() { + public @Nullable Collation getCollation() { return query != null ? query.getCollation().orElse(null) : null; } @@ -570,6 +583,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 4.1 */ + @Contract("_ -> this") public NearQuery withReadConcern(ReadConcern readConcern) { Assert.notNull(readConcern, "ReadConcern must not be null"); @@ -585,6 +599,7 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 4.1 */ + @Contract("_ -> this") public NearQuery withReadPreference(ReadPreference readPreference) { Assert.notNull(readPreference, "ReadPreference must not be null"); @@ -601,9 +616,8 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @since 4.1 * @see ReadConcernAware */ - @Nullable @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { if (query != null && query.hasReadConcern()) { return query.getReadConcern(); @@ -620,9 +634,8 @@ public final class NearQuery implements ReadConcernAware, ReadPreferenceAware { * @since 4.1 * @see ReadPreferenceAware */ - @Nullable @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { if (query != null && query.hasReadPreference()) { return query.getReadPreference(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java index 31c6b9069..47ce615fe 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java @@ -15,8 +15,9 @@ */ package org.springframework.data.mongodb.core.query; -import static org.springframework.data.mongodb.core.query.SerializationUtils.*; -import static org.springframework.util.ObjectUtils.*; +import static org.springframework.data.mongodb.core.query.SerializationUtils.serializeToJsonSafely; +import static org.springframework.util.ObjectUtils.nullSafeEquals; +import static org.springframework.util.ObjectUtils.nullSafeHashCode; import java.time.Duration; import java.util.ArrayList; @@ -30,6 +31,7 @@ import java.util.Optional; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.KeysetScrollPosition; import org.springframework.data.domain.Limit; import org.springframework.data.domain.OffsetScrollPosition; @@ -42,7 +44,7 @@ import org.springframework.data.mongodb.core.ReadConcernAware; import org.springframework.data.mongodb.core.ReadPreferenceAware; import org.springframework.data.mongodb.core.query.Meta.CursorOption; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.ReadConcern; @@ -69,7 +71,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { private long skip; private Limit limit = Limit.unlimited(); - private KeysetScrollPosition keysetScrollPosition; + private @Nullable KeysetScrollPosition keysetScrollPosition; private @Nullable ReadConcern readConcern; private @Nullable ReadPreference readPreference; @@ -123,6 +125,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 1.6 */ + @Contract("_ -> this") public Query addCriteria(CriteriaDefinition criteriaDefinition) { Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null"); @@ -157,6 +160,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param skip number of documents to skip. Use {@literal zero} or a {@literal negative} value to avoid skipping. * @return this. */ + @Contract("_ -> this") public Query skip(long skip) { this.skip = skip; return this; @@ -169,6 +173,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param limit number of documents to return. Use {@literal zero} or {@literal negative} for unlimited. * @return this. */ + @Contract("_ -> this") public Query limit(int limit) { this.limit = limit > 0 ? Limit.of(limit) : Limit.unlimited(); return this; @@ -181,6 +186,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 4.2 */ + @Contract("_ -> this") public Query limit(Limit limit) { Assert.notNull(limit, "Limit must not be null"); @@ -202,6 +208,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @see Document#parse(String) */ + @Contract("_ -> this") public Query withHint(String hint) { Assert.hasText(hint, "Hint must not be empty or null"); @@ -216,6 +223,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 3.1 */ + @Contract("_ -> this") public Query withReadConcern(ReadConcern readConcern) { Assert.notNull(readConcern, "ReadConcern must not be null"); @@ -230,6 +238,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 4.1 */ + @Contract("_ -> this") public Query withReadPreference(ReadPreference readPreference) { Assert.notNull(readPreference, "ReadPreference must not be null"); @@ -243,7 +252,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return this.readConcern; } @@ -253,7 +262,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { if (readPreference == null) { return getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred() : null; @@ -269,6 +278,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 2.2 */ + @Contract("_ -> this") public Query withHint(Document hint) { Assert.notNull(hint, "Hint must not be null"); @@ -283,6 +293,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param pageable must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(Pageable pageable) { if (pageable.isPaged()) { @@ -299,6 +310,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param position must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(ScrollPosition position) { Assert.notNull(position, "ScrollPosition must not be null"); @@ -320,6 +332,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param position must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(OffsetScrollPosition position) { Assert.notNull(position, "ScrollPosition must not be null"); @@ -335,6 +348,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param position must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(KeysetScrollPosition position) { Assert.notNull(position, "ScrollPosition must not be null"); @@ -349,8 +363,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { return keysetScrollPosition != null; } - @Nullable - public KeysetScrollPosition getKeyset() { + public @Nullable KeysetScrollPosition getKeyset() { return keysetScrollPosition; } @@ -360,6 +373,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param sort must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(Sort sort) { Assert.notNull(sort, "Sort must not be null"); @@ -393,6 +407,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @param additionalTypes may not be {@literal null} * @return this. */ + @Contract("_, _ -> this") public Query restrict(Class type, Class... additionalTypes) { Assert.notNull(type, "Type must not be null"); @@ -518,6 +533,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see Meta#setMaxTimeMsec(long) * @since 1.6 */ + @Contract("_ -> this") public Query maxTimeMsec(long maxTimeMsec) { meta.setMaxTimeMsec(maxTimeMsec); @@ -530,6 +546,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see Meta#setMaxTime(Duration) * @since 2.1 */ + @Contract("_ -> this") public Query maxTime(Duration timeout) { meta.setMaxTime(timeout); @@ -544,6 +561,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see Meta#setComment(String) * @since 1.6 */ + @Contract("_ -> this") public Query comment(String comment) { meta.setComment(comment); @@ -562,6 +580,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see Meta#setAllowDiskUse(Boolean) * @since 3.2 */ + @Contract("_ -> this") public Query allowDiskUse(boolean allowDiskUse) { meta.setAllowDiskUse(allowDiskUse); @@ -578,6 +597,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see Meta#setCursorBatchSize(int) * @since 2.1 */ + @Contract("_ -> this") public Query cursorBatchSize(int batchSize) { meta.setCursorBatchSize(batchSize); @@ -589,6 +609,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#NO_TIMEOUT * @since 1.10 */ + @Contract("-> this") public Query noCursorTimeout() { meta.addFlag(Meta.CursorOption.NO_TIMEOUT); @@ -600,6 +621,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#EXHAUST * @since 1.10 */ + @Contract("-> this") public Query exhaust() { meta.addFlag(Meta.CursorOption.EXHAUST); @@ -613,6 +635,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#SECONDARY_READS * @since 3.0.2 */ + @Contract("-> this") public Query allowSecondaryReads() { meta.addFlag(Meta.CursorOption.SECONDARY_READS); @@ -624,6 +647,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#PARTIAL * @since 1.10 */ + @Contract("-> this") public Query partialResults() { meta.addFlag(Meta.CursorOption.PARTIAL); @@ -655,6 +679,7 @@ public class Query implements ReadConcernAware, ReadPreferenceAware { * @return this. * @since 2.0 */ + @Contract("_ -> this") public Query collation(@Nullable Collation collation) { this.collation = Optional.ofNullable(collation); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java index 11e0f7fb2..29f8adb2c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/SerializationUtils.java @@ -23,9 +23,9 @@ import java.util.LinkedHashMap; import java.util.Map; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.ObjectUtils; /** @@ -110,8 +110,8 @@ public abstract class SerializationUtils { * @param value * @return the serialized value or {@literal null}. */ - @Nullable - public static String serializeToJsonSafely(@Nullable Object value) { + @Contract("null -> null; !null -> !null") + public static @Nullable String serializeToJsonSafely(@Nullable Object value) { if (value == null) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java index bd6d8c346..cc8743417 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Term.java @@ -15,7 +15,8 @@ */ package org.springframework.data.mongodb.core.query; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.ObjectUtils; /** @@ -61,6 +62,7 @@ public class Term { * * @return */ + @Contract("-> this") public Term negate() { this.negated = true; return this; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java index e1a7d0c4d..5cedc2e47 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextCriteria.java @@ -19,7 +19,8 @@ import java.util.ArrayList; import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -71,7 +72,8 @@ public class TextCriteria implements CriteriaDefinition { * @param language * @return */ - public static TextCriteria forLanguage(String language) { + @Contract("null -> fail") + public static TextCriteria forLanguage(@Nullable String language) { Assert.hasText(language, "Language must not be null or empty"); return new TextCriteria(language); @@ -83,6 +85,7 @@ public class TextCriteria implements CriteriaDefinition { * @param words the words to match. * @return */ + @Contract("_ -> this") public TextCriteria matchingAny(String... words) { for (String word : words) { @@ -97,6 +100,7 @@ public class TextCriteria implements CriteriaDefinition { * * @param term must not be {@literal null}. */ + @Contract("_ -> this") public TextCriteria matching(Term term) { Assert.notNull(term, "Term to add must not be null"); @@ -109,6 +113,7 @@ public class TextCriteria implements CriteriaDefinition { * @param term * @return */ + @Contract("_ -> this") public TextCriteria matching(String term) { if (StringUtils.hasText(term)) { @@ -121,6 +126,7 @@ public class TextCriteria implements CriteriaDefinition { * @param term * @return */ + @Contract("_ -> this") public TextCriteria notMatching(String term) { if (StringUtils.hasText(term)) { @@ -133,6 +139,7 @@ public class TextCriteria implements CriteriaDefinition { * @param words * @return */ + @Contract("_ -> this") public TextCriteria notMatchingAny(String... words) { for (String word : words) { @@ -147,6 +154,7 @@ public class TextCriteria implements CriteriaDefinition { * @param phrase * @return */ + @Contract("_ -> this") public TextCriteria notMatchingPhrase(String phrase) { if (StringUtils.hasText(phrase)) { @@ -161,6 +169,7 @@ public class TextCriteria implements CriteriaDefinition { * @param phrase * @return */ + @Contract("_ -> this") public TextCriteria matchingPhrase(String phrase) { if (StringUtils.hasText(phrase)) { @@ -176,6 +185,7 @@ public class TextCriteria implements CriteriaDefinition { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public TextCriteria caseSensitive(boolean caseSensitive) { this.caseSensitive = caseSensitive; @@ -189,6 +199,7 @@ public class TextCriteria implements CriteriaDefinition { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public TextCriteria diacriticSensitive(boolean diacriticSensitive) { this.diacriticSensitive = diacriticSensitive; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java index a6583299d..a9f82a857 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java @@ -19,8 +19,9 @@ import java.util.Locale; import java.util.Map.Entry; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * {@link Query} implementation to be used to for performing full text searches. @@ -100,6 +101,7 @@ public class TextQuery extends Query { * @see TextQuery#includeScore() * @return this. */ + @Contract("-> this") public TextQuery sortByScore() { this.sortByScoreIndex = getSortObject().size(); @@ -113,6 +115,7 @@ public class TextQuery extends Query { * * @return this. */ + @Contract("-> this") public TextQuery includeScore() { this.includeScore = true; @@ -125,6 +128,7 @@ public class TextQuery extends Query { * @param fieldname must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public TextQuery includeScore(String fieldname) { setScoreFieldName(fieldname); @@ -170,9 +174,8 @@ public class TextQuery extends Query { int sortByScoreIndex = this.sortByScoreIndex; - return sortByScoreIndex != 0 - ? sortByScoreAtPosition(super.getSortObject(), sortByScoreIndex) - : sortByScoreAtPositionZero(); + return sortByScoreIndex != 0 ? sortByScoreAtPosition(super.getSortObject(), sortByScoreIndex) + : sortByScoreAtPositionZero(); } return super.getSortObject(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java index 677575c9e..c02425214 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/UntypedExampleMatcher.java @@ -17,8 +17,8 @@ package org.springframework.data.mongodb.core.query; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.ExampleMatcher; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java index 32d98f580..cfb214a5a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java @@ -27,11 +27,12 @@ import java.util.Objects; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -114,6 +115,7 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $set */ + @Contract("_, _ -> this") public Update set(String key, @Nullable Object value) { addMultiFieldOperation("$set", key, value); return this; @@ -128,6 +130,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $setOnInsert */ + @Contract("_, _ -> this") public Update setOnInsert(String key, @Nullable Object value) { addMultiFieldOperation("$setOnInsert", key, value); return this; @@ -140,6 +143,7 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $unset */ + @Contract("_ -> this") public Update unset(String key) { addMultiFieldOperation("$unset", key, 1); return this; @@ -153,12 +157,14 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $inc */ + @Contract("_, _ -> this") public Update inc(String key, Number inc) { addMultiFieldOperation("$inc", key, inc); return this; } @Override + @Contract("_ -> this") public void inc(String key) { inc(key, 1L); } @@ -171,6 +177,7 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $push */ + @Contract("_, _ -> this") public Update push(String key, @Nullable Object value) { addMultiFieldOperation("$push", key, value); return this; @@ -207,6 +214,7 @@ public class Update implements UpdateDefinition { * @return new instance of {@link AddToSetBuilder}. * @since 1.5 */ + @Contract("_ -> new") public AddToSetBuilder addToSet(String key) { return new AddToSetBuilder(key); } @@ -220,6 +228,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $addToSet */ + @Contract("_, _ -> this") public Update addToSet(String key, @Nullable Object value) { addMultiFieldOperation("$addToSet", key, value); return this; @@ -233,6 +242,7 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $pop */ + @Contract("_, _ -> this") public Update pop(String key, Position pos) { addMultiFieldOperation("$pop", key, pos == Position.FIRST ? -1 : 1); return this; @@ -246,6 +256,7 @@ public class Update implements UpdateDefinition { * @return this. * @see MongoDB Update operator: $pull */ + @Contract("_, _ -> this") public Update pull(String key, @Nullable Object value) { addMultiFieldOperation("$pull", key, value); return this; @@ -260,6 +271,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $pullAll */ + @Contract("_, _ -> this") public Update pullAll(String key, Object[] values) { addMultiFieldOperation("$pullAll", key, Arrays.asList(values)); return this; @@ -274,6 +286,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $rename */ + @Contract("_, _ -> this") public Update rename(String oldName, String newName) { addMultiFieldOperation("$rename", oldName, newName); return this; @@ -288,6 +301,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $currentDate */ + @Contract("_ -> this") public Update currentDate(String key) { addMultiFieldOperation("$currentDate", key, true); @@ -303,6 +317,7 @@ public class Update implements UpdateDefinition { * @see MongoDB Update operator: * $currentDate */ + @Contract("_ -> this") public Update currentTimestamp(String key) { addMultiFieldOperation("$currentDate", key, new Document("$type", "timestamp")); @@ -318,6 +333,7 @@ public class Update implements UpdateDefinition { * @since 1.7 * @see MongoDB Update operator: $mul */ + @Contract("_, _ -> this") public Update multiply(String key, Number multiplier) { Assert.notNull(multiplier, "Multiplier must not be null"); @@ -335,6 +351,7 @@ public class Update implements UpdateDefinition { * @see Comparison/Sort Order * @see MongoDB Update operator: $max */ + @Contract("_, _ -> this") public Update max(String key, Object value) { Assert.notNull(value, "Value for max operation must not be null"); @@ -352,6 +369,7 @@ public class Update implements UpdateDefinition { * @see Comparison/Sort Order * @see MongoDB Update operator: $min */ + @Contract("_, _ -> this") public Update min(String key, Object value) { Assert.notNull(value, "Value for min operation must not be null"); @@ -366,6 +384,7 @@ public class Update implements UpdateDefinition { * @return this. * @since 1.7 */ + @Contract("_ -> new") public BitwiseOperatorBuilder bitwise(String key) { return new BitwiseOperatorBuilder(this, key); } @@ -378,6 +397,7 @@ public class Update implements UpdateDefinition { * @return this. * @since 2.0 */ + @Contract("-> this") public Update isolated() { isolated = true; @@ -392,6 +412,7 @@ public class Update implements UpdateDefinition { * @return this. * @since 2.2 */ + @Contract("_ -> this") public Update filterArray(CriteriaDefinition criteria) { if (arrayFilters == Collections.EMPTY_LIST) { @@ -411,6 +432,7 @@ public class Update implements UpdateDefinition { * @return this. * @since 2.2 */ + @Contract("_, _ -> this") public Update filterArray(String identifier, Object expression) { if (arrayFilters == Collections.EMPTY_LIST) { @@ -815,6 +837,7 @@ public class Update implements UpdateDefinition { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public PushOperatorBuilder slice(int count) { this.modifiers.addModifier(new Slice(count)); @@ -829,6 +852,7 @@ public class Update implements UpdateDefinition { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public PushOperatorBuilder sort(Direction direction) { Assert.notNull(direction, "Direction must not be null"); @@ -844,6 +868,7 @@ public class Update implements UpdateDefinition { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public PushOperatorBuilder sort(Sort sort) { Assert.notNull(sort, "Sort must not be null"); @@ -859,6 +884,7 @@ public class Update implements UpdateDefinition { * @return never {@literal null}. * @since 1.7 */ + @Contract("_ -> this") public PushOperatorBuilder atPosition(int position) { this.modifiers.addModifier(new PositionModifier(position)); @@ -872,6 +898,7 @@ public class Update implements UpdateDefinition { * @return never {@literal null}. * @since 1.7 */ + @Contract("_ -> this") public PushOperatorBuilder atPosition(@Nullable Position position) { if (position == null || Position.LAST.equals(position)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java index d3f67790a..7c6889e45 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/package-info.java @@ -1,6 +1,6 @@ /** * MongoDB specific query and update support. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.query; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java index b59c20c6b..da77a0199 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/DefaultMongoJsonSchema.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.schema; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -31,8 +31,7 @@ class DefaultMongoJsonSchema implements MongoJsonSchema { private final JsonSchemaObject root; - @Nullable // - private final Document encryptionMetadata; + private final @Nullable Document encryptionMetadata; DefaultMongoJsonSchema(JsonSchemaObject root) { this(root, null); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java index 26dbd7dff..503d591d9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/IdentifiableJsonSchemaProperty.java @@ -23,6 +23,7 @@ import java.util.Set; import java.util.UUID; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.mongodb.core.EncryptionAlgorithms; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject; @@ -33,7 +34,7 @@ import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.Numeri import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.TimestampJsonSchemaObject; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -97,6 +98,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -106,6 +108,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(new LinkedHashSet<>(Arrays.asList(allOf))); } @@ -115,6 +118,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf))); } @@ -124,6 +128,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf))); } @@ -133,6 +138,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty possibleValues(Collection possibleValues) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -142,6 +148,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty allOf(Collection allOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -151,6 +158,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty anyOf(Collection anyOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -160,6 +168,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty oneOf(Collection oneOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -169,6 +178,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch)); } @@ -178,6 +188,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#description(String) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty description(String description) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -186,6 +197,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public UntypedJsonSchemaProperty generatedDescription() { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -213,6 +225,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#minLength(int) */ + @Contract("_ -> new") public StringJsonSchemaProperty minLength(int length) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minLength(length)); } @@ -222,6 +235,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#maxLength(int) */ + @Contract("_ -> new") public StringJsonSchemaProperty maxLength(int length) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxLength(length)); } @@ -231,6 +245,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#matching(String) */ + @Contract("_ -> new") public StringJsonSchemaProperty matching(String pattern) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.matching(pattern)); } @@ -240,6 +255,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty possibleValues(String... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -249,6 +265,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(new LinkedHashSet<>(Arrays.asList(allOf))); } @@ -258,6 +275,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf))); } @@ -267,6 +285,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf))); } @@ -276,6 +295,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty possibleValues(Collection possibleValues) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -285,6 +305,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty allOf(Collection allOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -294,6 +315,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty anyOf(Collection anyOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -303,6 +325,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty oneOf(Collection oneOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -312,6 +335,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> new") public StringJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch)); } @@ -321,6 +345,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#description(String) */ + @Contract("_ -> new") public StringJsonSchemaProperty description(String description) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -329,6 +354,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public StringJsonSchemaProperty generatedDescription() { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -355,6 +381,7 @@ public class IdentifiableJsonSchemaProperty implemen * @param range must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaProperty}. */ + @Contract("_ -> new") public ObjectJsonSchemaProperty propertiesCount(Range range) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.propertiesCount(range)); } @@ -364,6 +391,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#minProperties(int) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty minProperties(int count) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minProperties(count)); } @@ -373,6 +401,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#maxProperties(int) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty maxProperties(int count) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxProperties(count)); } @@ -382,6 +411,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#required(String...) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty required(String... properties) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.required(properties)); } @@ -391,6 +421,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#additionalProperties(boolean) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty additionalProperties(boolean additionalPropertiesAllowed) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.additionalProperties(additionalPropertiesAllowed)); @@ -401,6 +432,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty additionalProperties(ObjectJsonSchemaObject additionalProperties) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.additionalProperties(additionalProperties)); @@ -411,6 +443,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty properties(JsonSchemaProperty... properties) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.properties(properties)); } @@ -420,6 +453,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -429,6 +463,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(new LinkedHashSet<>(Arrays.asList(allOf))); } @@ -438,6 +473,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf))); } @@ -447,6 +483,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf))); } @@ -456,6 +493,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty possibleValues(Collection possibleValues) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -465,6 +503,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty allOf(Collection allOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -474,6 +513,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty anyOf(Collection anyOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -483,6 +523,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty oneOf(Collection oneOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -492,6 +533,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch)); } @@ -501,6 +543,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#description(String) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty description(String description) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -509,6 +552,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public ObjectJsonSchemaProperty generatedDescription() { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -540,6 +584,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#multipleOf */ + @Contract("_ -> new") public NumericJsonSchemaProperty multipleOf(Number value) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.multipleOf(value)); } @@ -549,6 +594,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#within(Range) */ + @Contract("_ -> new") public NumericJsonSchemaProperty within(Range range) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.within(range)); } @@ -558,6 +604,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#gt(Number) */ + @Contract("_ -> new") public NumericJsonSchemaProperty gt(Number min) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gt(min)); } @@ -567,6 +614,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#gte(Number) */ + @Contract("_ -> new") public NumericJsonSchemaProperty gte(Number min) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.gte(min)); } @@ -576,6 +624,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#lt(Number) */ + @Contract("_ -> new") public NumericJsonSchemaProperty lt(Number max) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lt(max)); } @@ -585,6 +634,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#lte(Number) */ + @Contract("_ -> new") public NumericJsonSchemaProperty lte(Number max) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.lte(max)); } @@ -594,6 +644,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty possibleValues(Number... possibleValues) { return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues))); } @@ -603,6 +654,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(Arrays.asList(allOf)); } @@ -612,6 +664,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf))); } @@ -621,6 +674,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf))); } @@ -630,6 +684,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty possibleValues(Collection possibleValues) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -639,6 +694,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty allOf(Collection allOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -648,6 +704,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty anyOf(Collection anyOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -657,6 +714,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty oneOf(Collection oneOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -666,6 +724,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> new") public NumericJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch)); } @@ -675,6 +734,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#description(String) */ + @Contract("_ -> new") public NumericJsonSchemaProperty description(String description) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -710,6 +770,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#uniqueItems(boolean) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty uniqueItems(boolean uniqueItems) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.uniqueItems(uniqueItems)); } @@ -719,6 +780,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#range(Range) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty range(Range range) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.range(range)); } @@ -728,6 +790,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#minItems(int) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty minItems(int count) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.minItems(count)); } @@ -737,6 +800,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#maxItems(int) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty maxItems(int count) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.maxItems(count)); } @@ -746,6 +810,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#items(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty items(JsonSchemaObject... items) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(Arrays.asList(items))); } @@ -755,6 +820,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#items(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty items(Collection items) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(items)); } @@ -764,6 +830,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#additionalItems(boolean) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty additionalItems(boolean additionalItemsAllowed) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.additionalItems(additionalItemsAllowed)); } @@ -773,6 +840,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty possibleValues(Object... possibleValues) { return possibleValues(new LinkedHashSet<>(Arrays.asList(possibleValues))); } @@ -782,6 +850,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(new LinkedHashSet<>(Arrays.asList(allOf))); } @@ -791,6 +860,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { return anyOf(new LinkedHashSet<>(Arrays.asList(anyOf))); } @@ -800,6 +870,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { return oneOf(new LinkedHashSet<>(Arrays.asList(oneOf))); } @@ -809,6 +880,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty possibleValues(Collection possibleValues) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -818,6 +890,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty allOf(Collection allOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -827,6 +900,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty anyOf(Collection anyOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -836,6 +910,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty oneOf(Collection oneOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -845,6 +920,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.notMatch(notMatch)); } @@ -854,6 +930,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see ArrayJsonSchemaObject#description(String) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty description(String description) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -884,6 +961,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NumericJsonSchemaProperty}. * @see BooleanJsonSchemaObject#description(String) */ + @Contract("_ -> new") public BooleanJsonSchemaProperty description(String description) { return new BooleanJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -914,6 +992,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link NullJsonSchemaProperty}. * @see NullJsonSchemaObject#description(String) */ + @Contract("_ -> new") public NullJsonSchemaProperty description(String description) { return new NullJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -944,6 +1023,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link DateJsonSchemaProperty}. * @see DateJsonSchemaProperty#description(String) */ + @Contract("_ -> new") public DateJsonSchemaProperty description(String description) { return new DateJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -974,6 +1054,7 @@ public class IdentifiableJsonSchemaProperty implemen * @return new instance of {@link TimestampJsonSchemaProperty}. * @see TimestampJsonSchemaProperty#description(String) */ + @Contract("_ -> new") public TimestampJsonSchemaProperty description(String description) { return new TimestampJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -1085,6 +1166,7 @@ public class IdentifiableJsonSchemaProperty implemen * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("-> new") public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_random() { return algorithm(EncryptionAlgorithms.AEAD_AES_256_CBC_HMAC_SHA_512_Random); } @@ -1094,6 +1176,7 @@ public class IdentifiableJsonSchemaProperty implemen * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("-> new") public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_deterministic() { return algorithm(EncryptionAlgorithms.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic); } @@ -1103,6 +1186,7 @@ public class IdentifiableJsonSchemaProperty implemen * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("_ -> new") public EncryptedJsonSchemaProperty algorithm(String algorithm) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, keyIds); } @@ -1111,6 +1195,7 @@ public class IdentifiableJsonSchemaProperty implemen * @param keyId must not be {@literal null}. * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("_ -> new") public EncryptedJsonSchemaProperty keyId(String keyId) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, null); } @@ -1128,6 +1213,7 @@ public class IdentifiableJsonSchemaProperty implemen * @param keyId must not be {@literal null}. * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("_ -> new") public EncryptedJsonSchemaProperty keys(UUID... keyId) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, null, Arrays.asList(keyId)); } @@ -1136,6 +1222,7 @@ public class IdentifiableJsonSchemaProperty implemen * @param keyId must not be {@literal null}. * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("_ -> new") public EncryptedJsonSchemaProperty keys(Object... keyId) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, null, Arrays.asList(keyId)); } @@ -1180,8 +1267,8 @@ public class IdentifiableJsonSchemaProperty implemen return targetProperty.getTypes(); } - @Nullable - private Type extractPropertyType(Document source) { + + private @Nullable Type extractPropertyType(Document source) { if (source.containsKey("type")) { return Type.of(source.get("type", String.class)); @@ -1193,7 +1280,7 @@ public class IdentifiableJsonSchemaProperty implemen return null; } - public Object getKeyId() { + public @Nullable Object getKeyId() { if (keyId != null) { return keyId; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java index a84f361d3..24a40efa5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaObject.java @@ -31,6 +31,7 @@ import org.bson.types.Binary; import org.bson.types.Code; import org.bson.types.Decimal128; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.DateJsonSchemaObject; @@ -39,7 +40,6 @@ import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.Numeri import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.StringJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.TimestampJsonSchemaObject; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java index f64218cc5..87c46d63d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/MongoJsonSchema.java @@ -23,8 +23,9 @@ import java.util.Map; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -212,7 +213,7 @@ public interface MongoJsonSchema { /** * @return the name of the currently processed element */ - String currentElement(); + @Nullable String currentElement(); /** * @return the path leading to the currently processed element in dot {@literal '.'} notation. @@ -285,11 +286,11 @@ public interface MongoJsonSchema { * @param value the value to apply. * @return */ - static Resolution ofValue(String key, Object value) { + static Resolution ofValue(@Nullable String key, Object value) { return new Resolution() { @Override - public String getKey() { + public @Nullable String getKey() { return key; } @@ -311,8 +312,7 @@ public interface MongoJsonSchema { private ObjectJsonSchemaObject root; - @Nullable // - private Document encryptionMetadata; + private @Nullable Document encryptionMetadata; MongoJsonSchemaBuilder() { root = new ObjectJsonSchemaObject(); @@ -323,6 +323,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#minProperties(int) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder minProperties(int count) { root = root.minProperties(count); @@ -334,6 +335,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#maxProperties(int) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder maxProperties(int count) { root = root.maxProperties(count); @@ -345,6 +347,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#required(String...) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder required(String... properties) { root = root.required(properties); @@ -356,6 +359,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#additionalProperties(boolean) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesAllowed) { root = root.additionalProperties(additionalPropertiesAllowed); @@ -367,6 +371,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#additionalProperties(ObjectJsonSchemaObject) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema) { root = root.additionalProperties(schema); @@ -378,6 +383,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#properties(JsonSchemaProperty...) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) { root = root.properties(properties); @@ -389,6 +395,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#patternProperties(JsonSchemaProperty...) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties) { root = root.patternProperties(properties); @@ -400,6 +407,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#property(JsonSchemaProperty) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder property(JsonSchemaProperty property) { root = root.property(property); @@ -411,6 +419,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder possibleValues(Set possibleValues) { root = root.possibleValues(possibleValues); @@ -422,6 +431,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder allOf(Set allOf) { root = root.allOf(allOf); @@ -433,6 +443,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder anyOf(Set anyOf) { root = root.anyOf(anyOf); @@ -444,6 +455,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder oneOf(Set oneOf) { root = root.oneOf(oneOf); @@ -455,6 +467,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#notMatch(JsonSchemaObject) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) { root = root.notMatch(notMatch); @@ -466,6 +479,7 @@ public interface MongoJsonSchema { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#description(String) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder description(String description) { root = root.description(description); @@ -487,6 +501,7 @@ public interface MongoJsonSchema { * * @return new instance of {@link MongoJsonSchema}. */ + @Contract("-> new") public MongoJsonSchema build() { return new DefaultMongoJsonSchema(root, encryptionMetadata); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java index 95f116619..87bdd8c61 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypeUnifyingMergeFunction.java @@ -22,10 +22,10 @@ import java.util.Map; import java.util.function.BiFunction; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction; import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction.Path; import org.springframework.data.mongodb.core.schema.MongoJsonSchema.ConflictResolutionFunction.Resolution; -import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -119,8 +119,7 @@ class TypeUnifyingMergeFunction implements BiFunction, Map, Map new") public TypedJsonSchemaObject description(String description) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions); } @@ -110,6 +112,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("-> new") public TypedJsonSchemaObject generatedDescription() { return new TypedJsonSchemaObject(types, description, true, restrictions); } @@ -121,6 +124,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject possibleValues(Collection possibleValues) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.possibleValues(possibleValues)); @@ -133,6 +137,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject allOf(Collection allOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.allOf(allOf)); } @@ -144,6 +149,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject anyOf(Collection anyOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.anyOf(anyOf)); } @@ -155,6 +161,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject oneOf(Collection oneOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.oneOf(oneOf)); } @@ -166,6 +173,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.notMatch(notMatch)); } @@ -210,8 +218,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * * @return can be {@literal null}. */ - @Nullable - protected String generateDescription() { + protected @Nullable String generateDescription() { return null; } @@ -264,6 +271,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param count the allowed minimal number of properties. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject minProperties(int count) { Bound upper = this.propertiesCount != null ? this.propertiesCount.getUpperBound() : Bound.unbounded(); @@ -276,6 +284,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param count the allowed maximum number of properties. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject maxProperties(int count) { Bound lower = this.propertiesCount != null ? this.propertiesCount.getLowerBound() : Bound.unbounded(); @@ -288,6 +297,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param properties the names of required properties. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject required(String... properties) { ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -305,6 +315,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param additionalPropertiesAllowed * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject additionalProperties(boolean additionalPropertiesAllowed) { ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -319,6 +330,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param schema must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject additionalProperties(ObjectJsonSchemaObject schema) { ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -332,6 +344,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param properties must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject properties(JsonSchemaProperty... properties) { ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -349,6 +362,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param regularExpressions must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject patternProperties(JsonSchemaProperty... regularExpressions) { ObjectJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -365,41 +379,49 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param property must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject property(JsonSchemaProperty property) { return properties(property); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject possibleValues(Collection possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject allOf(Collection allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject anyOf(Collection anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject oneOf(Collection oneOf) { return newInstance(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return newInstance(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject description(String description) { return newInstance(description, generateDescription, restrictions); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject generatedDescription() { return newInstance(description, true, restrictions); } @@ -545,6 +567,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param value must not be {@literal null}. * @return must not be {@literal null}. */ + @Contract("_ -> new") public NumericJsonSchemaObject multipleOf(Number value) { Assert.notNull(value, "Value must not be null"); @@ -561,6 +584,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param range must not be {@literal null}. * @return new instance of {@link NumericJsonSchemaObject}. */ + @Contract("_ -> new") public NumericJsonSchemaObject within(Range range) { Assert.notNull(range, "Range must not be null"); @@ -578,6 +602,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link NumericJsonSchemaObject}. */ @SuppressWarnings("unchecked") + @Contract("_ -> new") public NumericJsonSchemaObject gt(Number min) { Assert.notNull(min, "Min must not be null"); @@ -593,6 +618,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link NumericJsonSchemaObject}. */ @SuppressWarnings("unchecked") + @Contract("_ -> new") public NumericJsonSchemaObject gte(Number min) { Assert.notNull(min, "Min must not be null"); @@ -608,6 +634,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link NumericJsonSchemaObject}. */ @SuppressWarnings("unchecked") + @Contract("_ -> new") public NumericJsonSchemaObject lt(Number max) { Assert.notNull(max, "Max must not be null"); @@ -623,6 +650,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @return new instance of {@link NumericJsonSchemaObject}. */ @SuppressWarnings("unchecked") + @Contract("_ -> new") public NumericJsonSchemaObject lte(Number max) { Assert.notNull(max, "Max must not be null"); @@ -632,36 +660,43 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public NumericJsonSchemaObject possibleValues(Collection possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject allOf(Collection allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject anyOf(Collection anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject oneOf(Collection oneOf) { return newInstance(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return newInstance(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject description(String description) { return newInstance(description, generateDescription, restrictions); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject generatedDescription() { return newInstance(description, true, restrictions); } @@ -785,6 +820,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param range must not be {@literal null}. * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject length(Range range) { Assert.notNull(range, "Range must not be null"); @@ -801,6 +837,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param length * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject minLength(int length) { Bound upper = this.length != null ? this.length.getUpperBound() : Bound.unbounded(); @@ -813,6 +850,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param length * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject maxLength(int length) { Bound lower = this.length != null ? this.length.getLowerBound() : Bound.unbounded(); @@ -825,6 +863,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param pattern must not be {@literal null}. * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject matching(String pattern) { Assert.notNull(pattern, "Pattern must not be null"); @@ -836,36 +875,43 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public StringJsonSchemaObject possibleValues(Collection possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject allOf(Collection allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject anyOf(Collection anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject oneOf(Collection oneOf) { return newInstance(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return newInstance(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject description(String description) { return newInstance(description, generateDescription, restrictions); } @Override + @Contract("-> new") public StringJsonSchemaObject generatedDescription() { return newInstance(description, true, restrictions); } @@ -946,6 +992,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param uniqueItems * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject uniqueItems(boolean uniqueItems) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -961,6 +1008,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param range must not be {@literal null}. Consider {@link Range#unbounded()} instead. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject range(Range range) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -975,6 +1023,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param count the allowed minimal number of array items. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject minItems(int count) { Bound upper = this.range != null ? this.range.getUpperBound() : Bound.unbounded(); @@ -987,6 +1036,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param count the allowed maximal number of array items. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject maxItems(int count) { Bound lower = this.range != null ? this.range.getLowerBound() : Bound.unbounded(); @@ -999,6 +1049,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param items the allowed items in the array. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject items(Collection items) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -1013,6 +1064,7 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { * @param additionalItemsAllowed {@literal true} to allow additional items in the array, {@literal false} otherwise. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject additionalItems(boolean additionalItemsAllowed) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -1022,36 +1074,43 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject possibleValues(Collection possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject allOf(Collection allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject anyOf(Collection anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject oneOf(Collection oneOf) { return newInstance(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return newInstance(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject description(String description) { return newInstance(description, generateDescription, restrictions); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject generatedDescription() { return newInstance(description, true, restrictions); } @@ -1147,41 +1206,49 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject possibleValues(Collection possibleValues) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject allOf(Collection allOf) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject anyOf(Collection anyOf) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject oneOf(Collection oneOf) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject description(String description) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject generatedDescription() { return new BooleanJsonSchemaObject(description, true, restrictions); } @Override + @Contract("-> new") protected String generateDescription() { return "Must be a boolean"; } @@ -1208,36 +1275,43 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public NullJsonSchemaObject possibleValues(Collection possibleValues) { return new NullJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject allOf(Collection allOf) { return new NullJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject anyOf(Collection anyOf) { return new NullJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject oneOf(Collection oneOf) { return new NullJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new NullJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject description(String description) { return new NullJsonSchemaObject(description, generateDescription, restrictions); } @Override + @Contract("-> new") public NullJsonSchemaObject generatedDescription() { return new NullJsonSchemaObject(description, true, restrictions); } @@ -1268,36 +1342,43 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public DateJsonSchemaObject possibleValues(Collection possibleValues) { return new DateJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject allOf(Collection allOf) { return new DateJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject anyOf(Collection anyOf) { return new DateJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject oneOf(Collection oneOf) { return new DateJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new DateJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject description(String description) { return new DateJsonSchemaObject(description, generateDescription, restrictions); } @Override + @Contract("-> new") public DateJsonSchemaObject generatedDescription() { return new DateJsonSchemaObject(description, true, restrictions); } @@ -1328,37 +1409,44 @@ public class TypedJsonSchemaObject extends UntypedJsonSchemaObject { } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject possibleValues(Collection possibleValues) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject allOf(Collection allOf) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject anyOf(Collection anyOf) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject oneOf(Collection oneOf) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.oneOf(oneOf)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.notMatch(notMatch)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject description(String description) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions); } @Override + @Contract("-> new") public TimestampJsonSchemaObject generatedDescription() { return new TimestampJsonSchemaObject(description, true, restrictions); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java index 54ca29e0e..d13f8d798 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/UntypedJsonSchemaObject.java @@ -23,7 +23,8 @@ import java.util.Set; import java.util.stream.Collectors; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -69,6 +70,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param description must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject description(String description) { return new UntypedJsonSchemaObject(restrictions, description, generateDescription); } @@ -78,6 +80,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("-> new") public UntypedJsonSchemaObject generatedDescription() { return new UntypedJsonSchemaObject(restrictions, description, true); } @@ -88,6 +91,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param possibleValues must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject possibleValues(Collection possibleValues) { return new UntypedJsonSchemaObject(restrictions.possibleValues(possibleValues), description, generateDescription); } @@ -98,6 +102,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param allOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject allOf(Collection allOf) { return new UntypedJsonSchemaObject(restrictions.allOf(allOf), description, generateDescription); } @@ -108,6 +113,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param anyOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject anyOf(Collection anyOf) { return new UntypedJsonSchemaObject(restrictions.anyOf(anyOf), description, generateDescription); } @@ -118,6 +124,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param oneOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject oneOf(Collection oneOf) { return new UntypedJsonSchemaObject(restrictions.oneOf(oneOf), description, generateDescription); } @@ -128,6 +135,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param notMatch must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject notMatch(JsonSchemaObject notMatch) { return new UntypedJsonSchemaObject(restrictions.notMatch(notMatch), description, generateDescription); } @@ -163,8 +171,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * * @return can be {@literal null}. */ - @Nullable - protected String generateDescription() { + protected @Nullable String generateDescription() { return null; } @@ -177,14 +184,14 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { */ static class Restrictions { - private final Collection possibleValues; + private final Collection possibleValues; private final Collection allOf; private final Collection anyOf; private final Collection oneOf; private final @Nullable JsonSchemaObject notMatch; - Restrictions(Collection possibleValues, Collection allOf, - Collection anyOf, Collection oneOf, JsonSchemaObject notMatch) { + Restrictions(Collection possibleValues, Collection allOf, + Collection anyOf, Collection oneOf, @Nullable JsonSchemaObject notMatch) { this.possibleValues = possibleValues; this.allOf = allOf; @@ -206,7 +213,8 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param possibleValues must not be {@literal null}. * @return */ - Restrictions possibleValues(Collection possibleValues) { + @Contract("_ -> new") + Restrictions possibleValues(Collection possibleValues) { Assert.notNull(possibleValues, "PossibleValues must not be null"); return new Restrictions(possibleValues, allOf, anyOf, oneOf, notMatch); @@ -216,6 +224,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param allOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions allOf(Collection allOf) { Assert.notNull(allOf, "AllOf must not be null"); @@ -226,6 +235,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param anyOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions anyOf(Collection anyOf) { Assert.notNull(anyOf, "AnyOf must not be null"); @@ -236,6 +246,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param oneOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions oneOf(Collection oneOf) { Assert.notNull(oneOf, "OneOf must not be null"); @@ -246,6 +257,7 @@ public class UntypedJsonSchemaObject implements JsonSchemaObject { * @param notMatch must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions notMatch(JsonSchemaObject notMatch) { Assert.notNull(notMatch, "NotMatch must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java index 380d92af0..cdc583e03 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/package-info.java @@ -1,6 +1,6 @@ /** * MongoDB-specific JSON schema implementation classes. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullFields package org.springframework.data.mongodb.core.schema; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java index 34eb8ea89..976b238fb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/script/package-info.java @@ -3,6 +3,6 @@ * * @since 1.7 */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.script; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java index b4550ee8d..a5b4a2aab 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionNode.java @@ -18,13 +18,13 @@ package org.springframework.data.mongodb.core.spel; import java.util.Collections; import java.util.Iterator; +import org.jspecify.annotations.Nullable; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.ast.Literal; import org.springframework.expression.spel.ast.MethodReference; import org.springframework.expression.spel.ast.Operator; import org.springframework.expression.spel.ast.OperatorNot; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -150,8 +150,7 @@ public class ExpressionNode implements Iterable { * * @return */ - @Nullable - public Object getValue() { + public @Nullable Object getValue() { return node.getValue(state); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java index 8869f51e0..89edd4eab 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformationContextSupport.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.spel; import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** @@ -67,8 +67,7 @@ public class ExpressionTransformationContextSupport { * * @return */ - @Nullable - public ExpressionNode getParentNode() { + public @Nullable ExpressionNode getParentNode() { return parentNode; } @@ -81,8 +80,7 @@ public class ExpressionTransformationContextSupport { * @see #addToPreviousOrReturn(Object) * @return */ - @Nullable - public Document getPreviousOperationObject() { + public @Nullable Document getPreviousOperationObject() { return previousOperationObject; } @@ -110,7 +108,7 @@ public class ExpressionTransformationContextSupport { * @param value * @return */ - public Document addToPreviousOperation(Object value) { + public Document addToPreviousOperation(@Nullable Object value) { Assert.state(previousOperationObject != null, "No previous operation available"); @@ -124,11 +122,14 @@ public class ExpressionTransformationContextSupport { * @param value * @return */ - public Object addToPreviousOrReturn(Object value) { + public @Nullable Object addToPreviousOrReturn(@Nullable Object value) { return hasPreviousOperation() ? addToPreviousOperation(value) : value; } + @SuppressWarnings("unchecked") private List extractArgumentListFrom(Document context) { - return (List) context.get(context.keySet().iterator().next()); + + Object o = context.get(context.keySet().iterator().next()); + return o instanceof List l ? (List) l : List.of(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java index 512f75304..da5748f52 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/ExpressionTransformer.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core.spel; +import org.jspecify.annotations.Nullable; + /** * SPI interface to implement components that can transform an {@link ExpressionTransformationContextSupport} into an * object. @@ -29,5 +31,5 @@ public interface ExpressionTransformer { * @param contentType must not be {@literal null}. * @return new instance of {@link Options}. */ + @Contract("_ -> new") public Options contentType(String contentType) { Options target = new Options(new Document(metadata), chunkSize); @@ -121,6 +123,7 @@ public interface GridFsObject { * @param metadata * @return new instance of {@link Options}. */ + @Contract("_ -> new") public Options metadata(Document metadata) { return new Options(metadata, chunkSize); } @@ -129,6 +132,7 @@ public interface GridFsObject { * @param chunkSize the file chunk size to use. * @return new instance of {@link Options}. */ + @Contract("_ -> new") public Options chunkSize(int chunkSize) { return new Options(metadata, chunkSize); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java index bf5a1d86e..4878b431f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperations.java @@ -19,11 +19,11 @@ import java.io.InputStream; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.gridfs.GridFsUpload.GridFsUploadBuilder; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -181,8 +181,7 @@ public interface GridFsOperations extends ResourcePatternResolver { * @param query must not be {@literal null}. * @return can be {@literal null}. */ - @Nullable - com.mongodb.client.gridfs.model.GridFSFile findOne(Query query); + com.mongodb.client.gridfs.model.@Nullable GridFSFile findOne(Query query); /** * Deletes all files matching the given {@link Query}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java index b3d3771f3..9a5621dcb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsOperationsSupport.java @@ -18,9 +18,9 @@ package org.springframework.data.mongodb.gridfs; import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.QueryMapper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java index 087343297..db6ce9833 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsResource.java @@ -21,10 +21,10 @@ import java.io.IOException; import java.io.InputStream; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.MongoGridFSException; @@ -105,6 +105,7 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< } @Override + @SuppressWarnings("NullAway") public long contentLength() throws IOException { verifyExists(); @@ -122,6 +123,7 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< } @Override + @SuppressWarnings("NullAway") public long lastModified() throws IOException { verifyExists(); @@ -139,6 +141,7 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< * @return never {@literal null}. * @throws IllegalStateException if the file does not {@link #exists()}. */ + @SuppressWarnings("NullAway") public Object getId() { Assert.state(exists(), () -> String.format("%s does not exist.", getDescription())); @@ -147,7 +150,8 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< } @Override - public Object getFileId() { + @SuppressWarnings("NullAway") + public @Nullable Object getFileId() { Assert.state(exists(), () -> String.format("%s does not exist.", getDescription())); return BsonUtils.toJavaType(getGridFSFile().getId()); @@ -157,8 +161,7 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< * @return the underlying {@link GridFSFile}. Can be {@literal null} if absent. * @since 2.2 */ - @Nullable - public GridFSFile getGridFSFile() { + public @Nullable GridFSFile getGridFSFile() { return this.file; } @@ -170,6 +173,7 @@ public class GridFsResource extends InputStreamResource implements GridFsObject< * provided via {@link GridFSFile}. * @throws IllegalStateException if the file does not {@link #exists()}. */ + @SuppressWarnings("NullAway") public String getContentType() { Assert.state(exists(), () -> String.format("%s does not exist.", getDescription())); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java index 8187c7dbc..722a57edc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsTemplate.java @@ -26,13 +26,13 @@ import java.util.function.Supplier; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -167,7 +167,7 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe } @Override - public ClassLoader getClassLoader() { + public @Nullable ClassLoader getClassLoader() { return null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java index 9f8d9a47d..6f2b9ed85 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsUpload.java @@ -20,9 +20,9 @@ import java.util.function.Supplier; import org.bson.Document; import org.bson.types.ObjectId; - +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.client.gridfs.model.GridFSFile; @@ -61,8 +61,7 @@ public class GridFsUpload implements GridFsObject { * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId() */ @Override - @Nullable - public ID getFileId() { + public @Nullable ID getFileId() { return id; } @@ -72,6 +71,7 @@ public class GridFsUpload implements GridFsObject { } @Override + @SuppressWarnings("NullAway") public InputStream getContent() { return dataStream.orElse(InputStream.nullInputStream()); } @@ -98,9 +98,9 @@ public class GridFsUpload implements GridFsObject { */ public static class GridFsUploadBuilder { - private Object id; - private Lazy dataStream; - private String filename; + private @Nullable Object id; + private @Nullable Lazy dataStream; + private @Nullable String filename; private Options options = Options.none(); private GridFsUploadBuilder() {} @@ -124,6 +124,7 @@ public class GridFsUpload implements GridFsObject { * @param stream the upload content. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder content(Supplier stream) { Assert.notNull(stream, "InputStream Supplier must not be null"); @@ -139,6 +140,8 @@ public class GridFsUpload implements GridFsObject { * @param * @return this. */ + @SuppressWarnings("unchecked") + @Contract("_ -> this") public GridFsUploadBuilder id(T1 id) { this.id = id; @@ -151,6 +154,7 @@ public class GridFsUpload implements GridFsObject { * @param filename the filename to use. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder filename(String filename) { this.filename = filename; @@ -163,6 +167,7 @@ public class GridFsUpload implements GridFsObject { * @param options must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder options(Options options) { Assert.notNull(options, "Options must not be null"); @@ -177,6 +182,7 @@ public class GridFsUpload implements GridFsObject { * @param metadata must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder metadata(Document metadata) { this.options = this.options.metadata(metadata); @@ -189,6 +195,7 @@ public class GridFsUpload implements GridFsObject { * @param chunkSize use negative number for default. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder chunkSize(int chunkSize) { this.options = this.options.chunkSize(chunkSize); @@ -201,6 +208,7 @@ public class GridFsUpload implements GridFsObject { * @param gridFSFile must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder gridFsFile(GridFSFile gridFSFile) { Assert.notNull(gridFSFile, "GridFSFile must not be null"); @@ -219,13 +227,20 @@ public class GridFsUpload implements GridFsObject { * @param contentType must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder contentType(String contentType) { this.options = this.options.contentType(contentType); return this; } + @Contract("-> new") public GridFsUpload build() { + + Assert.notNull(dataStream, "DataStream must be set first"); + Assert.notNull(filename, "Filename must be set first"); + Assert.notNull(options, "Options must be set first"); + return new GridFsUpload(id, dataStream, filename, options); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java index 9ee47e0bb..f8a6bd804 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsOperations.java @@ -20,12 +20,12 @@ import reactor.core.publisher.Mono; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.gridfs.ReactiveGridFsUpload.ReactiveGridFsUploadBuilder; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java index aec7cadef..e889ec718 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsResource.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.util.concurrent.atomic.AtomicBoolean; import org.bson.BsonValue; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.core.io.Resource; import org.springframework.core.io.buffer.DataBuffer; @@ -29,7 +30,6 @@ import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.client.gridfs.model.GridFSFile; @@ -115,7 +115,7 @@ public class ReactiveGridFsResource implements GridFsObject implements GridFsObject implements GridFsObject { private @Nullable Object id; - private Publisher dataStream; - private String filename; + private @Nullable Publisher dataStream; + private @Nullable String filename; private Options options = Options.none(); private ReactiveGridFsUploadBuilder() {} @@ -108,6 +108,7 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder content(Publisher source) { this.dataStream = source; return this; @@ -120,6 +121,7 @@ public class ReactiveGridFsUpload implements GridFsObject * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder id(T1 id) { this.id = id; @@ -132,6 +134,7 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder filename(String filename) { this.filename = filename; @@ -144,6 +147,7 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder options(Options options) { Assert.notNull(options, "Options must not be null"); @@ -156,8 +160,9 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder metadata(Document metadata) { this.options = this.options.metadata(metadata); @@ -168,8 +173,9 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder chunkSize(int chunkSize) { this.options = this.options.chunkSize(chunkSize); @@ -182,6 +188,7 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder gridFsFile(GridFSFile gridFSFile) { Assert.notNull(gridFSFile, "GridFSFile must not be null"); @@ -200,13 +207,20 @@ public class ReactiveGridFsUpload implements GridFsObject this") public ReactiveGridFsUploadBuilder contentType(String contentType) { this.options = this.options.contentType(contentType); return this; } + @Contract("-> new") public ReactiveGridFsUpload build() { + + Assert.notNull(dataStream, "DataStream must be set first"); + Assert.notNull(filename, "Filename must be set first"); + Assert.notNull(options, "Options must be set first"); + return new ReactiveGridFsUpload(id, dataStream, filename, options); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java index 2f3b5af15..57726d69c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/package-info.java @@ -1,6 +1,6 @@ /** * Support for MongoDB GridFS feature. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.gridfs; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java new file mode 100644 index 000000000..40073d602 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/package-info.java @@ -0,0 +1,6 @@ +/** + * MongoDB specific JMX monitoring support. + */ +@org.jspecify.annotations.NullMarked +package org.springframework.data.mongodb.monitor; + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java index 8c7c6a55c..550a71b30 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/DefaultMongoHandlerObservationConvention.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.observability; import io.micrometer.common.KeyValues; import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import com.mongodb.ConnectionString; @@ -64,6 +65,10 @@ class DefaultMongoHandlerObservationConvention implements MongoHandlerObservatio .and(LowCardinalityCommandKeyNames.MONGODB_COLLECTION.withValue(context.getCollectionName())); } + if(context.getCommandStartedEvent() == null) { + throw new IllegalStateException("not command started event present"); + } + ConnectionDescription connectionDescription = context.getCommandStartedEvent().getConnectionDescription(); if (connectionDescription != null) { @@ -98,6 +103,8 @@ class DefaultMongoHandlerObservationConvention implements MongoHandlerObservatio String collectionName = context.getCollectionName(); CommandStartedEvent commandStartedEvent = context.getCommandStartedEvent(); + Assert.notNull(commandStartedEvent, "CommandStartedEvent must not be null"); + if (ObjectUtils.isEmpty(collectionName)) { return commandStartedEvent.getCommandName(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java index 854e1481f..6185c95db 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MapRequestContext.java @@ -17,9 +17,11 @@ package org.springframework.data.mongodb.observability; import java.util.HashMap; import java.util.Map; +import java.util.NoSuchElementException; import java.util.stream.Stream; import com.mongodb.RequestContext; +import org.jspecify.annotations.Nullable; /** * A {@link Map}-based {@link RequestContext}. @@ -42,7 +44,13 @@ class MapRequestContext implements RequestContext { @Override public T get(Object key) { - return (T) map.get(key); + + + T value = (T) map.get(key); + if(value != null) { + return value; + } + throw new NoSuchElementException("%s is missing".formatted(key)); } @Override diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java index cc58aac56..cab9cd5cb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoHandlerContext.java @@ -25,8 +25,7 @@ import java.util.Set; import org.bson.BsonDocument; import org.bson.BsonValue; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.ConnectionString; import com.mongodb.RequestContext; @@ -55,12 +54,12 @@ public class MongoHandlerContext extends SenderContext { "killCursors", "listIndexes", "reIndex")); private final @Nullable ConnectionString connectionString; - private final CommandStartedEvent commandStartedEvent; - private final RequestContext requestContext; - private final String collectionName; + private final @Nullable CommandStartedEvent commandStartedEvent; + private final @Nullable RequestContext requestContext; + private final @Nullable String collectionName; - private CommandSucceededEvent commandSucceededEvent; - private CommandFailedEvent commandFailedEvent; + private @Nullable CommandSucceededEvent commandSucceededEvent; + private @Nullable CommandFailedEvent commandFailedEvent; public MongoHandlerContext(@Nullable ConnectionString connectionString, CommandStartedEvent commandStartedEvent, RequestContext requestContext) { @@ -72,28 +71,27 @@ public class MongoHandlerContext extends SenderContext { this.collectionName = getCollectionName(commandStartedEvent); } - public CommandStartedEvent getCommandStartedEvent() { + public @Nullable CommandStartedEvent getCommandStartedEvent() { return this.commandStartedEvent; } - public RequestContext getRequestContext() { + public @Nullable RequestContext getRequestContext() { return this.requestContext; } public String getDatabaseName() { - return commandStartedEvent.getDatabaseName(); + return commandStartedEvent != null ? commandStartedEvent.getDatabaseName() : "n/a"; } - public String getCollectionName() { + public @Nullable String getCollectionName() { return this.collectionName; } public String getCommandName() { - return commandStartedEvent.getCommandName(); + return commandStartedEvent != null ? commandStartedEvent.getCommandName() : "n/a"; } - @Nullable - public ConnectionString getConnectionString() { + public @Nullable ConnectionString getConnectionString() { return connectionString; } @@ -135,8 +133,7 @@ public class MongoHandlerContext extends SenderContext { * * @return trimmed string from {@code bsonValue} or null if the trimmed string was empty or the value wasn't a string */ - @Nullable - private static String getNonEmptyBsonString(@Nullable BsonValue bsonValue) { + private static @Nullable String getNonEmptyBsonString(@Nullable BsonValue bsonValue) { if (bsonValue == null || !bsonValue.isString()) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java index 9360a95de..914396ab9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoObservationCommandListener.java @@ -23,7 +23,7 @@ import java.util.function.BiConsumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import com.mongodb.ConnectionString; @@ -197,8 +197,7 @@ public class MongoObservationCommandListener implements CommandListener { * @param context * @return */ - @Nullable - private static Observation observationFromContext(RequestContext context) { + private static @Nullable Observation observationFromContext(RequestContext context) { Observation observation = context.getOrDefault(ObservationThreadLocalAccessor.KEY, null); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java index d240e12f9..d6319e5f4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/package-info.java @@ -1,5 +1,5 @@ /** * Infrastructure to provide driver observability using Micrometer. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.observability; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java index 900342bbc..989655f4a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/package-info.java @@ -1,5 +1,5 @@ /** * Spring Data's MongoDB abstraction. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java index b1ba6ea3f..00ff49873 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/RepositoryRuntimeHints.java @@ -19,6 +19,7 @@ import static org.springframework.data.mongodb.aot.MongoAotPredicates.*; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -28,7 +29,6 @@ import org.springframework.data.mongodb.repository.support.CrudMethodMetadata; import org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor; import org.springframework.data.mongodb.repository.support.ReactiveQuerydslMongoPredicateExecutor; import org.springframework.data.querydsl.QuerydslUtils; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java index 9016519d9..750cc3867 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/package-info.java @@ -1,5 +1,5 @@ /** * Ahead-Of-Time processors for MongoDB repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository.aot; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java index a2cbf659d..db7edc05b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/cdi/package-info.java @@ -1,6 +1,6 @@ /** * CDI support for MongoDB specific repository implementation. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository.cdi; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java index d0d9b0708..e276d4d1e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/config/package-info.java @@ -1,6 +1,6 @@ /** * Support infrastructure for the configuration of MongoDB specific repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository.config; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java index 8deddfe93..799597e19 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/package-info.java @@ -1,6 +1,6 @@ /** * MongoDB specific repository implementation. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index 910665253..e160fd879 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -20,7 +20,7 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; - +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueEvaluationContextProvider; import org.springframework.data.expression.ValueExpression; import org.springframework.data.mapping.model.ValueExpressionEvaluator; @@ -48,7 +48,6 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -106,7 +105,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { } @Override - public Object execute(Object[] parameters) { + public @Nullable Object execute(Object[] parameters) { ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(operations.getConverter(), new MongoParametersParameterAccessor(method, parameters)); @@ -126,8 +125,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @param accessor for providing invocation arguments. Never {@literal null}. * @param typeToRead the desired component target type. Can be {@literal null}. */ - @Nullable - protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, + protected @Nullable Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, @Nullable Class typeToRead) { Query query = createQuery(accessor); @@ -162,6 +160,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { return query.withReadPreference(com.mongodb.ReadPreference.valueOf(method.getAnnotatedReadPreference())); } + @SuppressWarnings("NullAway") private MongoQueryExecution getExecution(ConvertingParameterAccessor accessor, FindWithQuery operation) { if (isDeleteQuery()) { @@ -282,6 +281,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @throws IllegalStateException if no update could be found. * @since 3.4 */ + @SuppressWarnings("NullAway") protected UpdateDefinition createUpdate(ConvertingParameterAccessor accessor) { if (accessor.getUpdate() != null) { @@ -375,6 +375,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @return the {@link CodecRegistry} used. * @since 2.4 */ + @SuppressWarnings("NullAway") protected CodecRegistry getCodecRegistry() { return operations.execute(MongoDatabase::getCodecRegistry); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java index 76b4b2e08..d363c9344 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java @@ -24,6 +24,7 @@ import java.util.List; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.core.convert.converter.Converter; @@ -57,7 +58,6 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.spel.ExpressionDependencies; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -195,6 +195,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing); } + @SuppressWarnings("NullAway") private ReactiveMongoQueryExecution getExecutionToWrap(MongoParameterAccessor accessor, FindWithQuery operation) { if (isDeleteQuery()) { @@ -334,6 +335,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { * @throws IllegalStateException if no update could be found. * @since 3.4 */ + @SuppressWarnings("NullAway") protected Mono createUpdate(MongoParameterAccessor accessor) { if (accessor.getUpdate() != null) { @@ -425,7 +427,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { return new ValueExpressionEvaluator() { @Override - public T evaluate(String expressionString) { + public @Nullable T evaluate(String expressionString) { ValueExpression expression = valueExpressionDelegate.parse(expressionString); ValueEvaluationContext evaluationContext = valueEvaluationContextProvider .getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java index 6eb6a5da8..639c694ef 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java @@ -22,7 +22,7 @@ import java.util.function.IntUnaryOperator; import java.util.function.LongUnaryOperator; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort.Order; import org.springframework.data.mapping.model.ValueExpressionEvaluator; @@ -41,7 +41,6 @@ import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -166,8 +165,7 @@ abstract class AggregationUtils { * Prepares the AggregationPipeline including type discovery and calling {@link AggregationCallback} to run the * aggregation. */ - @Nullable - static T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor, + static @Nullable T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, Function evaluatorFunction, AggregationCallback callback) { @@ -308,8 +306,7 @@ abstract class AggregationUtils { * @return can be {@literal null} if source {@link Document#isEmpty() is empty}. * @throws IllegalArgumentException when none of the above rules is met. */ - @Nullable - static T extractSimpleTypeResult(@Nullable Document source, Class targetType, MongoConverter converter) { + static @Nullable T extractSimpleTypeResult(@Nullable Document source, Class targetType, MongoConverter converter) { if (ObjectUtils.isEmpty(source)) { return null; @@ -336,9 +333,8 @@ abstract class AggregationUtils { String.format("o_O no entry of type %s found in %s.", targetType.getSimpleName(), source.toJson())); } - @Nullable @SuppressWarnings("unchecked") - private static T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value, + private static @Nullable T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value, Class targetType) { if (value == null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java index 2aac6b77a..108c6ee79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java @@ -20,12 +20,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -55,8 +55,7 @@ abstract class CollationUtils { * @return can be {@literal null} if neither {@link ConvertingParameterAccessor#getCollation()} nor * {@literal collationExpression} are present. */ - @Nullable - static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor, + static @Nullable Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor, ValueExpressionEvaluator expressionEvaluator) { if (accessor.getCollation() != null) { @@ -98,6 +97,7 @@ abstract class CollationUtils { ObjectUtils.nullSafeClassName(placeholderValue))); } + Assert.notNull(placeholderValue, "PlaceholderValue must not be null"); return Collation.parse(collationExpression.replace(placeholder, placeholderValue.toString())); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java index dbf87f2f2..d075b67ef 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Limit; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; @@ -35,7 +36,6 @@ import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -74,7 +74,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { } @Override - public ScrollPosition getScrollPosition() { + public @Nullable ScrollPosition getScrollPosition() { return delegate.getScrollPosition(); } @@ -87,34 +87,34 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { } @Override - public Class findDynamicProjection() { + public @Nullable Class findDynamicProjection() { return delegate.findDynamicProjection(); } - public Object getBindableValue(int index) { + public @Nullable Object getBindableValue(int index) { return getConvertedValue(delegate.getBindableValue(index), null); } @Override - public Range getDistanceRange() { + public @Nullable Range getDistanceRange() { return delegate.getDistanceRange(); } - public Point getGeoNearLocation() { + public @Nullable Point getGeoNearLocation() { return delegate.getGeoNearLocation(); } - public TextCriteria getFullText() { + public @Nullable TextCriteria getFullText() { return delegate.getFullText(); } @Override - public Collation getCollation() { + public @Nullable Collation getCollation() { return delegate.getCollation(); } @Override - public UpdateDefinition getUpdate() { + public @Nullable UpdateDefinition getUpdate() { return delegate.getUpdate(); } @@ -130,8 +130,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { * @param typeInformation can be {@literal null}. * @return can be {@literal null}. */ - @Nullable - private Object getConvertedValue(Object value, @Nullable TypeInformation typeInformation) { + private @Nullable Object getConvertedValue(@Nullable Object value, @Nullable TypeInformation typeInformation) { return writer.convertToMongoType(value, typeInformation == null ? null : typeInformation.getActualType()); } @@ -161,11 +160,11 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { return delegate.hasNext(); } - public Object next() { + public @Nullable Object next() { return delegate.next(); } - public Object nextConverted(MongoPersistentProperty property) { + public @Nullable Object nextConverted(MongoPersistentProperty property) { Object next = next(); @@ -228,7 +227,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { } @Override - public Object[] getValues() { + public Object @Nullable[] getValues() { return delegate.getValues(); } @@ -244,6 +243,6 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { * * @return */ - Object nextConverted(MongoPersistentProperty property); + @Nullable Object nextConverted(MongoPersistentProperty property); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java index 8678e5a74..c54d689b5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoEntityInformation.java @@ -15,9 +15,9 @@ */ package org.springframework.data.mongodb.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.lang.Nullable; /** * Mongo specific {@link EntityInformation}. @@ -58,8 +58,7 @@ public interface MongoEntityInformation extends EntityInformation * @return can be {@literal null}. * @since 2.2 */ - @Nullable - default Object getVersion(T entity) { + default @Nullable Object getVersion(T entity) { return null; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java index 5db853e81..00d748f8a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameterAccessor.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.geo.Distance; import org.springframework.data.geo.Point; @@ -23,7 +24,6 @@ import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.lang.Nullable; /** * Mongo-specific {@link ParameterAccessor} exposing a maximum distance parameter. @@ -41,7 +41,7 @@ public interface MongoParameterAccessor extends ParameterAccessor { * @return the maximum distance to apply to the geo query or {@literal null} if there's no {@link Distance} parameter * at all or the given value for it was {@literal null}. */ - Range getDistanceRange(); + @Nullable Range getDistanceRange(); /** * Returns the {@link Point} to use for a geo-near query. @@ -75,7 +75,7 @@ public interface MongoParameterAccessor extends ParameterAccessor { * @return * @since 1.8 */ - Object[] getValues(); + Object @Nullable[] getValues(); /** * Returns the {@link Update} to be used for an update execution. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java index 1f66d5b77..cb91ccd8e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParameters.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.core.MethodParameter; import org.springframework.data.domain.Range; import org.springframework.data.geo.Distance; @@ -36,7 +37,6 @@ import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; /** * Custom extension of {@link Parameters} discovering additional @@ -53,9 +53,9 @@ public class MongoParameters extends Parameters private final int rangeIndex; private final int maxDistanceIndex; - private final @Nullable Integer fullTextIndex; - private final @Nullable Integer nearIndex; - private final @Nullable Integer collationIndex; + private final int fullTextIndex; + private final int nearIndex; + private final int collationIndex; private final int updateIndex; private final TypeInformation domainType; @@ -106,9 +106,8 @@ public class MongoParameters extends Parameters this.nearIndex = nearIndex.nearIndex; } - private MongoParameters(List parameters, int maxDistanceIndex, @Nullable Integer nearIndex, - @Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex, - TypeInformation domainType) { + private MongoParameters(List parameters, int maxDistanceIndex, int nearIndex, int fullTextIndex, + int rangeIndex, int collationIndex, int updateIndex, TypeInformation domainType) { super(parameters); @@ -141,7 +140,7 @@ public class MongoParameters extends Parameters static class NearIndex { - private final @Nullable Integer nearIndex; + private final int nearIndex; public NearIndex(ParametersSource parametersSource, boolean isGeoNearMethod) { @@ -226,7 +225,7 @@ public class MongoParameters extends Parameters * @since 1.6 */ public int getFullTextParameterIndex() { - return fullTextIndex != null ? fullTextIndex : -1; + return fullTextIndex; } /** @@ -234,7 +233,7 @@ public class MongoParameters extends Parameters * @since 1.6 */ public boolean hasFullTextParameter() { - return this.fullTextIndex != null && this.fullTextIndex >= 0; + return this.fullTextIndex >= 0; } /** @@ -252,7 +251,7 @@ public class MongoParameters extends Parameters * @since 2.2 */ public int getCollationParameterIndex() { - return collationIndex != null ? collationIndex : -1; + return collationIndex; } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java index ac1931e10..66529dfce 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoParametersParameterAccessor.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; import org.springframework.data.geo.Distance; @@ -24,7 +25,7 @@ import org.springframework.data.mongodb.core.query.Term; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.repository.query.ParametersParameterAccessor; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -53,7 +54,8 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso this.method = method; } - public Range getDistanceRange() { + @SuppressWarnings("NullAway") + public @Nullable Range getDistanceRange() { MongoParameters mongoParameters = method.getParameters(); @@ -70,7 +72,7 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso return Range.of(Bound.unbounded(), maxDistance); } - public Point getGeoNearLocation() { + public @Nullable Point getGeoNearLocation() { int nearIndex = method.getParameters().getNearIndex(); @@ -95,14 +97,14 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso return (Point) value; } - @Nullable @Override - public TextCriteria getFullText() { + public @Nullable TextCriteria getFullText() { int index = method.getParameters().getFullTextParameterIndex(); return index >= 0 ? potentiallyConvertFullText(getValue(index)) : null; } - protected TextCriteria potentiallyConvertFullText(Object fullText) { + @Contract("null -> fail") + protected TextCriteria potentiallyConvertFullText(@Nullable Object fullText) { Assert.notNull(fullText, "Fulltext parameter must not be 'null'."); @@ -124,7 +126,7 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso } @Override - public Collation getCollation() { + public @Nullable Collation getCollation() { if (method.getParameters().getCollationParameterIndex() == -1) { return null; @@ -134,12 +136,12 @@ public class MongoParametersParameterAccessor extends ParametersParameterAccesso } @Override - public Object[] getValues() { + public Object @Nullable[] getValues() { return super.getValues(); } @Override - public UpdateDefinition getUpdate() { + public @Nullable UpdateDefinition getUpdate() { int updateIndex = method.getParameters().getUpdateIndex(); return updateIndex == -1 ? null : (UpdateDefinition) getValue(updateIndex); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index 66a887062..7e327f4e2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -26,6 +26,7 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.BsonRegularExpression; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; import org.springframework.data.domain.Sort; @@ -52,7 +53,6 @@ import org.springframework.data.repository.query.parser.Part.IgnoreCaseType; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.util.Streamable; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -111,7 +111,7 @@ class MongoQueryCreator extends AbstractQueryCreator { protected Criteria create(Part part, Iterator iterator) { if (isGeoNearQuery && part.getType().equals(Type.NEAR)) { - return null; + return new Criteria(); } PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); @@ -141,7 +141,7 @@ class MongoQueryCreator extends AbstractQueryCreator { } @Override - protected Query complete(Criteria criteria, Sort sort) { + protected Query complete(@Nullable Criteria criteria, Sort sort) { Query query = (criteria == null ? new Query() : new Query(criteria)).with(sort); @@ -161,6 +161,7 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param parameters * @return */ + @SuppressWarnings("NullAway") private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria, Iterator parameters) { Type type = part.getType(); @@ -333,6 +334,7 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param value * @return the criteria extended with the regex. */ + @SuppressWarnings("NullAway") private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, Object value) { if (value == null) { @@ -348,8 +350,7 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param part * @return the regex options or {@literal null}. */ - @Nullable - private String toRegexOptions(Part part) { + private @Nullable String toRegexOptions(Part part) { String regexOptions = null; switch (part.shouldIgnoreCase()) { @@ -414,10 +415,11 @@ class MongoQueryCreator extends AbstractQueryCreator { return Streamable.of(value); } - private String toLikeRegex(String source, Part part) { + private @Nullable String toLikeRegex(String source, Part part) { return MongoRegexCreator.INSTANCE.toRegularExpression(source, toMatchMode(part.getType())); } + @SuppressWarnings("NullAway") private boolean isSpherical(MongoPersistentProperty property) { if (property.isAnnotationPresent(GeoSpatialIndexed.class)) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java index dd2b78de5..abdcf6293 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryExecution.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; import java.util.List; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; @@ -39,7 +40,6 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.mongodb.repository.util.SliceUtils; import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -171,10 +171,12 @@ interface MongoQueryExecution { return isListOfGeoResult(method.getReturnType()) ? results.getContent() : results; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","NullAway"}) GeoResults doExecuteQuery(Query query) { Point nearLocation = accessor.getGeoNearLocation(); + Assert.notNull(nearLocation, "[query.location] must not be null"); + NearQuery nearQuery = NearQuery.near(nearLocation); if (query != null) { @@ -182,6 +184,8 @@ interface MongoQueryExecution { } Range distances = accessor.getDistanceRange(); + Assert.notNull(nearLocation, "[query.distance] must not be null"); + distances.getLowerBound().getValue().ifPresent(it -> nearQuery.minDistance(it).in(it.getMetric())); distances.getUpperBound().getValue().ifPresent(it -> nearQuery.maxDistance(it).in(it.getMetric())); @@ -267,7 +271,7 @@ interface MongoQueryExecution { } @Override - public Object execute(Query query) { + public @Nullable Object execute(Query query) { String collectionName = method.getEntityInformation().getCollectionName(); Class type = method.getEntityInformation().getJavaType(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java index d3fe22b4e..4bd6e7db5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.annotation.Collation; @@ -43,7 +44,6 @@ import org.springframework.data.util.Lazy; import org.springframework.data.util.ReactiveWrappers; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; @@ -461,7 +461,7 @@ public class MongoQueryMethod extends QueryMethod { * @return the {@link Update} or {@literal null} if not present. * @since 3.4 */ - public Update getUpdateSource() { + public @Nullable Update getUpdateSource() { return lookupUpdateAnnotation().orElse(null); } @@ -471,6 +471,7 @@ public class MongoQueryMethod extends QueryMethod { * @since 3.4 * @throws IllegalStateException */ + @SuppressWarnings("NullAway") public void verify() { if (isModifyingQuery()) { @@ -509,6 +510,7 @@ public class MongoQueryMethod extends QueryMethod { } } + @SuppressWarnings("NullAway") private boolean isNumericOrVoidReturnValue() { Class resultType = getReturnedObjectType(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index fdf08ba9d..6116cc553 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -78,6 +78,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { } @Override + @SuppressWarnings("NullAway") protected Query createQuery(ConvertingParameterAccessor accessor) { MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index 431510f11..4b7262749 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -21,13 +21,12 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.aop.framework.ProxyFactory; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mapping.model.ValueExpressionEvaluator; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java index 324f01d61..9534a9cf4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoParameterAccessor.java @@ -15,6 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; +import org.jspecify.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -51,16 +52,21 @@ class ReactiveMongoParameterAccessor extends MongoParametersParameterAccessor { * @see org.springframework.data.mongodb.repository.query.MongoParametersParameterAccessor#getValues() */ @Override - public Object[] getValues() { + public Object @Nullable[] getValues() { - Object[] result = new Object[super.getValues().length]; + Object[] values = super.getValues(); + if(values == null) { + return new Object[0]; + } + + Object[] result = new Object[values.length]; for (int i = 0; i < result.length; i++) { result[i] = getValue(i); } return result; } - public Object getBindableValue(int index) { + public @Nullable Object getBindableValue(int index) { return getValue(getParameters().getBindableParameter(index).getIndex()); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java index d18c6a989..06f946d74 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecution.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.DtoInstantiatingConverter; @@ -37,7 +38,6 @@ import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.util.ReactiveWrappers; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -86,6 +86,8 @@ interface ReactiveMongoQueryExecution { private Flux> doExecuteQuery(@Nullable Query query, Class type, String collection) { Point nearLocation = accessor.getGeoNearLocation(); + Assert.notNull(nearLocation, "[query.location] ist not present"); + NearQuery nearQuery = NearQuery.near(nearLocation); if (query != null) { @@ -93,6 +95,8 @@ interface ReactiveMongoQueryExecution { } Range distances = accessor.getDistanceRange(); + + Assert.notNull(distances, "[query.range] ist not present"); distances.getUpperBound().getValue().ifPresent(it -> nearQuery.maxDistance(it).in(it.getMetric())); distances.getLowerBound().getValue().ifPresent(it -> nearQuery.minDistance(it).in(it.getMetric())); @@ -195,6 +199,7 @@ interface ReactiveMongoQueryExecution { } @Override + @SuppressWarnings("NullAway") public Publisher execute(Query query, Class type, String collection) { return (Publisher) converter.convert(delegate.execute(query, type, collection)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java index b27adfab9..4aa773091 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java @@ -87,6 +87,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { return Mono.fromSupplier(() -> createQueryInternal(accessor, true)); } + @SuppressWarnings("NullAway") private Query createQueryInternal(ConvertingParameterAccessor accessor, boolean isCountQuery) { MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, !isCountQuery && isGeoNearQuery); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java index cf6e7231f..ebc33cef9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java @@ -21,6 +21,7 @@ import reactor.core.publisher.Mono; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -32,7 +33,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.ReflectionUtils; -import org.springframework.lang.Nullable; /** * A reactive {@link org.springframework.data.repository.query.RepositoryQuery} to use a plain JSON String to create an diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java index 562ee026f..4bfe2ca39 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java @@ -15,12 +15,14 @@ */ package org.springframework.data.mongodb.repository.query; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import reactor.core.publisher.Mono; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; - +import org.jspecify.annotations.NonNull; import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -30,7 +32,6 @@ import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.spel.ExpressionDependencies; -import org.springframework.lang.NonNull; import org.springframework.util.Assert; /** @@ -46,7 +47,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { private static final Log LOG = LogFactory.getLog(ReactiveStringBasedMongoQuery.class); private final String query; - private final String fieldSpec; + private final @Nullable String fieldSpec; private final ValueExpressionParser expressionParser; @@ -63,6 +64,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param delegate must not be {@literal null}. * @since 4.4.0 */ + @SuppressWarnings("NullAway") public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, ValueExpressionDelegate delegate) { this(method.getAnnotatedQuery(), method, mongoOperations, delegate); @@ -78,7 +80,8 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param delegate must not be {@literal null}. * @since 4.4.0 */ - public ReactiveStringBasedMongoQuery(@NonNull String query, ReactiveMongoQueryMethod method, + @SuppressWarnings("NullAway") + public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, ValueExpressionDelegate delegate) { super(method, mongoOperations, delegate); @@ -132,7 +135,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { }); } - private Mono getBindingContext(String json, ConvertingParameterAccessor accessor, + private Mono getBindingContext(@Nullable String json, ConvertingParameterAccessor accessor, ParameterBindingDocumentCodec codec) { ExpressionDependencies dependencies = codec.captureExpressionDependencies(json, accessor::getBindableValue, diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java index 724c8f29e..289b953b2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java @@ -20,9 +20,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; -import org.springframework.lang.Nullable; /** * String-based aggregation operation for a repository query method. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java index 5596435eb..3f6a48e84 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.stream.Stream; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.SliceImpl; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; @@ -32,7 +32,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.ReflectionUtils; -import org.springframework.lang.Nullable; /** * {@link AbstractMongoQuery} implementation to run string-based aggregations using @@ -72,8 +71,7 @@ public class StringBasedAggregation extends AbstractMongoQuery { @SuppressWarnings("unchecked") @Override - @Nullable - protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, + protected @Nullable Object doExecute(MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, @Nullable Class ignore) { return AggregationUtils.doAggregate(AggregationUtils.computePipeline(this, method, accessor), method, processor, diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java index 5e2fba381..c990d3269 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java @@ -55,6 +55,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { * @param expressionSupport must not be {@literal null}. * @since 4.4.0 */ + @SuppressWarnings("NullAway") public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, ValueExpressionDelegate expressionSupport) { this(method.getAnnotatedQuery(), method, mongoOperations, expressionSupport); @@ -70,6 +71,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { * @param expressionSupport must not be {@literal null}. * @since 4.3 */ + @SuppressWarnings("NullAway") public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations, ValueExpressionDelegate expressionSupport) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java index c479f3faa..360f5e80e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ValueExpressionDelegateValueExpressionEvaluator.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.expression.ValueExpression; import org.springframework.data.mapping.model.ValueExpressionEvaluator; @@ -34,7 +35,7 @@ class ValueExpressionDelegateValueExpressionEvaluator implements ValueExpression @SuppressWarnings("unchecked") @Override - public T evaluate(String expressionString) { + public @Nullable T evaluate(String expressionString) { ValueExpression expression = delegate.parse(expressionString); return (T) expression.evaluate(expressionToContext.apply(expression)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java index 20c77e22a..5f0cc2104 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/package-info.java @@ -1,6 +1,6 @@ /** * Query derivation mechanism for MongoDB specific repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository.query; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java index f59a99517..abd828a9f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CrudMethodMetadataPostProcessor.java @@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentMap; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.jspecify.annotations.Nullable; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.BeanClassLoaderAware; @@ -32,7 +33,6 @@ import org.springframework.core.NamedThreadLocal; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; -import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -54,7 +54,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B private @Nullable ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); @Override - public void setBeanClassLoader(ClassLoader classLoader) { + public void setBeanClassLoader(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; } @@ -121,7 +121,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B } @Override - public Object invoke(MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); @@ -220,7 +220,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B } @Override - public Object getTarget() { + public @Nullable Object getTarget() { MethodInvocation invocation = CrudMethodMetadataPopulatingMethodInterceptor.currentInvocation(); return TransactionSynchronizationManager.getResource(invocation.getMethod()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java index 1d876289b..443108d2f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MappingMongoEntityInformation.java @@ -16,12 +16,12 @@ package org.springframework.data.mongodb.repository.support; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.repository.core.support.PersistentEntityInformation; -import org.springframework.lang.Nullable; /** * {@link MongoEntityInformation} implementation using a {@link MongoPersistentEntity} instance to lookup the necessary @@ -113,7 +113,7 @@ public class MappingMongoEntityInformation extends PersistentEntityInform } @Override - public Object getVersion(T entity) { + public @Nullable Object getVersion(T entity) { if (!isVersioned()) { return null; @@ -124,8 +124,7 @@ public class MappingMongoEntityInformation extends PersistentEntityInform return accessor.getProperty(this.entityMetadata.getRequiredVersionProperty()); } - @Nullable - public Collation getCollation() { + public @Nullable Collation getCollation() { return this.entityMetadata.getCollation(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java index 3c029ee5a..6deee469e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoAnnotationProcessor.java @@ -22,9 +22,8 @@ import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.tools.Diagnostic; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.lang.Nullable; import com.querydsl.apt.AbstractQuerydslProcessor; import com.querydsl.apt.Configuration; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java index d0a3f7a1e..1a3919875 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoEntityInformationSupport.java @@ -15,9 +15,9 @@ */ package org.springframework.data.mongodb.repository.support; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java index 91a3e39cb..e1abcdc2a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java @@ -21,6 +21,7 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.mapping.context.MappingContext; @@ -44,7 +45,6 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -77,14 +77,14 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { } @Override - public void setBeanClassLoader(ClassLoader classLoader) { + public void setBeanClassLoader(@Nullable ClassLoader classLoader) { super.setBeanClassLoader(classLoader); crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader); } @Override - protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) { + protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader, @Nullable BeanFactory beanFactory) { return this.operations.getConverter().getProjectionFactory(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java index c98d38c5f..cec54de0b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactoryBean.java @@ -17,13 +17,13 @@ package org.springframework.data.mongodb.repository.support; import java.io.Serializable; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -31,6 +31,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ +@SuppressWarnings("NullAway") public class MongoRepositoryFactoryBean, S, ID extends Serializable> extends RepositoryFactoryBeanSupport { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java index ec845510c..833ce6945 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/QuerydslMongoPredicateExecutor.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -245,12 +246,12 @@ public class QuerydslMongoPredicateExecutor extends QuerydslPredicateExecutor } @Override - public T oneValue() { + public @Nullable T oneValue() { return createQuery().fetchOne(); } @Override - public T firstValue() { + public @Nullable T firstValue() { return createQuery().fetchFirst(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java index fe18fda75..ae8561bc1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java @@ -21,6 +21,7 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -45,7 +46,6 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -79,14 +79,15 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup } @Override - public void setBeanClassLoader(ClassLoader classLoader) { + public void setBeanClassLoader(@Nullable ClassLoader classLoader) { super.setBeanClassLoader(classLoader); crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader); } @Override - protected ProjectionFactory getProjectionFactory(ClassLoader classLoader, BeanFactory beanFactory) { + @SuppressWarnings("NullAway") + protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader, @Nullable BeanFactory beanFactory) { return this.operations.getConverter().getProjectionFactory(); } @@ -130,6 +131,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup } @Override + @SuppressWarnings("NullAway") protected Optional getQueryLookupStrategy(Key key, ValueExpressionDelegate valueExpressionDelegate) { return Optional.of(new MongoQueryLookupStrategy(operations, mappingContext, valueExpressionDelegate)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java index dfb7c00fe..e3d71325f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactoryBean.java @@ -17,13 +17,13 @@ package org.springframework.data.mongodb.repository.support; import java.io.Serializable; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.index.IndexOperationsAdapter; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -78,6 +78,7 @@ public class ReactiveMongoRepositoryFactoryBean, S, } @Override + @SuppressWarnings("NullAway") protected RepositoryFactorySupport createRepositoryFactory() { RepositoryFactorySupport factory = getFactoryInstance(operations); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java index cf5191fd4..a86ada0aa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveSpringDataMongodbQuery.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.function.Consumer; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; @@ -36,7 +37,6 @@ import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -304,7 +304,7 @@ class ReactiveSpringDataMongodbQuery extends SpringDataMongodbQuerySupport implements MongoRepository { } @Override - public T oneValue() { + public @Nullable T oneValue() { return createQuery().oneValue(); } @Override - public T firstValue() { + public @Nullable T firstValue() { return createQuery().firstValue(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java index 1c1df2c9a..7e4a3aa66 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleReactiveMongoRepository.java @@ -30,6 +30,7 @@ import java.util.Optional; import java.util.function.Function; import java.util.function.UnaryOperator; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.dao.IncorrectResultSizeDataAccessException; @@ -49,7 +50,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; import org.springframework.data.repository.query.FluentQuery; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.ReadPreference; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java index 0ef6c3874..24a9342ca 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuery.java @@ -22,7 +22,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -36,7 +36,6 @@ import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.util.SliceUtils; import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.lang.Nullable; import com.mysema.commons.lang.CloseableIterator; import com.mysema.commons.lang.EmptyCloseableIterator; @@ -47,6 +46,7 @@ import com.querydsl.core.QueryResults; import com.querydsl.core.types.Expression; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; +import org.springframework.lang.Contract; /** * Spring Data specific simple {@link com.querydsl.core.Fetchable} {@link com.querydsl.core.SimpleQuery Query} @@ -200,7 +200,7 @@ public class SpringDataMongodbQuery extends SpringDataMongodbQuerySupport extends SpringDataMongodbQuerySupport extends SpringDataMongodbQuerySupport T handleException(RuntimeException e, T defaultValue) { + @Contract("_, !null -> !null") + private static @Nullable T handleException(RuntimeException e, @Nullable T defaultValue) { if (e.getClass().getName().endsWith("$NoResults")) { return defaultValue; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuerySupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuerySupport.java index a64f666f3..64ea5f238 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuerySupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbQuerySupport.java @@ -27,6 +27,8 @@ import com.querydsl.core.support.QueryMixin; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.mongodb.document.AbstractMongodbQuery; import com.querydsl.mongodb.document.MongodbDocumentSerializer; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; /** * Support query type to augment Spring Data-specific {@link #toString} representations and diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java index d9a550a0f..756d04d0c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java @@ -20,6 +20,8 @@ import java.util.Set; import java.util.regex.Pattern; import org.bson.Document; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.QueryMapper; @@ -27,7 +29,6 @@ import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -48,6 +49,7 @@ import com.querydsl.mongodb.document.MongodbDocumentSerializer; * @author Christoph Strobl * @author Mark Paluch */ +@NullUnmarked class SpringDataMongodbSerializer extends MongodbDocumentSerializer { private static final String ID_KEY = FieldName.ID.name(); @@ -146,8 +148,7 @@ class SpringDataMongodbSerializer extends MongodbDocumentSerializer { } @Override - @Nullable - protected Object convert(@Nullable Path path, @Nullable Constant constant) { + protected @Nullable Object convert(@Nullable Path path, @Nullable Constant constant) { if (constant == null) { return null; @@ -191,8 +192,7 @@ class SpringDataMongodbSerializer extends MongodbDocumentSerializer { return asReference(constant.getConstant(), path); } - @Nullable - private MongoPersistentProperty getPropertyFor(Path path) { + private @Nullable MongoPersistentProperty getPropertyFor(Path path) { Path parent = path.getMetadata().getParent(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java index 1d0b8beeb..42cd5a0b1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/package-info.java @@ -1,6 +1,6 @@ /** * Support infrastructure for query derivation of MongoDB specific repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.repository.support; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java index cbbd4a37a..83df5f779 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java @@ -40,11 +40,12 @@ import org.bson.json.JsonParseException; import org.bson.types.Binary; import org.bson.types.Decimal128; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.mongodb.core.mapping.FieldName.Type; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -73,8 +74,8 @@ public class BsonUtils { public static final Document EMPTY_DOCUMENT = new EmptyDocument(); @SuppressWarnings("unchecked") - @Nullable - public static T get(Bson bson, String key) { + @Contract("null, _ -> null") + public static @Nullable T get(@Nullable Bson bson, String key) { return (T) asMap(bson).get(key); } @@ -85,7 +86,7 @@ public class BsonUtils { * @param bson * @return */ - public static Map asMap(Bson bson) { + public static Map asMap(@Nullable Bson bson) { return asMap(bson, MongoClientSettings.getDefaultCodecRegistry()); } @@ -126,7 +127,7 @@ public class BsonUtils { * @return * @since 3.2.5 */ - public static Document asDocument(Bson bson) { + public static Document asDocument(@Nullable Bson bson) { return asDocument(bson, MongoClientSettings.getDefaultCodecRegistry()); } @@ -140,7 +141,7 @@ public class BsonUtils { * @return never {@literal null}. * @since 4.0 */ - public static Document asDocument(Bson bson, CodecRegistry codecRegistry) { + public static Document asDocument(@Nullable Bson bson, CodecRegistry codecRegistry) { Map map = asMap(bson, codecRegistry); @@ -326,14 +327,14 @@ public class BsonUtils { * @throws IllegalArgumentException if {@literal source} does not correspond to a {@link BsonValue} type. * @since 3.0 */ - public static BsonValue simpleToBsonValue(Object source) { + public static BsonValue simpleToBsonValue(@Nullable Object source) { return simpleToBsonValue(source, MongoClientSettings.getDefaultCodecRegistry()); } /** * Convert a given simple value (eg. {@link String}, {@link Long}) to its corresponding {@link BsonValue}. * - * @param source must not be {@literal null}. + * @param source can be {@literal null}. * @param codecRegistry The {@link CodecRegistry} used as a fallback to convert types using native {@link Codec}. Must * not be {@literal null}. * @return the corresponding {@link BsonValue} representation. @@ -341,7 +342,12 @@ public class BsonUtils { * @since 4.2 */ @SuppressWarnings("unchecked") - public static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegistry) { + @Contract("null, _ -> !null") + public static BsonValue simpleToBsonValue(@Nullable Object source, CodecRegistry codecRegistry) { + + if(source == null) { + return BsonNull.VALUE; + } if (source instanceof BsonValue bsonValue) { return bsonValue; @@ -398,7 +404,9 @@ public class BsonUtils { BsonCapturingWriter writer = new BsonCapturingWriter(value.getClass()); codec.encode(writer, value, ObjectUtils.isArray(value) || value instanceof Collection ? EncoderContext.builder().build() : null); - return writer.getCapturedValue(); + Object captured = writer.getCapturedValue(); + return captured instanceof BsonValue bv ? bv : BsonNull.VALUE; + } catch (CodecConfigurationException e) { throw new IllegalArgumentException( String.format("Unable to convert %s to BsonValue.", source != null ? source.getClass().getName() : "null")); @@ -450,8 +458,7 @@ public class BsonUtils { * @return * @since 2.2.1 */ - @Nullable - public static String toJson(@Nullable Document source) { + public static @Nullable String toJson(@Nullable Document source) { if (source == null) { return null; @@ -471,6 +478,7 @@ public class BsonUtils { * @return {@literal true} if the given value looks like a json document. * @since 3.0 */ + @Contract("null -> false") public static boolean isJsonDocument(@Nullable String value) { if (!StringUtils.hasText(value)) { @@ -488,6 +496,7 @@ public class BsonUtils { * @return {@literal true} if the given value looks like a json array. * @since 3.0 */ + @Contract("null -> false") public static boolean isJsonArray(@Nullable String value) { return StringUtils.hasText(value) && (value.startsWith("[") && value.endsWith("]")); } @@ -525,8 +534,7 @@ public class BsonUtils { * @return can be {@literal null}. * @since 3.0.8 */ - @Nullable - public static Object resolveValue(Bson bson, String key) { + public static @Nullable Object resolveValue(Bson bson, String key) { return resolveValue(asMap(bson), key); } @@ -541,7 +549,7 @@ public class BsonUtils { * @return can be {@literal null}. * @since 4.2 */ - public static Object resolveValue(Bson bson, FieldName fieldName) { + public static @Nullable Object resolveValue(Bson bson, FieldName fieldName) { return resolveValue(asMap(bson), fieldName); } @@ -556,8 +564,7 @@ public class BsonUtils { * @return can be {@literal null}. * @since 4.2 */ - @Nullable - public static Object resolveValue(Map source, FieldName fieldName) { + public static @Nullable Object resolveValue(Map source, FieldName fieldName) { if (fieldName.isKey()) { return source.get(fieldName.name()); @@ -590,8 +597,7 @@ public class BsonUtils { * @return can be {@literal null}. * @since 4.1 */ - @Nullable - public static Object resolveValue(Map source, String key) { + public static @Nullable Object resolveValue(Map source, String key) { if (source.containsKey(key)) { return source.get(key); @@ -643,9 +649,9 @@ public class BsonUtils { * @param source can be {@literal null}. * @return can be {@literal null}. */ - @Nullable @SuppressWarnings("unchecked") - private static Map getAsMap(Object source) { + @Contract("null -> null") + private static @Nullable Map getAsMap(@Nullable Object source) { if (source instanceof Document document) { return document; @@ -745,8 +751,8 @@ public class BsonUtils { return new Document(target); } - @Nullable - private static String toJson(@Nullable Object value) { + @Contract("null -> null") + private static @Nullable String toJson(@Nullable Object value) { if (value == null) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java index 191c7d24d..549c7ff72 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DotPath.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.util; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.StringUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java index 67255b878..78eb59a46 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/DurationUtil.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.util; import java.time.Duration; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.core.env.Environment; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.expression.ValueExpression; @@ -25,7 +26,6 @@ import org.springframework.data.expression.ValueExpressionParser; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.format.datetime.standard.DurationFormatterUtils; -import org.springframework.lang.Nullable; /** * Helper to evaluate Duration from expressions. @@ -70,13 +70,11 @@ public class DurationUtil { public static Duration evaluate(String value, Supplier evaluationContext) { return evaluate(value, new ValueEvaluationContext() { - @Nullable @Override public Environment getEnvironment() { - return null; + throw new IllegalStateException(); } - @Nullable @Override public EvaluationContext getEvaluationContext() { return evaluationContext.get(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java index ffc97402f..23ea9409c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java @@ -66,9 +66,8 @@ class EmptyDocument extends Document { throw new UnsupportedOperationException(); } - @Nullable @Override - public Object replace(String key, Object value) { + public @Nullable Object replace(String key, Object value) { throw new UnsupportedOperationException(); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java index 8fc4b108f..fbbba59e8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoClientVersion.java @@ -15,12 +15,9 @@ */ package org.springframework.data.mongodb.util; -import java.lang.reflect.Field; - +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Version; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; import com.mongodb.internal.build.MongoDriverVersion; @@ -94,14 +91,12 @@ public class MongoClientVersion { return version == null ? guessDriverVersionFromClassPath(classLoader) : version; } - @Nullable - private static Version getVersionFromPackage(ClassLoader classLoader) { + private static @Nullable Version getVersionFromPackage(ClassLoader classLoader) { if (ClassUtils.isPresent("com.mongodb.internal.build.MongoDriverVersion", classLoader)) { try { - Field field = ReflectionUtils.findField(MongoDriverVersion.class, "VERSION"); - return field != null ? Version.parse("" + field.get(null)) : null; - } catch (ReflectiveOperationException | IllegalArgumentException exception) { + return Version.parse(MongoDriverVersion.VERSION); + } catch (IllegalArgumentException exception) { // well not much we can do, right? } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java index 326a5c1e8..7fcc1383d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoDbErrorCodes.java @@ -15,12 +15,14 @@ */ package org.springframework.data.mongodb.util; +import java.util.Collections; import java.util.HashMap; - -import org.springframework.lang.Nullable; +import java.util.Map; import com.mongodb.MongoException; +import org.jspecify.annotations.Nullable; + /** * {@link MongoDbErrorCodes} holds MongoDB specific error codes outlined in {@literal mongo/base/error_codes.yml}. * @@ -128,7 +130,9 @@ public final class MongoDbErrorCodes { clientSessionCodes.put(263, "OperationNotSupportedInTransaction"); clientSessionCodes.put(264, "TooManyLogicalSessions"); - errorCodes = new HashMap<>( + transactionCodes = new HashMap<>(0); + + errorCodes = new HashMap<>( dataAccessResourceFailureCodes.size() + dataIntegrityViolationCodes.size() + duplicateKeyCodes.size() + invalidDataAccessApiUsageException.size() + permissionDeniedCodes.size() + clientSessionCodes.size(), 1f); @@ -140,8 +144,7 @@ public final class MongoDbErrorCodes { errorCodes.putAll(clientSessionCodes); } - @Nullable - public static String getErrorDescription(@Nullable Integer errorCode) { + public static @Nullable String getErrorDescription(@Nullable Integer errorCode) { return errorCode == null ? null : errorCodes.get(errorCode); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java index 23c96f9e4..8b0f4b83c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb.util; import java.util.regex.Pattern; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Utility to translate {@link Pattern#flags() regex flags} to MongoDB regex options and vice versa. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java index 344244717..950f9ec79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/aggregation/TestAggregationContext.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.util.aggregation; import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; @@ -27,7 +28,6 @@ import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * @author Christoph Strobl diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java index 9dd3f1d8f..be9a2e1cf 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java @@ -22,10 +22,10 @@ import java.util.function.Supplier; import org.bson.BsonBinary; import org.bson.BsonBinarySubType; import org.bson.types.Binary; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.util.spel.ExpressionUtils; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -48,8 +48,7 @@ public final class EncryptionUtils { * @return can be {@literal null}. * @throws IllegalArgumentException if one of the required arguments is {@literal null}. */ - @Nullable - public static Object resolveKeyId(String value, Supplier evaluationContext) { + public static @Nullable Object resolveKeyId(String value, Supplier evaluationContext) { Assert.notNull(value, "Value must not be null"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java index b5c26755c..3961fafc2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/DateTimeFormatter.java @@ -23,6 +23,8 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import org.jspecify.annotations.NullUnmarked; + /** * DateTimeFormatter implementation borrowed from MongoDB @@ -33,6 +35,7 @@ import java.time.ZonedDateTime; * @author Ross Lawley * @since 2.2 */ +@NullUnmarked class DateTimeFormatter { private static final int DATE_STRING_LENGTH = "1970-01-01".length(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java index 6c31a9721..57fecd284 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java @@ -18,12 +18,12 @@ package org.springframework.data.mongodb.util.json; import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.lang.Nullable; /** * @author Christoph Strobl @@ -40,9 +40,8 @@ class EvaluationContextExpressionEvaluator implements ValueExpressionEvaluator { this.expressionParser = expressionParser; } - @Nullable @Override - public T evaluate(String expression) { + public @Nullable T evaluate(String expression) { return evaluateExpression(expression, Collections.emptyMap()); } @@ -55,7 +54,7 @@ class EvaluationContextExpressionEvaluator implements ValueExpressionEvaluator { } @SuppressWarnings("unchecked") - T evaluateExpression(String expressionString, Map variables) { + @Nullable T evaluateExpression(String expressionString, Map variables) { Expression expression = getParsedExpression(expressionString); EvaluationContext ctx = getEvaluationContext(expressionString); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java index 4b4b497da..dcb9a3ff1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonBuffer.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.util.json; import org.bson.json.JsonParseException; +import org.jspecify.annotations.NullUnmarked; /** * JsonBuffer implementation borrowed from @@ -32,6 +33,7 @@ import org.bson.json.JsonParseException; * @author Christoph Strobl * @since 2.2 */ +@NullUnmarked class JsonScanner { private final JsonBuffer buffer; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java index 293736123..e73d57774 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonToken.java @@ -20,6 +20,7 @@ import static java.lang.String.*; import org.bson.BsonDouble; import org.bson.json.JsonParseException; import org.bson.types.Decimal128; +import org.jspecify.annotations.NullUnmarked; /** * JsonToken implementation borrowed from @@ -128,18 +128,15 @@ public class ParameterBindingContext { return new ParameterBindingContext(valueProvider, expressionEvaluator); } - @Nullable - public Object bindableValueForIndex(int index) { + public @Nullable Object bindableValueForIndex(int index) { return valueProvider.getBindableValue(index); } - @Nullable - public Object evaluateExpression(String expressionString) { + public @Nullable Object evaluateExpression(String expressionString) { return expressionEvaluator.evaluate(expressionString); } - @Nullable - public Object evaluateExpression(String expressionString, Map variables) { + public @Nullable Object evaluateExpression(String expressionString, Map variables) { if (expressionEvaluator instanceof EvaluationContextExpressionEvaluator expressionEvaluator) { return expressionEvaluator.evaluateExpression(expressionString, variables); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java index adce99c90..8138f397a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingDocumentCodec.java @@ -40,14 +40,14 @@ import org.bson.Transformer; import org.bson.codecs.*; import org.bson.codecs.configuration.CodecRegistry; import org.bson.json.JsonParseException; - +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.mapping.FieldName; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.ExpressionDependencies; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -66,6 +66,7 @@ import org.springframework.util.StringUtils; * @author Rocco Lagrotteria * @since 2.2 */ +@NullUnmarked public class ParameterBindingDocumentCodec implements CollectibleCodec { private static final String ID_FIELD_NAME = FieldName.ID.name(); @@ -396,9 +397,8 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec this.expressionParser = expressionParser; } - @Nullable @Override - public T evaluate(String expression) { + public @Nullable T evaluate(String expression) { dependencies.add(expressionParser.parse(expression).getExpressionDependencies()); return (T) PLACEHOLDER; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java index 8dd42e242..c1e519e2f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingJsonReader.java @@ -39,10 +39,11 @@ import org.bson.types.Decimal128; import org.bson.types.MaxKey; import org.bson.types.MinKey; import org.bson.types.ObjectId; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; @@ -62,6 +63,7 @@ import org.springframework.util.ObjectUtils; * @author Rocco Lagrotteria * @since 2.2 */ +@NullUnmarked public class ParameterBindingJsonReader extends AbstractBsonReader { private static final Pattern ENTIRE_QUERY_BINDING_PATTERN = Pattern.compile("^\\?(\\d+)$|^[\\?:][#$]\\{.*\\}$"); @@ -533,13 +535,11 @@ public class ParameterBindingJsonReader extends AbstractBsonReader { return BsonType.UNDEFINED; } - @Nullable - private Object evaluateExpression(String expressionString) { + private @Nullable Object evaluateExpression(String expressionString) { return bindingContext.evaluateExpression(expressionString, Collections.emptyMap()); } - @Nullable - private Object evaluateExpression(String expressionString, Map variables) { + private @Nullable Object evaluateExpression(String expressionString, Map variables) { return bindingContext.evaluateExpression(expressionString, variables); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java index 8f1d23885..2ce22214f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ValueProvider.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.util.json; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * A value provider to retrieve bindable values by their parameter index. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java index 8a86b3522..60e5e8c60 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/package-info.java @@ -1,5 +1,5 @@ /** * MongoDB driver-specific utility classes for Json conversion. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.util.json; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java index 7caec410f..a697bb700 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/package-info.java @@ -2,5 +2,5 @@ * MongoDB driver-specific utility classes for {@link org.bson.conversions.Bson} and {@link com.mongodb.DBObject} * interaction. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.util; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java index 9fa66b3b2..796f61890 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java @@ -17,12 +17,12 @@ package org.springframework.data.mongodb.util.spel; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ParserContext; import org.springframework.expression.common.LiteralExpression; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -42,8 +42,7 @@ public final class ExpressionUtils { * @param potentialExpression can be {@literal null} * @return can be {@literal null}. */ - @Nullable - public static Expression detectExpression(@Nullable String potentialExpression) { + public static @Nullable Expression detectExpression(@Nullable String potentialExpression) { if (!StringUtils.hasText(potentialExpression)) { return null; @@ -53,8 +52,7 @@ public final class ExpressionUtils { return expression instanceof LiteralExpression ? null : expression; } - @Nullable - public static Object evaluate(String value, Supplier evaluationContext) { + public static @Nullable Object evaluate(String value, Supplier evaluationContext) { Expression expression = detectExpression(value); if (expression == null) { diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt index d132482f6..d7784a776 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedUpdateExtensions.kt @@ -142,7 +142,7 @@ fun Update.pull(key: KProperty, value: Any) = * @since 4.4 * @see Update.pullAll */ -fun Update.pullAll(key: KProperty>, values: Array) = +fun Update.pullAll(key: KProperty>, values: Array) = pullAll(key.toDotPath(), values) /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java index 0448ad936..c05122873 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/CapturingTransactionOptionsResolver.java @@ -21,7 +21,7 @@ import java.util.Map; import org.assertj.core.api.Assertions; import org.assertj.core.api.ListAssert; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.CollectionUtils; /** @@ -36,9 +36,8 @@ public class CapturingTransactionOptionsResolver implements MongoTransactionOpti this.delegateResolver = delegateResolver; } - @Nullable @Override - public String getLabelPrefix() { + public @Nullable String getLabelPrefix() { return delegateResolver.getLabelPrefix(); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java index 44692348a..d89edc620 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoTransactionOptionsUnitTests.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.Assertions.*; import java.time.Duration; import java.util.concurrent.TimeUnit; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; -import org.springframework.lang.Nullable; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; @@ -90,27 +90,23 @@ class MongoTransactionOptionsUnitTests { assertThat(MongoTransactionOptions.NONE) // .isSameAs(MongoTransactionOptions.NONE) // .isNotEqualTo(new MongoTransactionOptions() { - @Nullable @Override - public Duration getMaxCommitTime() { + public @Nullable Duration getMaxCommitTime() { return null; } - @Nullable @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return null; } - @Nullable @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return null; } - @Nullable @Override - public WriteConcern getWriteConcern() { + public @Nullable WriteConcern getWriteConcern() { return null; } }); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java index 9730e61e5..bdc151ec6 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoExceptionTranslatorUnitTests.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core; import static org.assertj.core.api.Assertions.*; import org.bson.BsonDocument; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -31,7 +32,6 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.mongodb.ClientSessionException; import org.springframework.data.mongodb.MongoTransactionException; import org.springframework.data.mongodb.UncategorizedMongoDbException; -import org.springframework.lang.Nullable; import com.mongodb.MongoCursorNotFoundException; import com.mongodb.MongoException; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java index 51b3b005a..9a6bbb4f2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java @@ -29,6 +29,7 @@ import java.util.Objects; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -48,7 +49,6 @@ import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.test.util.Client; import org.springframework.data.mongodb.test.util.MongoClientExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.lang.Nullable; import com.mongodb.client.MongoClient; import com.mongodb.client.model.Filters; @@ -1936,9 +1936,8 @@ public class MongoTemplateDocumentReferenceTests { static class ReferencableConverter implements Converter> { - @Nullable @Override - public DocumentPointer convert(ReferenceAble source) { + public @Nullable DocumentPointer convert(ReferenceAble source) { return source::toReference; } } @@ -1947,9 +1946,8 @@ public class MongoTemplateDocumentReferenceTests { static class DocumentToSimpleObjectRefWithReadingConverter implements Converter, SimpleObjectRefWithReadingConverter> { - @Nullable @Override - public SimpleObjectRefWithReadingConverter convert(DocumentPointer source) { + public @Nullable SimpleObjectRefWithReadingConverter convert(DocumentPointer source) { Document document = client.getDatabase(DB_NAME).getCollection("simple-object-ref") .find(Filters.eq("_id", source.getPointer().get("ref-key-from-custom-write-converter"))).first(); @@ -1961,9 +1959,8 @@ public class MongoTemplateDocumentReferenceTests { static class SimpleObjectRefWithReadingConverterToDocumentConverter implements Converter> { - @Nullable @Override - public DocumentPointer convert(SimpleObjectRefWithReadingConverter source) { + public @Nullable DocumentPointer convert(SimpleObjectRefWithReadingConverter source) { return () -> new Document("ref-key-from-custom-write-converter", source.getId()); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java index 766929c73..772392f03 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateScrollTests.java @@ -26,6 +26,7 @@ import java.util.function.Function; import java.util.stream.Stream; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -47,7 +48,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.test.util.Client; import org.springframework.data.mongodb.test.util.MongoClientExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import com.mongodb.client.MongoClient; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index f3aeec6de..5a006bebf 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -36,6 +36,7 @@ import java.util.stream.Stream; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -91,7 +92,6 @@ import org.springframework.data.mongodb.test.util.MongoClientExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; import org.springframework.data.mongodb.test.util.MongoTestUtils; import org.springframework.data.mongodb.test.util.MongoVersion; -import org.springframework.lang.Nullable; import org.springframework.test.annotation.DirtiesContext; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index 79a0bb1fc..b5892c2ca 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -39,6 +39,7 @@ import org.assertj.core.api.Assertions; import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -99,7 +100,6 @@ import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.timeseries.Granularity; import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.lang.Nullable; import org.springframework.mock.env.MockEnvironment; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.CollectionUtils; @@ -2908,8 +2908,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { return values; } - @Nullable - public T getValue() { + public @Nullable T getValue() { return CollectionUtils.lastElement(values); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java index 18da8c516..fd1b70f3c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateValidationTests.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.Set; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -39,7 +40,6 @@ import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; import org.springframework.data.mongodb.test.util.Client; import org.springframework.data.mongodb.test.util.MongoClientExtension; -import org.springframework.lang.Nullable; import org.springframework.test.context.junit.jupiter.SpringExtension; import com.mongodb.client.MongoClient; @@ -242,13 +242,11 @@ public class MongoTemplateValidationTests { this.customFieldName = customFieldName; } - @Nullable - public String getNonNullString() { + public @Nullable String getNonNullString() { return this.nonNullString; } - @Nullable - public Integer getRangedInteger() { + public @Nullable Integer getRangedInteger() { return this.rangedInteger; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java index bc126e05f..7b07cd944 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Person.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core; import org.bson.types.ObjectId; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; public class Person { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java index 609a45691..b5a40f573 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMapReduceOperationSupportUnitTests.java @@ -74,7 +74,7 @@ public class ReactiveMapReduceOperationSupportUnitTests { mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).all(); verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); + eq(REDUCE_FUNCTION), any()); } @Test // DATAMONGO-1929 @@ -84,7 +84,7 @@ public class ReactiveMapReduceOperationSupportUnitTests { .inCollection("the-night-angel").all(); verify(template).mapReduce(any(Query.class), eq(Person.class), eq("the-night-angel"), eq(Person.class), - eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), isNull()); + eq(MAP_FUNCTION), eq(REDUCE_FUNCTION), any()); } @Test // DATAMONGO-1929 @@ -108,7 +108,7 @@ public class ReactiveMapReduceOperationSupportUnitTests { mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).matching(query).all(); verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); + eq(REDUCE_FUNCTION), any()); } @Test // DATAMONGO-2416 @@ -121,7 +121,7 @@ public class ReactiveMapReduceOperationSupportUnitTests { .matching(where("lastname").is("skywalker")).all(); verify(template).mapReduce(eq(query), eq(Person.class), eq(STAR_WARS), eq(Person.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); + eq(REDUCE_FUNCTION), any()); } @Test // DATAMONGO-1929 @@ -132,7 +132,7 @@ public class ReactiveMapReduceOperationSupportUnitTests { mapReduceOpsSupport.mapReduce(Person.class).map(MAP_FUNCTION).reduce(REDUCE_FUNCTION).as(Jedi.class).all(); verify(template).mapReduce(any(Query.class), eq(Person.class), eq(STAR_WARS), eq(Jedi.class), eq(MAP_FUNCTION), - eq(REDUCE_FUNCTION), isNull()); + eq(REDUCE_FUNCTION), any()); } interface Contact {} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java index f89b2fa8c..5ba0d947f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java @@ -43,6 +43,7 @@ import org.bson.BsonString; import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -93,7 +94,6 @@ import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.timeseries.Granularity; import org.springframework.data.mongodb.util.BsonUtils; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; -import org.springframework.lang.Nullable; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.CollectionUtils; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java index 8968f53a7..b8cc9cc97 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/TransactionOptionsTestService.java @@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core; import java.util.function.Function; import java.util.function.UnaryOperator; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.transaction.annotation.Transactional; /** @@ -48,45 +48,38 @@ public class TransactionOptionsTestService { return saveFunction.apply(entity); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readConcern=available" }) - public T availableReadConcernFind(Object id) { + public @Nullable T availableReadConcernFind(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readConcern=invalid" }) - public T invalidReadConcernFind(Object id) { + public @Nullable T invalidReadConcernFind(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readConcern=${tx.read.concern}" }) - public T environmentReadConcernFind(Object id) { + public @Nullable T environmentReadConcernFind(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readConcern=majority" }) - public T majorityReadConcernFind(Object id) { + public @Nullable T majorityReadConcernFind(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readPreference=primaryPreferred" }) - public T findFromPrimaryPreferredReplica(Object id) { + public @Nullable T findFromPrimaryPreferredReplica(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readPreference=invalid" }) - public T findFromInvalidReplica(Object id) { + public @Nullable T findFromInvalidReplica(Object id) { return findByIdFunction.apply(id); } - @Nullable @Transactional(transactionManager = "txManager", label = { "mongo:readPreference=primary" }) - public T findFromPrimaryReplica(Object id) { + public @Nullable T findFromPrimaryReplica(Object id) { return findByIdFunction.apply(id); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java index d4c2f37f6..61cd3ecce 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/UpdateOperationsUnitTests.java @@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import org.bson.Document; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.CodecRegistryProvider; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -29,8 +31,6 @@ import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.convert.UpdateMapper; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import com.mongodb.MongoClientSettings; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java index 25fbbbcb8..e9eae082e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/User.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; public class User { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java index 8dcf96231..efc206a0d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperationUnitTests.java @@ -20,13 +20,13 @@ import static org.assertj.core.api.Assertions.*; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link AddFieldsOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java index 47176fd8a..d830a4458 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DensifyOperationUnitTests.java @@ -19,6 +19,7 @@ import static org.springframework.data.mongodb.test.util.Assertions.*; import java.util.Date; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.aggregation.DensifyOperation.DensifyUnits; import org.springframework.data.mongodb.core.aggregation.DensifyOperation.Range; @@ -27,7 +28,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link DensifyOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java index 9496a51c0..1b9aba1ba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GeoNearOperationUnitTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.geo.Distance; @@ -33,7 +34,6 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.lang.Nullable; /** * Unit tests for {@link GeoNearOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java index 311496ba8..18980f6a0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MergeOperationUnitTests.java @@ -23,6 +23,7 @@ import static org.springframework.data.mongodb.core.aggregation.MergeOperation.W import java.util.Arrays; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -30,7 +31,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link MergeOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java index 1174507e1..a463b72cf 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/Order.java @@ -20,6 +20,8 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import org.springframework.lang.Contract; + /** * @author Thomas Darimont */ diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java index 24566089e..d29e32a98 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/RedactOperationUnitTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; @@ -27,7 +28,6 @@ import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.lang.Nullable; /** * Unit tests for {@link RedactOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java index d6f95216a..75632f4ac 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java @@ -20,13 +20,13 @@ import static org.assertj.core.api.Assertions.*; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link SetOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java index b5f5f596e..18eb659cd 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperationUnitTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import java.util.Date; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; @@ -30,7 +31,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link SetWindowFieldsOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java index e47fea289..ef514ca88 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnionWithOperationUnitTests.java @@ -21,13 +21,13 @@ import java.util.Arrays; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link UnionWithOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java index 2f081cc9f..c406b8962 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/UnsetOperationUnitTests.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Collections; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; @@ -29,7 +30,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.lang.Nullable; /** * Unit tests for {@link UnsetOperation}. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 787e8d674..71c395e82 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -34,6 +34,7 @@ import java.util.Set; import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledForJreRange; @@ -55,7 +56,6 @@ import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTe import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.lang.Nullable; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.SerializationUtils; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index a343d15c7..5bd7e06b9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -40,6 +40,8 @@ import org.bson.types.Binary; import org.bson.types.Code; import org.bson.types.Decimal128; import org.bson.types.ObjectId; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -104,8 +106,6 @@ import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.EntityProjectionIntrospector; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.test.util.ReflectionTestUtils; import com.mongodb.BasicDBList; @@ -3167,15 +3167,14 @@ class MappingMongoConverterUnitTests { registrar.registerConverter(WithValueConverters.class, "viaRegisteredConverter", new PropertyValueConverter() { - @Nullable @Override - public String read(@Nullable org.bson.Document nativeValue, MongoConversionContext context) { + public @Nullable String read(org.bson.@Nullable Document nativeValue, MongoConversionContext context) { return nativeValue.getString("bar"); } - @Nullable + @Override - public org.bson.Document write(@Nullable String domainValue, MongoConversionContext context) { + public org.bson.@Nullable Document write(@Nullable String domainValue, MongoConversionContext context) { return new org.bson.Document("bar", domainValue); } }); @@ -4214,9 +4213,9 @@ class MappingMongoConverterUnitTests { @WritingConverter static class TypeImplementingMapToDocumentConverter implements Converter { - @Nullable + @Override - public org.bson.Document convert(TypeImplementingMap source) { + public org.bson.@Nullable Document convert(TypeImplementingMap source) { return new org.bson.Document("1st", source.val1).append("2nd", source.val2); } } @@ -4224,9 +4223,8 @@ class MappingMongoConverterUnitTests { @ReadingConverter static class DocumentToTypeImplementingMapConverter implements Converter { - @Nullable @Override - public TypeImplementingMap convert(org.bson.Document source) { + public @Nullable TypeImplementingMap convert(org.bson.Document source) { return new TypeImplementingMap(source.getString("1st"), source.getInteger("2nd")); } } @@ -4412,30 +4410,28 @@ class MappingMongoConverterUnitTests { INSTANCE; - @Nullable @Override - public String read(@Nullable org.bson.Document value, MongoConversionContext context) { + public @Nullable String read(org.bson.@Nullable Document value, MongoConversionContext context) { return value.getString("bar"); } - @Nullable + @Override - public org.bson.Document write(@Nullable String value, MongoConversionContext context) { + public org.bson.@Nullable Document write(@Nullable String value, MongoConversionContext context) { return new org.bson.Document("bar", value); } } static class Converter1 implements MongoValueConverter { - @Nullable @Override - public String read(@Nullable org.bson.Document value, MongoConversionContext context) { + public @Nullable String read(org.bson.@Nullable Document value, MongoConversionContext context) { return value.getString("foo"); } - @Nullable + @Override - public org.bson.Document write(@Nullable String value, MongoConversionContext context) { + public org.bson.@Nullable Document write(@Nullable String value, MongoConversionContext context) { return new org.bson.Document("foo", value); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java index eb3b1aba1..0fe791784 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.convert; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Christoph Strobl diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java index 6ea0f5aa9..b12b83fe3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/TextQueryTests.java @@ -21,6 +21,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -35,7 +36,6 @@ import org.springframework.data.mongodb.core.query.TextQueryTests.FullTextDoc.Fu import org.springframework.data.mongodb.test.util.MongoTemplateExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; import org.springframework.data.mongodb.test.util.Template; -import org.springframework.lang.Nullable; /** * @author Christoph Strobl diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java index 3e5f416ca..a7fba9a04 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/ReactivePerformanceTests.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,7 +49,6 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StopWatch; import org.springframework.util.StringUtils; @@ -99,9 +99,8 @@ public class ReactivePerformanceTests { converter = new MappingMongoConverter(new DbRefResolver() { - @Nullable @Override - public Object resolveReference(MongoPersistentProperty property, Object source, + public @Nullable Object resolveReference(MongoPersistentProperty property, Object source, ReferenceLookupDelegate referenceLookupDelegate, MongoEntityReader entityReader) { return null; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java index 534f44c8f..be5be2d9b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Address.java @@ -14,8 +14,7 @@ * limitations under the License. */ package org.springframework.data.mongodb.repository; - -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.ObjectUtils; import com.querydsl.core.annotations.QueryEmbeddable; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java index c41abf4aa..e0c2caee3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MongoRepositoryTextSearchIntegrationTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,7 +39,6 @@ import org.springframework.data.mongodb.repository.support.MongoRepositoryFactor import org.springframework.data.mongodb.test.util.MongoTemplateExtension; import org.springframework.data.mongodb.test.util.MongoTestTemplate; import org.springframework.data.mongodb.test.util.Template; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java index 3dace8928..4e589d589 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/MyId.java @@ -17,7 +17,7 @@ package org.springframework.data.mongodb.repository; import java.io.Serializable; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java index 664b5279c..eeca60bc3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import org.jspecify.annotations.Nullable; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; @@ -30,7 +31,6 @@ import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.DocumentReference; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.lang.Nullable; /** * Sample domain class. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index c66b55407..93a293ecf 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -23,6 +23,7 @@ import java.util.UUID; import java.util.regex.Pattern; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Limit; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -43,7 +44,6 @@ import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.repository.query.Param; -import org.springframework.lang.Nullable; /** * Sample repository managing {@link Person} entities. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java index 0af684b9c..b2b350dc4 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepositoryTransactionalTests.java @@ -27,6 +27,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.bson.Document; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -46,7 +47,6 @@ import org.springframework.data.mongodb.test.util.EnableIfMongoServerVersion; import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; import org.springframework.data.mongodb.test.util.MongoClientExtension; import org.springframework.data.mongodb.test.util.ReplSetClient; -import org.springframework.lang.Nullable; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.transaction.AfterTransaction; @@ -205,9 +205,8 @@ public class PersonRepositoryTransactionalTests { AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable() { - @Nullable @Override - public Object getId() { + public @Nullable Object getId() { return person.id; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java index 9198e002c..751fc51de 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/SimpleReactiveMongoRepositoryTests.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; @@ -48,7 +49,6 @@ import org.springframework.data.mongodb.repository.support.ReactiveMongoReposito import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository; import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable; import org.springframework.data.repository.query.FluentQuery; -import org.springframework.lang.Nullable; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.TransactionDefinition; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java index 606cca864..c3bb9cb72 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/UserWithComplexId.java @@ -15,9 +15,9 @@ */ package org.springframework.data.mongodb.repository; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java index 294e4ea50..4f1adc714 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/VersionedPerson.java @@ -17,9 +17,9 @@ package org.springframework.data.mongodb.repository; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Version; import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.lang.Nullable; /** * @author Christoph Strobl @@ -48,8 +48,7 @@ public class VersionedPerson extends Contact { return this.firstname; } - @Nullable - public String getLastname() { + public @Nullable String getLastname() { return this.lastname; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java index b4bc48cad..b55ee7773 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -57,7 +58,6 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.ReadPreference; @@ -239,14 +239,12 @@ public class ReactiveStringBasedAggregationUnitTests { return invocation.aggregation.getInputType(); } - @Nullable - private Collation collationOf(AggregationInvocation invocation) { + private @Nullable Collation collationOf(AggregationInvocation invocation) { return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null) : null; } - @Nullable - private Object hintOf(AggregationInvocation invocation) { + private @Nullable Object hintOf(AggregationInvocation invocation) { return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null) : null; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java index 463bb2a22..827168007 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.stream.Stream; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -64,7 +65,6 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.MongoClientSettings; @@ -323,14 +323,12 @@ public class StringBasedAggregationUnitTests { return invocation.aggregation.getInputType(); } - @Nullable - private Collation collationOf(AggregationInvocation invocation) { + private @Nullable Collation collationOf(AggregationInvocation invocation) { return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getCollation().orElse(null) : null; } - @Nullable - private Object hintOf(AggregationInvocation invocation) { + private @Nullable Object hintOf(AggregationInvocation invocation) { return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null) : null; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java index 1927378e8..3ed7ace0f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StubParameterAccessor.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; import java.util.Arrays; import java.util.Iterator; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; @@ -30,7 +31,6 @@ import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.lang.Nullable; /** * Simple {@link ParameterAccessor} that returns the given parameters unfiltered. @@ -121,7 +121,7 @@ class StubParameterAccessor implements MongoParameterAccessor { * @see org.springframework.data.mongodb.repository.query.MongoParameterAccessor#getValues() */ @Override - public Object[] getValues() { + public Object @Nullable[] getValues() { return this.values; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java index 15a053860..eda1e501a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MappingContextConfigurer.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Utility to configure {@link org.springframework.data.mongodb.core.mapping.MongoMappingContext} properties. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java index 09149c02e..8300690cc 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/MongoTestTemplateConfiguration.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectFactory; import org.springframework.context.ApplicationContext; @@ -39,7 +40,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback; import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; -import org.springframework.lang.Nullable; /** * @author Christoph Strobl