diff --git a/Jenkinsfile b/Jenkinsfile index 0e83b47e2f..59898e5d9b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { triggers { pollSCM 'H/10 * * * *' - upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) + upstream(upstreamProjects: "spring-data-commons/4.0.x", threshold: hudson.model.Result.SUCCESS) } options { diff --git a/pom.xml b/pom.xml index ded4d85d02..5f78acf4a6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb-parent</artifactId> - <version>4.5.0-SNAPSHOT</version> + <version>5.0.x-GH-4874-SNAPSHOT</version> <packaging>pom</packaging> <name>Spring Data MongoDB</name> @@ -15,7 +15,7 @@ <parent> <groupId>org.springframework.data.build</groupId> <artifactId>spring-data-parent</artifactId> - <version>3.5.0-SNAPSHOT</version> + <version>4.0.0-SNAPSHOT</version> </parent> <modules> @@ -26,7 +26,7 @@ <properties> <project.type>multi</project.type> <dist.id>spring-data-mongodb</dist.id> - <springdata.commons>3.5.0-SNAPSHOT</springdata.commons> + <springdata.commons>4.0.0-SNAPSHOT</springdata.commons> <mongo>5.3.1</mongo> <mongodb-crypt>${mongo}</mongodb-crypt> <mongo.reactivestreams>${mongo}</mongo.reactivestreams> diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 58c63dfc97..5d61bbb01c 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -15,7 +15,7 @@ <parent> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb-parent</artifactId> - <version>4.5.0-SNAPSHOT</version> + <version>5.0.x-GH-4874-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 37e68c6f78..597905ce54 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -13,7 +13,7 @@ <parent> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb-parent</artifactId> - <version>4.5.0-SNAPSHOT</version> + <version>5.0.x-GH-4874-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> @@ -43,12 +43,6 @@ <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> - <exclusions> - <exclusion> - <groupId>commons-logging</groupId> - <artifactId>commons-logging</artifactId> - </exclusion> - </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> @@ -360,8 +354,76 @@ </dependencies> - <build> + <profiles> + <profile> + <id>nullaway</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>com.querydsl</groupId> + <artifactId>querydsl-apt</artifactId> + <version>${querydsl}</version> + </path> + <path> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh}</version> + </path> + <path> + <groupId>com.google.errorprone</groupId> + <artifactId>error_prone_core</artifactId> + <version>${errorprone}</version> + </path> + <path> + <groupId>com.uber.nullaway</groupId> + <artifactId>nullaway</artifactId> + <version>${nullaway}</version> + </path> + </annotationProcessorPaths> + </configuration> + <executions> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + <execution> + <id>java-compile</id> + <phase>compile</phase> + <goals> + <goal>compile</goal> + </goals> + <configuration> + <compilerArgs> + <arg>-XDcompilePolicy=simple</arg> + <arg>--should-stop=ifError=FLOW</arg> + <arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:TreatGeneratedAsUnannotated=true -XepOpt:NullAway:CustomContractAnnotations=org.springframework.lang.Contract</arg> + </compilerArgs> + </configuration> + </execution> + <execution> + <id>java-test-compile</id> + <phase>test-compile</phase> + <goals> + <goal>testCompile</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <plugins> <plugin> 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 1f6875c080..3ae41aad35 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 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 @@ * A {@link MongoExpression} using the {@link ParameterBindingDocumentCodec} for parsing a raw ({@literal json}) * expression. The expression will be wrapped within <code>{ ... }</code> 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()}. - * <br /> + * {@link #toDocument()}. <br /> * * <pre class="code"> * $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<Document> 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 BindableMongoExpression(String expression, @Nullable Object[] args) { * * @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 BindableMongoExpression(String expression, @Nullable CodecRegistryProvide * @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 BindableMongoExpression withCodecRegistry(CodecRegistry codecRegistry) { * @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 @@ private Document parse() { 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 b36382a58e..12d8c966af 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 @@ 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 53acf65470..c59eecb43a 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 c07e2dbe4a..87201ef9ee 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 @@ 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 @@ public MongoTransactionOptions convert(Map<String, String> options) { 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 f73f9fb7ed..042a5ba1d3 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 @@ /** * 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. - * <br /> + * {@link com.mongodb.client.MongoCollection} suitable for transactional usage. <br /> * <strong>Note:</strong> 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}. - * <br /> + * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br /> * 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 static MongoDatabase getDatabase(MongoDatabaseFactory factory) { } /** - * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}. - * <br /> + * Obtain the default {@link MongoDatabase database} form the given {@link MongoDatabaseFactory factory}. <br /> * 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 static MongoDatabase getDatabase(MongoDatabaseFactory factory, SessionSyn /** * Obtain the {@link MongoDatabase database} with given name form the given {@link MongoDatabaseFactory factory} using - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - * <br /> + * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br /> * 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 static boolean isTransactionActive(MongoDatabaseFactory dbFactory) { 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 a1e8344a9f..81c25d0998 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 @@ /** * MongoDB specific {@link ResourceHolderSupport resource holder}, wrapping a {@link ClientSession}. - * {@link MongoTransactionManager} binds instances of this class to the thread. - * <br /> + * {@link MongoTransactionManager} binds instances of this class to the thread. <br /> * <strong>Note:</strong> 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 4215479f62..3d7bec6780 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 eda657f5f1..1f97bb69e9 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 @@ /** * A {@link org.springframework.transaction.PlatformTransactionManager} implementation that manages - * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. - * <br /> - * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. - * <br /> + * {@link ClientSession} based transactions for a single {@link MongoDatabaseFactory}. <br /> + * Binds a {@link ClientSession} from the specified {@link MongoDatabaseFactory} to the thread. <br /> * {@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. - * <br /> + * commit} or {@link ClientSession#abortTransaction() abort} a transaction. <br /> * 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. - * <br /> + * {@link org.springframework.data.mongodb.core.MongoTemplate} use this strategy implicitly. <br /> * By default failure of a {@literal commit} operation raises a {@link TransactionSystemException}. One may override * {@link #doCommit(MongoTransactionObject)} to implement the * <a href="https://docs.mongodb.com/manual/core/transactions/#retry-commit-operation">Retry Commit Operation</a> @@ -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 @@ protected void doBegin(Object transaction, TransactionDefinition definition) thr } 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 @@ protected final void doCommit(DefaultTransactionStatus status) throws Transactio * 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. <br /> + * * <pre> * <code> * int retries = 3; @@ -302,8 +302,7 @@ public void setOptions(@Nullable TransactionOptions options) { * * @return can be {@literal null}. */ - @Nullable - public MongoDatabaseFactory getDatabaseFactory() { + public @Nullable MongoDatabaseFactory getDatabaseFactory() { return databaseFactory; } @@ -461,8 +460,7 @@ void closeSession() { } } - @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 e411bd5d2d..04bcd36e35 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.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 WriteConcern getWriteConcern() { * @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 @@ default MongoTransactionOptions mergeWith(@Nullable MongoTransactionOptions fall 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 @@ default <T> T map(Function<MongoTransactionOptions, T> mappingFunction) { * @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 @@ default TransactionOptions toDriverOptions() { /** * 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 @@ static MongoTransactionOptions of(@Nullable TransactionOptions options) { 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 b73b079a99..c4bdbcca53 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 @@ 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 f397818a4c..3d1c2ee89c 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 @@ 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 @@ /** * 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. - * <br /> + * suitable for transactional usage. <br /> * <strong>Note:</strong> Intended for internal usage only. * * @author Mark Paluch @@ -74,8 +73,7 @@ public static Mono<Boolean> isTransactionActive(ReactiveMongoDatabaseFactory dat /** * Obtain the default {@link MongoDatabase database} form the given {@link ReactiveMongoDatabaseFactory factory} using - * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - * <br /> + * {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br /> * 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 static Mono<MongoDatabase> getDatabase(ReactiveMongoDatabaseFactory facto /** * Obtain the {@link MongoDatabase database} with given name form the given {@link ReactiveMongoDatabaseFactory - * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. - * <br /> + * factory} using {@link SessionSynchronization#ON_ACTUAL_TRANSACTION native session synchronization}. <br /> * 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<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory) { + public static Mono<MongoDatabase> 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}. - * <br /> + * factory}. <br /> * 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<MongoDatabase> getDatabase(String dbName, ReactiveMongoDatabaseFactory factory, + public static Mono<MongoDatabase> 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 33caa5e7fe..d01364b202 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. - * <br /> + * instances of this class to the subscriber context. <br /> * <strong>Note:</strong> Intended for internal usage only. * * @author Mark Paluch @@ -103,8 +102,7 @@ boolean hasSession() { * @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 2c65c26b79..4f293c8ed6 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 @@ 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 @@ 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 ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact * 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 ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact * * @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 ReactiveMongoTransactionManager(ReactiveMongoDatabaseFactory databaseFact this.databaseFactory = databaseFactory; this.transactionOptionsResolver = transactionOptionsResolver; - this.options = defaultTransactionOptions; + this.options = defaultTransactionOptions != null ? defaultTransactionOptions : MongoTransactionOptions.NONE; } @Override @@ -318,8 +321,7 @@ public void setOptions(@Nullable TransactionOptions options) { * * @return can be {@literal null}. */ - @Nullable - public ReactiveMongoDatabaseFactory getDatabaseFactory() { + public @Nullable ReactiveMongoDatabaseFactory getDatabaseFactory() { return databaseFactory; } @@ -470,8 +472,7 @@ void closeSession() { } } - @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 93dbf5db69..ec30478a54 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 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 @@ /** * {@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. - * <br /> + * {@link ClientSession} as its first argument. This allows seamless integration with the existing code base. <br /> * 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 <T> SessionAwareMethodInterceptor(ClientSession session, T target, Class< 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 b52fc0bd71..5c50ba686a 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.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<String> 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<String, String> options) { return new SimpleMongoTransactionOptions(options); @@ -58,27 +58,23 @@ private SimpleMongoTransactionOptions(Map<String, String> options) { 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 @@ public String toString() { + ", readPreference=" + readPreference + ", writeConcern=" + writeConcern + '}'; } - @Nullable - private static Duration doGetMaxCommitTime(Map<String, String> options) { + private static @Nullable Duration doGetMaxCommitTime(Map<String, String> options) { return getValue(options, OptionKey.MAX_COMMIT_TIME, value -> { @@ -100,18 +95,15 @@ private static Duration doGetMaxCommitTime(Map<String, String> options) { }); } - @Nullable - private static ReadConcern doGetReadConcern(Map<String, String> options) { + private static @Nullable ReadConcern doGetReadConcern(Map<String, String> options) { return getValue(options, OptionKey.READ_CONCERN, value -> new ReadConcern(ReadConcernLevel.fromString(value))); } - @Nullable - private static ReadPreference doGetReadPreference(Map<String, String> options) { + private static @Nullable ReadPreference doGetReadPreference(Map<String, String> options) { return getValue(options, OptionKey.READ_PREFERENCE, ReadPreference::valueOf); } - @Nullable - private static WriteConcern doGetWriteConcern(Map<String, String> options) { + private static @Nullable WriteConcern doGetWriteConcern(Map<String, String> options) { return getValue(options, OptionKey.WRITE_CONCERN, value -> { @@ -123,8 +115,8 @@ private static WriteConcern doGetWriteConcern(Map<String, String> options) { }); } - @Nullable - private static <T> T getValue(Map<String, String> options, OptionKey key, Function<String, T> convertFunction) { + private static <T> @Nullable T getValue(Map<String, String> options, OptionKey key, + Function<String, T> 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 cd5f58d5b1..57ecec0342 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 @@ 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 37c7e3686b..e42c26d95a 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 bec05d0d68..69ec086e5a 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 2fe27a2c9e..86a70600a8 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 @@ 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 a33f20ffb6..4b7aa10c3f 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 538fe4e812..f2442960ed 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.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 b070a0190f..0f6ba01704 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 @@ 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 164b4defb6..f3a7dc0437 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.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.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 @@ * @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 BeanDefinition parse(Element element, ParserContext parserContext) { 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 @@ private static void parseFieldNamingStrategy(Element element, ReaderContext cont } } - @Nullable - private BeanDefinition getCustomConversions(Element element, ParserContext parserContext) { + private @Nullable BeanDefinition getCustomConversions(Element element, ParserContext parserContext) { List<Element> customConvertersElements = DomUtils.getChildElementsByTagName(element, "custom-converters"); @@ -354,8 +354,7 @@ private static Set<String> getInitialEntityClasses(Element element) { 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 4e05fe6c39..a304199776 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 @@ 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.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 @@ * @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 0594f6176c..b01827d8c6 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.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 @@ protected Collection<String> getMappingBasePackages() { * 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 @@ protected Set<Class<?>> scanForEntities(String basePackage) throws ClassNotFound 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 b8f23a35af..93d778c861 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.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 2e733cc79f..2d3649c53a 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 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.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 @@ * @author Viktor Khoroshko * @author Mark Paluch */ +@NullUnmarked public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { private static final Set<String> MONGO_URI_ALLOWED_ADDITIONAL_ATTRIBUTES = Set.of("id", "write-concern"); @@ -125,8 +127,7 @@ private BeanDefinition registerMongoBeanDefinition(Element element, ParserContex * @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/MongoJmxParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoJmxParser.java deleted file mode 100644 index 07e9aace0c..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoJmxParser.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2011-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.config; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.parsing.BeanComponentDefinition; -import org.springframework.beans.factory.parsing.CompositeComponentDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.data.mongodb.core.MongoAdmin; -import org.springframework.data.mongodb.monitor.*; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -/** - * @author Mark Pollack - * @author Thomas Risberg - * @author John Brisbin - * @author Oliver Gierke - * @author Christoph Strobl - */ -public class MongoJmxParser implements BeanDefinitionParser { - - public BeanDefinition parse(Element element, ParserContext parserContext) { - String name = element.getAttribute("mongo-ref"); - if (!StringUtils.hasText(name)) { - name = BeanNames.MONGO_BEAN_NAME; - } - registerJmxComponents(name, element, parserContext); - return null; - } - - protected void registerJmxComponents(String mongoRefName, Element element, ParserContext parserContext) { - Object eleSource = parserContext.extractSource(element); - - CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); - - createBeanDefEntry(AssertMetrics.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(BackgroundFlushingMetrics.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(BtreeIndexCounters.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(ConnectionMetrics.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(GlobalLockMetrics.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(MemoryMetrics.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(OperationCounters.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(ServerInfo.class, compositeDef, mongoRefName, eleSource, parserContext); - createBeanDefEntry(MongoAdmin.class, compositeDef, mongoRefName, eleSource, parserContext); - - parserContext.registerComponent(compositeDef); - - } - - protected void createBeanDefEntry(Class<?> clazz, CompositeComponentDefinition compositeDef, String mongoRefName, - Object eleSource, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz); - builder.getRawBeanDefinition().setSource(eleSource); - builder.addConstructorArgReference(mongoRefName); - BeanDefinition assertDef = builder.getBeanDefinition(); - String assertName = parserContext.getReaderContext().registerWithGeneratedName(assertDef); - compositeDef.addNestedComponent(new BeanComponentDefinition(assertDef, assertName)); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java index 47519ca615..62a4a1082d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoNamespaceHandler.java @@ -31,7 +31,6 @@ public void init() { registerBeanDefinitionParser("mapping-converter", new MappingMongoConverterParser()); registerBeanDefinitionParser("mongo-client", new MongoClientParser()); registerBeanDefinitionParser("db-factory", new MongoDbFactoryParser()); - registerBeanDefinitionParser("jmx", new MongoJmxParser()); registerBeanDefinitionParser("auditing", new MongoAuditingBeanDefinitionParser()); registerBeanDefinitionParser("template", new MongoTemplateParser()); registerBeanDefinitionParser("gridFsTemplate", new GridFsTemplateParser()); 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 95b56b58f3..00e993fdc8 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 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 @@ * @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 1e1b11356f..5053e540fe 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 @@ 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 @@ * @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 60bf126ae7..3f5cb0ca62 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 @@ 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 5ed9b66619..f24c435348 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 @@ 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 9c51900902..9ff59e5b22 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 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 void setAsText(@Nullable String replicaSetString) { * @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 @@ private ServerAddress parseServerAddress(String source) { 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 @@ private ServerAddress parseServerAddress(String source) { 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 b777969967..23c15102ac 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 @@ 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 ee0d09e555..32c19e24c3 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 @@ 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 5a1e5b725e..555cc9f66e 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 a00d95a9ad..ec7c368eaf 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 @@ 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.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 17b8835b7e..8a74ace28b 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 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 ChangeStreamEvent(@Nullable ChangeStreamDocument<Document> raw, Class<T> * * @return can be {@literal null}. */ - @Nullable - public ChangeStreamDocument<Document> getRaw() { + public @Nullable ChangeStreamDocument<Document> getRaw() { return raw; } @@ -88,10 +87,10 @@ public ChangeStreamDocument<Document> getRaw() { * * @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 BsonTimestamp getBsonTimestamp() { * * @return can be {@literal null}. */ - @Nullable - public BsonValue getResumeToken() { + public @Nullable BsonValue getResumeToken() { return raw != null ? raw.getResumeToken() : null; } @@ -121,8 +119,7 @@ public BsonValue getResumeToken() { * * @return can be {@literal null}. */ - @Nullable - public OperationType getOperationType() { + public @Nullable OperationType getOperationType() { return raw != null ? raw.getOperationType() : null; } @@ -131,8 +128,7 @@ public OperationType getOperationType() { * * @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 String getDatabaseName() { * * @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 String getCollectionName() { * @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 T getBody() { } /** - * 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 @@ private T getConvertedFullDocument(Document fullDocument) { return (T) doGetConverted(fullDocument, CONVERTED_FULL_DOCUMENT_UPDATER); } + @SuppressWarnings("NullAway") private Object doGetConverted(Document fullDocument, AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> 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 aaee3b76af..9c99b0e01f 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.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 @@ private ChangeStreamOptionsBuilder() {} * @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 ChangeStreamOptionsBuilder collation(Collation collation) { } /** - * Set the filter to apply. - * <br /> + * Set the filter to apply. <br /> * 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 - * <a href="https://docs.mongodb.com/manual/reference/change-events/">structure of Change Events</a>. - * <br /> + * <a href="https://docs.mongodb.com/manual/reference/change-events/">structure of Change Events</a>. <br /> * Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to ensure filter expressions are * mapped to domain type fields. * @@ -272,6 +272,7 @@ public ChangeStreamOptionsBuilder collation(Collation collation) { * {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder filter(Aggregation filter) { Assert.notNull(filter, "Filter must not be null"); @@ -286,6 +287,7 @@ public ChangeStreamOptionsBuilder filter(Aggregation filter) { * @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 ChangeStreamOptionsBuilder filter(Document... filter) { * @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 ChangeStreamOptionsBuilder returnFullDocumentOnUpdate() { * @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 ChangeStreamOptionsBuilder fullDocumentLookup(FullDocument lookup) { * @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 ChangeStreamOptionsBuilder fullDocumentBeforeChangeLookup(FullDocumentBef * * @return this. * @since 4.0 - * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) + * @see #fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) */ public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() { return fullDocumentBeforeChangeLookup(FullDocumentBeforeChange.WHEN_AVAILABLE); @@ -370,6 +375,7 @@ public ChangeStreamOptionsBuilder returnFullDocumentBeforeChange() { * @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 ChangeStreamOptionsBuilder resumeAt(Instant resumeTimestamp) { * @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 ChangeStreamOptionsBuilder resumeAt(BsonTimestamp resumeTimestamp) { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) { resumeToken(resumeToken); @@ -415,6 +423,7 @@ public ChangeStreamOptionsBuilder resumeAfter(BsonValue resumeToken) { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) { resumeToken(resumeToken); @@ -426,6 +435,7 @@ public ChangeStreamOptionsBuilder startAfter(BsonValue resumeToken) { /** * @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 c142aca173..bf8be5ba69 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 d627ba2468..ff2c708aa9 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 @@ -19,6 +19,7 @@ import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; @@ -26,7 +27,7 @@ import org.springframework.data.mongodb.core.timeseries.GranularityDefinition; import org.springframework.data.mongodb.core.validation.Validator; 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.ObjectUtils; @@ -184,7 +185,7 @@ public CollectionOptions collation(@Nullable Collation collation) { * @since 2.1 */ public CollectionOptions schema(@Nullable MongoJsonSchema schema) { - return validator(Validator.schema(schema)); + return validator(schema != null ? Validator.schema(schema) : null); } /** @@ -461,7 +462,8 @@ public static class ValidationOptions { private final @Nullable ValidationLevel validationLevel; private final @Nullable ValidationAction validationAction; - public ValidationOptions(Validator validator, ValidationLevel validationLevel, ValidationAction validationAction) { + public ValidationOptions(@Nullable Validator validator, @Nullable ValidationLevel validationLevel, + @Nullable ValidationAction validationAction) { this.validator = validator; this.validationLevel = validationLevel; @@ -483,6 +485,7 @@ public static ValidationOptions none() { * @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); } @@ -493,6 +496,7 @@ public ValidationOptions validator(@Nullable Validator validator) { * @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); } @@ -503,6 +507,7 @@ public ValidationOptions validationLevel(ValidationLevel validationLevel) { * @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); } @@ -677,6 +682,7 @@ public static TimeSeriesOptions timeSeries(String timeField) { * @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); } @@ -688,6 +694,7 @@ public TimeSeriesOptions metaField(String metaField) { * @return new instance of {@link TimeSeriesOptions}. * @see Granularity */ + @Contract("_ -> new") public TimeSeriesOptions granularity(GranularityDefinition granularity) { return new TimeSeriesOptions(timeField, metaField, granularity, expireAfter); } @@ -700,6 +707,7 @@ public TimeSeriesOptions granularity(GranularityDefinition granularity) { * @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); } @@ -715,8 +723,7 @@ public String getTimeField() { * @return can be {@literal null}. Might be an {@literal empty} {@link String} as well, so maybe check via * {@link org.springframework.util.StringUtils#hasText(String)}. */ - @Nullable - public String getMetaField() { + public @Nullable String getMetaField() { return metaField; } 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 644a3a54d1..bdf0b90ee3 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.Function; import org.bson.Document; +import org.jspecify.annotations.Nullable; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; @@ -84,7 +85,7 @@ public boolean hasReadConcern() { } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { for (Object aware : sources) { if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) { @@ -108,7 +109,7 @@ public boolean hasReadPreference() { } @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 4fa6b3e97d..11d9f09afd 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.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 @@ private Collection<Object> rewriteCollection(Collection<?> source) { * @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 @@ private static boolean containsNearWithMinDistance(Document source) { 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 9b7408b0cf..3b53cef8d0 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 @@ 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 @@ default FindIterable<Document> initiateFind(MongoCollection<Document> collection * @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 9d588ad16d..f450bddb30 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 52343522a7..8bc5349e61 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.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.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 @@ void setDefaultWriteConcern(@Nullable WriteConcern defaultWriteConcern) { } @Override + @Contract("_ -> this") public BulkOperations insert(Object document) { Assert.notNull(document, "Document must not be null"); @@ -127,6 +129,7 @@ public BulkOperations insert(Object document) { } @Override + @Contract("_ -> this") public BulkOperations insert(List<? extends Object> documents) { Assert.notNull(documents, "Documents must not be null"); @@ -137,6 +140,7 @@ public BulkOperations insert(List<? extends Object> documents) { } @Override + @Contract("_, _ -> this") public BulkOperations updateOne(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -146,6 +150,7 @@ public BulkOperations updateOne(Query query, UpdateDefinition update) { } @Override + @Contract("_ -> this") public BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) { Assert.notNull(updates, "Updates must not be null"); @@ -158,6 +163,7 @@ public BulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) { } @Override + @Contract("_, _ -> this") public BulkOperations updateMulti(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -169,6 +175,7 @@ public BulkOperations updateMulti(Query query, UpdateDefinition update) { } @Override + @Contract("_ -> this") public BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) { Assert.notNull(updates, "Updates must not be null"); @@ -181,11 +188,13 @@ public BulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) { } @Override + @Contract("_, _ -> this") public BulkOperations upsert(Query query, UpdateDefinition update) { return update(query, update, true, true); } @Override + @Contract("_ -> this") public BulkOperations upsert(List<Pair<Query, Update>> updates) { for (Pair<Query, Update> update : updates) { @@ -196,6 +205,7 @@ public BulkOperations upsert(List<Pair<Query, Update>> updates) { } @Override + @Contract("_ -> this") public BulkOperations remove(Query query) { Assert.notNull(query, "Query must not be null"); @@ -209,6 +219,7 @@ public BulkOperations remove(Query query) { } @Override + @Contract("_ -> this") public BulkOperations remove(List<Query> removes) { Assert.notNull(removes, "Removals must not be null"); @@ -221,6 +232,7 @@ public BulkOperations remove(List<Query> removes) { } @Override + @Contract("_, _, _ -> this") public BulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) { Assert.notNull(query, "Query must not be null"); @@ -412,7 +424,7 @@ public boolean skipEventPublishing() { return eventPublisher == null; } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) { if (skipEntityCallbacks()) { @@ -422,7 +434,7 @@ public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, St return entityCallbacks.callback(callbackType, entity, collectionName); } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Document document, String collectionName) { @@ -433,6 +445,7 @@ public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Do 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 2057e2f046..24d22bd80a 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.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.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 DefaultIndexOperations(MongoOperations mongoOperations, String collection } @Override + @SuppressWarnings("NullAway") public String ensureIndex(IndexDefinition indexDefinition) { return execute(collection -> { @@ -131,8 +132,7 @@ public String ensureIndex(IndexDefinition indexDefinition) { }); } - @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 void dropIndex(String name) { } @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 void dropAllIndexes() { } @Override + @SuppressWarnings("NullAway") public List<IndexInfo> getIndexInfo() { return execute(new CollectionCallback<List<IndexInfo>>() { @@ -208,8 +210,7 @@ private List<IndexInfo> getIndexData(MongoCursor<Document> cursor) { }); } - @Nullable - public <T> T execute(CollectionCallback<T> callback) { + public <T> @Nullable T execute(CollectionCallback<T> callback) { Assert.notNull(callback, "CollectionCallback must not be null"); @@ -228,6 +229,7 @@ private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document source 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 e2471dbb14..a34c1fb945 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 59b7ccd63e..92c6a957dc 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.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.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 @@ void setDefaultWriteConcern(@Nullable WriteConcern defaultWriteConcern) { } @Override + @Contract("_ -> this") public ReactiveBulkOperations insert(Object document) { Assert.notNull(document, "Document must not be null"); @@ -120,6 +122,7 @@ public ReactiveBulkOperations insert(Object document) { } @Override + @Contract("_ -> this") public ReactiveBulkOperations insert(List<? extends Object> documents) { Assert.notNull(documents, "Documents must not be null"); @@ -130,6 +133,7 @@ public ReactiveBulkOperations insert(List<? extends Object> documents) { } @Override + @Contract("_, _, _ -> this") public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -140,6 +144,7 @@ public ReactiveBulkOperations updateOne(Query query, UpdateDefinition update) { } @Override + @Contract("_, _ -> this") public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update) { Assert.notNull(query, "Query must not be null"); @@ -150,11 +155,13 @@ public ReactiveBulkOperations updateMulti(Query query, UpdateDefinition update) } @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 @@ public ReactiveBulkOperations remove(Query query) { } @Override + @Contract("_ -> this") public ReactiveBulkOperations remove(List<Query> removes) { Assert.notNull(removes, "Removals must not be null"); @@ -181,6 +189,7 @@ public ReactiveBulkOperations remove(List<Query> removes) { } @Override + @Contract("_, _, _ -> this") public ReactiveBulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) { Assert.notNull(query, "Query must not be null"); @@ -359,7 +368,7 @@ public boolean skipEventPublishing() { return eventPublisher == null; } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, String collectionName) { if (skipEntityCallbacks()) { @@ -369,7 +378,7 @@ public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T enti return entityCallbacks.callback(callbackType, entity, collectionName); } - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes", "NullAway" }) public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T entity, Document document, String collectionName) { @@ -380,6 +389,7 @@ public <T> Mono<T> callback(Class<? extends EntityCallback> callbackType, T enti 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 8e78f421f4..69ade2e163 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.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<Class<?>> 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 DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, S * @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<Class<?>> 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 @@ private DefaultReactiveIndexOperations(ReactiveMongoOperations mongoOperations, } @Override + @SuppressWarnings("NullAway") public Mono<String> 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 Mono<Void> alterIndex(String name, org.springframework.data.mongodb.core. }).then(); } - @Nullable - private MongoPersistentEntity<?> lookupPersistentEntity(String collection) { + private @Nullable MongoPersistentEntity<?> lookupPersistentEntity(String collection) { Collection<? extends MongoPersistentEntity<?>> entities = queryMapper.getMappingContext().getPersistentEntities(); @@ -152,6 +144,14 @@ public Flux<IndexInfo> getIndexInfo() { .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 @@ private IndexOptions addPartialFilterIfPresent(IndexOptions ops, Document source 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 b236b4df28..6dde79e0e8 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 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.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 @@ * @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 @@ public NamedMongoScript register(NamedMongoScript script) { } @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<Object>() { + 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<Object>() { - - @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 8b4de14e05..c445e06f8a 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 94352ad65c..ad3c2b8564 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 @@ public void setEventsEnabled(boolean eventsEnabled) { * * @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 65a5131dd1..87a937a09e 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 @@ -25,9 +25,11 @@ 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; @@ -63,7 +65,6 @@ 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; @@ -413,6 +414,7 @@ interface Entity<T> { * * @return */ + @Nullable Object getId(); /** @@ -518,10 +520,9 @@ interface AdaptibleEntity<T> extends Entity<T> { * 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); /** @@ -564,12 +565,12 @@ public String getIdFieldName() { } @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); } @@ -578,7 +579,6 @@ public Query getByIdQuery() { return Query.query(Criteria.where(ID_FIELD).is(map.get(ID_FIELD))); } - @Nullable @Override public T populateIdIfNecessary(@Nullable Object id) { @@ -605,8 +605,7 @@ public T initializeVersionProperty() { } @Override - @Nullable - public Number getVersion() { + public @Nullable Number getVersion() { return null; } @@ -723,7 +722,7 @@ public Object getId() { } @Override - public Object getPropertyValue(String key) { + public @Nullable Object getPropertyValue(String key) { return propertyAccessor.getProperty(entity.getRequiredPersistentProperty(key)); } @@ -790,8 +789,7 @@ public boolean isVersionedEntity() { } @Override - @Nullable - public Object getVersion() { + public @Nullable Object getVersion() { return propertyAccessor.getProperty(entity.getRequiredVersionProperty()); } @@ -839,7 +837,6 @@ public Map<String, Object> extractKeys(Document sortObject, Class<?> sourceType) return keyset; } - @Nullable private Object getNestedPropertyValue(String key) { String[] segments = key.split("\\."); @@ -852,6 +849,10 @@ private Object getNestedPropertyValue(String key) { currentValue = currentEntity.getPropertyValue(segment); if (i < segments.length - 1) { + if (currentValue == null) { + return BsonNull.VALUE; + } + currentEntity = entityOperations.forEntity(currentValue); } } @@ -888,7 +889,6 @@ private static <T> AdaptibleEntity<T> of(T bean, new ConvertingPropertyAccessor<>(propertyAccessor, conversionService), entityOperations); } - @Nullable @Override public T populateIdIfNecessary(@Nullable Object id) { @@ -910,8 +910,7 @@ public T populateIdIfNecessary(@Nullable Object id) { } @Override - @Nullable - public Number getVersion() { + public @Nullable Number getVersion() { MongoPersistentProperty versionProperty = entity.getRequiredVersionProperty(); @@ -1127,7 +1126,7 @@ public TimeSeriesOptions mapTimeSeriesOptions(TimeSeriesOptions source) { @Override public String getIdKeyName() { - return entity.getIdProperty().getName(); + return entity.getIdProperty() != null ? entity.getIdProperty().getName() : ID_FIELD; } private String mappedNameOrDefault(String name) { @@ -1147,7 +1146,8 @@ private ValueEvaluationContext getEvaluationContextForEntity(@Nullable Persisten 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 ca5aa7a513..d28afada0a 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 @@ 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 @@ static class ExecutableAggregationSupport<T> private final MongoTemplate template; private final Class<T> domainType; - private final Aggregation aggregation; - private final String collection; + private final @Nullable Aggregation aggregation; + private final @Nullable String collection; - public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, Aggregation aggregation, - String collection) { + public ExecutableAggregationSupport(MongoTemplate template, Class<T> domainType, @Nullable Aggregation aggregation, + @Nullable String collection) { this.template = template; this.domainType = domainType; this.aggregation = aggregation; @@ -84,21 +85,25 @@ public TerminatingAggregation<T> by(Aggregation aggregation) { @Override public AggregationResults<T> all() { + + Assert.notNull(aggregation, "Aggregation must be set first"); return template.aggregate(aggregation, getCollectionName(aggregation), domainType); } @Override public Stream<T> 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 3358ff2b17..21cb37ae86 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.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.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 4e6c3547c5..af8c80903a 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 <T> ExecutableFind<T> query(Class<T> domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -84,6 +84,7 @@ static class ExecutableFindSupport<T> } @Override + @Contract("_ -> new") public FindWithProjection<T> inCollection(String collection) { Assert.hasText(collection, "Collection name must not be null nor empty"); @@ -92,6 +93,7 @@ public FindWithProjection<T> inCollection(String collection) { } @Override + @Contract("_ -> new") public <T1> FindWithQuery<T1> as(Class<T1> returnType) { Assert.notNull(returnType, "ReturnType must not be null"); @@ -100,6 +102,7 @@ public <T1> FindWithQuery<T1> as(Class<T1> returnType) { } @Override + @Contract("_ -> new") public TerminatingFind<T> matching(Query query) { Assert.notNull(query, "Query must not be null"); @@ -108,7 +111,7 @@ public TerminatingFind<T> matching(Query query) { } @Override - public T oneValue() { + public @Nullable T oneValue() { List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(2)); @@ -124,7 +127,7 @@ public T oneValue() { } @Override - public T firstValue() { + public @Nullable T firstValue() { List<T> result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(1)); @@ -206,10 +209,10 @@ private String asString() { * @author Christoph Strobl * @since 2.0 */ - static class DelegatingQueryCursorPreparer implements SortingQueryCursorPreparer { + static class DelegatingQueryCursorPreparer implements CursorPreparer { private final @Nullable CursorPreparer delegate; - private Optional<Integer> limit = Optional.empty(); + private int limit = -1; DelegatingQueryCursorPreparer(@Nullable CursorPreparer delegate) { this.delegate = delegate; @@ -219,25 +222,22 @@ static class DelegatingQueryCursorPreparer implements SortingQueryCursorPreparer public FindIterable<Document> prepare(FindIterable<Document> iterable) { FindIterable<Document> 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 @@ public DistinctOperationSupport(ExecutableFindSupport<T> delegate, String field) @Override @SuppressWarnings("unchecked") + @Contract("_ -> new") public <R> TerminatingDistinct<R> as(Class<R> resultType) { Assert.notNull(resultType, "ResultType must not be null"); @@ -266,6 +267,7 @@ public <R> TerminatingDistinct<R> as(Class<R> resultType) { } @Override + @Contract("_ -> new") public TerminatingDistinct<T> 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 47b7127deb..599a910035 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 @@ 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 <T> ExecutableInsert<T> insert(Class<T> domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -56,10 +58,11 @@ static class ExecutableInsertSupport<T> implements ExecutableInsert<T> { private final MongoTemplate template; private final Class<T> 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<T> domainType, String collection, BulkMode bulkMode) { + ExecutableInsertSupport(MongoTemplate template, Class<T> domainType, @Nullable String collection, + @Nullable BulkMode bulkMode) { this.template = template; this.domainType = domainType; @@ -93,6 +96,7 @@ public BulkWriteResult bulk(Collection<? extends T> objects) { } @Override + @Contract("_ -> new") public InsertWithBulkMode<T> inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -101,6 +105,7 @@ public InsertWithBulkMode<T> inCollection(String collection) { } @Override + @Contract("_ -> new") public TerminatingBulkInsert<T> 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 9f78693540..55864cbd8e 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 @@ 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 <T> ExecutableMapReduceSupport<T> mapReduce(Class<T> domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -89,6 +91,7 @@ static class ExecutableMapReduceSupport<T> * @see in org.springframework.data.mongodb.core.ExecutableMapReduceOperation.TerminatingMapReduce#all() */ @Override + @SuppressWarnings("NullAway") public List<T> 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 8e84aa7dd6..e53e80b10f 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 @@ 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 @@ public ExecutableRemoveOperationSupport(MongoTemplate tempate) { } @Override + @Contract("_ -> new") public <T> ExecutableRemove<T> remove(Class<T> domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -60,7 +62,8 @@ static class ExecutableRemoveSupport<T> implements ExecutableRemove<T>, RemoveWi private final Query query; @Nullable private final String collection; - public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query, String collection) { + public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Query query, + @Nullable String collection) { this.template = template; this.domainType = domainType; this.query = query; @@ -68,6 +71,7 @@ public ExecutableRemoveSupport(MongoTemplate template, Class<T> domainType, Quer } @Override + @Contract("_ -> new") public RemoveWithQuery<T> inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -76,6 +80,7 @@ public RemoveWithQuery<T> inCollection(String collection) { } @Override + @Contract("_ -> new") public TerminatingRemove<T> 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 a5c63e9b67..69365459ba 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 @@ 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 593d863d39..75756c6f1e 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 <T> ExecutableUpdate<T> update(Class<T> domainType) { Assert.notNull(domainType, "DomainType must not be null"); @@ -52,6 +54,7 @@ public <T> ExecutableUpdate<T> update(Class<T> domainType) { * @author Christoph Strobl * @since 2.0 */ + @SuppressWarnings("rawtypes") static class ExecutableUpdateSupport<T> implements ExecutableUpdate<T>, UpdateWithCollection<T>, UpdateWithQuery<T>, TerminatingUpdate<T>, FindAndReplaceWithOptions<T>, TerminatingFindAndReplace<T>, FindAndReplaceWithProjection<T> { @@ -66,9 +69,9 @@ static class ExecutableUpdateSupport<T> @Nullable private final Object replacement; private final Class<T> targetType; - ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, UpdateDefinition update, - String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions, - Object replacement, Class<T> targetType) { + ExecutableUpdateSupport(MongoTemplate template, Class domainType, Query query, @Nullable UpdateDefinition update, + @Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, + @Nullable FindAndReplaceOptions findAndReplaceOptions, @Nullable Object replacement, Class<T> targetType) { this.template = template; this.domainType = domainType; @@ -82,6 +85,7 @@ static class ExecutableUpdateSupport<T> } @Override + @Contract("_ -> new") public TerminatingUpdate<T> apply(UpdateDefinition update) { Assert.notNull(update, "Update must not be null"); @@ -91,6 +95,7 @@ public TerminatingUpdate<T> apply(UpdateDefinition update) { } @Override + @Contract("_ -> new") public UpdateWithQuery<T> inCollection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -100,6 +105,7 @@ public UpdateWithQuery<T> inCollection(String collection) { } @Override + @Contract("_ -> new") public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) { Assert.notNull(options, "Options must not be null"); @@ -109,6 +115,7 @@ public TerminatingFindAndModify<T> withOptions(FindAndModifyOptions options) { } @Override + @Contract("_ -> new") public FindAndReplaceWithProjection<T> replaceWith(T replacement) { Assert.notNull(replacement, "Replacement must not be null"); @@ -118,6 +125,7 @@ public FindAndReplaceWithProjection<T> replaceWith(T replacement) { } @Override + @Contract("_ -> new") public FindAndReplaceWithProjection<T> withOptions(FindAndReplaceOptions options) { Assert.notNull(options, "Options must not be null"); @@ -127,6 +135,7 @@ public FindAndReplaceWithProjection<T> withOptions(FindAndReplaceOptions options } @Override + @Contract("_ -> new") public TerminatingReplace withOptions(ReplaceOptions options) { FindAndReplaceOptions target = new FindAndReplaceOptions(); @@ -138,6 +147,7 @@ public TerminatingReplace withOptions(ReplaceOptions options) { } @Override + @Contract("_ -> new") public UpdateWithUpdate<T> matching(Query query) { Assert.notNull(query, "Query must not be null"); @@ -147,6 +157,7 @@ public UpdateWithUpdate<T> matching(Query query) { } @Override + @Contract("_ -> new") public <R> FindAndReplaceWithOptions<R> as(Class<R> resultType) { Assert.notNull(resultType, "ResultType must not be null"); @@ -171,6 +182,7 @@ public UpdateResult upsert() { } @Override + @SuppressWarnings("NullAway") public @Nullable T findAndModifyValue() { return template.findAndModify(query, update, @@ -179,6 +191,7 @@ public UpdateResult upsert() { } @Override + @SuppressWarnings({ "unchecked", "NullAway" }) public @Nullable T findAndReplaceValue() { return (T) template.findAndReplace(query, replacement, @@ -187,6 +200,7 @@ public UpdateResult upsert() { } @Override + @SuppressWarnings({ "unchecked", "NullAway" }) public UpdateResult replaceFirst() { if (replacement != null) { @@ -198,6 +212,7 @@ public UpdateResult replaceFirst() { 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 51a2c5b86a..6e9b775324 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 @@ 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 static FindAndModifyOptions of(@Nullable FindAndModifyOptions source) { 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 FindAndModifyOptions remove(boolean remove) { * @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 266a0742c2..2005ba3c6c 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 * <a href="https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndReplace/">findOneAndReplace</a>. @@ -95,6 +97,7 @@ public static FindAndReplaceOptions empty() { * * @return this. */ + @Contract("-> this") public FindAndReplaceOptions returnNew() { this.returnNew = true; @@ -106,6 +109,7 @@ public FindAndReplaceOptions returnNew() { * * @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 625a85950e..f04417325c 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 @@ 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 @@ default FindPublisher<Document> initiateFind(MongoCollection<Document> collectio * @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 57abe9a529..043613122a 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 @@ 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/IndexConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java index f5856100d0..c206593fdc 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java @@ -18,12 +18,11 @@ import java.util.concurrent.TimeUnit; import org.bson.Document; - +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.mongodb.core.index.IndexDefinition; import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.util.MongoCompatibilityAdapter; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import com.mongodb.client.model.Collation; @@ -129,8 +128,7 @@ private static Converter<IndexDefinition, IndexOptions> getIndexDefinitionIndexO }; } - @Nullable - public static Collation fromDocument(@Nullable Document source) { + public static @Nullable Collation fromDocument(@Nullable Document source) { if (source == null) { return null; 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 da4766343a..cd9ba90453 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 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 boolean hasNonNullId() { return hasId() && document.get(ID_FIELD) != null; } - public Object getId() { + public @Nullable Object getId() { return document.get(ID_FIELD); } @@ -86,7 +87,7 @@ public Bson getIdFilter() { 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 839f49c7da..789b9a013e 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,6 +24,7 @@ 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; @@ -41,6 +42,7 @@ import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder; 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; @@ -89,6 +91,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { } @Override + @Contract("_ -> new") public MongoJsonSchemaCreator filter(Predicate<JsonSchemaPropertyContext> filter) { return new MappingMongoJsonSchemaCreator(converter, mappingContext, filter, mergeProperties); } @@ -106,6 +109,7 @@ public PropertySpecifier property(String path) { * @return new instance of {@link MongoJsonSchemaCreator}. * @since 3.4 */ + @Contract("_, _ -> new") public MongoJsonSchemaCreator withTypesFor(String path, Class<?>... types) { LinkedMultiValueMap<String, Class<?>> clone = mergeProperties.clone(); @@ -176,6 +180,7 @@ private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistent return schemaProperties; } + @SuppressWarnings("NullAway") private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty> path) { String stringPath = path.stream().map(MongoPersistentProperty::getName).collect(Collectors.joining(".")); @@ -337,7 +342,9 @@ private TypedJsonSchemaObject createSchemaObject(Object type, Collection<?> poss 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(); @@ -409,7 +416,8 @@ public MongoPersistentProperty getProperty() { } @Override - public <T> MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property) { + @SuppressWarnings("unchecked") + public <T> @Nullable MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property) { return (MongoPersistentEntity<T>) 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 fdfeaa81ad..c827c5b8a9 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 String getCollectionName() { 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 c5fee9cf54..9210dd85ec 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.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 void setMongoClientSettings(@Nullable MongoClientSettings mongoClientOpti * * @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 void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exce } @Override - @Nullable - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) { return exceptionTranslator.translateExceptionIfPossible(ex); } @@ -316,13 +315,13 @@ private <T> void applySettings(Consumer<T> settingsBuilder, @Nullable T value) { settingsBuilder.accept(value); } - private <S, T> T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder, + private <S, T> @Nullable T computeSettingsValue(Function<S, T> function, S defaultValueHolder, S settingsValueHolder, @Nullable T connectionStringValue) { return computeSettingsValue(function.apply(defaultValueHolder), function.apply(settingsValueHolder), connectionStringValue); } - private <T> T computeSettingsValue(T defaultValue, T fromSettings, T fromConnectionString) { + private <T> @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 @@ private MongoClient createMongoClient(MongoClientSettings settings) throws Unkno 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/MongoClientSettingsFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java index 02913b4303..0bce8db304 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoClientSettingsFactoryBean.java @@ -25,10 +25,9 @@ import org.bson.UuidRepresentation; import org.bson.codecs.configuration.CodecRegistry; - +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.data.mongodb.util.MongoCompatibilityAdapter; -import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; 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 eab6b5d7f4..0a62b7aa49 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 void destroy() throws Exception { } @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 7aef5a3a82..f361b19bba 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.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 @@ public class MongoEncryptionSettingsFactoryBean implements FactoryBean<AutoEncryptionSettings> { private boolean bypassAutoEncryption; - private String keyVaultNamespace; - private Map<String, Object> extraOptions; - private MongoClientSettings keyVaultClientSettings; - private Map<String, Map<String, Object>> kmsProviders; - private Map<String, BsonDocument> schemaMap; + private @Nullable String keyVaultNamespace; + private @Nullable Map<String, Object> extraOptions; + private @Nullable MongoClientSettings keyVaultClientSettings; + private @Nullable Map<String, Map<String, Object>> kmsProviders; + private @Nullable Map<String, BsonDocument> 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 1ec7d3ffc0..2bde873c2f 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 @@ 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.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<String> 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 66b1cf209e..84c395bf2f 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 @@ interface JsonSchemaPropertyContext { * @return {@literal null} if the property is not an entity. It is nevertheless recommend to check * {@link PersistentProperty#isEntity()} first. */ - @Nullable - <T> MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property); + <T> @Nullable MongoPersistentEntity<T> resolveEntity(MongoPersistentProperty property); } @@ -162,6 +161,7 @@ public boolean test(JsonSchemaPropertyContext context) { 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 f400b35a68..6eb29fe0bf 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.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.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; @@ -195,7 +195,8 @@ default SessionScoped withSession(Supplier<ClientSession> sessionProvider) { private @Nullable ClientSession session; @Override - public <T> T execute(SessionCallback<T> action, Consumer<ClientSession> onComplete) { + @SuppressWarnings("NullAway") + public <T> @Nullable T execute(SessionCallback<T> action, Consumer<ClientSession> onComplete) { lock.executeWithoutResult(() -> { @@ -732,8 +733,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin * @param entityClass the parametrized type of the returned list. * @return the converted object. */ - @Nullable - <T> T findOne(Query query, Class<T> entityClass); + <T> @Nullable T findOne(Query query, Class<T> entityClass); /** * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified @@ -749,8 +749,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin * @param collectionName name of the collection to retrieve the objects from. * @return the converted object. */ - @Nullable - <T> T findOne(Query query, Class<T> entityClass, String collectionName); + <T> @Nullable T findOne(Query query, Class<T> entityClass, String collectionName); /** * Determine result of given {@link Query} contains at least one element. <br /> @@ -870,8 +869,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin * @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> T findById(Object id, Class<T> entityClass); + <T> @Nullable T findById(Object id, Class<T> entityClass); /** * Returns the document with the given id from the given collection mapped onto the given target class. @@ -881,8 +879,7 @@ <T> MapReduceResults<T> mapReduce(Query query, String inputCollectionName, Strin * @param collectionName the collection to query for the document. * @return he converted object or {@literal null} if document does not exist. */ - @Nullable - <T> T findById(Object id, Class<T> entityClass, String collectionName); + <T> @Nullable T findById(Object id, Class<T> entityClass, String collectionName); /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and @@ -959,8 +956,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C * @see Update * @see AggregationUpdate */ - @Nullable - <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass); + <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass); /** * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a> @@ -979,8 +975,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C * @see Update * @see AggregationUpdate */ - @Nullable - <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName); + <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName); /** * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a> @@ -1002,8 +997,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C * @see Update * @see AggregationUpdate */ - @Nullable - <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass); + <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass); /** * Triggers <a href="https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/">findAndModify </a> @@ -1026,8 +1020,7 @@ default <T> List<T> findDistinct(Query query, String field, String collection, C * @see Update * @see AggregationUpdate */ - @Nullable - <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass, + <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass, String collectionName); /** @@ -1047,8 +1040,7 @@ <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions o * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default <T> T findAndReplace(Query query, T replacement) { + default <T> @Nullable T findAndReplace(Query query, T replacement) { return findAndReplace(query, replacement, FindAndReplaceOptions.empty()); } @@ -1067,8 +1059,7 @@ default <T> T findAndReplace(Query query, T replacement) { * @return the converted object that was updated or {@literal null}, if not found. * @since 2.1 */ - @Nullable - default <T> T findAndReplace(Query query, T replacement, String collectionName) { + default <T> @Nullable T findAndReplace(Query query, T replacement, String collectionName) { return findAndReplace(query, replacement, FindAndReplaceOptions.empty(), collectionName); } @@ -1090,8 +1081,7 @@ default <T> T findAndReplace(Query query, T replacement, String collectionName) * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) { + default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options) { return findAndReplace(query, replacement, options, getCollectionName(ClassUtils.getUserClass(replacement))); } @@ -1111,8 +1101,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o * as it is after the update. * @since 2.1 */ - @Nullable - default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, String collectionName) { + default <T> @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<T>) ClassUtils.getUserClass(replacement), collectionName); @@ -1136,8 +1125,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o * as it is after the update. * @since 2.1 */ - @Nullable - default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class<T> entityType, + default <T> @Nullable T findAndReplace(Query query, T replacement, FindAndReplaceOptions options, Class<T> entityType, String collectionName) { return findAndReplace(query, replacement, options, entityType, collectionName, entityType); @@ -1165,8 +1153,7 @@ default <T> T findAndReplace(Query query, T replacement, FindAndReplaceOptions o * {@link #getCollectionName(Class) derived} from the given replacement value. * @since 2.1 */ - @Nullable - default <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, + default <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, Class<T> resultType) { return findAndReplace(query, replacement, options, entityType, @@ -1193,8 +1180,7 @@ default <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOption * as it is after the update. * @since 2.1 */ - @Nullable - <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, + <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, String collectionName, Class<T> resultType); /** @@ -1210,8 +1196,7 @@ <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions option * @param entityClass the parametrized type of the returned list. * @return the converted object */ - @Nullable - <T> T findAndRemove(Query query, Class<T> entityClass); + <T> @Nullable T findAndRemove(Query query, Class<T> entityClass); /** * Map the results of an ad-hoc query on the specified collection to a single instance of an object of the specified @@ -1228,8 +1213,7 @@ <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions option * @param collectionName name of the collection to retrieve the objects from. * @return the converted object. */ - @Nullable - <T> T findAndRemove(Query query, Class<T> entityClass, String collectionName); + <T> @Nullable T findAndRemove(Query query, Class<T> 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 37001faa4e..574c0c8931 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 @@ */ public class MongoServerApiFactoryBean implements FactoryBean<ServerApi> { - private String version; + private @Nullable String version; private @Nullable Boolean deprecationErrors; private @Nullable Boolean strict; @@ -59,9 +59,8 @@ public void setStrict(@Nullable Boolean strict) { 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<?> getObjectType() { } 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 67ef3a3081..6ec454cd93 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; @@ -30,7 +40,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; @@ -85,17 +95,25 @@ import org.springframework.data.mongodb.core.convert.MongoWriter; import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.convert.UpdateMapper; -import org.springframework.data.mongodb.core.index.DefaultSearchIndexOperations; import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.index.IndexOperationsProvider; import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher; import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator; -import org.springframework.data.mongodb.core.index.SearchIndexOperations; -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; @@ -111,7 +129,7 @@ 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; @@ -134,7 +152,21 @@ 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; @@ -184,10 +216,9 @@ * @author Bartłomiej Mazur * @author Michael Krog * @author Jakub Zurawa - * @author Florian Lüdiger */ -public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, - SearchIndexOperationsProvider, ReadPreferenceAware { +public class MongoTemplate + implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, ReadPreferenceAware { private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class); private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE; @@ -343,7 +374,7 @@ public boolean hasReadPreference() { } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return this.readPreference; } @@ -479,7 +510,7 @@ public <T> Stream<T> stream(Query query, Class<T> entityType, String collectionN return doStream(query, entityType, collectionName, entityType); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected <T> Stream<T> doStream(Query query, Class<?> entityType, String collectionName, Class<T> returnType) { Assert.notNull(query, "Query must not be null"); @@ -512,7 +543,7 @@ public String getCollectionName(Class<?> entityClass) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(String jsonCommand) { Assert.hasText(jsonCommand, "JsonCommand must not be null nor empty"); @@ -521,7 +552,7 @@ public Document executeCommand(String jsonCommand) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(Document command) { Assert.notNull(command, "Command must not be null"); @@ -530,7 +561,7 @@ public Document executeCommand(Document command) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Document executeCommand(Document command, @Nullable ReadPreference readPreference) { Assert.notNull(command, "Command must not be null"); @@ -577,7 +608,7 @@ protected void executeQuery(Query query, String collectionName, DocumentCallback } @Override - public <T> T execute(DbCallback<T> action) { + public <T> @Nullable T execute(DbCallback<T> action) { Assert.notNull(action, "DbCallback must not be null"); @@ -590,14 +621,14 @@ public <T> T execute(DbCallback<T> action) { } @Override - public <T> T execute(Class<?> entityClass, CollectionCallback<T> callback) { + public <T> @Nullable T execute(Class<?> entityClass, CollectionCallback<T> callback) { Assert.notNull(entityClass, "EntityClass must not be null"); return execute(getCollectionName(entityClass), callback); } @Override - public <T> T execute(String collectionName, CollectionCallback<T> callback) { + public <T> @Nullable T execute(String collectionName, CollectionCallback<T> callback) { Assert.notNull(collectionName, "CollectionName must not be null"); Assert.notNull(callback, "CollectionCallback must not be null"); @@ -619,6 +650,7 @@ public SessionScoped withSession(ClientSessionOptions options) { } @Override + @Contract("_ -> new") public MongoTemplate withSession(ClientSession session) { Assert.notNull(session, "ClientSession must not be null"); @@ -692,6 +724,7 @@ private MongoCollection<Document> createView(String name, String source, Aggrega return doCreateView(name, source, aggregation.getAggregationPipeline(), options); } + @SuppressWarnings("NullAway") protected MongoCollection<Document> doCreateView(String name, String source, List<Document> pipeline, @Nullable ViewOptions options) { @@ -707,8 +740,9 @@ protected MongoCollection<Document> doCreateView(String name, String source, Lis } @Override - @SuppressWarnings("ConstantConditions") - public MongoCollection<Document> getCollection(String collectionName) { + @SuppressWarnings({ "ConstantConditions", "NullAway" }) + @Contract("null -> fail") + public MongoCollection<Document> getCollection(@Nullable String collectionName) { Assert.notNull(collectionName, "CollectionName must not be null"); @@ -721,7 +755,7 @@ public <T> boolean collectionExists(Class<T> entityClass) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public boolean collectionExists(String collectionName) { Assert.notNull(collectionName, "CollectionName must not be null"); @@ -772,21 +806,6 @@ public IndexOperations indexOps(Class<?> entityClass) { return indexOps(getCollectionName(entityClass), entityClass); } - @Override - public SearchIndexOperations searchIndexOps(String collectionName) { - return searchIndexOps(null, collectionName); - } - - @Override - public SearchIndexOperations searchIndexOps(Class<?> type) { - return new DefaultSearchIndexOperations(this, type); - } - - @Override - public SearchIndexOperations searchIndexOps(@Nullable Class<?> type, String collectionName) { - return new DefaultSearchIndexOperations(this, collectionName, type); - } - @Override public BulkOperations bulkOps(BulkMode mode, String collectionName) { return bulkOps(mode, null, collectionName); @@ -819,15 +838,13 @@ public ScriptOperations scriptOps() { // Find methods that take a Query to express the query and that return a single object. - @Nullable @Override - public <T> T findOne(Query query, Class<T> entityClass) { + public <T> @Nullable T findOne(Query query, Class<T> entityClass) { return findOne(query, entityClass, getCollectionName(entityClass)); } - @Nullable @Override - public <T> T findOne(Query query, Class<T> entityClass, String collectionName) { + public <T> @Nullable T findOne(Query query, Class<T> entityClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "EntityClass must not be null"); @@ -855,7 +872,7 @@ public boolean exists(Query query, String collectionName) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public boolean exists(Query query, @Nullable Class<?> entityClass, String collectionName) { if (query == null) { @@ -931,15 +948,13 @@ sourceClass, new QueryCursorPreparer(query, query.getSortObject(), limit, query. return ScrollUtils.createWindow(result, query.getLimit(), OffsetScrollPosition.positionFunction(query.getSkip())); } - @Nullable @Override - public <T> T findById(Object id, Class<T> entityClass) { + public <T> @Nullable T findById(Object id, Class<T> entityClass) { return findById(id, entityClass, getCollectionName(entityClass)); } - @Nullable @Override - public <T> T findById(Object id, Class<T> entityClass, String collectionName) { + public <T> @Nullable T findById(Object id, Class<T> entityClass, String collectionName) { Assert.notNull(id, "Id must not be null"); Assert.notNull(entityClass, "EntityClass must not be null"); @@ -957,7 +972,7 @@ public <T> List<T> findDistinct(Query query, String field, Class<?> entityClass, } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "NullAway" }) public <T> List<T> findDistinct(Query query, String field, String collectionName, Class<?> entityClass, Class<T> resultClass) { @@ -1067,28 +1082,26 @@ public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String col return new GeoResults<>(result, avgDistance); } - @Nullable @Override - public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass) { + public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass) { return findAndModify(query, update, new FindAndModifyOptions(), entityClass, getCollectionName(entityClass)); } - @Nullable @Override - public <T> T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, String collectionName) { + public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, Class<T> entityClass, + String collectionName) { return findAndModify(query, update, new FindAndModifyOptions(), entityClass, collectionName); } - @Nullable @Override - public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass) { + public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, + Class<T> entityClass) { return findAndModify(query, update, options, entityClass, getCollectionName(entityClass)); } - @Nullable @Override - public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, Class<T> entityClass, - String collectionName) { + public <T> @Nullable T findAndModify(Query query, UpdateDefinition update, FindAndModifyOptions options, + Class<T> entityClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(update, "Update must not be null"); @@ -1112,8 +1125,8 @@ public <T> T findAndModify(Query query, UpdateDefinition update, FindAndModifyOp } @Override - public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, - String collectionName, Class<T> resultType) { + public <S, T> @Nullable T findAndReplace(Query query, S replacement, FindAndReplaceOptions options, + Class<S> entityType, String collectionName, Class<T> resultType) { Assert.notNull(query, "Query must not be null"); Assert.notNull(replacement, "Replacement must not be null"); @@ -1154,15 +1167,13 @@ public <S, T> T findAndReplace(Query query, S replacement, FindAndReplaceOptions // Find methods that take a Query to express the query and that return a single object that is also removed from the // collection in the database. - @Nullable @Override - public <T> T findAndRemove(Query query, Class<T> entityClass) { + public <T> @Nullable T findAndRemove(Query query, Class<T> entityClass) { return findAndRemove(query, entityClass, getCollectionName(entityClass)); } - @Nullable @Override - public <T> T findAndRemove(Query query, Class<T> entityClass, String collectionName) { + public <T> @Nullable T findAndRemove(Query query, Class<T> entityClass, String collectionName) { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "EntityClass must not be null"); @@ -1224,6 +1235,7 @@ public long estimatedCount(String collectionName) { return doEstimatedCount(CollectionPreparerDelegate.of(this), collectionName, new EstimatedDocumentCountOptions()); } + @SuppressWarnings("NullAway") protected long doEstimatedCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName, EstimatedDocumentCountOptions options) { return execute(collectionName, @@ -1241,6 +1253,7 @@ public long exactCount(Query query, @Nullable Class<?> entityClass, String colle return doExactCount(createDelegate(query), collectionName, mappedQuery, options); } + @SuppressWarnings("NullAway") protected long doExactCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName, Document filter, CountOptions options) { return execute(collectionName, collection -> collectionPreparer.prepare(collection) @@ -1320,15 +1333,13 @@ protected MongoCollection<Document> prepareCollection(MongoCollection<Document> * @param mongoAction any MongoAction already configured or null * @return The prepared WriteConcern or null */ - @Nullable - protected WriteConcern prepareWriteConcern(MongoAction mongoAction) { + protected @Nullable WriteConcern prepareWriteConcern(MongoAction mongoAction) { WriteConcern wc = writeConcernResolver.resolve(mongoAction); return potentiallyForceAcknowledgedWrite(wc); } - @Nullable - private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) { + private @Nullable WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc) { if (ObjectUtils.nullSafeEquals(WriteResultChecking.EXCEPTION, writeResultChecking)) { if (wc == null || wc.getWObject() == null @@ -1541,7 +1552,7 @@ protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> wri return maybeCallAfterSave(saved, dbDoc, collectionName); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected Object insertDocument(String collectionName, Document document, Class<?> entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1595,6 +1606,7 @@ protected List<Object> insertDocumentList(String collectionName, List<Document> return MappedDocument.toIds(documents); } + @SuppressWarnings("NullAway") protected Object saveDocument(String collectionName, Document dbDoc, Class<?> entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1693,7 +1705,7 @@ public UpdateResult updateMulti(Query query, UpdateDefinition update, Class<?> e 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) { @@ -1701,6 +1713,12 @@ protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefini Assert.notNull(query, "Query must not be null"); Assert.notNull(update, "Update must not be null"); + if (query.isSorted() && LOGGER.isWarnEnabled()) { + + LOGGER.warn(String.format("%s does not support sort ('%s'); Please use findAndModify() instead", + upsert ? "Upsert" : "UpdateFirst", serializeToJsonSafely(query.getSortObject()))); + } + MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass); UpdateContext updateContext = multi ? queryOperations.updateContext(update, query, upsert) @@ -1708,7 +1726,7 @@ protected UpdateResult doUpdate(String collectionName, Query query, UpdateDefini updateContext.increaseVersionForUpdateIfNecessary(entity); Document queryObj = updateContext.getMappedQuery(entity); - UpdateOptions opts = updateContext.getUpdateOptions(entityClass, query); + UpdateOptions opts = updateContext.getUpdateOptions(entityClass); if (updateContext.isAggregationUpdate()) { @@ -1803,7 +1821,7 @@ public DeleteResult remove(Query query, Class<?> entityClass, String collectionN return doRemove(collectionName, query, entityClass, true); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected <T> DeleteResult doRemove(String collectionName, Query query, @Nullable Class<T> entityClass, boolean multi) { @@ -2129,6 +2147,7 @@ protected <S, T> UpdateResult replace(Query query, Class<S> entityType, T replac * @param entityClass * @return */ + @SuppressWarnings("NullAway") protected <T> List<T> doFindAndDelete(String collectionName, Query query, Class<T> entityClass) { List<T> result = find(query, entityClass, collectionName); @@ -2162,7 +2181,7 @@ private <O> AggregationResults<O> doAggregate(Aggregation aggregation, String co return doAggregate(aggregation, collectionName, outputType, context.getAggregationOperationContext()); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected <O> AggregationResults<O> doAggregate(Aggregation aggregation, String collectionName, Class<O> outputType, AggregationOperationContext context) { @@ -2245,7 +2264,7 @@ protected <O> AggregationResults<O> doAggregate(Aggregation aggregation, String }); } - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected <O> Stream<O> aggregateStream(Aggregation aggregation, String collectionName, Class<O> outputType, @Nullable AggregationOperationContext context) { @@ -2360,7 +2379,7 @@ protected String replaceWithResourceIfNecessary(String function) { } @Override - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) public Set<String> getCollectionNames() { return execute(db -> { Set<String> result = new LinkedHashSet<>(); @@ -2444,7 +2463,7 @@ protected MongoCollection<Document> doCreateCollection(String collectionName, Do * @return the collection that was created * @since 3.3.3 */ - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({ "ConstantConditions", "NullAway" }) protected MongoCollection<Document> doCreateCollection(String collectionName, CreateCollectionOptions collectionOptions) { @@ -2522,9 +2541,9 @@ private CreateCollectionOptions getCreateCollectionOptions(Document document) { * @param entityClass the parameterized type of the returned list. * @return the converted object or {@literal null} if none exists. */ - @Nullable - protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer, - Document query, Document fields, Class<T> entityClass) { + protected <T> @Nullable T doFindOne(String collectionName, + CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, + Class<T> entityClass) { return doFindOne(collectionName, collectionPreparer, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass); } @@ -2541,10 +2560,10 @@ protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollect * @return the converted object or {@literal null} if none exists. * @since 2.2 */ - @Nullable @SuppressWarnings("ConstantConditions") - protected <T> T doFindOne(String collectionName, CollectionPreparer<MongoCollection<Document>> collectionPreparer, - Document query, Document fields, CursorPreparer preparer, Class<T> entityClass) { + protected <T> @Nullable T doFindOne(String collectionName, + CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, + CursorPreparer preparer, Class<T> entityClass) { MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass); @@ -2610,7 +2629,7 @@ protected <S, T> List<T> doFind(String collectionName, if (LOGGER.isDebugEnabled()) { - Document mappedSort = preparer instanceof SortingQueryCursorPreparer sqcp ? getMappedSortObject(sqcp.getSortObject(), entity) : null; + Document mappedSort = getMappedSortObject(query, entityClass); LOGGER.debug(String.format("find using query: %s fields: %s sort: %s for class: %s in collection: %s", serializeToJsonSafely(mappedQuery), mappedFields, serializeToJsonSafely(mappedSort), entityClass, collectionName)); @@ -2635,12 +2654,9 @@ <S, T> List<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPr QueryContext queryContext = queryOperations.createQueryContext(new BasicQuery(query, fields)); Document mappedFields = queryContext.getMappedFields(entity, projection); Document mappedQuery = queryContext.getMappedQuery(entity); + Document mappedSort = getMappedSortObject(query, sourceClass); if (LOGGER.isDebugEnabled()) { - - Document mappedSort = preparer instanceof SortingQueryCursorPreparer sqcp - ? getMappedSortObject(sqcp.getSortObject(), entity) - : null; LOGGER.debug(String.format("find using query: %s fields: %s sort: %s for class: %s in collection: %s", serializeToJsonSafely(mappedQuery), mappedFields, serializeToJsonSafely(mappedSort), sourceClass, collectionName)); @@ -2720,8 +2736,9 @@ Document getMappedValidator(Validator validator, Class<?> domainType) { * @return the List of converted objects. */ @SuppressWarnings("ConstantConditions") - protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, Document query, - Document fields, Document sort, @Nullable Collation collation, Class<T> entityClass) { + protected <T> @Nullable T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, + Document query, @Nullable Document fields, @Nullable Document sort, @Nullable Collation collation, + Class<T> entityClass) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s", @@ -2736,8 +2753,8 @@ protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String co } @SuppressWarnings("ConstantConditions") - protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, Document query, - Document fields, Document sort, Class<T> entityClass, UpdateDefinition update, + protected <T> @Nullable T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, + Document query, @Nullable Document fields, @Nullable Document sort, Class<T> entityClass, UpdateDefinition update, @Nullable FindAndModifyOptions options) { if (options == null) { @@ -2781,10 +2798,10 @@ protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String co * @return {@literal null} if object does not exist, {@link FindAndReplaceOptions#isReturnNew() return new} is * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}. */ - @Nullable - protected <T> 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<T> resultType) { + protected <T> @Nullable 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<T> resultType) { EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType); @@ -2823,10 +2840,10 @@ CollectionPreparer<MongoCollection<Document>> createCollectionPreparer(Query que * {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}. * @since 3.4 */ - @Nullable - private <T> 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<T, ?> projection) { + private <T> @Nullable 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<T, ?> projection) { if (LOGGER.isDebugEnabled()) { LOGGER @@ -2842,6 +2859,7 @@ private <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String col collectionName); } + @SuppressWarnings("NullAway") private <S> UpdateResult doReplace(ReplaceOptions options, Class<S> entityType, String collectionName, UpdateContext updateContext, CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document replacement) { @@ -2896,8 +2914,7 @@ private MongoCollection<Document> getAndPrepareCollection(MongoDatabase db, Stri * @param collectionName the collection to be queried * @return */ - @Nullable - private <T> T executeFindOneInternal(CollectionCallback<Document> collectionCallback, + private <T> @Nullable T executeFindOneInternal(CollectionCallback<Document> collectionCallback, DocumentCallback<T> documentCallback, String collectionName) { try { @@ -2992,8 +3009,7 @@ private static MongoConverter getDefaultMongoConverter(MongoDatabaseFactory fact return converter; } - @Nullable - private Document getMappedSortObject(@Nullable Query query, Class<?> type) { + private @Nullable Document getMappedSortObject(@Nullable Query query, Class<?> type) { if (query == null) { return null; @@ -3002,19 +3018,13 @@ private Document getMappedSortObject(@Nullable Query query, Class<?> type) { return getMappedSortObject(query.getSortObject(), type); } - @Nullable - private Document getMappedSortObject(Document sortObject, Class<?> type) { - return getMappedSortObject(sortObject, mappingContext.getPersistentEntity(type)); - } - - @Nullable - private Document getMappedSortObject(Document sortObject, @Nullable MongoPersistentEntity<?> entity) { + private @Nullable Document getMappedSortObject(Document sortObject, Class<?> type) { if (ObjectUtils.isEmpty(sortObject)) { return null; } - return queryMapper.getMappedSort(sortObject, entity); + return queryMapper.getMappedSort(sortObject, mappingContext.getPersistentEntity(type)); } /** @@ -3084,10 +3094,10 @@ private static class FindCallback implements CollectionCallback<FindIterable<Doc private final CollectionPreparer<MongoCollection<Document>> 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<MongoCollection<Document>> 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"); @@ -3123,10 +3133,10 @@ private class ExistsCallback implements CollectionCallback<Boolean> { 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; @@ -3151,12 +3161,12 @@ private static class FindAndRemoveCallback implements CollectionCallback<Documen private final CollectionPreparer<MongoCollection<Document>> 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> collation; FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> 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; @@ -3179,14 +3189,15 @@ private static class FindAndModifyCallback implements CollectionCallback<Documen private final CollectionPreparer<MongoCollection<Document>> 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<Document> arrayFilters; private final FindAndModifyOptions options; FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, - Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) { + @Nullable Document fields, @Nullable Document sort, Object update, List<Document> arrayFilters, + FindAndModifyOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -3240,11 +3251,11 @@ private static class FindAndReplaceCallback implements CollectionCallback<Docume 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<MongoCollection<Document>> 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; @@ -3350,10 +3361,6 @@ private class ProjectingReadCallback<S, T> implements DocumentCallback<T> { @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); @@ -3367,11 +3374,14 @@ public T doWith(Document document) { } } - class QueryCursorPreparer implements SortingQueryCursorPreparer { + class QueryCursorPreparer implements CursorPreparer { private final Query query; + private final Document sortObject; + private final int limit; + private final long skip; private final @Nullable Class<?> type; @@ -3462,11 +3472,6 @@ public FindIterable<Document> prepare(FindIterable<Document> iterable) { return cursorToUse; } - @Nullable - @Override - public Document getSortObject() { - return sortObject; - } } /** @@ -3568,9 +3573,8 @@ public boolean hasNext() { } } - @Nullable @Override - public T next() { + public @Nullable T next() { if (cursor == null) { return null; @@ -3598,8 +3602,6 @@ public void close() { throw potentiallyConvertRuntimeException(ex, exceptionTranslator); } finally { cursor = null; - exceptionTranslator = null; - objectReadCallback = null; } } } @@ -3631,7 +3633,7 @@ static class SessionBoundMongoTemplate extends MongoTemplate { } @Override - public MongoCollection<Document> getCollection(String collectionName) { + public MongoCollection<Document> 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 28ca85fbd7..4ae618eaa1 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.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.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 @@ <T> MappedDocument prepareId(Class<T> type) { * @param <T> * @return the {@link MappedDocument} containing the changes. */ + @SuppressWarnings("NullAway") <T> MappedDocument prepareId(@Nullable MongoPersistentEntity<T> entity) { if (entity == null || source.hasId()) { @@ -361,6 +363,7 @@ <T> Document getMappedQuery(@Nullable MongoPersistentEntity<T> entity) { return queryMapper.getMappedObject(getQueryObject(), entity); } + @SuppressWarnings("NullAway") Document getMappedFields(@Nullable MongoPersistentEntity<?> entity, EntityProjection<?, ?> projection) { Document fields = evaluateFields(entity); @@ -888,6 +891,8 @@ Document getMappedShardKey(MongoPersistentEntity<?> entity) { */ List<Document> 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 @@ List<Document> getUpdatePipeline(@Nullable Class<?> domainType) { * @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 954fd61716..978aa9634f 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 @@ static class ReactiveAggregationSupport<T> private final ReactiveMongoTemplate template; private final Class<T> domainType; - private final Aggregation aggregation; - private final String collection; + private final @Nullable Aggregation aggregation; + private final @Nullable String collection; - ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, Aggregation aggregation, - String collection) { + ReactiveAggregationSupport(ReactiveMongoTemplate template, Class<T> domainType, @Nullable Aggregation aggregation, + @Nullable String collection) { this.template = template; this.domainType = domainType; @@ -89,6 +90,9 @@ public TerminatingAggregationOperation<T> by(Aggregation aggregation) { @Override public Flux<T> 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 afeb6c5e0e..589f264f17 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 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 d1aec8af36..9445dbdadb 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.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.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 @@ static class ReactiveFindSupport<T> private final ReactiveMongoTemplate template; private final Class<?> domainType; private final Class<T> returnType; - private final String collection; + private final @Nullable String collection; private final Query query; - ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType, String collection, + ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> 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 06d3c6eae7..9d424c2446 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 @@ static class ReactiveInsertSupport<T> implements ReactiveInsert<T> { private final ReactiveMongoTemplate template; private final Class<T> domainType; - private final String collection; + private final @Nullable String collection; - ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> domainType, String collection) { + ReactiveInsertSupport(ReactiveMongoTemplate template, Class<T> 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 4f0d395950..4e3379bad0 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 @@ 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 @@ static class ReactiveMapReduceSupport<T> @Override public Flux<T> 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 89d1cd78ac..89caf3273c 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 void setExceptionTranslator(@Nullable PersistenceExceptionTranslator exce } @Override - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + public @Nullable DataAccessException translateExceptionIfPossible(RuntimeException ex) { return exceptionTranslator.translateExceptionIfPossible(ex); } @@ -124,7 +124,9 @@ protected MongoClient createInstance() throws Exception { @Override protected void destroyInstance(@Nullable MongoClient instance) throws Exception { - instance.close(); + if (instance != null) { + instance.close(); + } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java index 90f2d2345d..ebec41e3aa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java @@ -23,6 +23,7 @@ import java.util.function.Supplier; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import org.springframework.data.domain.KeysetScrollPosition; @@ -48,7 +49,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.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index ea427a3e1f..c203e33a55 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -15,7 +15,7 @@ */ 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 reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -44,9 +44,9 @@ import org.bson.Document; import org.bson.conversions.Bson; import org.bson.types.ObjectId; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -109,7 +109,18 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; -import org.springframework.data.mongodb.core.mapping.event.*; +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.AfterSaveEvent; +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.BeforeSaveEvent; +import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; +import org.springframework.data.mongodb.core.mapping.event.ReactiveAfterConvertCallback; +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.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Collation; @@ -121,7 +132,7 @@ import org.springframework.data.mongodb.util.MongoCompatibilityAdapter; import org.springframework.data.projection.EntityProjection; 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; @@ -345,7 +356,8 @@ public void setWriteConcern(@Nullable WriteConcern writeConcern) { * @param writeConcernResolver can be {@literal null}. */ public void setWriteConcernResolver(@Nullable WriteConcernResolver writeConcernResolver) { - this.writeConcernResolver = writeConcernResolver; + this.writeConcernResolver = writeConcernResolver != null ? writeConcernResolver + : DefaultWriteConcernResolver.INSTANCE; } /** @@ -739,10 +751,11 @@ public <T> Mono<Boolean> collectionExists(Class<T> entityClass) { @Override public Mono<Boolean> collectionExists(String collectionName) { - return createMono(db -> Flux.from(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames()) // - .filter(s -> s.equals(collectionName)) // - .map(s -> true) // - .single(false)); + return createMono( + db -> Flux.from(MongoCompatibilityAdapter.reactiveMongoDatabaseAdapter().forDb(db).listCollectionNames()) // + .filter(s -> s.equals(collectionName)) // + .map(s -> true) // + .single(false)); } @Override @@ -899,7 +912,7 @@ <T> Mono<Window<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetC Mono<List<T>> 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)); } @@ -907,7 +920,7 @@ <T> Mono<Window<T>> doScroll(Query query, Class<?> sourceClass, Class<T> targetC Mono<List<T>> 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()))); @@ -1146,6 +1159,7 @@ public <T> Mono<T> findAndModify(Query query, UpdateDefinition update, FindAndMo } @Override + @SuppressWarnings("NullAway") public <S, T> Mono<T> findAndReplace(Query query, S replacement, FindAndReplaceOptions options, Class<S> entityType, String collectionName, Class<T> resultType) { @@ -1351,6 +1365,7 @@ public <T> Mono<T> insert(T objectToSave, String collectionName) { return doInsert(collectionName, objectToSave, this.mongoConverter); } + @SuppressWarnings("NullAway") protected <T> Mono<T> doInsert(String collectionName, T objectToSave, MongoWriter<Object> writer) { return Mono.just(PersistableEntityModel.of(objectToSave, collectionName)) // @@ -1401,6 +1416,7 @@ public <T> Flux<T> insertAll(Mono<? extends Collection<? extends T>> objectsToSa return Flux.from(objectsToSave).flatMapSequential(this::insertAll); } + @SuppressWarnings("NullAway") protected <T> Flux<T> doInsertAll(Collection<? extends T> listToSave, MongoWriter<Object> writer) { Map<String, List<T>> elementsByCollection = new HashMap<>(); @@ -1417,6 +1433,7 @@ protected <T> Flux<T> doInsertAll(Collection<? extends T> listToSave, MongoWrite .concatMap(collectionName -> doInsertBatch(collectionName, elementsByCollection.get(collectionName), writer)); } + @SuppressWarnings("NullAway") protected <T> Flux<T> doInsertBatch(String collectionName, Collection<? extends T> batchToSave, MongoWriter<Object> writer) { @@ -1532,6 +1549,7 @@ private <T> Mono<T> doSaveVersioned(AdaptibleEntity<T> source, String collection }); } + @SuppressWarnings("NullAway") protected <T> Mono<T> doSave(String collectionName, T objectToSave, MongoWriter<Object> writer) { assertUpdateableIdIfNotSet(objectToSave); @@ -1625,6 +1643,7 @@ private MongoCollection<Document> prepareCollection(MongoCollection<Document> co return collectionToUse; } + @SuppressWarnings("NullAway") protected Mono<Object> saveDocument(String collectionName, Document document, Class<?> entityClass) { if (LOGGER.isDebugEnabled()) { @@ -1728,7 +1747,8 @@ public Mono<UpdateResult> updateMulti(Query query, UpdateDefinition update, Clas return doUpdate(collectionName, query, update, entityClass, false, true); } - protected Mono<UpdateResult> doUpdate(String collectionName, Query query, @Nullable UpdateDefinition update, + @SuppressWarnings("NullAway") + protected Mono<UpdateResult> doUpdate(String collectionName, Query query, UpdateDefinition update, @Nullable Class<?> entityClass, boolean upsert, boolean multi) { MongoPersistentEntity<?> entity = entityClass == null ? null : getPersistentEntity(entityClass); @@ -1810,7 +1830,8 @@ protected Mono<UpdateResult> doUpdate(String collectionName, Query query, @Nulla 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)); } } }); @@ -2008,18 +2029,18 @@ public <T> Flux<T> tail(Query query, Class<T> entityClass) { @Override public <T> Flux<T> tail(@Nullable Query query, Class<T> 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 @@ protected <S, T> Flux<T> doFind(String collectionName, 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<MongoCollection<Document>> createCollectionPreparer(Query query) { @@ -2448,8 +2469,8 @@ protected CreateCollectionOptions convertToCreateCollectionOptions(@Nullable Col * @return the List of converted objects. */ protected <T> Mono<T> doFindAndRemove(String collectionName, - CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort, - @Nullable Collation collation, Class<T> entityClass) { + CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, + @Nullable Document sort, @Nullable Collation collation, Class<T> 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 @@ protected <T> Mono<T> doFindAndRemove(String collectionName, } protected <T> Mono<T> doFindAndModify(String collectionName, - CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, Document sort, - Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) { + CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields, + @Nullable Document sort, Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) { MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass); UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false); @@ -2481,8 +2502,7 @@ protected <T> Mono<T> doFindAndModify(String collectionName, 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 @@ protected MongoDatabase prepareDatabase(MongoDatabase database) { * @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 @@ private WriteConcern potentiallyForceAcknowledgedWrite(@Nullable WriteConcern wc 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 @@ private <T> Mono<T> executeFindOneInternal(ReactiveCollectionCallback<Document> * @return */ private <T> Flux<T> executeFindMultiInternal(ReactiveCollectionQueryCallback<Document> collectionCallback, - @Nullable FindPublisherPreparer preparer, DocumentCallback<T> objectCallback, String collectionName) { + FindPublisherPreparer preparer, DocumentCallback<T> objectCallback, String collectionName) { return createFlux(collectionName, collection -> { return Flux.from(preparer.initiateFind(collection, collectionCallback::doInCollection)) @@ -2764,8 +2783,7 @@ private static RuntimeException potentiallyConvertRuntimeException(RuntimeExcept 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 @@ private MappingMongoConverter getDefaultMongoConverter() { 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 @@ private Document getMappedSortObject(Query query, Class<?> type) { 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 @@ private static class FindCallback implements ReactiveCollectionQueryCallback<Doc this(collectionPreparer, query, null); } - FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields) { + FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, @Nullable Document query, + @Nullable Document fields) { this.collectionPreparer = collectionPreparer; this.query = query; this.fields = fields; @@ -2898,11 +2917,11 @@ private static class FindAndRemoveCallback implements ReactiveCollectionCallback private final CollectionPreparer<MongoCollection<Document>> collectionPreparer; private final Document query; private final Document fields; - private final Document sort; + private final @Nullable Document sort; private final Optional<Collation> collation; FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> 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 @@ private static class FindAndModifyCallback implements ReactiveCollectionCallback private final CollectionPreparer<MongoCollection<Document>> 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<Document> arrayFilters; private final FindAndModifyOptions options; FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, - Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) { + @Nullable Document fields, @Nullable Document sort, Object update, List<Document> arrayFilters, + FindAndModifyOptions options) { this.collectionPreparer = collectionPreparer; this.query = query; @@ -2973,7 +2993,7 @@ public Publisher<Document> doInCollection(MongoCollection<Document> collection) } private static FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options, - Document fields, Document sort, List<Document> arrayFilters) { + @Nullable Document fields, @Nullable Document sort, List<Document> arrayFilters) { FindOneAndUpdateOptions result = new FindOneAndUpdateOptions(); @@ -3009,11 +3029,11 @@ private static class FindAndReplaceCallback implements ReactiveCollectionCallbac 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<MongoCollection<Document>> 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 @@ private FindOneAndReplaceOptions convertToFindOneAndReplaceOptions(FindAndReplac } } - 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 97c9cb0d0e..5c935ec628 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 @@ static class ReactiveRemoveSupport<T> implements ReactiveRemove<T>, RemoveWithCo private final ReactiveMongoTemplate template; private final Class<T> domainType; private final Query query; - private final String collection; + private final @Nullable String collection; - ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> domainType, Query query, String collection) { + ReactiveRemoveSupport(ReactiveMongoTemplate template, Class<T> 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 51cd99dc93..75bfeef314 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 @@ 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 @@ static class ReactiveUpdateSupport<T> 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<T> targetType; - ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, UpdateDefinition update, - String collection, FindAndModifyOptions findAndModifyOptions, FindAndReplaceOptions findAndReplaceOptions, - Object replacement, Class<T> targetType) { + ReactiveUpdateSupport(ReactiveMongoTemplate template, Class<?> domainType, Query query, @Nullable UpdateDefinition update, + @Nullable String collection, @Nullable FindAndModifyOptions findAndModifyOptions, @Nullable FindAndReplaceOptions findAndReplaceOptions, + @Nullable Object replacement, Class<T> targetType) { this.template = template; this.domainType = domainType; @@ -108,6 +108,7 @@ public Mono<UpdateResult> upsert() { } @Override + @SuppressWarnings("NullAway") public Mono<T> findAndModify() { String collectionName = getCollectionName(); @@ -118,7 +119,11 @@ public Mono<T> findAndModify() { } @Override + @SuppressWarnings({"unchecked","rawtypes"}) public Mono<T> 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 @@ public <R> FindAndReplaceWithOptions<R> as(Class<R> resultType) { } @Override + @SuppressWarnings("NullAway") public Mono <UpdateResult> replaceFirst() { if (replacement != null) { @@ -197,6 +203,7 @@ public Mono <UpdateResult> replaceFirst() { findAndReplaceOptions != null ? findAndReplaceOptions : ReplaceOptions.none(), getCollectionName()); } + @SuppressWarnings("NullAway") private Mono<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/ReadConcernAware.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReadConcernAware.java index 00c5815fc9..7a7e5fdfb2 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 74bca9abea..e6f3fc0daf 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 a2e2ba24c0..a487cde669 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 static ReplaceOptions none() { * * @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 a01760368a..2ec71b415a 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 @@ 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 85ddce7656..62e6d6c513 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.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 @@ static <T> Window<T> createWindow(Query query, List<T> result, Class<?> sourceTy 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 55a87ecadf..76a6d525f8 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 33ad9d7318..906d682685 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 @@ 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}. * <br /> @@ -42,8 +42,7 @@ public interface SessionScoped { * @param <T> return type. * @return a result object returned by the action. Can be {@literal null}. */ - @Nullable - default <T> T execute(SessionCallback<T> action) { + default <T> @Nullable T execute(SessionCallback<T> action) { return execute(action, session -> {}); } @@ -60,6 +59,5 @@ default <T> T execute(SessionCallback<T> action) { * @param <T> return type. * @return a result object returned by the action. Can be {@literal null}. */ - @Nullable - <T> T execute(SessionCallback<T> action, Consumer<ClientSession> doFinally); + <T> @Nullable T execute(SessionCallback<T> action, Consumer<ClientSession> 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 84edf13d57..529f912e6c 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 @@ 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 e50e1088cb..b4b525fc97 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 @@ 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 Optional<Collation> getCollation() { * @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 d6e4119b20..bdc7de6663 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 8df4171844..a72c656e47 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 d4cdece411..710b570ed7 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 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 @@ protected <T> T get(int index) { * @since 2.1 */ @SuppressWarnings("unchecked") - protected <T> T get(Object key) { + protected <T> @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 cf6485c230..fa44656c99 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.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 static AccumulatorOperatorFactory valueOf(AggregationExpression expressio */ 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 AccumulatorOperatorFactory(AggregationExpression expression) { * * @return new instance of {@link Sum}. */ + @SuppressWarnings("NullAway") public Sum sum() { return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression); } @@ -103,6 +106,7 @@ public Sum sum() { * * @return new instance of {@link Avg}. */ + @SuppressWarnings("NullAway") public Avg avg() { return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression); } @@ -113,6 +117,7 @@ public Avg avg() { * * @return new instance of {@link Max}. */ + @SuppressWarnings("NullAway") public Max max() { return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression); } @@ -134,6 +139,7 @@ public Max max(int numberOfResults) { * * @return new instance of {@link Min}. */ + @SuppressWarnings("NullAway") public Min min() { return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression); } @@ -155,6 +161,7 @@ public Min min(int numberOfResults) { * * @return new instance of {@link StdDevPop}. */ + @SuppressWarnings("NullAway") public StdDevPop stdDevPop() { return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression); } @@ -165,6 +172,7 @@ public StdDevPop stdDevPop() { * * @return new instance of {@link StdDevSamp}. */ + @SuppressWarnings("NullAway") public StdDevSamp stdDevSamp() { return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression); } @@ -193,6 +201,7 @@ public CovariancePop covariancePop(AggregationExpression expression) { return covariancePop().and(expression); } + @SuppressWarnings("NullAway") private CovariancePop covariancePop() { return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression); } @@ -221,6 +230,7 @@ public CovarianceSamp covarianceSamp(AggregationExpression expression) { return covarianceSamp().and(expression); } + @SuppressWarnings("NullAway") private CovarianceSamp covarianceSamp() { return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference) : CovarianceSamp.covarianceSampOf(expression); @@ -233,6 +243,7 @@ private CovarianceSamp covarianceSamp() { * @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 ExpMovingAvg alpha(double exponentialDecayValue) { } /** - * 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 Percentile percentile(Double... percentages) { * @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 static Sum sumOf(String fieldReference) { * @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 static Sum sumOf(AggregationExpression expression) { * @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 Sum and(String fieldReference) { * @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 Sum and(AggregationExpression expression) { * @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 Sum and(Number value) { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List<?> list && list.size() == 1) { @@ -444,6 +460,7 @@ public static Avg avgOf(AggregationExpression expression) { * @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 Avg and(String fieldReference) { * @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 Avg and(AggregationExpression expression) { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List<?> list && list.size() == 1) { @@ -522,6 +539,7 @@ public static Max maxOf(AggregationExpression expression) { * @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 Max and(String fieldReference) { * @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 Max and(AggregationExpression expression) { * @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 static Min minOf(AggregationExpression expression) { * @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 Min and(String fieldReference) { * @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 Min and(AggregationExpression expression) { * @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 Document toDocument(AggregationOperationContext context) { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List<?> list && list.size() == 1) { @@ -717,6 +741,7 @@ public static StdDevPop stdDevPopOf(AggregationExpression expression) { * @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 StdDevPop and(String fieldReference) { * @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 StdDevPop and(AggregationExpression expression) { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List<?> list && list.size() == 1) { @@ -795,6 +820,7 @@ public static StdDevSamp stdDevSampOf(AggregationExpression expression) { * @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 StdDevSamp and(String fieldReference) { * @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 StdDevSamp and(AggregationExpression expression) { } @Override - @SuppressWarnings("unchecked") public Document toDocument(Object value, AggregationOperationContext context) { if (value instanceof List<?> list && list.size() == 1) { @@ -866,6 +892,7 @@ public static CovariancePop covariancePopOf(AggregationExpression expression) { * @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 CovariancePop and(String fieldReference) { * @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 static CovarianceSamp covarianceSampOf(AggregationExpression expression) * @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 CovarianceSamp and(String fieldReference) { * @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 static ExpMovingAvg expMovingAvgOf(AggregationExpression expression) { * @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 static ExpMovingAvg expMovingAvgOf(AggregationExpression expression) { * @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 static Percentile percentileOf(AggregationExpression expression) { * @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 Percentile percentages(Double... percentages) { * @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 Percentile and(String fieldReference) { * @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 static Median medianOf(AggregationExpression expression) { * @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 Median and(String fieldReference) { * @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 0dc1588bf8..e76ebb894d 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.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 @@ -82,6 +83,7 @@ public static ValueAppender addField(String field) { * @param value the value to assign. * @return new instance of {@link AddFieldsOperation}. */ + @Contract("_ -> new") public AddFieldsOperation addField(Object field, Object value) { LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap()); @@ -95,6 +97,7 @@ public AddFieldsOperation addField(Object field, Object value) { * * @return new instance of {@link AddFieldsOperationBuilder}. */ + @Contract("-> new") public AddFieldsOperationBuilder and() { return new AddFieldsOperationBuilder(getValueMap()); } @@ -139,7 +142,7 @@ public ValueAppender addField(String field) { 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 00db38329f..e33c565d11 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 a49c7e46d5..5027328461 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 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 fd5f7ed979..6437ec981d 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.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 327d40b8c7..278da408c6 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.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 boolean hasReadConcern() { } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return readConcern.orElse(null); } @@ -309,7 +310,7 @@ public boolean hasReadPreference() { } @Override - public ReadPreference getReadPreference() { + public @Nullable ReadPreference getReadPreference() { return readPreference.orElse(null); } @@ -426,7 +427,7 @@ static Document createCursor(int cursorBatchSize) { */ 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 static class Builder { * @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 Builder allowDiskUse(boolean allowDiskUse) { * @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 Builder explain(boolean explain) { * @param cursor must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Builder cursor(Document cursor) { this.cursor = cursor; @@ -481,6 +485,7 @@ public Builder cursor(Document cursor) { * @return this. * @since 2.0 */ + @Contract("_ -> this") public Builder cursorBatchSize(int batchSize) { this.cursor = createCursor(batchSize); @@ -494,6 +499,7 @@ public Builder cursorBatchSize(int batchSize) { * @return this. * @since 2.0 */ + @Contract("_ -> this") public Builder collation(@Nullable Collation collation) { this.collation = collation; @@ -507,6 +513,7 @@ public Builder collation(@Nullable Collation collation) { * @return this. * @since 2.2 */ + @Contract("_ -> this") public Builder comment(@Nullable String comment) { this.comment = comment; @@ -520,6 +527,7 @@ public Builder comment(@Nullable String comment) { * @return this. * @since 3.1 */ + @Contract("_ -> this") public Builder hint(@Nullable Document hint) { this.hint = hint; @@ -533,6 +541,7 @@ public Builder hint(@Nullable Document hint) { * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder hint(@Nullable String indexName) { this.hint = indexName; @@ -546,6 +555,7 @@ public Builder hint(@Nullable String indexName) { * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder readConcern(@Nullable ReadConcern readConcern) { this.readConcern = readConcern; @@ -559,6 +569,7 @@ public Builder readConcern(@Nullable ReadConcern readConcern) { * @return this. * @since 4.1 */ + @Contract("_ -> this") public Builder readPreference(@Nullable ReadPreference readPreference) { this.readPreference = readPreference; @@ -573,6 +584,7 @@ public Builder readPreference(@Nullable ReadPreference readPreference) { * @return this. * @since 3.0 */ + @Contract("_ -> this") public Builder maxTime(@Nullable Duration maxTime) { this.maxTime = maxTime; @@ -587,6 +599,7 @@ public Builder maxTime(@Nullable Duration maxTime) { * @return this. * @since 3.0.2 */ + @Contract("-> this") public Builder skipOutput() { this.resultOptions = ResultOptions.SKIP; @@ -600,6 +613,7 @@ public Builder skipOutput() { * @return this. * @since 3.2 */ + @Contract("-> this") public Builder strictMapping() { this.domainTypeMapping = DomainTypeMapping.STRICT; @@ -613,6 +627,7 @@ public Builder strictMapping() { * @return this. * @since 3.2 */ + @Contract("-> this") public Builder relaxedMapping() { this.domainTypeMapping = DomainTypeMapping.RELAXED; @@ -625,6 +640,7 @@ public Builder relaxedMapping() { * @return this. * @since 3.2 */ + @Contract("-> this") public Builder noMapping() { this.domainTypeMapping = DomainTypeMapping.NONE; @@ -636,6 +652,7 @@ public Builder noMapping() { * * @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 68662ec0df..40966bcf3d 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.function.Predicate; import org.bson.Document; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -63,6 +64,7 @@ public AggregationPipeline(List<AggregationOperation> aggregationOperations) { * @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 438eb9e49f..f5a861cddd 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.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 List<T> getMappedResults() { * @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 Document getRawResults() { 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 1626d672bc..c5b53ef0c6 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 static AggregationSpELExpression expressionOf(String expressionString, Ob @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 15d700309e..9e8564c03e 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.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 static AggregationUpdate from(List<AggregationOperation> pipeline) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/set/">$set Aggregation Reference</a> */ + @Contract("_ -> this") public AggregationUpdate set(SetOperation setOperation) { Assert.notNull(setOperation, "SetOperation must not be null"); @@ -148,6 +149,7 @@ public AggregationUpdate set(SetOperation setOperation) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset/">$unset Aggregation * Reference</a> */ + @Contract("_ -> this") public AggregationUpdate unset(UnsetOperation unsetOperation) { Assert.notNull(unsetOperation, "UnsetOperation must not be null"); @@ -166,6 +168,7 @@ public AggregationUpdate unset(UnsetOperation unsetOperation) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/">$replaceWith Aggregation * Reference</a> */ + @Contract("_ -> this") public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) { Assert.notNull(replaceWithOperation, "ReplaceWithOperation must not be null"); @@ -179,6 +182,7 @@ public AggregationUpdate replaceWith(ReplaceWithOperation replaceWithOperation) * @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 AggregationUpdate replaceWith(Object value) { * @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 AggregationUpdate toValueOf(Object value) { * @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 AggregationUpdate unset(String... keys) { * * @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 ed79202345..522dd5eae5 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 e2c31c6346..c7787b382c 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.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.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 static Rand rand() { */ 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 ArithmeticOperatorFactory(AggregationExpression expression) { * * @return new instance of {@link Abs}. */ + @SuppressWarnings("NullAway") public Abs abs() { return usesFieldRef() ? Abs.absoluteValueOf(fieldReference) : Abs.absoluteValueOf(expression); } @@ -158,6 +160,7 @@ public Add add(Number value) { return createAdd().add(value); } + @SuppressWarnings("NullAway") private Add createAdd() { return usesFieldRef() ? Add.valueOf(fieldReference) : Add.valueOf(expression); } @@ -168,6 +171,7 @@ private Add createAdd() { * * @return new instance of {@link Ceil}. */ + @SuppressWarnings("NullAway") public Ceil ceil() { return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression); } @@ -205,6 +209,7 @@ public Derivative derivative(WindowUnit unit) { * @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 Divide divideBy(Number value) { return createDivide().divideBy(value); } + @SuppressWarnings("NullAway") private Divide createDivide() { return usesFieldRef() ? Divide.valueOf(fieldReference) : Divide.valueOf(expression); } @@ -259,6 +265,7 @@ private Divide createDivide() { * * @return new instance of {@link Exp}. */ + @SuppressWarnings("NullAway") public Exp exp() { return usesFieldRef() ? Exp.expValueOf(fieldReference) : Exp.expValueOf(expression); } @@ -269,6 +276,7 @@ public Exp exp() { * * @return new instance of {@link Floor}. */ + @SuppressWarnings("NullAway") public Floor floor() { return usesFieldRef() ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression); } @@ -279,6 +287,7 @@ public Floor floor() { * @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 Integral integral(String unit) { * * @return new instance of {@link Ln}. */ + @SuppressWarnings("NullAway") public Ln ln() { return usesFieldRef() ? Ln.lnValueOf(fieldReference) : Ln.lnValueOf(expression); } @@ -345,7 +355,7 @@ public Log log(String fieldReference) { 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 Log log(Number base) { return createLog().log(base); } + @SuppressWarnings("NullAway") private Log createLog() { return usesFieldRef() ? Log.valueOf(fieldReference) : Log.valueOf(expression); } @@ -370,6 +381,7 @@ private Log createLog() { * * @return new instance of {@link Log10}. */ + @SuppressWarnings("NullAway") public Log10 log10() { return usesFieldRef() ? Log10.log10ValueOf(fieldReference) : Log10.log10ValueOf(expression); } @@ -413,6 +425,7 @@ public Mod mod(Number value) { return createMod().mod(value); } + @SuppressWarnings("NullAway") private Mod createMod() { return usesFieldRef() ? Mod.valueOf(fieldReference) : Mod.valueOf(expression); } @@ -453,6 +466,7 @@ public Multiply multiplyBy(Number value) { return createMultiply().multiplyBy(value); } + @SuppressWarnings("NullAway") private Multiply createMultiply() { return usesFieldRef() ? Multiply.valueOf(fieldReference) : Multiply.valueOf(expression); } @@ -493,6 +507,7 @@ public Pow pow(Number value) { return createPow().pow(value); } + @SuppressWarnings("NullAway") private Pow createPow() { return usesFieldRef() ? Pow.valueOf(fieldReference) : Pow.valueOf(expression); } @@ -502,6 +517,7 @@ private Pow createPow() { * * @return new instance of {@link Sqrt}. */ + @SuppressWarnings("NullAway") public Sqrt sqrt() { return usesFieldRef() ? Sqrt.sqrtOf(fieldReference) : Sqrt.sqrtOf(expression); } @@ -542,6 +558,7 @@ public Subtract subtract(Number value) { return createSubtract().subtract(value); } + @SuppressWarnings("NullAway") private Subtract createSubtract() { return usesFieldRef() ? Subtract.valueOf(fieldReference) : Subtract.valueOf(expression); } @@ -551,6 +568,7 @@ private Subtract createSubtract() { * * @return new instance of {@link Trunc}. */ + @SuppressWarnings("NullAway") public Trunc trunc() { return usesFieldRef() ? Trunc.truncValueOf(fieldReference) : Trunc.truncValueOf(expression); } @@ -560,6 +578,7 @@ public Trunc trunc() { * * @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 Sum sum() { * * @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 Avg avg() { * * @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 Max max() { * * @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 Min min() { * * @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 StdDevPop stdDevPop() { * * @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 CovariancePop covariancePop(AggregationExpression expression) { return covariancePop().and(expression); } + @SuppressWarnings("NullAway") private CovariancePop covariancePop() { return usesFieldRef() ? CovariancePop.covariancePopOf(fieldReference) : CovariancePop.covariancePopOf(expression); } @@ -667,6 +692,7 @@ public CovarianceSamp covarianceSamp(AggregationExpression expression) { return covarianceSamp().and(expression); } + @SuppressWarnings("NullAway") private CovarianceSamp covarianceSamp() { return usesFieldRef() ? CovarianceSamp.covarianceSampOf(fieldReference) : CovarianceSamp.covarianceSampOf(expression); @@ -679,6 +705,7 @@ private CovarianceSamp covarianceSamp() { * @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 Sin sin() { * @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 Sinh sinh() { * @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 Sinh sinh(AngularUnit unit) { * @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 ASin asin() { * @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 Cos cos() { * @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 Cosh cosh() { * @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 Cosh cosh(AngularUnit unit) { * @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 ACos acos() { * @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 Tan tan() { * @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 ATan atan() { * @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 ATan2 atan2(AggregationExpression expression) { return createATan2().atan2of(expression); } + @SuppressWarnings("NullAway") private ATan2 createATan2() { - return usesFieldRef() ? ATan2.valueOf(fieldReference) : ATan2.valueOf(expression); } @@ -897,6 +934,7 @@ private ATan2 createATan2() { * @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 ATanh atanh() { * @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 Tanh tanh() { * @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 Percentile percentile(Double... percentages) { } /** - * 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 static Add valueOf(Number value) { * @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 Add add(String fieldReference) { * @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 Add add(AggregationExpression expression) { * @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 static Divide valueOf(Number value) { * @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 Divide divideBy(String fieldReference) { * @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 Divide divideBy(AggregationExpression expression) { * @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 static Log valueOf(Number value) { * @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 Log log(String fieldReference) { * @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 Log log(AggregationExpression expression) { * @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 static Mod valueOf(Number value) { * @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 Mod mod(String fieldReference) { * @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 Mod mod(AggregationExpression expression) { * @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 static Multiply valueOf(Number value) { * @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 Multiply multiplyBy(String fieldReference) { * @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 Multiply multiplyBy(AggregationExpression expression) { * @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 static Pow valueOf(Number value) { * @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 Pow pow(String fieldReference) { * @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 Pow pow(AggregationExpression expression) { * @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 static Subtract valueOf(Number value) { * @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 Subtract subtract(String fieldReference) { * @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 Subtract subtract(AggregationExpression expression) { * @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 static Round round(Number value) { * @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 Round place(int place) { * @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 Round placeOf(AggregationExpression expression) { * @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 static Derivative derivativeOfValue(Number value) { 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 static Integral integralOf(AggregationExpression expression) { * @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 @@ private Sin(Object value) { /** * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - * <br /> + * {@link AngularUnit#RADIANS radians}. <br /> * Use {@code sinhOf("angle", DEGREES)} as shortcut for * * <pre> @@ -2330,8 +2395,7 @@ public static Sinh sinhOf(String fieldReference) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in - * the given {@link AngularUnit unit}. - * <br /> + * the given {@link AngularUnit unit}. <br /> * Use {@code sinhOf("angle", DEGREES)} as shortcut for * * <pre> @@ -2350,8 +2414,7 @@ public static Sinh sinhOf(String fieldReference, AngularUnit unit) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in - * {@link AngularUnit#RADIANS}. - * <br /> + * {@link AngularUnit#RADIANS}. <br /> * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -2434,8 +2497,7 @@ public static ASin asinOf(String fieldReference) { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value. - * <br /> + * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value. <br /> * * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ASin}. @@ -2484,8 +2546,7 @@ public static ASinh asinhOf(String fieldReference) { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value. - * <br /> + * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value. <br /> * * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ASinh}. @@ -2525,8 +2586,7 @@ private Cos(Object value) { /** * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - * <br /> + * {@link AngularUnit#RADIANS radians}. <br /> * Use {@code cosOf("angle", DEGREES)} as shortcut for * * <pre> @@ -2636,8 +2696,7 @@ public static Cosh coshOf(String fieldReference) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in - * the given {@link AngularUnit unit}. - * <br /> + * the given {@link AngularUnit unit}. <br /> * Use {@code coshOf("angle", DEGREES)} as shortcut for * * <pre> @@ -2654,8 +2713,7 @@ public static Cosh coshOf(String fieldReference, AngularUnit unit) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in - * {@link AngularUnit#RADIANS}. - * <br /> + * {@link AngularUnit#RADIANS}. <br /> * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -2738,8 +2796,7 @@ public static ACos acosOf(String fieldReference) { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value. - * <br /> + * Creates a new {@link AggregationExpression} that calculates the inverse cosine of a value. <br /> * * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ACos}. @@ -2788,8 +2845,7 @@ public static ACosh acoshOf(String fieldReference) { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value. - * <br /> + * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic cosine of a value. <br /> * * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ACosh}. @@ -2829,8 +2885,7 @@ private Tan(Object value) { /** * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in - * {@link AngularUnit#RADIANS radians}. - * <br /> + * {@link AngularUnit#RADIANS radians}. <br /> * Use {@code tanOf("angle", DEGREES)} as shortcut for * * <pre> @@ -3008,8 +3063,8 @@ public static ATan2 valueOf(AggregationExpression expression) { * 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 ATan2 atan2of(String fieldReference) { * 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 static Tanh tanhOf(String fieldReference) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in - * the given {@link AngularUnit unit}. - * <br /> + * the given {@link AngularUnit unit}. <br /> * Use {@code tanhOf("angle", DEGREES)} as shortcut for * * <pre> @@ -3093,8 +3147,7 @@ public static Tanh tanhOf(String fieldReference, AngularUnit unit) { /** * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in - * {@link AngularUnit#RADIANS}. - * <br /> + * {@link AngularUnit#RADIANS}. <br /> * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}. * @@ -3165,11 +3218,9 @@ private ATanh(Object value) { } /** - * 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 static ATanh atanhOf(String fieldReference) { } /** - * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. - * <br /> + * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value. <br /> * * @param expression the {@link AggregationExpression expression} that resolves to a numeric value. * @return new instance of {@link ATanh}. @@ -3188,11 +3238,10 @@ public static ATanh atanhOf(AggregationExpression expression) { } /** - * 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 41688bfc62..7770728fe3 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,12 +22,13 @@ 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.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; /** @@ -158,6 +159,7 @@ public ArrayElemAt elementAt(String fieldReference) { return createArrayElemAt().elementAt(fieldReference); } + @SuppressWarnings("NullAway") private ArrayElemAt createArrayElemAt() { if (usesFieldRef()) { @@ -193,6 +195,7 @@ public ConcatArrays concat(AggregationExpression expression) { return createConcatArrays().concat(expression); } + @SuppressWarnings("NullAway") private ConcatArrays createConcatArrays() { if (usesFieldRef()) { @@ -208,6 +211,7 @@ private ConcatArrays createConcatArrays() { * * @return new instance of {@link AsBuilder} to create a {@link Filter}. */ + @SuppressWarnings("NullAway") public AsBuilder filter() { if (usesFieldRef()) { @@ -227,6 +231,7 @@ public AsBuilder filter() { * * @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"); @@ -239,6 +244,7 @@ public IsArray isArray() { * * @return new instance of {@link Size}. */ + @SuppressWarnings("NullAway") public Size length() { if (usesFieldRef()) { @@ -253,6 +259,7 @@ public Size length() { * * @return new instance of {@link Slice}. */ + @SuppressWarnings("NullAway") public Slice slice() { if (usesFieldRef()) { @@ -269,6 +276,7 @@ public Slice slice() { * @param value must not be {@literal null}. * @return new instance of {@link IndexOfArray}. */ + @SuppressWarnings("NullAway") public IndexOfArray indexOf(Object value) { if (usesFieldRef()) { @@ -284,6 +292,7 @@ public IndexOfArray indexOf(Object value) { * * @return new instance of {@link ReverseArray}. */ + @SuppressWarnings("NullAway") public ReverseArray reverse() { if (usesFieldRef()) { @@ -301,6 +310,7 @@ public ReverseArray reverse() { * @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) @@ -314,6 +324,7 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(AggregationExpressi * @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)) @@ -327,6 +338,7 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression. * @return new instance of {@link SortArray}. * @since 4.0 */ + @SuppressWarnings("NullAway") public SortArray sort(Sort sort) { if (usesFieldRef()) { @@ -344,6 +356,7 @@ public SortArray sort(Sort sort) { * @param arrays must not be {@literal null}. * @return new instance of {@link Zip}. */ + @SuppressWarnings("NullAway") public Zip zipWith(Object... arrays) { if (usesFieldRef()) { @@ -360,6 +373,7 @@ public Zip zipWith(Object... arrays) { * @param value must not be {@literal null}. * @return new instance of {@link In}. */ + @SuppressWarnings("NullAway") public In containsValue(Object value) { if (usesFieldRef()) { @@ -376,6 +390,7 @@ public In containsValue(Object value) { * @return new instance of {@link ArrayToObject}. * @since 2.1 */ + @SuppressWarnings("NullAway") public ArrayToObject toObject() { if (usesFieldRef()) { @@ -392,6 +407,7 @@ public ArrayToObject toObject() { * @return new instance of {@link First}. * @since 3.4 */ + @SuppressWarnings("NullAway") public First first() { if (usesFieldRef()) { @@ -408,6 +424,7 @@ public First first() { * @return new instance of {@link Last}. * @since 3.4 */ + @SuppressWarnings("NullAway") public Last last() { if (usesFieldRef()) { @@ -506,6 +523,7 @@ public static ArrayElemAt arrayOf(Collection<?> values) { * @param index the index number * @return new instance of {@link ArrayElemAt}. */ + @Contract("_ -> new") public ArrayElemAt elementAt(int index) { return new ArrayElemAt(append(index)); } @@ -516,6 +534,7 @@ public ArrayElemAt elementAt(int index) { * @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"); @@ -528,6 +547,7 @@ public ArrayElemAt elementAt(AggregationExpression expression) { * @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"); @@ -594,6 +614,7 @@ public static ConcatArrays arrayOf(Collection<?> values) { * @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"); @@ -606,6 +627,7 @@ public ConcatArrays concat(String arrayFieldReference) { * @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"); @@ -681,9 +703,12 @@ public static AsBuilder filter(List<?> values) { @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(); @@ -697,7 +722,7 @@ private Document toFilter(ExposedFields exposedFields, AggregationOperationConte 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(); @@ -710,7 +735,7 @@ private Object getMappedInput(AggregationOperationContext context) { return input; } - private Object getMappedCondition(AggregationOperationContext context) { + private @Nullable Object getMappedCondition(AggregationOperationContext context) { if (!(condition instanceof AggregationExpression aggregationExpression)) { return condition; @@ -817,6 +842,7 @@ public static InputBuilder newBuilder() { } @Override + @Contract("_ -> this") public AsBuilder filter(List<?> array) { Assert.notNull(array, "Array must not be null"); @@ -825,6 +851,7 @@ public AsBuilder filter(List<?> array) { } @Override + @Contract("_ -> this") public AsBuilder filter(Field field) { Assert.notNull(field, "Field must not be null"); @@ -833,6 +860,7 @@ public AsBuilder filter(Field field) { } @Override + @Contract("_ -> this") public AsBuilder filter(AggregationExpression expression) { Assert.notNull(expression, "Expression must not be null"); @@ -841,6 +869,7 @@ public AsBuilder filter(AggregationExpression expression) { } @Override + @Contract("_ -> this") public ConditionBuilder as(String variableName) { Assert.notNull(variableName, "Variable name must not be null"); @@ -1028,6 +1057,7 @@ public static Slice sliceArrayOf(Collection<?> values) { * @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)); } @@ -1040,6 +1070,7 @@ public Slice itemCount(int count) { * @return new instance of {@link Slice}. * @since 4.5 */ + @Contract("_ -> new") public Slice itemCount(AggregationExpression count) { return new Slice(append(count)); } @@ -1158,6 +1189,7 @@ public static IndexOfArrayBuilder arrayOf(Collection<?> values) { * @param range the lookup range. * @return new instance of {@link IndexOfArray}. */ + @Contract("_ -> new") public IndexOfArray within(Range<Long> range) { return new IndexOfArray(append(AggregationUtils.toRangeValues(range))); } @@ -1233,6 +1265,7 @@ public static RangeOperatorBuilder rangeStartingAt(long value) { return new RangeOperatorBuilder(value); } + @Contract("_ -> new") public RangeOperator withStepSize(long stepSize) { return new RangeOperator(append(stepSize)); } @@ -1365,6 +1398,7 @@ public Document toDocument(AggregationOperationContext context) { return new Document("$reduce", document); } + @SuppressWarnings("NullAway") private Object getMappedValue(Object value, AggregationOperationContext context) { if (value instanceof Document) { @@ -1684,6 +1718,7 @@ public static ZipBuilder arrayOf(Collection<?> values) { * * @return new instance of {@link Zip}. */ + @Contract("-> new") public Zip useLongestLength() { return new Zip(append("useLongestLength", true)); } @@ -1694,6 +1729,7 @@ public Zip useLongestLength() { * @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"); @@ -1706,6 +1742,7 @@ public Zip defaultTo(String fieldReference) { * @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"); @@ -1718,6 +1755,7 @@ public Zip defaultTo(AggregationExpression expression) { * @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"); @@ -2055,6 +2093,7 @@ public static SortArray sortArrayOf(AggregationExpression expression) { * @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 69689908c9..f3ffdb7ad1 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.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 static Not not(AggregationExpression expression) { */ 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 And and(String fieldReference) { return createAnd().andField(fieldReference); } + @SuppressWarnings("NullAway") private And createAnd() { return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression); } @@ -160,6 +163,7 @@ public Or or(String fieldReference) { return createOr().orField(fieldReference); } + @SuppressWarnings("NullAway") private Or createOr() { return usesFieldRef() ? Or.or(Fields.field(fieldReference)) : Or.or(expression); } @@ -169,6 +173,7 @@ private Or createOr() { * * @return new instance of {@link Not}. */ + @SuppressWarnings("NullAway") public Not not() { return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression); } @@ -211,6 +216,7 @@ public static And and(Object... expressions) { * @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 And andExpression(AggregationExpression expression) { * @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 And andField(String fieldReference) { * @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 static Or or(Object... expressions) { * @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 Or orExpression(AggregationExpression expression) { * @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 Or orField(String fieldReference) { * @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 36492e2a81..16eca4ec22 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<BucketAutoOperat implements FieldsExposingAggregationOperation { private final int buckets; - private final String granularity; + private final @Nullable String granularity; /** * Creates a new {@link BucketAutoOperation} given a {@link Field group-by field}. @@ -80,7 +81,7 @@ private BucketAutoOperation(BucketAutoOperation bucketOperation, Outputs outputs this.granularity = bucketOperation.granularity; } - private BucketAutoOperation(BucketAutoOperation bucketOperation, int buckets, String granularity) { + private BucketAutoOperation(BucketAutoOperation bucketOperation, int buckets, @Nullable String granularity) { super(bucketOperation); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java index 525789e628..6ed686c086 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BucketOperation.java @@ -21,7 +21,9 @@ import java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.aggregation.BucketOperation.BucketOperationOutputBuilder; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -40,7 +42,7 @@ public class BucketOperation extends BucketOperationSupport<BucketOperation, Buc implements FieldsExposingAggregationOperation { private final List<Object> 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 @@ private BucketOperation(BucketOperation bucketOperation, Outputs outputs) { this.defaultBucket = bucketOperation.defaultBucket; } - private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, Object defaultBucket) { + private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, @Nullable Object defaultBucket) { super(bucketOperation); @@ -111,6 +113,7 @@ public String getOperator() { * @param literal must not be {@literal null}. * @return new instance of {@link BucketOperation}. */ + @Contract("_ -> new") public BucketOperation withDefaultBucket(Object literal) { Assert.notNull(literal, "Default bucket literal must not be null"); @@ -124,6 +127,7 @@ public BucketOperation withDefaultBucket(Object literal) { * @param boundaries must not be {@literal null}. * @return new instance of {@link BucketOperation}. */ + @Contract("_ -> 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 e19ad59a3f..3d5ded05c2 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.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 @@ public abstract class BucketOperationSupport<T extends BucketOperationSupport<T, B>, B extends OutputBuilder<B, T>> 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 Document toDocument(AggregationOperationContext context) { } @Override + public Document toDocument(AggregationOperationContext context) { Document document = new Document(); - document.put("groupBy", groupByExpression == null ? context.getReference(groupByField).toString() - : groupByExpression.toDocument(context)); + if(groupByExpression != null) { + document.put("groupBy", groupByExpression.toDocument(context)); + } else if (groupByField != null) { + document.put("groupBy", context.getReference(groupByField).toString()); + + } if (!outputs.isEmpty()) { document.put("output", outputs.toDocument(context)); @@ -625,7 +631,9 @@ public SpelExpressionOutput(String expression, Object[] parameters) { @Override public Document toDocument(AggregationOperationContext context) { - return (Document) TRANSFORMER.transform(expression, context, params); + + Object o = TRANSFORMER.transform(expression, context, params); + return o instanceof Document document ? document : new Document(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java index f27b7f16cb..e2626c3a16 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java @@ -18,6 +18,8 @@ import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -50,8 +52,8 @@ public static ComparisonOperatorFactory valueOf(AggregationExpression expression public static class ComparisonOperatorFactory { - private final String fieldReference; - private final AggregationExpression expression; + private final @Nullable String fieldReference; + private final @Nullable AggregationExpression expression; /** * Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}. @@ -107,6 +109,7 @@ public Cmp compareToValue(Object value) { return createCmp().compareToValue(value); } + @SuppressWarnings("NullAway") private Cmp createCmp() { return usesFieldRef() ? Cmp.valueOf(fieldReference) : Cmp.valueOf(expression); } @@ -144,6 +147,7 @@ public Eq equalToValue(Object value) { return createEq().equalToValue(value); } + @SuppressWarnings("NullAway") private Eq createEq() { return usesFieldRef() ? Eq.valueOf(fieldReference) : Eq.valueOf(expression); } @@ -181,6 +185,7 @@ public Gt greaterThanValue(Object value) { return createGt().greaterThanValue(value); } + @SuppressWarnings("NullAway") private Gt createGt() { return usesFieldRef() ? Gt.valueOf(fieldReference) : Gt.valueOf(expression); } @@ -218,6 +223,7 @@ public Gte greaterThanEqualToValue(Object value) { return createGte().greaterThanEqualToValue(value); } + @SuppressWarnings("NullAway") private Gte createGte() { return usesFieldRef() ? Gte.valueOf(fieldReference) : Gte.valueOf(expression); } @@ -255,6 +261,7 @@ public Lt lessThanValue(Object value) { return createLt().lessThanValue(value); } + @SuppressWarnings("NullAway") private Lt createLt() { return usesFieldRef() ? Lt.valueOf(fieldReference) : Lt.valueOf(expression); } @@ -292,6 +299,7 @@ public Lte lessThanEqualToValue(Object value) { return createLte().lessThanEqualToValue(value); } + @SuppressWarnings("NullAway") private Lte createLte() { return usesFieldRef() ? Lte.valueOf(fieldReference) : Lte.valueOf(expression); } @@ -329,6 +337,7 @@ public Ne notEqualToValue(Object value) { return createNe().notEqualToValue(value); } + @SuppressWarnings("NullAway") private Ne createNe() { return usesFieldRef() ? Ne.valueOf(fieldReference) : Ne.valueOf(expression); } @@ -384,6 +393,7 @@ public static Cmp valueOf(AggregationExpression expression) { * @param fieldReference must not be {@literal null}. * @return new instance of {@link Cmp}. */ + @Contract("_ -> new") public Cmp compareTo(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -396,6 +406,7 @@ public Cmp compareTo(String fieldReference) { * @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 Cmp compareTo(AggregationExpression expression) { * @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 static Eq valueOf(AggregationExpression expression) { * @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 Eq equalTo(String fieldReference) { * @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 Eq equalTo(AggregationExpression expression) { * @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 static Gt valueOf(AggregationExpression expression) { * @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 Gt greaterThan(String fieldReference) { * @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 Gt greaterThan(AggregationExpression expression) { * @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 static Lt valueOf(AggregationExpression expression) { * @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 Lt lessThan(String fieldReference) { * @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 Lt lessThan(AggregationExpression expression) { * @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 static Gte valueOf(AggregationExpression expression) { * @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 Gte greaterThanEqualTo(String fieldReference) { * @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 Gte greaterThanEqualTo(AggregationExpression expression) { * @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 static Lte valueOf(AggregationExpression expression) { * @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 Lte lessThanEqualTo(String fieldReference) { * @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 Lte lessThanEqualTo(AggregationExpression expression) { * @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 static Ne valueOf(AggregationExpression expression) { * @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 Ne notEqualTo(String fieldReference) { * @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 Ne notEqualTo(AggregationExpression expression) { * @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 323a11895b..462d94d6f1 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.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 OtherwiseBuilder thenValueOf(String fieldReference) { return createThenBuilder().thenValueOf(fieldReference); } + @SuppressWarnings("NullAway") private ThenBuilder createThenBuilder() { if (usesFieldRef()) { @@ -303,6 +305,7 @@ private Object mapCondition(Object condition, AggregationOperationContext contex } } + @SuppressWarnings("NullAway") private Object resolve(Object value, AggregationOperationContext context) { if (value instanceof Field field) { @@ -389,7 +392,7 @@ public interface ThenBuilder extends OrBuilder { */ static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder { - private @Nullable List<Object> conditions; + private List<Object> conditions; private IfNullOperatorBuilder() { conditions = new ArrayList<>(); @@ -404,6 +407,7 @@ public static IfNullOperatorBuilder newBuilder() { 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 ThenBuilder ifNull(String fieldReference) { } @Override + @Contract("_ -> this") public ThenBuilder ifNull(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression name must not be null or empty"); @@ -420,25 +425,30 @@ public ThenBuilder ifNull(AggregationExpression expression) { } @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 static Switch switchCases(List<CaseOperator> conditions) { * @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 Document toDocument(AggregationOperationContext context) { 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 static ConditionalExpressionBuilder newBuilder() { } @Override + @Contract("_ -> this") public ConditionalExpressionBuilder when(Document booleanExpression) { Assert.notNull(booleanExpression, "'Boolean expression' must not be null"); @@ -895,6 +908,7 @@ public ConditionalExpressionBuilder when(Document booleanExpression) { } @Override + @Contract("_ -> this") public ThenBuilder when(CriteriaDefinition criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -903,6 +917,7 @@ public ThenBuilder when(CriteriaDefinition criteria) { } @Override + @Contract("_ -> this") public ThenBuilder when(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression field must not be null"); @@ -911,6 +926,7 @@ public ThenBuilder when(AggregationExpression expression) { } @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 ThenBuilder when(String booleanField) { } @Override + @Contract("_ -> this") public OtherwiseBuilder then(Object thenValue) { Assert.notNull(thenValue, "Then-value must not be null"); @@ -927,6 +944,7 @@ public OtherwiseBuilder then(Object thenValue) { } @Override + @Contract("_ -> this") public OtherwiseBuilder thenValueOf(String fieldReference) { Assert.notNull(fieldReference, "FieldReference must not be null"); @@ -935,6 +953,7 @@ public OtherwiseBuilder thenValueOf(String fieldReference) { } @Override + @Contract("_ -> this") public OtherwiseBuilder thenValueOf(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression must not be null"); @@ -943,23 +962,32 @@ public OtherwiseBuilder thenValueOf(AggregationExpression expression) { } @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 aa085b2a29..35a6ad061c 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 @@ 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 DegreesToRadians convertDegreesToRadians() { 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 ff6ed7e983..7bf8a231ff 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.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 TsSecond tsSecond() { return TsSecond.tsSecond(dateReference()); } + @SuppressWarnings("NullAway") private Object dateReference() { if (usesFieldRef()) { @@ -1076,6 +1078,7 @@ public static DayOfYear dayOfYear(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfYear withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1148,6 +1151,7 @@ public static DayOfMonth dayOfMonth(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfMonth withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1220,6 +1224,7 @@ public static DayOfWeek dayOfWeek(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public DayOfWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1292,6 +1297,7 @@ public static Year yearOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Year withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1364,6 +1370,7 @@ public static Month monthOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Month withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1436,6 +1443,7 @@ public static Week weekOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Week withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1508,6 +1516,7 @@ public static Hour hourOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Hour withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1580,6 +1589,7 @@ public static Minute minuteOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Minute withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1652,6 +1662,7 @@ public static Second secondOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Second withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1724,6 +1735,7 @@ public static Millisecond millisecondOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public Millisecond withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1810,6 +1822,7 @@ public static FormatBuilder dateOf(final AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public DateToString withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -1824,6 +1837,7 @@ public DateToString withTimezone(Timezone timezone) { * @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 DateToString onNullReturn(Object value) { * @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 DateToString onNullReturnValueOf(String fieldReference) { * @return new instance of {@link DateToString}. * @since 2.1 */ + @Contract("_ -> new") public DateToString onNullReturnValueOf(AggregationExpression expression) { return onNullReturn(expression); } @@ -1973,6 +1989,7 @@ public static IsoDayOfWeek isoDayOfWeek(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoDayOfWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2045,6 +2062,7 @@ public static IsoWeek isoWeekOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoWeek withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2117,6 +2135,7 @@ public static IsoWeekYear isoWeekYearOf(AggregationExpression expression) { * @since 2.1 */ @Override + @Contract("_ -> new") public IsoWeekYear withTimezone(Timezone timezone) { Assert.notNull(timezone, "Timezone must not be null"); @@ -2301,6 +2320,7 @@ public static DateFromPartsWithYear dateFromParts() { * @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 DateFromParts month(Object month) { * @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 DateFromParts monthOf(String fieldReference) { * @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 DateFromParts monthOf(AggregationExpression expression) { * @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 DateFromParts day(Object day) { * @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 DateFromParts dayOf(String fieldReference) { * @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 DateFromParts millisecond(Object millisecond) { * @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 static IsoDateFromPartsWithYear dateFromParts() { * @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 IsoDateFromParts isoWeek(Object isoWeek) { * @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 IsoDateFromParts isoWeekOf(String fieldReference) { * @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 IsoDateFromParts isoWeekOf(AggregationExpression expression) { * @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 IsoDateFromParts isoDayOfWeek(Object day) { * @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 IsoDateFromParts isoDayOfWeekOf(String fieldReference) { * @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 IsoDateFromParts millisecond(Object millisecond) { * @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 static DateToParts datePartsOf(AggregationExpression expression) { * * @return new instance of {@link DateToParts}. */ + @Contract("_ -> new") public DateToParts iso8601() { return new DateToParts(append("iso8601", true)); } @@ -2689,6 +2731,7 @@ public DateToParts iso8601() { * @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 static DateFromString fromString(Object value) { * @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 static DateFromString fromStringOf(String fieldReference) { * @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 static DateFromString fromStringOf(AggregationExpression expression) { * @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 DateFromString withTimezone(Timezone timezone) { * @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 static DateAdd addValue(Object value, String unit) { * @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 DateAdd toDateOf(AggregationExpression expression) { * @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 DateAdd toDateOf(String fieldReference) { * @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 DateAdd toDate(Object dateExpression) { * @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 static DateSubtract subtractValue(Object value, String unit) { * @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 DateSubtract fromDateOf(AggregationExpression expression) { * @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 DateSubtract fromDateOf(String fieldReference) { * @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 DateSubtract fromDate(Object dateExpression) { * @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 static DateDiff diffValue(Object value, String unit) { * @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 DateDiff toDateOf(AggregationExpression expression) { * @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 DateDiff toDateOf(String fieldReference) { * @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 DateDiff toDate(Object dateExpression) { * @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 DateDiff withTimezone(Timezone timezone) { * @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 static DateTrunc truncateValue(Object value) { * @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 DateTrunc to(String unit) { * @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 DateTrunc to(AggregationExpression unit) { * @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 DateTrunc startOfWeek(java.time.DayOfWeek day) { * @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 DateTrunc startOfWeek(String day) { * @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 DateTrunc binSize(int binSize) { * @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 DateTrunc binSize(AggregationExpression expression) { * @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 DateTrunc binSize(Object binSize) { * @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 0da9343ddf..1a559fd26e 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.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 static DensifyOperationBuilder builder() { @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 @@ default Document toDocument() { 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 Document toDocument(AggregationOperationContext ctx) { * @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 DensifyRange incrementBy(Number step) { * @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 DensifyRange incrementBy(Number step, DensifyUnit unit) { * @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 7f260c3785..431215e852 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.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 @@ private static Document toSetEntry(Entry<Object, Object> entry, AggregationOpera 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 @@ static class ExpressionProjection { 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 ff63ad834d..a0cd1d056a 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 @@ 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 static Shift shift(AggregationExpression expression) { * @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 Shift by(int shiftBy) { * @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 Shift defaultTo(Object value) { * @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 56f20dde17..dfdc2d620c 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 static EvaluationOperatorFactory valueOf(AggregationExpression expression 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 EvaluationOperatorFactory(AggregationExpression expression) { * * @return new instance of {@link Expr}. */ + @SuppressWarnings("NullAway") public Expr expr() { return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression); } @@ -91,6 +93,7 @@ public Expr expr() { * * @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 458bc43437..703d5d5f06 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.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 ExposedFields and(ExposedField field) { * @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 131fa8a845..1639a54d48 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 @@ 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 @@ private FieldReference getReference(@Nullable Field field, String name) { * @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 f5c73dd09c..d1ca95f659 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 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 83fc7c2b87..7f574a850e 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.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 @@ private Fields(Fields existing, Field tail) { * @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 int size() { 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 @@ static class AggregationField implements Field { 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 f4a5fb4498..bcfc64f2b4 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.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 @@ private GeoNearOperation(NearQuery nearQuery, String distanceField, @Nullable St * @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 72a917c599..ad1f8ae643 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.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 @@ * We recommend to use the static factory method {@link Aggregation#graphLookup(String)} instead of creating instances * of this class directly. * - * @see <a href="https://docs.mongodb.org/manual/reference/aggregation/graphLookup/">https://docs.mongodb.org/manual/reference/aggregation/graphLookup/</a> + * @see <a href= + * "https://docs.mongodb.org/manual/reference/aggregation/graphLookup/">https://docs.mongodb.org/manual/reference/aggregation/graphLookup/</a> * @author Mark Paluch * @author Christoph Strobl * @since 1.10 */ public class GraphLookupOperation implements InheritsFieldsAggregationOperation { - private static final Set<Class<?>> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class, Field.class, Document.class); + private static final Set<Class<?>> ALLOWED_START_TYPES = Set.of(AggregationExpression.class, String.class, + Field.class, Document.class); private final String from; private final List<Object> startWith; @@ -126,7 +129,7 @@ public ExposedFields getFields() { List<ExposedField> 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 @@ static final class GraphLookupOperationFromBuilder implements FromBuilder, StartWithBuilder, ConnectFromBuilder, ConnectToBuilder { private @Nullable String from; - private @Nullable List<? extends Object> 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 StartWithBuilder from(String collectionName) { } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(String... fieldReferences) { Assert.notNull(fieldReferences, "FieldReferences must not be null"); @@ -246,6 +251,7 @@ public ConnectFromBuilder startWith(String... fieldReferences) { } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(AggregationExpression... expressions) { Assert.notNull(expressions, "AggregationExpressions must not be null"); @@ -256,6 +262,7 @@ public ConnectFromBuilder startWith(AggregationExpression... expressions) { } @Override + @Contract("_ -> this") public ConnectFromBuilder startWith(Object... expressions) { Assert.notNull(expressions, "Expressions must not be null"); @@ -297,6 +304,7 @@ private void assertStartWithType(Object expression) { } @Override + @Contract("_ -> this") public ConnectToBuilder connectFrom(String fieldName) { Assert.hasText(fieldName, "ConnectFrom must not be null or empty"); @@ -306,10 +314,14 @@ public ConnectToBuilder connectFrom(String fieldName) { } @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 static final class GraphLookupOperationBuilder { private @Nullable Field depthField; private @Nullable CriteriaDefinition restrictSearchWithMatch; - private GraphLookupOperationBuilder(String from, List<? extends Object> 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 @@ private GraphLookupOperationBuilder(String from, List<? extends Object> startWit * @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 GraphLookupOperationBuilder maxDepth(long numberOfRecursions) { * @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 GraphLookupOperationBuilder depthField(String fieldName) { * @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 GraphLookupOperationBuilder restrict(CriteriaDefinition criteriaDefinitio * @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 10d58a7682..b6d36f1baf 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.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 Operation withAlias(String key) { } public ExposedField asField() { + Assert.notNull(key, "Key must be set first"); return new ExposedField(key, true); } @@ -506,10 +507,12 @@ public Document toDocument(AggregationOperationContext context) { 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 ca6a2e2754..739b7c52a9 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 282ffbd9e0..a0e0cc03cd 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.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 static FromBuilder newBuilder() { } @Override + @Contract("_ -> this") public LocalFieldBuilder from(String name) { Assert.hasText(name, "'From' must not be null or empty"); @@ -289,6 +291,7 @@ public LocalFieldBuilder from(String name) { } @Override + @Contract("_ -> this") public AsBuilder foreignField(String name) { Assert.hasText(name, "'ForeignField' must not be null or empty"); @@ -297,6 +300,7 @@ public AsBuilder foreignField(String name) { } @Override + @Contract("_ -> this") public ForeignFieldBuilder localField(String name) { Assert.hasText(name, "'LocalField' must not be null or empty"); @@ -305,6 +309,7 @@ public ForeignFieldBuilder localField(String name) { } @Override + @Contract("_ -> this") public PipelineBuilder let(Let let) { Assert.notNull(let, "Let must not be null"); @@ -313,6 +318,7 @@ public PipelineBuilder let(Let let) { } @Override + @Contract("_ -> this") public AsBuilder pipeline(AggregationPipeline pipeline) { Assert.notNull(pipeline, "Pipeline must not be null"); @@ -321,9 +327,11 @@ public AsBuilder pipeline(AggregationPipeline pipeline) { } @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 da1dbfc027..5f736b55a0 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 @@ 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 @@ */ 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 MatchOperation(AggregationExpression expression) { } @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 314f83fc7c..bda9a3330d 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.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 Document toDocument(AggregationOperationContext context) { */ 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 MergeOperationBuilder() {} * @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 MergeOperationBuilder intoCollection(String collection) { * @param database must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder inDatabase(String database) { this.database = database; @@ -456,6 +459,7 @@ public MergeOperationBuilder inDatabase(String database) { * @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 MergeOperationBuilder into(MergeOperationTarget into) { * @param target must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder target(MergeOperationTarget target) { return into(target); } @@ -482,6 +487,7 @@ public MergeOperationBuilder target(MergeOperationTarget target) { * @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 MergeOperationBuilder on(String... fields) { * @param id must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder id(UniqueMergeId id) { this.id = id; @@ -506,6 +513,7 @@ public MergeOperationBuilder id(UniqueMergeId id) { * @param let the variable expressions * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder let(Let let) { this.let = let; @@ -519,6 +527,7 @@ public MergeOperationBuilder let(Let let) { * @param let the variable expressions * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder exposeVariablesOf(Let let) { return let(let); } @@ -529,6 +538,7 @@ public MergeOperationBuilder exposeVariablesOf(Let let) { * @param whenMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenMatched(WhenDocumentsMatch whenMatched) { this.whenMatched = whenMatched; @@ -541,6 +551,7 @@ public MergeOperationBuilder whenMatched(WhenDocumentsMatch whenMatched) { * @param whenMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenDocumentsMatch(WhenDocumentsMatch whenMatched) { return whenMatched(whenMatched); } @@ -551,6 +562,7 @@ public MergeOperationBuilder whenDocumentsMatch(WhenDocumentsMatch whenMatched) * @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 MergeOperationBuilder whenDocumentsMatchApply(Aggregation aggregation) { * @param whenNotMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenNotMatched(WhenDocumentsDontMatch whenNotMatched) { this.whenNotMatched = whenNotMatched; @@ -573,6 +586,7 @@ public MergeOperationBuilder whenNotMatched(WhenDocumentsDontMatch whenNotMatche * @param whenNotMatched must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public MergeOperationBuilder whenDocumentsDontMatch(WhenDocumentsDontMatch whenNotMatched) { return whenNotMatched(whenNotMatched); } @@ -580,7 +594,10 @@ public MergeOperationBuilder whenDocumentsDontMatch(WhenDocumentsDontMatch whenN /** * @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 c553a7be02..a5124320f6 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 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 @@ public Document getMappedObject(Document document) { } @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 25189241b7..4e21ab7bde 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.Collections; import org.bson.Document; +import org.springframework.lang.Contract; import org.springframework.util.Assert; /** @@ -277,7 +278,7 @@ public Document toDocument(Object value, AggregationOperationContext context) { 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 static GetField getField(Field field) { * @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 GetField of(String fieldRef) { * @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 static SetField field(Field field) { * @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 SetField input(String fieldRef) { * @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 SetField input(AggregationExpression expression) { * @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 @@ private SetField input(Object fieldRef) { * @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 SetField toValueOf(String fieldReference) { * @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 SetField toValueOf(AggregationExpression expression) { * @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 51520f0868..7dbed3a855 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 @@ private OutOperation(@Nullable String databaseName, String collectionName, @Null * @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 OutOperation in(@Nullable String database) { * @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 OutOperation uniqueKey(@Nullable String key) { * @return new instance of {@link OutOperation}. * @since 2.2 */ + @Contract("_ -> new") public OutOperation uniqueKeyOf(Iterable<String> fields) { Assert.notNull(fields, "Fields must not be null"); @@ -144,6 +148,7 @@ public OutOperation uniqueKeyOf(Iterable<String> fields) { * @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 OutOperation mode(OutMode mode) { * @see OutMode#REPLACE_COLLECTION * @since 2.2 */ + @Contract("-> new") public OutOperation replaceCollection() { return mode(OutMode.REPLACE_COLLECTION); } @@ -170,6 +176,7 @@ public OutOperation replaceCollection() { * @see OutMode#REPLACE * @since 2.2 */ + @Contract("-> new") public OutOperation replaceDocuments() { return mode(OutMode.REPLACE); } @@ -182,6 +189,7 @@ public OutOperation replaceDocuments() { * @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 9524171fed..54ed40b035 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 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 35db2214f5..af7cf5bfb2 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.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 ProjectionOperationBuilder and(AggregationExpression expression) { * @param fieldNames must not be {@literal null}. * @return */ + @Contract("_ -> new") public ProjectionOperation andExclude(String... fieldNames) { List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fieldNames), false); @@ -161,6 +163,7 @@ public ProjectionOperation andExclude(String... fieldNames) { * @param fieldNames must not be {@literal null}. * @return */ + @Contract("_ -> new") public ProjectionOperation andInclude(String... fieldNames) { List<FieldProjection> projections = FieldProjection.from(Fields.fields(fieldNames), true); @@ -173,6 +176,7 @@ public ProjectionOperation andInclude(String... fieldNames) { * @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 ProjectionOperation andInclude(Fields fields) { * @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 Document toDocument(AggregationOperationContext context) { 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 ArrayProjectionOperationBuilder(ProjectionOperation target) { * @param expression * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(AggregationExpression expression) { Assert.notNull(expression, "AggregationExpression must not be null"); @@ -1794,6 +1800,7 @@ public ArrayProjectionOperationBuilder and(AggregationExpression expression) { * @param field * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(Field field) { Assert.notNull(field, "Field must not be null"); @@ -1808,6 +1815,7 @@ public ArrayProjectionOperationBuilder and(Field field) { * @param value * @return */ + @Contract("_ -> this") public ArrayProjectionOperationBuilder and(Object value) { this.projections.add(value); @@ -1820,6 +1828,7 @@ public ArrayProjectionOperationBuilder and(Object value) { * @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 a370016356..5f16fcfc16 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 @@ * </pre> * * @author Christoph Strobl - * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/redact/">https://docs.mongodb.com/manual/reference/operator/aggregation/redact/</a> + * @see <a href= + * "https://docs.mongodb.com/manual/reference/operator/aggregation/redact/">https://docs.mongodb.com/manual/reference/operator/aggregation/redact/</a> * @since 3.0 */ public class RedactOperation implements AggregationOperation { @@ -94,9 +97,9 @@ public static RedactOperationBuilder builder() { */ 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 @@ private RedactOperationBuilder() { * @param criteria must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(CriteriaDefinition criteria) { this.when = criteria; @@ -120,6 +124,7 @@ public RedactOperationBuilder when(CriteriaDefinition criteria) { * @param condition must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(AggregationExpression condition) { this.when = condition; @@ -132,6 +137,7 @@ public RedactOperationBuilder when(AggregationExpression condition) { * @param condition must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder when(Document condition) { this.when = condition; @@ -143,6 +149,7 @@ public RedactOperationBuilder when(Document condition) { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenDescend() { return then(DESCEND); } @@ -152,6 +159,7 @@ public RedactOperationBuilder thenDescend() { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenKeep() { return then(KEEP); } @@ -161,6 +169,7 @@ public RedactOperationBuilder thenKeep() { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder thenPrune() { return then(PRUNE); } @@ -172,6 +181,7 @@ public RedactOperationBuilder thenPrune() { * @param then must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public RedactOperationBuilder then(Object then) { this.then = then; @@ -183,6 +193,7 @@ public RedactOperationBuilder then(Object then) { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwiseDescend() { return otherwise(DESCEND); } @@ -192,6 +203,7 @@ public RedactOperationBuilder otherwiseDescend() { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwiseKeep() { return otherwise(KEEP); } @@ -201,6 +213,7 @@ public RedactOperationBuilder otherwiseKeep() { * * @return this. */ + @Contract("-> this") public RedactOperationBuilder otherwisePrune() { return otherwise(PRUNE); } @@ -212,6 +225,7 @@ public RedactOperationBuilder otherwisePrune() { * @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 RedactOperationBuilder otherwise(Object otherwise) { /** * @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 130182a001..ec306eb6c5 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 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 DocumentContributor(Object value) { } @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 9eab041e88..ed615d9863 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.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. - * <br /> + * Gateway to {@literal $function} and {@literal $accumulator} aggregation operations. <br /> * Using {@link ScriptOperators} as part of the {@link Aggregation} requires MongoDB server to have * <a href="https://docs.mongodb.com/master/core/server-side-javascript/">server-side JavaScript</a> execution * <a href="https://docs.mongodb.com/master/reference/configuration-options/#security.javascriptEnabled">enabled</a>. @@ -53,8 +53,8 @@ public static Function function(String body) { } /** - * Create a custom <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator operator</a> - * in Javascript. + * Create a custom <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator + * operator</a> in Javascript. * * @return new instance of {@link AccumulatorInitBuilder}. */ @@ -74,8 +74,7 @@ public static AccumulatorInitBuilder accumulatorBuilder() { * lang: "js" * } * } - * </code> - * <br /> + * </code> <br /> * {@link Function} cannot be used as part of {@link org.springframework.data.mongodb.core.schema.MongoJsonSchema * schema} validation query expression. <br /> * <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a> @@ -150,10 +149,12 @@ List<Object> getArgs() { 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 String toString() { * {@link Accumulator} defines a custom aggregation * <a href="https://docs.mongodb.com/master/reference/operator/aggregation/accumulator/">$accumulator operator</a>, * one that maintains its state (e.g. totals, maximums, minimums, and related data) as documents progress through the - * pipeline, in JavaScript. - * <br /> + * pipeline, in JavaScript. <br /> * <code class="java"> * { * $accumulator: { @@ -192,8 +192,7 @@ public String toString() { * lang: "js" * } * } - * </code> - * <br /> + * </code> <br /> * {@link Accumulator} can be used as part of {@link GroupOperation $group}, {@link BucketOperation $bucket} and * {@link BucketAutoOperation $bucketAuto} pipeline stages. <br /> * <b>NOTE:</b> <a href="https://docs.mongodb.com/master/core/server-side-javascript/">Server-Side JavaScript</a> @@ -240,8 +239,7 @@ public interface AccumulatorInitBuilder { /** * 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. - * <br /> + * receives its arguments from the {@link Function#args(Object...) initArgs} array expression. <br /> * <code class="java"> * function(initArg1, initArg2, ...) { * ... @@ -253,13 +251,16 @@ public interface AccumulatorInitBuilder { * @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<Object> 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. - * <br /> + * its arguments from the {@link AccumulatorInitArgsBuilder#initArgs(Object...)} array expression. <br /> * <code class="java"> * function(initArg1, initArg2, ...) { * ... @@ -307,8 +308,7 @@ public interface AccumulatorAccumulateBuilder { /** * 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}. - * <br /> + * accumulateArgs}. <br /> * <code class="java"> * function(state, accumArg1, accumArg2, ...) { * ... @@ -320,14 +320,17 @@ public interface AccumulatorAccumulateBuilder { * @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<Object> 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...)}. - * <br /> + * {@link AccumulatorAccumulateArgsBuilder#accumulateArgs(Object...)}. <br /> * <code class="java"> * function(state, accumArg1, accumArg2, ...) { * ... @@ -369,8 +372,7 @@ public interface AccumulatorMergeBuilder { /** * Set the {@code merge} function used to merge two internal states. <br /> * This might be required because the operation is run on a sharded cluster or when the operator exceeds its - * memory limit. - * <br /> + * memory limit. <br /> * <code class="java"> * function(state1, state2) { * ... @@ -388,8 +390,7 @@ public interface AccumulatorFinalizeBuilder { /** * Set the {@code finalize} function used to update the result of the accumulation when all documents have been - * processed. - * <br /> + * processed. <br /> * <code class="java"> * function(state) { * ... @@ -414,18 +415,17 @@ static class AccumulatorBuilder implements AccumulatorInitBuilder, AccumulatorInitArgsBuilder, AccumulatorAccumulateBuilder, AccumulatorAccumulateArgsBuilder, AccumulatorMergeBuilder, AccumulatorFinalizeBuilder { - private List<Object> initArgs; - private String initFunction; - private List<Object> accumulateArgs; - private String accumulateFunction; - private String mergeFunction; - private String finalizeFunction; + private @Nullable List<Object> initArgs; + private @Nullable String initFunction; + private @Nullable List<Object> 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. - * <br /> + * its arguments from the {@link #initArgs(Object...)} array expression. <br /> * <code class="java"> * function(initArg1, initArg2, ...) { * ... @@ -437,6 +437,7 @@ static class AccumulatorBuilder * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder init(String function) { this.initFunction = function; @@ -450,6 +451,7 @@ public AccumulatorBuilder init(String function) { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder initArgs(List<Object> args) { Assert.notNull(args, "Args must not be null"); @@ -460,8 +462,7 @@ public AccumulatorBuilder initArgs(List<Object> args) { /** * 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...)}. - * <br /> + * the current {@code state}, additional arguments can be defined via {@link #accumulateArgs(Object...)}. <br /> * <code class="java"> * function(state, accumArg1, accumArg2, ...) { * ... @@ -473,6 +474,7 @@ public AccumulatorBuilder initArgs(List<Object> args) { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder accumulate(String function) { Assert.notNull(function, "Accumulate function must not be null"); @@ -488,6 +490,7 @@ public AccumulatorBuilder accumulate(String function) { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder accumulateArgs(List<Object> args) { Assert.notNull(args, "Args must not be null"); @@ -499,8 +502,7 @@ public AccumulatorBuilder accumulateArgs(List<Object> args) { /** * Set the {@code merge} function used to merge two internal states. <br /> * This might be required because the operation is run on a sharded cluster or when the operator exceeds its - * memory limit. - * <br /> + * memory limit. <br /> * <code class="java"> * function(state1, state2) { * ... @@ -512,6 +514,7 @@ public AccumulatorBuilder accumulateArgs(List<Object> args) { * @return this. */ @Override + @Contract("_ -> this") public AccumulatorBuilder merge(String function) { Assert.notNull(function, "Merge function must not be null"); @@ -526,6 +529,7 @@ public AccumulatorBuilder merge(String function) { * @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 AccumulatorBuilder lang(String lang) { /** * Set the {@code finalize} function used to update the result of the accumulation when all documents have been - * processed. - * <br /> + * processed. <br /> * <code class="java"> * function(state) { * ... @@ -549,6 +552,7 @@ public AccumulatorBuilder lang(String lang) { * @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 Accumulator finalize(String function) { } @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 9da80c4668..4db0181d39 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.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 static Bottom bottom(int numberOfResults) { * @param numberOfResults * @return new instance of {@link Bottom}. */ + @Contract("_ -> new") public Bottom limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -80,10 +82,12 @@ public Bottom limit(int numberOfResults) { * @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 @@ private Bottom limit(Object value) { * @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 Bottom sortBy(Sort sort) { * @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 Bottom output(Fields out) { * @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 Bottom output(String... fieldNames) { * @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 static Top top(int numberOfResults) { * @param numberOfResults * @return new instance of {@link Top}. */ + @Contract("_ -> new") public Top limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -183,6 +192,7 @@ public Top limit(int numberOfResults) { * @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 @@ private Top limit(Object value) { * @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 Top sortBy(Sort sort) { * @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 Top output(Fields out) { * @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 Top output(String... fieldNames) { * @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 static First first(int numberOfResults) { * @param numberOfResults * @return new instance of {@link First}. */ + @Contract("_ -> new") public First limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -274,6 +289,7 @@ public First limit(int numberOfResults) { * @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 @@ private First limit(Object value) { * @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 First of(String fieldName) { * @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 First of(AggregationExpression expression) { * @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 First input(String fieldName) { * @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 static Last last(int numberOfResults) { * @param numberOfResults * @return new instance of {@link Last}. */ + @Contract("_ -> new") public Last limit(int numberOfResults) { return limit((Object) numberOfResults); } @@ -368,6 +389,7 @@ public Last limit(int numberOfResults) { * @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 @@ private Last limit(Object value) { * @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 Last of(String fieldName) { * @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 Last of(AggregationExpression expression) { * @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 Last input(String fieldName) { * @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 7f5c1c7722..6ef4f1323f 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.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 static ValueAppender set(String field) { * @param value the value to assign. * @return new instance of {@link SetOperation}. */ + @Contract("_, _ -> new") public SetOperation set(Object field, Object value) { LinkedHashMap<Object, Object> target = new LinkedHashMap<>(getValueMap()); @@ -131,7 +133,7 @@ public ValueAppender set(String field) { 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 094ef7365b..a99c0926f4 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.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 static SetOperatorFactory arrayAsSet(AggregationExpression expression) { */ 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 SetEquals isEqualTo(AggregationExpression... expressions) { return createSetEquals().isEqualTo(expressions); } + @SuppressWarnings("NullAway") private SetEquals createSetEquals() { return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression); } @@ -130,6 +133,7 @@ public SetIntersection intersects(AggregationExpression... expressions) { return createSetIntersection().intersects(expressions); } + @SuppressWarnings("NullAway") private SetIntersection createSetIntersection() { return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression); } @@ -156,6 +160,7 @@ public SetUnion union(AggregationExpression... expressions) { return createSetUnion().union(expressions); } + @SuppressWarnings("NullAway") private SetUnion createSetUnion() { return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression); } @@ -182,6 +187,7 @@ public SetDifference differenceTo(AggregationExpression expression) { return createSetDifference().differenceTo(expression); } + @SuppressWarnings("NullAway") private SetDifference createSetDifference() { return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression); } @@ -208,6 +214,7 @@ public SetIsSubset isSubsetOf(AggregationExpression expression) { return createSetIsSubset().isSubsetOf(expression); } + @SuppressWarnings("NullAway") private SetIsSubset createSetIsSubset() { return usesFieldRef() ? SetIsSubset.arrayAsSet(fieldReference) : SetIsSubset.arrayAsSet(expression); } @@ -218,6 +225,7 @@ private SetIsSubset createSetIsSubset() { * * @return new instance of {@link AnyElementTrue}. */ + @SuppressWarnings("NullAway") public AnyElementTrue anyElementTrue() { return usesFieldRef() ? AnyElementTrue.arrayAsSet(fieldReference) : AnyElementTrue.arrayAsSet(expression); } @@ -228,6 +236,7 @@ public AnyElementTrue anyElementTrue() { * * @return new instance of {@link AllElementsTrue}. */ + @SuppressWarnings("NullAway") public AllElementsTrue allElementsTrue() { return usesFieldRef() ? AllElementsTrue.arrayAsSet(fieldReference) : AllElementsTrue.arrayAsSet(expression); } @@ -283,6 +292,7 @@ public static SetEquals arrayAsSet(AggregationExpression expression) { * @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 SetEquals isEqualTo(String... arrayReferences) { * @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 SetEquals isEqualTo(AggregationExpression... expressions) { * @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 static SetIntersection arrayAsSet(AggregationExpression expression) { * @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 SetIntersection intersects(String... arrayReferences) { * @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 static SetUnion arrayAsSet(AggregationExpression expression) { * @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 SetUnion union(String... arrayReferences) { * @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 static SetDifference arrayAsSet(AggregationExpression expression) { * @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 SetDifference differenceTo(String arrayReference) { * @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 static SetIsSubset arrayAsSet(AggregationExpression expression) { * @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 SetIsSubset isSubsetOf(String arrayReference) { * @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 static AnyElementTrue arrayAsSet(AggregationExpression expression) { return new AnyElementTrue(Collections.singletonList(expression)); } + @Contract("-> this") public AnyElementTrue anyElementTrue() { return this; } @@ -659,6 +680,7 @@ public static AllElementsTrue arrayAsSet(AggregationExpression expression) { 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 2b8df539e1..e1fec17811 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.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 @@ * * @author Christoph Strobl * @since 3.3 - * @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/">https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/</a> + * @see <a href= + * "https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/">https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/</a> */ public class SetWindowFieldsOperation implements AggregationOperation, FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation { @@ -137,6 +139,7 @@ public WindowOutput(ComputedField outputField) { * @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 WindowOutput append(ComputedField field) { * @return new instance of {@link ComputedFieldAppender}. * @see #append(ComputedField) */ + @Contract("_ -> new") public ComputedFieldAppender append(AggregationExpression expression) { return new ComputedFieldAppender() { @@ -249,8 +253,7 @@ public AggregationExpression getWindowOperator() { return windowOperator; } - @Nullable - public Window getWindow() { + public @Nullable Window getWindow() { return window; } } @@ -360,6 +363,7 @@ public static class RangeWindowBuilder { * @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 RangeWindowBuilder from(String lower) { * @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 RangeWindowBuilder to(String upper) { * @param lower * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder from(Number lower) { this.lower = lower; @@ -400,6 +406,7 @@ public RangeWindowBuilder from(Number lower) { * @param upper * @return this. */ + @Contract("_ -> this") public RangeWindowBuilder to(Number upper) { this.upper = upper; @@ -411,6 +418,7 @@ public RangeWindowBuilder to(Number upper) { * * @return this. */ + @Contract("-> this") public RangeWindowBuilder fromCurrent() { return from(CURRENT); } @@ -420,6 +428,7 @@ public RangeWindowBuilder fromCurrent() { * * @return this. */ + @Contract("-> this") public RangeWindowBuilder fromUnbounded() { return from(UNBOUNDED); } @@ -429,6 +438,7 @@ public RangeWindowBuilder fromUnbounded() { * * @return this. */ + @Contract("-> this") public RangeWindowBuilder toCurrent() { return to(CURRENT); } @@ -438,6 +448,7 @@ public RangeWindowBuilder toCurrent() { * * @return this. */ + @Contract("-> this") public RangeWindowBuilder toUnbounded() { return to(UNBOUNDED); } @@ -448,6 +459,7 @@ public RangeWindowBuilder toUnbounded() { * @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 RangeWindowBuilder unit(WindowUnit windowUnit) { * * @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 static class DocumentWindowBuilder { * @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 DocumentWindowBuilder to(String upper) { * @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 DocumentWindowBuilder from(String lower) { * @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 enum WindowUnits implements WindowUnit { */ 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 static class SetWindowFieldsOperationBuilder { * @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 SetWindowFieldsOperationBuilder partitionByField(String fieldName) { * @param expression must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public SetWindowFieldsOperationBuilder partitionByExpression(AggregationExpression expression) { return partitionBy(expression); } @@ -721,6 +745,7 @@ public SetWindowFieldsOperationBuilder partitionByExpression(AggregationExpressi * @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 SetWindowFieldsOperationBuilder sortBy(String... fields) { * @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 SetWindowFieldsOperationBuilder sortBy(Sort sort) { * @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 SetWindowFieldsOperationBuilder sortBy(SortOperation sort) { * @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 SetWindowFieldsOperationBuilder output(WindowOutput output) { * @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 interface WindowChoice extends As { * @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 SetWindowFieldsOperationBuilder partitionBy(Object value) { * * @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 ffc0aa0654..e6a9a23d31 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 SortByCountOperation(AggregationExpression groupByExpression) { } @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 3119e2729c..ade4f5328e 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.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.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 @@ public Object transform(String expression, AggregationOperationContext context, return transform(new AggregationExpressionTransformationContext<>(node, null, null, context)); } - public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) { + public @Nullable Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) { return lookupConversionFor(context.getCurrentNode()).convert(context); } @@ -137,7 +137,7 @@ private static abstract class ExpressionNodeConversion<T extends ExpressionNode> * * @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 @@ protected boolean supports(ExpressionNode node) { * @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 @@ protected Object transform(ExpressionNode node, AggregationExpressionTransformat * @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 @@ protected Object transform(ExpressionNode node, @Nullable ExpressionNode parent, } @Override - public Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) { + public @Nullable Object transform(AggregationExpressionTransformationContext<ExpressionNode> context) { return transformer.transform(context); } @@ -204,7 +204,7 @@ public Object transform(AggregationExpressionTransformationContext<ExpressionNod * @param context * @return */ - protected abstract Object convert(AggregationExpressionTransformationContext<T> context); + protected abstract @Nullable Object convert(AggregationExpressionTransformationContext<T> context); } /** @@ -247,6 +247,7 @@ protected Object convert(AggregationExpressionTransformationContext<OperatorNode return operationObject; } + @SuppressWarnings("NullAway") private Document createOperationObjectAndAddToPreviousArgumentsIfNecessary( AggregationExpressionTransformationContext<OperatorNode> context, OperatorNode currentNode) { @@ -301,7 +302,7 @@ private static class IndexerNodeConversion extends ExpressionNodeConversion<Expr } @Override - protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { return context.addToPreviousOrReturn(context.getCurrentNode().getValue()); } @@ -322,9 +323,8 @@ private static class InlineListNodeConversion extends ExpressionNodeConversion<E super(transformer); } - @Nullable @Override - protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { ExpressionNode currentNode = context.getCurrentNode(); @@ -355,7 +355,7 @@ private static class PropertyOrFieldReferenceNodeConversion extends ExpressionNo } @Override - protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { String fieldReference = context.getFieldReference().toString(); return context.addToPreviousOrReturn(fieldReference); @@ -381,14 +381,14 @@ private static class LiteralNodeConversion extends ExpressionNodeConversion<Lite @Override @SuppressWarnings("unchecked") - protected Object convert(AggregationExpressionTransformationContext<LiteralNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<LiteralNode> 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<Number>) value.getClass()); // retain type, e.g. int to -int @@ -419,7 +419,7 @@ private static class MethodReferenceNodeConversion extends ExpressionNodeConvers } @Override - protected Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<MethodReferenceNode> context) { MethodReferenceNode node = context.getCurrentNode(); AggregationMethodReference methodReference = node.getMethodReference(); @@ -469,7 +469,7 @@ private static class CompoundExpressionNodeConversion extends ExpressionNodeConv } @Override - protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { ExpressionNode currentNode = context.getCurrentNode(); @@ -503,7 +503,7 @@ static class NotOperatorNodeConversion extends ExpressionNodeConversion<NotOpera } @Override - protected Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<NotOperatorNode> context) { NotOperatorNode node = context.getCurrentNode(); List<Object> args = new ArrayList<>(); @@ -537,7 +537,7 @@ static class ValueRetrievingNodeConversion extends ExpressionNodeConversion<Expr } @Override - protected Object convert(AggregationExpressionTransformationContext<ExpressionNode> context) { + protected @Nullable Object convert(AggregationExpressionTransformationContext<ExpressionNode> 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 9788497601..0f3447a476 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.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 static StringOperatorFactory valueOf(AggregationExpression fieldReference */ 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 Concat concat(String value) { return createConcat().concat(value); } + @SuppressWarnings("NullAway") private Concat createConcat() { return usesFieldRef() ? Concat.valueOf(fieldReference) : Concat.valueOf(expression); } @@ -153,6 +156,7 @@ public Substr substring(int start, int nrOfChars) { return createSubstr().substring(start, nrOfChars); } + @SuppressWarnings("NullAway") private Substr createSubstr() { return usesFieldRef() ? Substr.valueOf(fieldReference) : Substr.valueOf(expression); } @@ -162,6 +166,7 @@ private Substr createSubstr() { * * @return new instance of {@link ToLower}. */ + @SuppressWarnings("NullAway") public ToLower toLower() { return usesFieldRef() ? ToLower.lowerValueOf(fieldReference) : ToLower.lowerValueOf(expression); } @@ -171,6 +176,7 @@ public ToLower toLower() { * * @return new instance of {@link ToUpper}. */ + @SuppressWarnings("NullAway") public ToUpper toUpper() { return usesFieldRef() ? ToUpper.upperValueOf(fieldReference) : ToUpper.upperValueOf(expression); } @@ -214,6 +220,7 @@ public StrCaseCmp strCaseCmpValueOf(AggregationExpression expression) { return createStrCaseCmp().strcasecmpValueOf(expression); } + @SuppressWarnings("NullAway") private StrCaseCmp createStrCaseCmp() { return usesFieldRef() ? StrCaseCmp.valueOf(fieldReference) : StrCaseCmp.valueOf(expression); } @@ -260,6 +267,7 @@ public IndexOfBytes indexOf(AggregationExpression expression) { return createIndexOfBytesSubstringBuilder().indexOf(expression); } + @SuppressWarnings("NullAway") private IndexOfBytes.SubstringBuilder createIndexOfBytesSubstringBuilder() { return usesFieldRef() ? IndexOfBytes.valueOf(fieldReference) : IndexOfBytes.valueOf(expression); } @@ -306,6 +314,7 @@ public IndexOfCP indexOfCP(AggregationExpression expression) { return createIndexOfCPSubstringBuilder().indexOf(expression); } + @SuppressWarnings("NullAway") private IndexOfCP.SubstringBuilder createIndexOfCPSubstringBuilder() { return usesFieldRef() ? IndexOfCP.valueOf(fieldReference) : IndexOfCP.valueOf(expression); } @@ -343,6 +352,7 @@ public Split split(AggregationExpression expression) { return createSplit().split(expression); } + @SuppressWarnings("NullAway") private Split createSplit() { return usesFieldRef() ? Split.valueOf(fieldReference) : Split.valueOf(expression); } @@ -353,6 +363,7 @@ private Split createSplit() { * * @return new instance of {@link StrLenBytes}. */ + @SuppressWarnings("NullAway") public StrLenBytes length() { return usesFieldRef() ? StrLenBytes.stringLengthOf(fieldReference) : StrLenBytes.stringLengthOf(expression); } @@ -363,6 +374,7 @@ public StrLenBytes length() { * * @return new instance of {@link StrLenCP}. */ + @SuppressWarnings("NullAway") public StrLenCP lengthCP() { return usesFieldRef() ? StrLenCP.stringLengthOfCP(fieldReference) : StrLenCP.stringLengthOfCP(expression); } @@ -390,6 +402,7 @@ public SubstrCP substringCP(int codePointStart, int nrOfCodePoints) { return createSubstrCP().substringCP(codePointStart, nrOfCodePoints); } + @SuppressWarnings("NullAway") private SubstrCP createSubstrCP() { return usesFieldRef() ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression); } @@ -432,6 +445,7 @@ public Trim trim(AggregationExpression expression) { return trim().charsOf(expression); } + @SuppressWarnings("NullAway") private Trim createTrim() { return usesFieldRef() ? Trim.valueOf(fieldReference) : Trim.valueOf(expression); } @@ -474,6 +488,7 @@ public LTrim ltrim(AggregationExpression expression) { return ltrim().charsOf(expression); } + @SuppressWarnings("NullAway") private LTrim createLTrim() { return usesFieldRef() ? LTrim.valueOf(fieldReference) : LTrim.valueOf(expression); } @@ -516,6 +531,7 @@ public RTrim rtrim(AggregationExpression expression) { return rtrim().charsOf(expression); } + @SuppressWarnings("NullAway") private RTrim createRTrim() { return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression); } @@ -572,6 +588,7 @@ public RegexFind regexFind(String regex, String options) { return createRegexFind().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexFind createRegexFind() { return usesFieldRef() ? RegexFind.valueOf(fieldReference) : RegexFind.valueOf(expression); } @@ -628,6 +645,7 @@ public RegexFindAll regexFindAll(String regex, String options) { return createRegexFindAll().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexFindAll createRegexFindAll() { return usesFieldRef() ? RegexFindAll.valueOf(fieldReference) : RegexFindAll.valueOf(expression); } @@ -683,6 +701,7 @@ public RegexMatch regexMatch(String regex, String options) { return createRegexMatch().regex(regex).options(options); } + @SuppressWarnings("NullAway") private RegexMatch createRegexMatch() { return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression); } @@ -713,6 +732,7 @@ public ReplaceOne replaceOne(AggregationExpression search, String replacement) { return createReplaceOne().findValueOf(search).replacement(replacement); } + @SuppressWarnings("NullAway") private ReplaceOne createReplaceOne() { return usesFieldRef() ? ReplaceOne.valueOf(fieldReference) : ReplaceOne.valueOf(expression); } @@ -743,6 +763,7 @@ public ReplaceAll replaceAll(AggregationExpression search, String replacement) { return createReplaceAll().findValueOf(search).replacement(replacement); } + @SuppressWarnings("NullAway") private ReplaceAll createReplaceAll() { return usesFieldRef() ? ReplaceAll.valueOf(fieldReference) : ReplaceAll.valueOf(expression); } @@ -810,6 +831,7 @@ public static Concat stringValue(String value) { * @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 Concat concatValueOf(String fieldReference) { * @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 Concat concatValueOf(AggregationExpression expression) { * @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 static Substr valueOf(AggregationExpression expression) { * @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 Substr substring(int start) { * @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 static StrCaseCmp stringValue(String value) { 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 static SubstringBuilder valueOf(AggregationExpression expression) { * @param range must not be {@literal null}. * @return new instance of {@link IndexOfBytes}. */ + @Contract("_ -> new") public IndexOfBytes within(Range<Long> range) { return new IndexOfBytes(append(AggregationUtils.toRangeValues(range))); } @@ -1208,6 +1238,7 @@ public static SubstringBuilder valueOf(AggregationExpression expression) { * @param range must not be {@literal null}. * @return new instance of {@link IndexOfCP}. */ + @Contract("_ -> new") public IndexOfCP within(Range<Long> range) { return new IndexOfCP(append(AggregationUtils.toRangeValues(range))); } @@ -1298,6 +1329,7 @@ public static Split valueOf(AggregationExpression expression) { * @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 Split split(String delimiter) { * @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 Split split(Field fieldReference) { * @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 static SubstrCP valueOf(AggregationExpression expression) { 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 static Trim valueOf(AggregationExpression expression) { * @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 Trim chars(String chars) { * @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 Trim charsOf(String fieldReference) { * @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 static LTrim valueOf(AggregationExpression expression) { * @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 LTrim chars(String chars) { * @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 LTrim charsOf(String fieldReference) { * @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 static RTrim valueOf(AggregationExpression expression) { * @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 RTrim chars(String chars) { * @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 RTrim charsOf(String fieldReference) { * @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 static RegexFind valueOf(AggregationExpression expression) { * @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 RegexFind options(String options) { * @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 RegexFind optionsOf(String fieldReference) { * @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 RegexFind optionsOf(AggregationExpression expression) { * @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 RegexFind regex(String regex) { * @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 RegexFind pattern(Pattern pattern) { * @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 RegexFind regexOf(String fieldReference) { * @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 static RegexFindAll valueOf(AggregationExpression expression) { * @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 RegexFindAll options(String options) { * @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 RegexFindAll optionsOf(String fieldReference) { * @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 RegexFindAll optionsOf(AggregationExpression expression) { * @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 RegexFindAll pattern(Pattern pattern) { * @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 RegexFindAll regex(String regex) { * @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 RegexFindAll regexOf(String fieldReference) { * @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 static RegexMatch valueOf(AggregationExpression expression) { * @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 RegexMatch options(String options) { * @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 RegexMatch optionsOf(String fieldReference) { * @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 RegexMatch optionsOf(AggregationExpression expression) { * @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 RegexMatch pattern(Pattern pattern) { * @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 RegexMatch regex(String regex) { * @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 RegexMatch regexOf(String fieldReference) { * @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 static ReplaceOne valueOf(AggregationExpression expression) { * @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 ReplaceOne replacement(String replacement) { * @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 ReplaceOne replacementOf(String fieldReference) { * @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 ReplaceOne replacementOf(AggregationExpression expression) { * @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 ReplaceOne find(String value) { * @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 ReplaceOne findValueOf(String fieldReference) { * @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 static ReplaceAll valueOf(AggregationExpression expression) { * @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 ReplaceAll replacement(String replacement) { * @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 ReplaceAll replacementValueOf(String fieldReference) { * @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 ReplaceAll replacementValueOf(AggregationExpression expression) { * @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 ReplaceAll find(String value) { * @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 ReplaceAll findValueOf(String fieldReference) { * @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 1fcf87d2a0..cc0296c900 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 String getTarget() { 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 f30ebf394b..d2d49abf78 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 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.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 057ada12d5..c93c1bad9e 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.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 ff765c37f7..0bcc192ded 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 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 static UnsetOperation unset(String... fields) { * @param fields must not be {@literal null}. * @return new instance of {@link UnsetOperation}. */ + @Contract("_ -> new") public UnsetOperation and(String... fields) { List<Object> target = new ArrayList<>(this.fields); @@ -80,6 +82,7 @@ public UnsetOperation and(String... fields) { * @param fields must not be {@literal null}. * @return new instance of {@link UnsetOperation}. */ + @Contract("_ -> new") public UnsetOperation and(Field... fields) { List<Object> 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 d59ae01b12..cc0552cd1c 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 static PathBuilder newBuilder() { @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 UnwindOperation preserveNullAndEmptyArrays() { @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 UnwindOperation skipNullAndEmptyArrays() { } @Override + @Contract("_ -> this") public EmptyArraysBuilder arrayIndex(String field) { Assert.hasText(field, "'ArrayIndex' must not be null or empty"); @@ -227,6 +233,7 @@ public EmptyArraysBuilder arrayIndex(String field) { } @Override + @Contract("-> this") public EmptyArraysBuilder noArrayIndex() { arrayIndex = null; @@ -234,6 +241,7 @@ public EmptyArraysBuilder noArrayIndex() { } @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 8e676c72bc..b5a9ca0f21 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.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 static class Let implements AggregationExpression { private final List<ExpressionVariable> vars; - @Nullable // - private final AggregationExpression expression; + private final @Nullable AggregationExpression expression; private Let(List<ExpressionVariable> vars, @Nullable AggregationExpression expression) { @@ -333,6 +332,7 @@ private Document getMappedVariable(ExpressionVariable var, AggregationOperationC 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/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/package-info.java index 0e30b8b855..2769990ca6 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 3e08dc1014..9ada2014a0 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 7a01677939..9b1c744be2 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 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.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 40afbb8c10..3b4dd99d4e 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 0235694030..ee1f568494 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 @@ 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 @@ Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbref, DbR * @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 bf6b882375..fd80029118 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 22b1ce7981..13c0198aa0 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 @@ 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 de66c3ea94..c72bc4b886 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 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.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 DefaultDbRefResolver(MongoDatabaseFactory mongoDbFactory) { } @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 Object resolveDbRef(MongoPersistentProperty property, @Nullable DBRef dbr } @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 @@ private boolean isLazyDbRef(MongoPersistentProperty property) { private static Stream<Document> documentWithId(Object identifier, Collection<Document> 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 82e5c9d0eb..376e0dd8cd 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 @@ 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 2c2b52afd5..f5db41f006 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 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.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 DefaultMongoTypeMapper(@Nullable String typeKey, List<? extends TypeInfor } private DefaultMongoTypeMapper(@Nullable String typeKey, TypeAliasAccessor<Bson> accessor, - MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext, + @Nullable MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext, List<? extends TypeInformationMapper> 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 a7b3d6f21f..4df7c02f91 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 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 DefaultReferenceResolver(ReferenceLoader referenceLoader, PersistenceExce } @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 Object resolveReference(MongoPersistentProperty property, Object source, * @see DBRef#lazy() * @see DocumentReference#lazy() */ + @SuppressWarnings("NullAway") protected boolean isLazyReference(MongoPersistentProperty property) { if (property.isDocumentReference()) { @@ -106,6 +109,7 @@ LazyLoadingProxyFactory getProxyFactory() { 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 c795add9c8..ff50dd5df3 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 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 @@ public void put(MongoPersistentProperty prop, @Nullable Object value) { * @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 @@ public Object get(MongoPersistentProperty property) { * @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 8429584a6f..e03d215088 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<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, MongoPersistentProperty property, Object value, Class<?> typeHint) { @@ -87,7 +88,7 @@ DocumentPointer<?> computePointer( if (usesDefaultLookup(property)) { - MongoPersistentProperty idProperty = persistentEntity.getIdProperty(); + MongoPersistentProperty idProperty = persistentEntity.getRequiredIdProperty(); Object idValue = persistentEntity.getIdentifierAccessor(value).getIdentifier(); if (idProperty.hasExplicitWriteTarget() @@ -114,6 +115,7 @@ DocumentPointer<?> computePointer( .getDocumentPointer(mappingContext, persistentEntity, propertyAccessor); } + @SuppressWarnings("NullAway") private boolean usesDefaultLookup(MongoPersistentProperty property) { if (property.isDocumentReference()) { @@ -216,9 +218,16 @@ Object updatePlaceholders(org.bson.Document source, org.bson.Document target, 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 @@ Object updatePlaceholders(org.bson.Document source, org.bson.Document target, 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 ea5ce01b44..a41e17c0ec 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 @@ 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 bf21781058..b1e894efe9 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 Object getSelf() { * * @return can be {@literal null}. */ - @Nullable - public Object getTargetSource() { + public @Nullable Object getTargetSource() { return targetSource; } @@ -67,8 +67,7 @@ public Object getTargetSource() { * @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 @@ static Object getTargetSource(Object source) { * @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 2bca260b79..ae73ab68bd 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 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.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 @@ enum DocumentToPointConverter implements Converter<Document, Point> { 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 @@ enum PointToDocumentConverter implements Converter<Point, Document> { 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 @@ enum BoxToDocumentConverter implements Converter<Box, Document> { 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 @@ enum DocumentToBoxConverter implements Converter<Document, Box> { 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 @@ enum CircleToDocumentConverter implements Converter<Circle, Document> { 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 @@ enum DocumentToCircleConverter implements Converter<Document, Circle> { 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 @@ enum SphereToDocumentConverter implements Converter<Sphere, Document> { 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 @@ enum DocumentToSphereConverter implements Converter<Document, Sphere> { 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 @@ enum PolygonToDocumentConverter implements Converter<Polygon, Document> { 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 @@ enum DocumentToPolygonConverter implements Converter<Document, Polygon> { @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<Document> points = (List<Document>) source.get("points"); - List<Point> newPoints = new ArrayList<>(points.size()); + Assert.notNull(points, "Points elements of polygon must not be null"); + List<Point> 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 @@ enum GeoCommandToDocumentConverter implements Converter<GeoCommand, Document> { @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 @@ enum GeoJsonToDocumentConverter implements Converter<GeoJson<?>, Document> { 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 @@ public Document convert(GeoJson<?> source) { private Object convertIfNecessary(Object candidate) { - if (candidate instanceof GeoJson geoJson) { + if (candidate instanceof GeoJson<?> geoJson) { return convertIfNecessary(geoJson.getCoordinates()); } @@ -551,7 +567,8 @@ enum DocumentToGeoJsonPointConverter implements Converter<Document, GeoJsonPoint @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 @@ public GeoJsonPoint convert(Document source) { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"), String.format("Cannot convert type '%s' to Point", source.get("type"))); - List<Number> dbl = (List<Number>) source.get("coordinates"); + if(!(source.get("coordinates") instanceof List<?> sourceCoordinates)) { + throw new IllegalArgumentException("Coordinates need to be present"); + } + List<Number> dbl = (List<Number>) sourceCoordinates; return new GeoJsonPoint(toPrimitiveDoubleValue(dbl.get(0)), toPrimitiveDoubleValue(dbl.get(1))); } } @@ -574,7 +594,8 @@ enum DocumentToGeoJsonPolygonConverter implements Converter<Document, GeoJsonPol 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 @@ enum DocumentToGeoJsonMultiPolygonConverter implements Converter<Document, GeoJs 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 @@ public GeoJsonMultiPolygon convert(Document source) { String.format("Cannot convert type '%s' to MultiPolygon", source.get("type"))); List<?> dbl = (List<?>) source.get("coordinates"); - List<GeoJsonPolygon> polygones = new ArrayList<>(); + Assert.notNull(dbl, "Source needs to contain coordinates"); + List<GeoJsonPolygon> polygones = new ArrayList<>(dbl.size()); for (Object polygon : dbl) { polygones.add(toGeoJsonPolygon((List<?>) polygon)); } @@ -625,7 +648,8 @@ enum DocumentToGeoJsonLineStringConverter implements Converter<Document, GeoJson 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 @@ enum DocumentToGeoJsonMultiPointConverter implements Converter<Document, GeoJson 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 @@ enum DocumentToGeoJsonMultiLineStringConverter implements Converter<Document, Ge 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 @@ public GeoJsonMultiLineString convert(Document source) { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiLineString"), String.format("Cannot convert type '%s' to MultiLineString", source.get("type"))); - List<GeoJsonLineString> 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<GeoJsonLineString> 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 @@ enum DocumentToGeoJsonGeometryCollectionConverter implements Converter<Document, 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 @@ public GeoJsonGeometryCollection convert(Document source) { Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"), String.format("Cannot convert type '%s' to GeometryCollection", source.get("type"))); - List<GeoJson<?>> 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<GeoJson<?>> geometries = new ArrayList<>(sourceGeometries.size()); + for (Object o : sourceGeometries) { geometries.add(toGenericGeoJson((Document) o)); } @@ -732,7 +765,10 @@ static List<Double> toList(Point point) { * @since 1.7 */ @SuppressWarnings("unchecked") - static List<Point> toListOfPoint(List<?> listOfCoordinatePairs) { + @Contract("null -> fail") + static List<Point> toListOfPoint(@Nullable List<?> listOfCoordinatePairs) { + + Assert.notNull(listOfCoordinatePairs, "ListOfCoordinatePairs must not be null"); List<Point> points = new ArrayList<>(listOfCoordinatePairs.size()); @@ -755,7 +791,10 @@ static List<Point> toListOfPoint(List<?> listOfCoordinatePairs) { * @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 @@ private static GeoJson<?> toGenericGeoJson(Document source) { 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 77aac55813..6329d74d4f 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 76539ea431..eff58e7bd4 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.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.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 @@ private ProxyFactory prepareProxyFactory(Class<?> propertyType, Supplier<LazyLoa } 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 @@ private Class<?> getEnhancedTypeFor(Class<?> type) { return enhancer.createClass(); } + @NullUnmarked public static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable { @@ -180,10 +182,10 @@ public static class LazyLoadingInterceptor 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 static class LazyLoadingInterceptor * @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 Object intercept(Object o, Method method, Object[] args, MethodProxy prox }; } - 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 LazyLoadingInterceptor(MongoPersistentProperty property, DbRefResolverCal 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 Object intercept(Object o, Method method, Object[] args, MethodProxy prox return proxyToString(source); } - if (ReflectionUtils.isEqualsMethod(method)) { + if (ReflectionUtils.isEqualsMethod(method) && args != null) { return proxyEquals(o, args[0]); } @@ -347,8 +346,8 @@ private void readObject(ObjectInputStream in) throws IOException { } } - @Nullable - private Object resolve() { + @SuppressWarnings("NullAway") + private @Nullable Object resolve() { try (AcquiredLock l = readLock.lock()) { if (resolved) { @@ -370,7 +369,7 @@ private Object resolve() { 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 864cc1c3e3..2d073869ae 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.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.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; @@ -71,7 +72,6 @@ import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.mapping.model.SpELContext; -import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider; import org.springframework.data.mongodb.CodecRegistryProvider; @@ -95,7 +95,7 @@ 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; @@ -158,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; @@ -308,7 +308,9 @@ public void setApplicationContext(ApplicationContext applicationContext) throws 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)); @@ -419,7 +421,7 @@ FieldName getFieldName(MongoPersistentProperty prop) { 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()) { @@ -434,12 +436,12 @@ static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor Map<String, Object> 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()); } @@ -467,10 +469,14 @@ protected <S extends Object> S read(TypeInformation<S> type, Bson bson) { * @return the converted object, will never be {@literal null}. * @since 3.2 */ - @SuppressWarnings("unchecked") - protected <S extends Object> S readDocument(ConversionContext context, Bson bson, + @SuppressWarnings({"unchecked","NullAway"}) + protected <S extends Object> S readDocument(ConversionContext context, @Nullable Bson bson, TypeInformation<? extends S> typeHint) { + if(bson == null) { + bson = new Document(); + } + Document document = bson instanceof BasicDBObject dbObject ? new Document(dbObject) : (Document) bson; TypeInformation<? extends S> typeToRead = getTypeMapper().readType(document, typeHint); Class<? extends S> rawType = typeToRead.getType(); @@ -546,7 +552,7 @@ public EvaluatingDocumentAccessor(Bson document) { } @Override - public <T> T evaluate(String expression) { + public <T> @Nullable T evaluate(String expression) { return expressionEvaluatorFactory.create(getDocument()).evaluate(expression); } } @@ -600,8 +606,7 @@ private <S> S populateProperties(ConversionContext context, MongoPersistentEntit * 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); @@ -685,8 +690,7 @@ private DbRefResolverCallback getDbRefResolverCallback(ConversionContext context (prop, bson, e, path) -> MappingMongoConverter.this.getValueInternal(context, prop, bson, e)); } - @Nullable - private Object readAssociation(Association<MongoPersistentProperty> association, DocumentAccessor documentAccessor, + private @Nullable Object readAssociation(Association<MongoPersistentProperty> association, DocumentAccessor documentAccessor, DbRefProxyHandler handler, DbRefResolverCallback callback, ConversionContext context) { MongoPersistentProperty property = association.getInverse(); @@ -746,8 +750,8 @@ && peek(collection) instanceof Document) { } } - @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)) { @@ -763,13 +767,14 @@ private Object readUnwrapped(ConversionContext context, DocumentAccessor documen } @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 @@ -781,6 +786,7 @@ public DBRef toDBRef(Object object, @Nullable MongoPersistentProperty referringP } @Override + @SuppressWarnings("NullAway") public DocumentPointer toDocumentPointer(Object source, @Nullable MongoPersistentProperty referringProperty) { if (source instanceof LazyLoadingProxy proxy) { @@ -800,6 +806,7 @@ public DocumentPointer toDocumentPointer(Object source, @Nullable MongoPersisten 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) { @@ -864,7 +871,7 @@ private boolean requiresTypeHint(Class<?> type) { /** * 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) { @@ -1268,6 +1275,7 @@ protected String potentiallyEscapeMapKey(String source) { * * @param key */ + @SuppressWarnings("NullAway") private String potentiallyConvertMapKey(Object key) { if (key instanceof String stringValue) { @@ -1333,20 +1341,23 @@ private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersist 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> T getPropertyValue(MongoPersistentProperty property) { + public <T> @Nullable T getPropertyValue(MongoPersistentProperty property) { return (T) persistentPropertyAccessor.getProperty(property); } }, property, this, spELContext); - PropertyValueConverter<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter = conversions - .getPropertyValueConversions().getValueConverter(property); + + PropertyValueConversions propertyValueConversions = conversions.getPropertyValueConversions(); + if(propertyValueConversions == null) { + return value; + } + + PropertyValueConverter<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter = propertyValueConversions.getValueConverter(property); return value != null ? valueConverter.write(value, context) : valueConverter.writeNull(context); } @@ -1354,8 +1365,9 @@ public <T> T getPropertyValue(MongoPersistentProperty property) { * 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; @@ -1391,7 +1403,7 @@ private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, @Nulla * * @since 3.2 */ - protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation<?> target) { + protected @Nullable Object getPotentiallyConvertedSimpleRead(@Nullable Object value, TypeInformation<?> target) { return getPotentiallyConvertedSimpleRead(value, target.getType()); } @@ -1399,10 +1411,11 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation * 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; } @@ -1421,6 +1434,7 @@ private Object getPotentiallyConvertedSimpleRead(Object value, @Nullable Class<? return doConvert(value, target); } + @SuppressWarnings("NullAway") protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty property) { Assert.notNull(target, "Target object must not be null"); @@ -1456,8 +1470,7 @@ protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty pro 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); } @@ -1472,11 +1485,12 @@ private Object getValueInternal(ConversionContext context, MongoPersistentProper * @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() // @@ -1518,7 +1532,7 @@ protected Object readCollectionOrArray(ConversionContext context, Collection<?> * @return the converted {@link Map}, will never be {@literal null}. * @since 3.2 */ - protected Map<Object, Object> readMap(ConversionContext context, Bson bson, TypeInformation<?> targetType) { + protected @Nullable Map<Object, Object> readMap(ConversionContext context, @Nullable Bson bson, TypeInformation<?> targetType) { Assert.notNull(bson, "Document must not be null"); Assert.notNull(targetType, "TypeInformation must not be null"); @@ -1715,9 +1729,8 @@ private Object removeTypeInfo(Object object, boolean recursively) { return document; } - @Nullable @SuppressWarnings("unchecked") - <T> T readValue(ConversionContext context, @Nullable Object value, TypeInformation<?> type) { + <T> @Nullable T readValue(ConversionContext context, @Nullable Object value, TypeInformation<?> type) { if (value == null) { return null; @@ -1736,8 +1749,7 @@ <T> T readValue(ConversionContext context, @Nullable Object value, TypeInformati 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; @@ -1802,6 +1814,7 @@ private <T> List<T> bulkReadAndConvertDBRefs(ConversionContext context, List<DBR return targetList; } + @SuppressWarnings("NullAway") private void maybeEmitEvent(MongoMappingEvent<?> event) { if (canPublishEvent()) { @@ -1881,12 +1894,12 @@ public MappingMongoConverter with(MongoDatabaseFactory dbFactory) { return target; } - private <T extends Object> T doConvert(Object value, Class<? extends T> target) { + private <T extends Object> @Nullable T doConvert(Object value, Class<? extends T> target) { return doConvert(value, target, null); } @SuppressWarnings("ConstantConditions") - private <T extends Object> T doConvert(Object value, Class<? extends T> target, + private <T extends Object> @Nullable T doConvert(Object value, Class<? extends T> target, @Nullable Class<? extends T> fallback) { if (conversionService.canConvert(value.getClass(), target) || fallback == null) { @@ -1940,7 +1953,7 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo 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 @@ -1963,7 +1976,7 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo * @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; @@ -1972,9 +1985,8 @@ static class MongoDbPropertyValueProvider implements PropertyValueProvider<Mongo } @Override - @Nullable - @SuppressWarnings("unchecked") - public <T> T getPropertyValue(MongoPersistentProperty property) { + @SuppressWarnings({"unchecked", "NullAway"}) + public <T> @Nullable T getPropertyValue(MongoPersistentProperty property) { String expression = property.getSpelExpression(); Object value = expression != null ? evaluator.evaluate(expression) : accessor.get(property); @@ -2059,7 +2071,7 @@ public <T> T getPropertyValue(MongoPersistentProperty property) { } /** - * Extension of {@link SpELExpressionParameterValueProvider} to recursively trigger value conversion on the raw + * Extension of {@link ValueExpressionParameterValueProvider} to recursively trigger value conversion on the raw * resolved SpEL value. * * @author Oliver Gierke @@ -2110,7 +2122,7 @@ enum NoOpParameterValueProvider implements ParameterValueProvider<MongoPersisten INSTANCE; @Override - public <T> T getParameterValue(Parameter<T, MongoPersistentProperty> parameter) { + public <T> @Nullable T getParameterValue(Parameter<T, MongoPersistentProperty> parameter) { return null; } } @@ -2138,7 +2150,7 @@ public List<org.springframework.data.util.TypeInformation<?>> getParameterTypes( } @Override - public org.springframework.data.util.TypeInformation<?> getProperty(String property) { + public org.springframework.data.util.@Nullable TypeInformation<?> getProperty(String property) { return delegate.getProperty(property); } @@ -2173,7 +2185,7 @@ public TypeInformation<?> getRawTypeInformation() { } @Override - public org.springframework.data.util.TypeInformation<?> getActualType() { + public org.springframework.data.util.@Nullable TypeInformation<?> getActualType() { return delegate.getActualType(); } @@ -2188,7 +2200,7 @@ public List<org.springframework.data.util.TypeInformation<?>> getParameterTypes( } @Override - public org.springframework.data.util.TypeInformation<?> getSuperTypeInformation(Class superType) { + public org.springframework.data.util.@Nullable TypeInformation<?> getSuperTypeInformation(Class superType) { return delegate.getSuperTypeInformation(superType); } @@ -2280,8 +2292,7 @@ default ConversionContext forProperty(MongoPersistentProperty property) { * @return * @param <S> */ - @Nullable - default <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) { + default <S> @Nullable S findContextualEntity(MongoPersistentEntity<S> entity, Document document) { return null; } @@ -2315,7 +2326,7 @@ public ConversionContext withPath(ObjectPath currentPath) { } @Override - public <S> S findContextualEntity(MongoPersistentEntity<S> entity, Document document) { + public <S> @Nullable S findContextualEntity(MongoPersistentEntity<S> entity, Document document) { Object identifier = document.get(BasicMongoPersistentProperty.ID_FIELD_NAME); @@ -2352,15 +2363,15 @@ protected static class DefaultConversionContext implements ConversionContext { final ObjectPath path; final ContainerValueConverter<Bson> documentConverter; final ContainerValueConverter<Collection<?>> collectionConverter; - final ContainerValueConverter<Bson> mapConverter; - final ContainerValueConverter<DBRef> dbRefConverter; - final ValueConverter<Object> 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<Bson> documentConverter, ContainerValueConverter<Collection<?>> collectionConverter, - ContainerValueConverter<Bson> mapConverter, ContainerValueConverter<DBRef> dbRefConverter, - ValueConverter<Object> 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; @@ -2372,8 +2383,8 @@ protected static class DefaultConversionContext implements ConversionContext { this.elementConverter = elementConverter; } - @SuppressWarnings("unchecked") @Override + @SuppressWarnings({"unchecked", "NullAway"}) public <S extends Object> S convert(Object source, TypeInformation<? extends S> typeHint, ConversionContext context) { @@ -2454,7 +2465,7 @@ public ObjectPath getPath() { */ interface ValueConverter<T> { - Object convert(T source, TypeInformation<?> typeHint); + @Nullable Object convert(@Nullable T source, TypeInformation<?> typeHint); } @@ -2466,7 +2477,7 @@ interface ValueConverter<T> { */ interface ContainerValueConverter<T> { - Object convert(ConversionContext context, T source, TypeInformation<?> typeHint); + @Nullable Object convert(ConversionContext context, @Nullable T source, TypeInformation<?> typeHint); } @@ -2481,7 +2492,7 @@ class ProjectingConversionContext extends DefaultConversionContext { ProjectingConversionContext(MongoConverter sourceConverter, CustomConversions customConversions, ObjectPath path, ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> mapConverter, - ContainerValueConverter<DBRef> dbRefConverter, ValueConverter<Object> elementConverter, + ContainerValueConverter<@Nullable DBRef> dbRefConverter, ValueConverter<@Nullable Object> elementConverter, EntityProjection<?, ?> projection) { super(sourceConverter, customConversions, path, (context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection), @@ -2533,7 +2544,7 @@ public void setProperty(PersistentProperty<?> property, @Nullable Object value) } @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 5fde0acddd..74be2949d0 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,12 @@ 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.Nullable; /** * {@link ValueConversionContext} that allows to delegate read/write to an underlying {@link MongoConverter}. @@ -63,25 +62,23 @@ public MongoPersistentProperty getProperty() { return persistentProperty; } - @Nullable - public Object getValue(String propertyPath) { + public @Nullable Object getValue(String propertyPath) { return accessor.getPropertyValue(getProperty().getOwner().getRequiredPersistentProperty(propertyPath)); } @Override @SuppressWarnings("unchecked") - public <T> T write(@Nullable Object value, TypeInformation<T> target) { + public <T> @Nullable T write(@Nullable Object value, TypeInformation<T> target) { return (T) mongoConverter.convertToMongoType(value, target); } @Override - public <T> T read(@Nullable Object value, TypeInformation<T> target) { + public <T> @Nullable T read(@Nullable Object value, TypeInformation<T> target) { return value instanceof Bson bson ? mongoConverter.read(target.getType(), bson) : ValueConversionContext.super.read(value, target); } - @Nullable - public SpELContext getSpELContext() { + public @Nullable SpELContext getSpELContext() { return spELContext; } } 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 3676e74c8b..e147d64cc5 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.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.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 <S, T> T mapValueToTargetType(S source, Class<T> targetType, DbRefResolver dbRefResolver) { + @SuppressWarnings({"unchecked","NullAway"}) + default <S, T> @Nullable T mapValueToTargetType(S source, Class<T> 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 f9a67d73a0..1fd45e1960 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.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 @@ public String convert(ObjectId id) { enum StringToObjectIdConverter implements Converter<String, ObjectId> { INSTANCE; - public ObjectId convert(String source) { + public @Nullable ObjectId convert(String source) { return StringUtils.hasText(source) ? new ObjectId(source) : null; } } @@ -206,7 +207,7 @@ public Decimal128 convert(BigInteger source) { enum StringToBigDecimalConverter implements Converter<String, BigDecimal> { INSTANCE; - public BigDecimal convert(String source) { + public @Nullable BigDecimal convert(String source) { return StringUtils.hasText(source) ? new BigDecimal(source) : null; } } @@ -235,7 +236,7 @@ public String convert(BigInteger source) { enum StringToBigIntegerConverter implements Converter<String, BigInteger> { INSTANCE; - public BigInteger convert(String source) { + public @Nullable BigInteger convert(String source) { return StringUtils.hasText(source) ? new BigInteger(source) : null; } } @@ -312,20 +313,25 @@ public String convert(Term source) { * @author Christoph Strobl * @since 1.7 */ + @SuppressWarnings("NullAway") enum DocumentToNamedMongoScriptConverter implements Converter<Document, NamedMongoScript> { 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 @@ enum StringToCurrencyConverter implements Converter<String, Currency> { 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 050c3bd27d..8dccced380 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.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.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 Set<ConvertiblePair> getConvertibleTypes() { 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 static MongoConverterConfigurationAdapter from(List<?> converters) { * @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 MongoConverterConfigurationAdapter registerConverter(Converter<?, ?> conv * @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 MongoConverterConfigurationAdapter registerConverters(Collection<?> conve * @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 MongoConverterConfigurationAdapter registerConverterFactory(ConverterFact * @return this. * @since 3.4 */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter registerPropertyValueConverterFactory( PropertyValueConverterFactory converterFactory) { @@ -249,6 +254,7 @@ public MongoConverterConfigurationAdapter registerPropertyValueConverterFactory( * @return this. * @since 3.4 */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter configurePropertyConversions( Consumer<PropertyValueConverterRegistrar<MongoPersistentProperty>> configurationAdapter) { @@ -271,6 +277,7 @@ public MongoConverterConfigurationAdapter configurePropertyConversions( * @param useNativeDriverJavaTimeCodecs * @return this. */ + @Contract("_ -> this") public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs(boolean useNativeDriverJavaTimeCodecs) { this.useNativeDriverJavaTimeCodecs = useNativeDriverJavaTimeCodecs; @@ -285,6 +292,7 @@ public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs(boolean * @return this. * @see #useNativeDriverJavaTimeCodecs(boolean) */ + @Contract("-> this") public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs() { return useNativeDriverJavaTimeCodecs(true); } @@ -299,6 +307,7 @@ public MongoConverterConfigurationAdapter useNativeDriverJavaTimeCodecs() { * @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 0316251dc1..67f9d5ec46 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.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 Document getMappedExample(Example<?> example) { * @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 8d199083e7..a5d329045e 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.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 867a6213d2..8aeb576c67 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<T> extends EntityWriter<T, Bson> { * @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 @@ default Object convertToMongoType(@Nullable Object obj) { @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 265257af5c..68578f32b9 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 @@ 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 5fefd472c4..d5f034eb1d 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 @@ 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 @@ ObjectPath push(Object object, MongoPersistentEntity<?> entity, @Nullable Object * @return {@literal null} when no match found. * @since 2.0 */ - @Nullable - <T> T getPathItem(Object id, String collection, Class<T> type) { + <T> @Nullable T getPathItem(Object id, String collection, Class<T> type) { Assert.notNull(id, "Id must not be null"); Assert.hasText(collection, "Collection name must not be null"); @@ -133,13 +132,11 @@ Object getCurrentObject() { 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 cce809adc6..7aa9019ffd 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,7 +37,8 @@ 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; @@ -66,7 +67,7 @@ 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; @@ -135,6 +136,7 @@ public Document getMappedObject(Bson query, Optional<? extends MongoPersistentEn * @param entity can be {@literal null}. * @return */ + @SuppressWarnings("NullAway") public Document getMappedObject(Bson query, @Nullable MongoPersistentEntity<?> entity) { if (isNestedKeyword(query)) { @@ -275,6 +277,7 @@ public Document addMetaAttributes(Document source, @Nullable MongoPersistentEnti return mapMetaAttributes(source, entity, MetaMapping.FORCE); } + @SuppressWarnings("NullAway") private Document mapMetaAttributes(Document source, @Nullable MongoPersistentEntity<?> entity, MetaMapping metaMapping) { @@ -347,7 +350,7 @@ private Document getMappedTextScoreField(MongoPersistentProperty property) { * @param rawValue * @return */ - protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) { + protected Entry<String, @Nullable Object> getMappedObjectForField(Field field, @Nullable Object rawValue) { String key = field.getMappedKey(); Object value; @@ -411,7 +414,9 @@ protected Document getMappedKeyword(Keyword keyword, @Nullable MongoPersistentEn } 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()) { @@ -452,9 +457,8 @@ protected Document getMappedKeyword(Field property, Keyword keyword) { * @param sourceValue the source object to be mapped * @return */ - @Nullable - @SuppressWarnings("unchecked") - protected Object getMappedValue(Field documentField, Object sourceValue) { + @SuppressWarnings("NullAway") + protected @Nullable Object getMappedValue(Field documentField, @Nullable Object sourceValue) { Object value = applyFieldTargetTypeHintToValue(documentField, sourceValue); @@ -491,6 +495,7 @@ private boolean isIdField(Field documentField) { && documentField.getProperty().getOwner().isIdProperty(documentField.getProperty()); } + @SuppressWarnings("NullAway") private Class<?> getIdTypeForField(Field documentField) { return isIdField(documentField) ? documentField.getProperty().getFieldType() : ObjectId.class; } @@ -529,7 +534,7 @@ protected boolean isAssociationConversionNecessary(Field documentField, @Nullabl } MongoPersistentEntity<?> entity = documentField.getPropertyEntity(); - return entity.hasIdProperty() + return entity != null && entity.hasIdProperty() && (type.equals(DBRef.class) || entity.getRequiredIdProperty().getActualType().isAssignableFrom(type)); } @@ -540,8 +545,7 @@ protected boolean isAssociationConversionNecessary(Field documentField, @Nullabl * @param entity * @return */ - @Nullable - protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersistentEntity<?> entity) { + protected @Nullable Object convertSimpleOrDocument(Object source, @Nullable MongoPersistentEntity<?> entity) { if (source instanceof Example<?> example) { return exampleMapper.getMappedExample(example, entity); @@ -601,8 +605,7 @@ protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersisten * @param entity * @return the converted mongo type or null if source is null */ - @Nullable - 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); @@ -611,8 +614,7 @@ protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersis return converter.convertToMongoType(source, entity == null ? null : entity.getTypeInformation()); } - @Nullable - protected Object convertAssociation(Object source, Field field) { + protected @Nullable Object convertAssociation(Object source, Field field) { Object value = convertAssociation(source, field.getProperty()); if (value != null && field.isIdField() && field.getFieldType() != value.getClass()) { return convertId(value, field.getFieldType()); @@ -665,8 +667,7 @@ protected Object convertAssociation(@Nullable Object source, @Nullable MongoPers 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<Object, Object, ValueConversionContext<MongoPersistentProperty>> valueConverter) { MongoPersistentProperty property = documentField.getProperty(); @@ -688,7 +689,7 @@ private Object convertValue(Field documentField, Object sourceValue, Object valu 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)) { @@ -698,11 +699,11 @@ private Object convertValue(Field documentField, Object sourceValue, Object valu }); } - 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; @@ -807,8 +808,7 @@ private Object createReferenceFor(Object source, MongoPersistentProperty propert * @return * @since 2.2 */ - @Nullable - public Object convertId(@Nullable Object id) { + public @Nullable Object convertId(@Nullable Object id) { return convertId(id, ObjectId.class); } @@ -820,8 +820,7 @@ public Object convertId(@Nullable Object id) { * @return the converted {@literal id} or {@literal null} if the source was already {@literal null}. * @since 2.2 */ - @Nullable - public Object convertId(@Nullable Object id, Class<?> targetType) { + public @Nullable Object convertId(@Nullable Object id, Class<?> targetType) { if (Quirks.skipConversion(id)) { return id; @@ -836,6 +835,7 @@ public Object convertId(@Nullable Object id, Class<?> targetType) { * @param candidate * @return */ + @Contract("null -> false") protected boolean isNestedKeyword(@Nullable Object candidate) { if (!(candidate instanceof Document)) { @@ -884,8 +884,7 @@ protected boolean isKeyword(String candidate) { * @param value the actual value. Can be {@literal null}. * @return the potentially converted target value. */ - @Nullable - private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) { + private @Nullable Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) { if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget() || value instanceof Document || value instanceof DBObject || Quirks.skipConversion(value)) { @@ -916,6 +915,7 @@ private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Ob * @author Oliver Gierke * @author Christoph Strobl */ + @SuppressWarnings("NullAway") static class Keyword { private static final Set<String> NON_DBREF_CONVERTING_KEYWORDS = Set.of("$", "$size", "$slice", "$gt", "$lt"); @@ -1053,8 +1053,7 @@ public boolean isIdField() { * * @return can be {@literal null}. */ - @Nullable - public MongoPersistentProperty getProperty() { + public @Nullable MongoPersistentProperty getProperty() { return null; } @@ -1063,8 +1062,7 @@ public MongoPersistentProperty getProperty() { * * @return can be {@literal null}. */ - @Nullable - public MongoPersistentEntity<?> getPropertyEntity() { + public @Nullable MongoPersistentEntity<?> getPropertyEntity() { return null; } @@ -1100,8 +1098,7 @@ public boolean containsAssociation() { return false; } - @Nullable - public Association<MongoPersistentProperty> getAssociation() { + public @Nullable Association<MongoPersistentProperty> getAssociation() { return null; } @@ -1164,6 +1161,7 @@ public MetadataBackedField(String name, MongoPersistentEntity<?> entity, * @param context must not be {@literal null}. * @param property may be {@literal null}. */ + @SuppressWarnings("NullAway") public MetadataBackedField(String name, MongoPersistentEntity<?> entity, MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context, @Nullable MongoPersistentProperty property) { @@ -1207,14 +1205,13 @@ public MongoPersistentProperty getProperty() { } @Override - public MongoPersistentEntity<?> getPropertyEntity() { + public @Nullable MongoPersistentEntity<?> getPropertyEntity() { MongoPersistentProperty property = getProperty(); return property == null ? null : mappingContext.getPersistentEntity(property); } - @Nullable @Override - public MongoPersistentEntity<?> getEntity() { + public @Nullable MongoPersistentEntity<?> getEntity() { return entity; } @@ -1224,7 +1221,7 @@ public boolean isAssociation() { } @Override - public Association<MongoPersistentProperty> getAssociation() { + public @Nullable Association<MongoPersistentProperty> getAssociation() { return association; } @@ -1265,8 +1262,7 @@ public String getMappedKey() { return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter()); } - @Nullable - protected PersistentPropertyPath<MongoPersistentProperty> getPath() { + protected @Nullable PersistentPropertyPath<MongoPersistentProperty> getPath() { return path; } @@ -1276,8 +1272,7 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() { * @param pathExpression * @return */ - @Nullable - private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression, + private @Nullable PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression, @Nullable MongoPersistentProperty sourceProperty) { if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) { @@ -1327,8 +1322,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre return propertyPath; } - @Nullable - private PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPropertyPath(PropertyPath path) { + private @Nullable PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPropertyPath(PropertyPath path) { try { return mappingContext.getPersistentPropertyPath(path); @@ -1427,6 +1421,7 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() { * @return * @since 1.7 */ + @SuppressWarnings("NullAway") protected Converter<MongoPersistentProperty, String> getAssociationConverter() { return new AssociationConverter(name, getAssociation()); } @@ -1578,7 +1573,7 @@ public AssociationConverter(String name, Association<MongoPersistentProperty> as } @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 5a1adf9114..cd7d55311d 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 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<Document> 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 b912cfb540..a0e5a6f2bb 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 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.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 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 Object readReference(MongoPersistentProperty property, Object source, Loo @Nullable private Iterable<Document> 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 @@ private Iterable<Document> retrieveRawDocuments(MongoPersistentProperty property 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 @@ private ReferenceCollection computeReferenceContext(MongoPersistentProperty prop * @return can be {@literal null}. */ @SuppressWarnings("unchecked") - private <T> T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier<T> defaultValue) { + private <T> T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier<@Nullable T> defaultValue) { if (!StringUtils.hasText(value)) { return defaultValue.get(); @@ -220,7 +220,7 @@ private <T> T parseValueOrGet(String value, ParameterBindingContext bindingConte 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 @@ ParameterBindingContext bindingContext(MongoPersistentProperty property, Object () -> evaluationContextFor(property, source, spELContext)); } - ValueProvider valueProviderFor(Object source) { + ValueProvider valueProviderFor(@Nullable Object source) { return index -> { if (source instanceof Document document) { @@ -238,7 +238,7 @@ ValueProvider valueProviderFor(Object source) { }; } - 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 @@ EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object * @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 715327d18e..0698b08bf8 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 @@ Object resolveReference(MongoPersistentProperty property, Object source, */ class ReferenceCollection { - @Nullable // - private final String database; + private final @Nullable String database; private final String collection; /** @@ -95,8 +94,7 @@ public String getCollection() { * * @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 35cb578c23..ad3e4986fb 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 org.bson.Document; import org.bson.conversions.Bson; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; @@ -34,7 +35,6 @@ 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; /** @@ -129,7 +129,7 @@ public static boolean isUpdateObject(@Nullable Document updateObj) { * 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); @@ -140,7 +140,8 @@ protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersis } @Override - protected Entry<String, Object> getMappedObjectForField(Field field, Object rawValue) { + @SuppressWarnings("NullAway") + protected Entry<String, @Nullable Object> getMappedObjectForField(Field field, @Nullable Object rawValue) { if (isDocument(rawValue)) { @@ -196,11 +197,12 @@ private boolean isQuery(@Nullable Object value) { 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(); @@ -211,7 +213,7 @@ private Object getMappedModifier(@Nullable Field field, Modifier modifier) { : getMappedSort(sortObject, field.getPropertyEntity()); } - if (isAssociationConversionNecessary(field, value)) { + if (field != null && isAssociationConversionNecessary(field, value)) { if (ObjectUtils.isArray(value) || value instanceof Collection) { List<Object> targetPointers = new ArrayList<>(); for (Object val : converter.getConversionService().convert(value, List.class)) { @@ -229,7 +231,7 @@ private Object getMappedModifier(@Nullable Field field, Modifier modifier) { 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; @@ -247,7 +249,7 @@ private TypeInformation<?> getTypeHintForEntity(@Nullable Object source, MongoPe } @Override - protected Field createPropertyField(MongoPersistentEntity<?> entity, String key, + protected Field createPropertyField(@Nullable MongoPersistentEntity<?> entity, String key, MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) { return entity == null ? super.createPropertyField(entity, key, mappingContext) @@ -306,6 +308,7 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() { } @Override + @SuppressWarnings("NullAway") protected Converter<MongoPersistentProperty, String> getAssociationConverter() { return new UpdateAssociationConverter(getMappingContext(), getAssociation(), key); } @@ -333,7 +336,7 @@ public UpdateAssociationConverter( } @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 0a96cc867a..8eed053a09 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 @@ 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 4097be7704..b31d8a2b7c 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 @@ public interface EncryptingConverter<S, T> extends MongoValueConverter<S, T> { @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 @@ default S read(Object value, MongoConversionContext context) { * @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 @@ default T write(Object value, MongoConversionContext context) { * @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 f8d814fee4..07ddf93d48 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,12 +15,12 @@ */ 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.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. @@ -41,29 +41,33 @@ public MongoPersistentProperty getProperty() { 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> T read(@Nullable Object value, TypeInformation<T> target) { + public <T> @Nullable T read(@Nullable Object value, TypeInformation<T> target) { return conversionContext.read(value, target); } @Override - public <T> T write(@Nullable Object value, TypeInformation<T> target) { + public <T> @Nullable T write(@Nullable Object value, TypeInformation<T> 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 1ce24b25fe..9e4991dfb1 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 @@ -27,6 +27,7 @@ import org.bson.BsonValue; import org.bson.Document; import org.bson.types.Binary; +import org.jspecify.annotations.Nullable; import org.springframework.core.CollectionFactory; import org.springframework.data.mongodb.core.convert.MongoConversionContext; import org.springframework.data.mongodb.core.encryption.Encryption; @@ -36,7 +37,6 @@ import org.springframework.data.mongodb.core.mapping.Encrypted; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.util.BsonUtils; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -59,16 +59,15 @@ public MongoEncryptionConverter(Encryption<BsonValue, BsonBinary> encryption, En this.keyResolver = keyResolver; } - @Nullable @Override - public Object read(Object value, MongoConversionContext context) { + public @Nullable Object read(Object value, MongoConversionContext context) { Object decrypted = EncryptingConverter.super.read(value, context); return decrypted instanceof BsonValue bsonValue ? BsonUtils.toJavaType(bsonValue) : decrypted; } @Override - public Object decrypt(Object encryptedValue, EncryptionContext context) { + public @Nullable Object decrypt(Object encryptedValue, EncryptionContext context) { Object decryptedValue = encryptedValue; if (encryptedValue instanceof Binary || encryptedValue instanceof BsonBinary) { @@ -142,7 +141,8 @@ public Object decrypt(Object encryptedValue, EncryptionContext context) { } @Override - public Object encrypt(Object value, EncryptionContext context) { + @SuppressWarnings("NullAway") + public Object encrypt(@Nullable Object value, EncryptionContext context) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(String.format("Encrypting %s.%s.", getProperty(context).getOwner().getName(), diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java index 4a6f78357a..a0e8ea27f7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/package-info.java @@ -3,5 +3,5 @@ * <a href="https://www.mongodb.com/docs/manual/core/csfle/fundamentals/manual-encryption/">explicit encryption * mechanism of Client-Side Field Level Encryption</a>. */ -@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 cfa07fa8f9..dbef5cbb90 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 89beaadedb..a2a2083255 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,11 +15,11 @@ */ package org.springframework.data.mongodb.core.encryption; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; 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}. @@ -43,7 +43,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}. @@ -52,7 +52,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> T read(@Nullable Object value) { + default <T> @Nullable T read(@Nullable Object value) { return (T) read(value, getProperty().getTypeInformation()); } @@ -64,7 +64,7 @@ default <T> T read(@Nullable Object value) { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}. */ - default <T> T read(@Nullable Object value, Class<T> target) { + default <T> @Nullable T read(@Nullable Object value, Class<T> target) { return read(value, TypeInformation.of(target)); } @@ -76,7 +76,7 @@ default <T> T read(@Nullable Object value, Class<T> target) { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be read as an instance of {@link Class type}. */ - <T> T read(@Nullable Object value, TypeInformation<T> target); + <T> @Nullable T read(@Nullable Object value, TypeInformation<T> target); /** * Write the value as an instance of the {@link PersistentProperty#getTypeInformation() property type}. @@ -88,8 +88,7 @@ default <T> T read(@Nullable Object value, Class<T> target) { * @see PersistentProperty#getTypeInformation() * @see #write(Object, TypeInformation) */ - @Nullable - default <T> T write(@Nullable Object value) { + default <T> @Nullable T write(@Nullable Object value) { return (T) write(value, getProperty().getTypeInformation()); } @@ -101,8 +100,7 @@ default <T> T write(@Nullable Object value) { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}. */ - @Nullable - default <T> T write(@Nullable Object value, Class<T> target) { + default <T> @Nullable T write(@Nullable Object value, Class<T> target) { return write(value, TypeInformation.of(target)); } @@ -114,8 +112,7 @@ default <T> T write(@Nullable Object value, Class<T> target) { * @return can be {@literal null}. * @throws IllegalStateException if value cannot be written as an instance of {@link Class type}. */ - @Nullable - <T> T write(@Nullable Object value, TypeInformation<T> target); + <T> @Nullable T write(@Nullable Object value, TypeInformation<T> target); /** * Lookup the value for a given path within the current context. @@ -126,6 +123,6 @@ default <T> T write(@Nullable Object value, Class<T> target) { @Nullable Object lookupValue(String path); - EvaluationContext getEvaluationContext(Object source); + EvaluationContext getEvaluationContext(@Nullable Object source); } 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 f3906d89dd..90a3ab8720 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 <a href="https://www.mongodb.com/docs/manual/core/csfle/fundamentals/manual-encryption/">explicit * encryption mechanism of Client-Side Field Level Encryption</a>. */ -@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 2372700aec..74f36e3198 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.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 bc74a56df3..cfae6761a8 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.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 @@ private static void registerDeserializersIn(SimpleModule module) { */ private static abstract class GeoJsonDeserializer<T extends GeoJson<?>> extends JsonDeserializer<T> { - @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 T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext c * @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 T deserialize(@Nullable JsonParser jp, @Nullable DeserializationContext c * @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 @@ protected GeoJsonPoint toGeoJsonPoint(@Nullable ArrayNode node) { * @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 @@ protected GeoJsonLineString toLineString(ArrayNode node) { */ private static class GeoJsonPointDeserializer extends GeoJsonDeserializer<GeoJsonPoint> { - @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 8dafe9ea00..833a1dd9f6 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.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 bcb4c3e79e..e30ed5d6ed 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.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 12b9de9da4..a7e6306b49 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.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 166a10df08..990be290cd 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.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 GeoJsonPolygon(List<Point> points) { * @return new {@link GeoJsonPolygon}. * @since 1.10 */ + @Contract("_, _, _, _, _ -> 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 GeoJsonPolygon withInnerRing(Point first, Point second, Point third, Poin * @param points must not be {@literal null}. * @return new {@link GeoJsonPolygon}. */ + @Contract("_ -> new") public GeoJsonPolygon withInnerRing(List<Point> points) { return withInnerRing(new GeoJsonLineString(points)); } @@ -99,6 +102,7 @@ public GeoJsonPolygon withInnerRing(List<Point> points) { * @return new {@link GeoJsonPolygon}. * @since 1.10 */ + @Contract("_ -> 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 a482c136e7..47be645869 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 @@ 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 6cc77f832b..e5adfb26f4 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/GeospatialIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java index 0949506195..7188da6972 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 @@ -18,9 +18,10 @@ import java.util.Optional; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.util.MongoClientVersion; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -41,7 +42,7 @@ public class GeospatialIndex implements IndexDefinition { private @Nullable Integer max; private @Nullable Integer bits; private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D; - private Double bucketSize = MongoClientVersion.isVersion5orNewer() ? null : 1.0; + private @Nullable Double bucketSize = MongoClientVersion.isVersion5orNewer() ? null : 1.0; private @Nullable String additionalField; private Optional<IndexFilter> filter = Optional.empty(); private Optional<Collation> collation = Optional.empty(); @@ -62,6 +63,7 @@ public GeospatialIndex(String field) { * @param name must not be {@literal null} or empty. * @return this. */ + @Contract("_ -> this") public GeospatialIndex named(String name) { this.name = name; @@ -72,6 +74,7 @@ public GeospatialIndex named(String name) { * @param min * @return this. */ + @Contract("_ -> this") public GeospatialIndex withMin(int min) { this.min = min; return this; @@ -81,6 +84,7 @@ public GeospatialIndex withMin(int min) { * @param max * @return this. */ + @Contract("_ -> this") public GeospatialIndex withMax(int max) { this.max = max; return this; @@ -90,6 +94,7 @@ public GeospatialIndex withMax(int max) { * @param bits * @return this. */ + @Contract("_ -> this") public GeospatialIndex withBits(int bits) { this.bits = bits; return this; @@ -99,6 +104,7 @@ public GeospatialIndex withBits(int bits) { * @param type must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GeospatialIndex typed(GeoSpatialIndexType type) { Assert.notNull(type, "Type must not be null"); @@ -113,6 +119,7 @@ public GeospatialIndex typed(GeoSpatialIndexType type) { * @deprecated since MongoDB server version 4.4 */ @Deprecated + @Contract("_ -> this") public GeospatialIndex withBucketSize(double bucketSize) { this.bucketSize = bucketSize; return this; @@ -122,6 +129,7 @@ public GeospatialIndex withBucketSize(double bucketSize) { * @param fieldName * @return this. */ + @Contract("_ -> this") public GeospatialIndex withAdditionalField(String fieldName) { this.additionalField = fieldName; return this; @@ -136,6 +144,7 @@ public GeospatialIndex withAdditionalField(String fieldName) { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a> * @since 1.10 */ + @Contract("_ -> this") public GeospatialIndex partial(@Nullable IndexFilter filter) { this.filter = Optional.ofNullable(filter); @@ -152,6 +161,7 @@ public GeospatialIndex partial(@Nullable IndexFilter filter) { * @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 95f4226e28..91195a40f4 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.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 Index(String key, Direction direction) { 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 Index named(String name) { * @see <a href= * "https://docs.mongodb.org/manual/core/index-unique/">https://docs.mongodb.org/manual/core/index-unique/</a> */ + @Contract("-> this") public Index unique() { this.options.setUnique(Unique.YES); @@ -82,6 +86,7 @@ public Index unique() { * @see <a href= * "https://docs.mongodb.org/manual/core/index-sparse/">https://docs.mongodb.org/manual/core/index-sparse/</a> */ + @Contract("-> this") public Index sparse() { this.sparse = true; return this; @@ -92,7 +97,7 @@ public Index sparse() { * * @return this. * @since 1.5 - */ + */@Contract("-> this") public Index background() { this.background = true; @@ -107,6 +112,7 @@ public Index background() { * "https://www.mongodb.com/docs/manual/core/index-hidden/">https://www.mongodb.com/docs/manual/core/index-hidden/</a> * @since 4.1 */ + @Contract("-> this") public Index hidden() { options.setHidden(true); @@ -120,6 +126,7 @@ public Index hidden() { * @return this. * @since 1.5 */ + @Contract("_ -> this") public Index expire(long value) { return expire(value, TimeUnit.SECONDS); } @@ -132,6 +139,7 @@ public Index expire(long value) { * @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 Index expire(Duration timeout) { * @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 Index expire(long value, TimeUnit unit) { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a> * @since 1.10 */ + @Contract("_ -> this") public Index partial(@Nullable IndexFilter filter) { this.filter = Optional.ofNullable(filter); @@ -178,6 +188,7 @@ public Index partial(@Nullable IndexFilter filter) { * @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 a5cbf6c896..2e7268699c 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 String getKey() { * * @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 de7153bfb5..e9817746c3 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.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 IndexInfo(List<IndexField> indexFields, String name, boolean unique, bool */ 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<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements); @@ -105,9 +106,10 @@ public static IndexInfo indexInfoOf(Document sourceDocument) { } 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 static IndexInfo indexInfoOf(Document sourceDocument) { } } - 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 static IndexInfo indexInfoOf(Document sourceDocument) { * @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 ca3d951c94..aec1ba817d 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 887542cb0c..a390d1eb3e 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 @@ 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 @@ */ 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 void setExpire(Duration expire) { /** * @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 void setHidden(boolean hidden) { /** * @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 362247725f..3bb3fdbd0f 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 <jbrisbin@vmware.com> @@ -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 e20b0704cc..f1550d1501 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 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.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 boolean isIndexCreatorFor(MappingContext<?, ?> context) { return this.mappingContext.equals(context); } - @Nullable - private IndexInfo fetchIndexInformation(@Nullable IndexDefinitionHolder indexDefinition) { + private @Nullable IndexInfo fetchIndexInformation(@Nullable IndexDefinitionHolder indexDefinition) { if (indexDefinition == null) { return null; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java index a5988b8c1d..9daedff91c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java @@ -33,6 +33,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; @@ -61,7 +62,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.EvaluationContext; 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; @@ -165,7 +165,7 @@ private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, Mongo } if (persistentProperty.isEntity()) { - indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), + indexes.addAll(resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(persistentProperty), persistentProperty.isUnwrapped() ? "" : persistentProperty.getFieldName(), Path.of(persistentProperty), root.getCollection(), guard)); } @@ -232,7 +232,7 @@ private void guardAndPotentiallyAddIndexForProperty(MongoPersistentProperty pers if (persistentProperty.isEntity()) { try { - indexes.addAll(resolveIndexForEntity(mappingContext.getPersistentEntity(persistentProperty), + indexes.addAll(resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(persistentProperty), propertyDotPath.toString(), propertyPath, collection, guard)); } catch (CyclicPropertyReferenceException e) { LOGGER.info(e.getMessage()); @@ -385,7 +385,7 @@ public void doWithPersistentProperty(MongoPersistentProperty persistentProperty) try { appendTextIndexInformation(propertyDotPath, propertyPath, indexDefinitionBuilder, - mappingContext.getPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard); + mappingContext.getRequiredPersistentEntity(persistentProperty.getActualType()), optionsForNestedType, guard); } catch (CyclicPropertyReferenceException e) { LOGGER.info(e.getMessage()); } catch (InvalidDataAccessApiUsageException e) { @@ -520,8 +520,7 @@ private org.bson.Document resolveCompoundIndexKeyFromStringDefinition(String dot * @param persistentProperty * @return */ - @Nullable - protected IndexDefinitionHolder createIndexDefinition(String dotPath, String collection, + protected @Nullable IndexDefinitionHolder createIndexDefinition(String dotPath, String collection, MongoPersistentProperty persistentProperty) { Indexed index = persistentProperty.findAnnotation(Indexed.class); @@ -577,7 +576,7 @@ protected IndexDefinitionHolder createIndexDefinition(String dotPath, String col return new IndexDefinitionHolder(dotPath, indexDefinition, collection); } - private PartialIndexFilter evaluatePartialFilter(String filterExpression, PersistentEntity<?, ?> entity) { + private PartialIndexFilter evaluatePartialFilter(String filterExpression, @Nullable PersistentEntity<?, ?> entity) { Object result = ExpressionUtils.evaluate(filterExpression, () -> getEvaluationContextForProperty(entity)); @@ -588,7 +587,7 @@ private PartialIndexFilter evaluatePartialFilter(String filterExpression, Persis 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)); @@ -599,7 +598,7 @@ private org.bson.Document evaluateWildcardProjection(String projectionExpression 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) { @@ -692,8 +691,7 @@ public void setEvaluationContextProvider(EvaluationContextProvider evaluationCon * @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); @@ -812,8 +810,7 @@ private static Duration computeIndexTimeout(String timeoutValue, Supplier<Evalua * @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(() -> { @@ -1138,8 +1135,7 @@ public IncludeStrategy getStrategy() { 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 a87b15de45..0b473388fb 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.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 TextIndexDefinitionBuilder() { * @param name * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder named(String name) { this.instance.name = name; return this; @@ -246,6 +248,7 @@ public TextIndexDefinitionBuilder named(String name) { * * @return */ + @Contract("-> this") public TextIndexDefinitionBuilder onAllFields() { if (!instance.fieldSpecs.isEmpty()) { @@ -262,6 +265,7 @@ public TextIndexDefinitionBuilder onAllFields() { * @param fieldnames * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder onFields(String... fieldnames) { for (String fieldname : fieldnames) { @@ -276,6 +280,7 @@ public TextIndexDefinitionBuilder onFields(String... fieldnames) { * @param fieldname * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder onField(String fieldname) { return onField(fieldname, 1F); } @@ -286,6 +291,7 @@ public TextIndexDefinitionBuilder onField(String fieldname) { * @param fieldname * @return */ + @Contract("_, _ -> this") public TextIndexDefinitionBuilder onField(String fieldname, Float weight) { if (this.instance.fieldSpecs.contains(ALL_FIELDS)) { @@ -305,6 +311,7 @@ public TextIndexDefinitionBuilder onField(String fieldname, Float weight) { * @see <a href= * "https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index">https://docs.mongodb.org/manual/tutorial/specify-language-for-text-index/#specify-default-language-text-index</a> */ + @Contract("_ -> this") public TextIndexDefinitionBuilder withDefaultLanguage(String language) { this.instance.defaultLanguage = language; @@ -317,6 +324,7 @@ public TextIndexDefinitionBuilder withDefaultLanguage(String language) { * @param fieldname * @return */ + @Contract("_ -> this") public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) { if (StringUtils.hasText(this.instance.languageOverride)) { @@ -338,6 +346,7 @@ public TextIndexDefinitionBuilder withLanguageOverride(String fieldname) { * "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a> * @since 1.10 */ + @Contract("_ -> this") public TextIndexDefinitionBuilder partial(@Nullable IndexFilter filter) { this.instance.filter = filter; @@ -349,12 +358,14 @@ public TextIndexDefinitionBuilder partial(@Nullable IndexFilter filter) { * * @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/WildcardIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/WildcardIndex.java index dcd2b7c022..ff0a92ada1 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.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 WildcardIndex(@Nullable String path) { * * @return this. */ + @Contract("-> this") public WildcardIndex includeId() { wildcardProjection.put(FieldName.ID.name(), 1); @@ -89,6 +91,7 @@ public WildcardIndex includeId() { * @return this. */ @Override + @Contract("_ -> this") public WildcardIndex named(String name) { super.named(name); @@ -101,6 +104,7 @@ public WildcardIndex named(String name) { * @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 Index unique() { * @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 Index expire(long seconds) { * @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 Index expire(long value, TimeUnit timeUnit) { * @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 Index expire(Duration duration) { * @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 WildcardIndex wildcardProjectionInclude(String... paths) { * @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 WildcardIndex wildcardProjectionExclude(String... paths) { * @param includeExclude must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public WildcardIndex wildcardProjection(Map<String, Object> 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 c49f501d8d..8524ee62f7 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 3d68dbaac2..e865009319 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.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.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.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 String getLanguage() { return this.language; } - @Nullable @Override - public MongoPersistentProperty getTextScoreProperty() { + public @Nullable MongoPersistentProperty getTextScoreProperty() { return getPersistentProperty(TextScore.class); } @@ -151,7 +151,7 @@ public boolean hasTextScoreProperty() { } @Override - public org.springframework.data.mongodb.core.query.Collation getCollation() { + public @Nullable Collation getCollation() { Object collationValue = collationExpression != null ? collationExpression.evaluate(getValueEvaluationContext(null)) @@ -189,22 +189,22 @@ public void verify() { } @Override - public EvaluationContext getEvaluationContext(Object rootObject) { + public EvaluationContext getEvaluationContext(@Nullable Object rootObject) { return super.getEvaluationContext(rootObject); } @Override - public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) { + public EvaluationContext getEvaluationContext(@Nullable Object rootObject, ExpressionDependencies dependencies) { return super.getEvaluationContext(rootObject, dependencies); } @Override - public ValueEvaluationContext getValueEvaluationContext(Object rootObject) { + public ValueEvaluationContext getValueEvaluationContext(@Nullable Object rootObject) { return super.getValueEvaluationContext(rootObject); } @Override - public ValueEvaluationContext getValueEvaluationContext(Object rootObject, ExpressionDependencies dependencies) { + public ValueEvaluationContext getValueEvaluationContext(@Nullable Object rootObject, ExpressionDependencies dependencies) { return super.getValueEvaluationContext(rootObject, dependencies); } @@ -243,7 +243,11 @@ public int compare(@Nullable MongoPersistentProperty o1, @Nullable MongoPersiste return -1; } - return o1.getFieldOrder() - o2.getFieldOrder(); + if(o1 != null && o2 != null) { + return o1.getFieldOrder() - o2.getFieldOrder(); + } + + return o1 != null ? o1.getFieldOrder() : -1; } } @@ -257,7 +261,7 @@ public int compare(@Nullable MongoPersistentProperty o1, @Nullable MongoPersiste * @return can be {@literal null}. */ @Override - protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(MongoPersistentProperty property) { + protected @Nullable MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(MongoPersistentProperty property) { Assert.notNull(property, "MongoPersistentProperty must not be null"); @@ -268,7 +272,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul MongoPersistentProperty currentIdProperty = getIdProperty(); boolean currentIdPropertyIsSet = currentIdProperty != null; - @SuppressWarnings("null") + @SuppressWarnings("NullAway") boolean currentIdPropertyIsExplicit = currentIdPropertyIsSet && currentIdProperty.isExplicitIdProperty(); boolean newIdPropertyIsExplicit = property.isExplicitIdProperty(); @@ -277,7 +281,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul } - @SuppressWarnings("null") + @SuppressWarnings("NullAway") Field currentIdPropertyField = currentIdProperty.getField(); if (newIdPropertyIsExplicit && currentIdPropertyIsExplicit) { @@ -308,8 +312,7 @@ protected MongoPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNul * @param potentialExpression can be {@literal null} * @return can be {@literal null}. */ - @Nullable - private static ValueExpression detectExpression(@Nullable String potentialExpression) { + private static @Nullable ValueExpression detectExpression(@Nullable String potentialExpression) { if (!StringUtils.hasText(potentialExpression)) { return null; @@ -352,7 +355,7 @@ private void assertUniqueness(MongoPersistentProperty property) { } @Override - public Collection<Object> getEncryptionKeyIds() { + public @Nullable Collection<Object> getEncryptionKeyIds() { Encrypted encrypted = findAnnotation(Encrypted.class); if (encrypted == null) { @@ -405,6 +408,7 @@ private static void potentiallyAssertTextScoreType(MongoPersistentProperty persi } } + @SuppressWarnings("NullAway") private static void potentiallyAssertDBRefTargetType(MongoPersistentProperty persistentProperty) { if (persistentProperty.isDbReference() && persistentProperty.getDBRef().lazy()) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java index 5c3b4e6532..027a570fa3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java @@ -23,7 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.jspecify.annotations.Nullable; import org.springframework.core.env.StandardEnvironment; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.mapping.Association; @@ -39,7 +39,6 @@ import org.springframework.data.util.Lazy; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -153,8 +152,7 @@ public boolean hasExplicitFieldName() { return StringUtils.hasText(getAnnotatedFieldName()); } - @Nullable - private String getAnnotatedFieldName() { + private @Nullable String getAnnotatedFieldName() { org.springframework.data.mongodb.core.mapping.Field annotation = findAnnotation( org.springframework.data.mongodb.core.mapping.Field.class); @@ -197,9 +195,8 @@ public DBRef getDBRef() { return findAnnotation(DBRef.class); } - @Nullable @Override - public DocumentReference getDocumentReference() { + public @Nullable DocumentReference getDocumentReference() { return findAnnotation(DocumentReference.class); } @@ -258,6 +255,7 @@ public MongoField getMongoField() { } @Override + @SuppressWarnings("NullAway") public Collection<Object> getEncryptionKeyIds() { Encrypted encrypted = findAnnotation(Encrypted.class); @@ -282,6 +280,7 @@ public Collection<Object> getEncryptionKeyIds() { return target; } + @SuppressWarnings("NullAway") protected MongoField doGetMongoField() { MongoFieldBuilder builder = MongoField.builder(); @@ -295,6 +294,7 @@ protected MongoField doGetMongoField() { 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 105c38b288..eb8d08db64 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 boolean isDbReference() { } @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 6f0e1ae4c3..881d741ee4 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 String toString() { */ 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 static class MongoFieldBuilder { * @param fieldType * @return */ + @Contract("_ -> this") public MongoFieldBuilder fieldType(FieldType fieldType) { this.type = fieldType; @@ -163,6 +166,7 @@ public MongoFieldBuilder fieldType(FieldType fieldType) { * @param fieldName * @return */ + @Contract("_ -> this") public MongoFieldBuilder name(String fieldName) { Assert.hasText(fieldName, "Field name must not be empty"); @@ -178,6 +182,7 @@ public MongoFieldBuilder name(String fieldName) { * @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 MongoFieldBuilder path(String path) { * @param order * @return */ + @Contract("_ -> this") public MongoFieldBuilder order(int order) { this.order = order; @@ -204,7 +210,10 @@ public MongoFieldBuilder order(int order) { * * @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 76c0269861..4540493124 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 @@ 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.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<MongoPersistentE private FieldNamingStrategy fieldNamingStrategy = DEFAULT_NAMING_STRATEGY; private boolean autoIndexCreation = false; - @Nullable - private ApplicationContext applicationContext; + private @Nullable ApplicationContext applicationContext; /** * Creates a new {@link MongoMappingContext}. @@ -125,9 +124,8 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) { this.autoIndexCreation = autoCreateIndexes; } - @Nullable @Override - public MongoPersistentEntity<?> 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 e02bd00c8d..f1d67e4ae8 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 @@ 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<T> extends MutablePersistentEntity<T, Mon * @return {@literal null} if not set. * @since 2.2 */ - @Nullable - org.springframework.data.mongodb.core.query.Collation getCollation(); + @Nullable Collation getCollation(); /** * @return {@literal true} if the entity is annotated with diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java index e75ac015aa..cdbf940720 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/MongoPersistentProperty.java @@ -17,12 +17,12 @@ import java.util.Collection; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; /** * MongoDB specific {@link org.springframework.data.mapping.PersistentProperty} extension. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java index d78494d23b..01bb7792fa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/PersistentPropertyTranslator.java @@ -17,8 +17,8 @@ import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Predicates; -import org.springframework.lang.Nullable; /** * Utility to translate a {@link MongoPersistentProperty} into a corresponding property from a different diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java index 28a114a918..1976cc35f3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/ShardKey.java @@ -21,7 +21,7 @@ import java.util.List; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java index b3b73397ff..9b42a0d37e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrapEntityContext.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.core.mapping; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java index fed08815b8..5c404a9d12 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/UnwrappedMongoPersistentEntity.java @@ -24,6 +24,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.core.env.Environment; import org.springframework.data.mapping.*; import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory; @@ -31,7 +32,7 @@ import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.util.Streamable; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; /** * Unwrapped variant of {@link MongoPersistentEntity}. @@ -62,8 +63,7 @@ public String getLanguage() { } @Override - @Nullable - public MongoPersistentProperty getTextScoreProperty() { + public @Nullable MongoPersistentProperty getTextScoreProperty() { return delegate.getTextScoreProperty(); } @@ -73,8 +73,7 @@ public boolean hasTextScoreProperty() { } @Override - @Nullable - public Collation getCollation() { + public @Nullable Collation getCollation() { return delegate.getCollation(); } @@ -98,13 +97,7 @@ public String getName() { return delegate.getName(); } - @Override @Nullable - @Deprecated - public PreferredConstructor<T, MongoPersistentProperty> getPersistenceConstructor() { - return delegate.getPersistenceConstructor(); - } - @Override public InstanceCreatorMetadata<MongoPersistentProperty> getInstanceCreatorMetadata() { return delegate.getInstanceCreatorMetadata(); @@ -126,8 +119,7 @@ public boolean isVersionProperty(PersistentProperty<?> property) { } @Override - @Nullable - public MongoPersistentProperty getIdProperty() { + public @Nullable MongoPersistentProperty getIdProperty() { return delegate.getIdProperty(); } @@ -137,8 +129,7 @@ public MongoPersistentProperty getRequiredIdProperty() { } @Override - @Nullable - public MongoPersistentProperty getVersionProperty() { + public @Nullable MongoPersistentProperty getVersionProperty() { return delegate.getVersionProperty(); } @@ -148,8 +139,7 @@ public MongoPersistentProperty getRequiredVersionProperty() { } @Override - @Nullable - public MongoPersistentProperty getPersistentProperty(String name) { + public @Nullable MongoPersistentProperty getPersistentProperty(String name) { return wrap(delegate.getPersistentProperty(name)); } @@ -165,8 +155,7 @@ public MongoPersistentProperty getRequiredPersistentProperty(String name) { } @Override - @Nullable - public MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) { + public @Nullable MongoPersistentProperty getPersistentProperty(Class<? extends Annotation> annotationType) { return wrap(delegate.getPersistentProperty(annotationType)); } @@ -232,8 +221,7 @@ public void doWithAssociations(SimpleAssociationHandler handler) { } @Override - @Nullable - public <A extends Annotation> A findAnnotation(Class<A> annotationType) { + public <A extends Annotation> @Nullable A findAnnotation(Class<A> annotationType) { return delegate.findAnnotation(annotationType); } @@ -295,7 +283,9 @@ public Spliterator<MongoPersistentProperty> spliterator() { 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; } @@ -338,7 +328,7 @@ public boolean isUnwrapped() { } @Override - public Collection<Object> getEncryptionKeyIds() { + public @Nullable Collection<Object> 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 1d4877478f..ac7f24a555 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.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 @@ public UnwrappedMongoPersistentProperty(MongoPersistentProperty delegate, Unwrap } @Override + @SuppressWarnings("NullAway") public String getFieldName() { if (!context.getProperty().isUnwrapped()) { @@ -57,6 +58,7 @@ public String getFieldName() { } @Override + @SuppressWarnings("NullAway") public boolean hasExplicitFieldName() { return delegate.hasExplicitFieldName() || !ObjectUtils.isEmpty(context.getProperty().findAnnotation(Unwrapped.class).prefix()); @@ -108,14 +110,12 @@ public boolean isTextScoreProperty() { } @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 @@ public Class<?> getType() { } @Override + @SuppressWarnings("NullAway") public MongoField getMongoField() { if (!context.getProperty().isUnwrapped()) { @@ -165,8 +166,7 @@ public Iterable<? extends TypeInformation<?>> getPersistentEntityTypeInformation } @Override - @Nullable - public Method getGetter() { + public @Nullable Method getGetter() { return delegate.getGetter(); } @@ -176,8 +176,7 @@ public Method getRequiredGetter() { } @Override - @Nullable - public Method getSetter() { + public @Nullable Method getSetter() { return delegate.getSetter(); } @@ -187,8 +186,7 @@ public Method getRequiredSetter() { } @Override - @Nullable - public Method getWither() { + public @Nullable Method getWither() { return delegate.getWither(); } @@ -198,8 +196,7 @@ public Method getRequiredWither() { } @Override - @Nullable - public Field getField() { + public @Nullable Field getField() { return delegate.getField(); } @@ -209,14 +206,12 @@ public Field getRequiredField() { } @Override - @Nullable - public String getSpelExpression() { + public @Nullable String getSpelExpression() { return delegate.getSpelExpression(); } @Override - @Nullable - public Association<MongoPersistentProperty> getAssociation() { + public @Nullable Association<MongoPersistentProperty> getAssociation() { return delegate.getAssociation(); } @@ -291,8 +286,7 @@ public Collection<Object> getEncryptionKeyIds() { } @Override - @Nullable - public Class<?> getComponentType() { + public @Nullable Class<?> getComponentType() { return delegate.getComponentType(); } @@ -302,8 +296,7 @@ public Class<?> getRawType() { } @Override - @Nullable - public Class<?> getMapValueType() { + public @Nullable Class<?> getMapValueType() { return delegate.getMapValueType(); } @@ -313,8 +306,7 @@ public Class<?> getActualType() { } @Override - @Nullable - public <A extends Annotation> A findAnnotation(Class<A> annotationType) { + public <A extends Annotation> @Nullable A findAnnotation(Class<A> annotationType) { return delegate.findAnnotation(annotationType); } @@ -324,8 +316,7 @@ public <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType) t } @Override - @Nullable - public <A extends Annotation> A findPropertyOrOwnerAnnotation(Class<A> annotationType) { + public <A extends Annotation> @Nullable A findPropertyOrOwnerAnnotation(Class<A> annotationType) { return delegate.findPropertyOrOwnerAnnotation(annotationType); } @@ -340,13 +331,12 @@ public boolean hasActualTypeAnnotation(Class<? extends Annotation> annotationTyp } @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 73f4890dec..a8e2c93773 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 AbstractDeleteEvent(Document document, @Nullable Class<T> type, String co * * @return can be {@literal null}. */ - @Nullable - public Class<T> getType() { + public @Nullable Class<T> 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 55ccaa5f3f..10f4cdbbb7 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 49d509fb43..c826cadb4e 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 eec9a3edf1..bec1986720 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 @@ 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 0cc9d071a3..71ed503b20 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 0a513f1a18..f5c917d7d7 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 32a9ed5118..ed9c148a1c 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 9f34ec44e4..2e5f543534 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.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 @@ -65,6 +66,7 @@ public static MapReduceOptions options() { * @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; @@ -78,6 +80,7 @@ public MapReduceOptions limit(int limit) { * @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; @@ -91,6 +94,7 @@ public MapReduceOptions outputCollection(String collectionName) { * @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); @@ -105,6 +109,7 @@ public MapReduceOptions outputDatabase(@Nullable String outputDatabase) { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionInline() { this.mapReduceAction = null; @@ -119,6 +124,7 @@ public MapReduceOptions actionInline() { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionMerge() { this.mapReduceAction = MapReduceAction.MERGE; @@ -133,6 +139,7 @@ public MapReduceOptions actionMerge() { * @return this. * @since 3.0 */ + @Contract("-> this") public MapReduceOptions actionReduce() { this.mapReduceAction = MapReduceAction.REDUCE; @@ -146,6 +153,7 @@ public MapReduceOptions actionReduce() { * @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; @@ -159,6 +167,7 @@ public MapReduceOptions actionReplace() { * @param outputShared if true, output will be sharded based on _id key. * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions outputSharded(boolean outputShared) { this.outputSharded = Optional.of(outputShared); @@ -171,6 +180,7 @@ public MapReduceOptions outputSharded(boolean outputShared) { * @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); @@ -184,6 +194,7 @@ public MapReduceOptions finalizeFunction(@Nullable String finalizeFunction) { * @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<String, Object> scopeVariables) { this.scopeVariables = scopeVariables; @@ -197,6 +208,7 @@ public MapReduceOptions scopeVariables(Map<String, Object> scopeVariables) { * @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; @@ -208,6 +220,7 @@ public MapReduceOptions javaScriptMode(boolean javaScriptMode) { * * @return MapReduceOptions so that methods can be chained in a fluent API style */ + @Contract("_ -> this") public MapReduceOptions verbose(boolean verbose) { this.verbose = verbose; @@ -221,6 +234,7 @@ public MapReduceOptions verbose(boolean verbose) { * @return * @since 2.0 */ + @Contract("_ -> this") public MapReduceOptions collation(@Nullable Collation collation) { this.collation = Optional.ofNullable(collation); @@ -231,13 +245,11 @@ public Optional<String> getFinalizeFunction() { 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; } @@ -279,8 +291,7 @@ public Optional<Collation> getCollation() { * @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 865a4e9438..1d4f644bd1 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.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 MapReduceCounts getCounts() { 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 @@ private static String parseOutputCollection(Document rawResults) { 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 28de7fe850..d99f6d9237 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 65522d8613..c5f5840e6b 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 fec7fa60ef..e1da0b33ce 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 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 ChangeStreamOptions getChangeStreamOptions() { } @Override - public String getCollectionName() { + public @Nullable String getCollectionName() { return collectionName; } @Override - public String getDatabaseName() { + public @Nullable String getDatabaseName() { return databaseName; } @@ -253,6 +254,7 @@ private ChangeStreamRequestBuilder() {} * @param databaseName must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> database(String databaseName) { Assert.hasText(databaseName, "DatabaseName must not be null"); @@ -267,6 +269,7 @@ public ChangeStreamRequestBuilder<T> database(String databaseName) { * @param collectionName must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> collection(String collectionName) { Assert.hasText(collectionName, "CollectionName must not be null"); @@ -281,6 +284,7 @@ public ChangeStreamRequestBuilder<T> collection(String collectionName) { * @param messageListener must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> publishTo( MessageListener<ChangeStreamDocument<Document>, ? super T> messageListener) { @@ -308,6 +312,7 @@ public ChangeStreamRequestBuilder<T> publishTo( * @see ChangeStreamOptions#getFilter() * @see ChangeStreamOptionsBuilder#filter(Aggregation) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> filter(Aggregation aggregation) { Assert.notNull(aggregation, "Aggregation must not be null"); @@ -323,6 +328,7 @@ public ChangeStreamRequestBuilder<T> filter(Aggregation aggregation) { * @return this. * @see ChangeStreamOptions#getFilter() */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> filter(Document... pipeline) { Assert.notNull(pipeline, "Aggregation pipeline must not be null"); @@ -340,6 +346,7 @@ public ChangeStreamRequestBuilder<T> filter(Document... pipeline) { * @see ChangeStreamOptions#getCollation() * @see ChangeStreamOptionsBuilder#collation(Collation) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> collation(Collation collation) { Assert.notNull(collation, "Collation must not be null"); @@ -357,6 +364,7 @@ public ChangeStreamRequestBuilder<T> collation(Collation collation) { * @see ChangeStreamOptions#getResumeToken() * @see ChangeStreamOptionsBuilder#resumeToken(org.bson.BsonValue) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> resumeToken(BsonValue resumeToken) { Assert.notNull(resumeToken, "Resume token not be null"); @@ -373,6 +381,7 @@ public ChangeStreamRequestBuilder<T> resumeToken(BsonValue resumeToken) { * @see ChangeStreamOptions#getResumeTimestamp() * @see ChangeStreamOptionsBuilder#resumeAt(java.time.Instant) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> resumeAt(Instant clusterTime) { Assert.notNull(clusterTime, "ClusterTime must not be null"); @@ -388,6 +397,7 @@ public ChangeStreamRequestBuilder<T> resumeAt(Instant clusterTime) { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> resumeAfter(BsonValue resumeToken) { Assert.notNull(resumeToken, "ResumeToken must not be null"); @@ -403,6 +413,7 @@ public ChangeStreamRequestBuilder<T> resumeAfter(BsonValue resumeToken) { * @return this. * @since 2.2 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> startAfter(BsonValue resumeToken) { Assert.notNull(resumeToken, "ResumeToken must not be null"); @@ -418,6 +429,7 @@ public ChangeStreamRequestBuilder<T> startAfter(BsonValue resumeToken) { * @see ChangeStreamOptions#getFullDocumentLookup() * @see ChangeStreamOptionsBuilder#fullDocumentLookup(FullDocument) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> fullDocumentLookup(FullDocument lookup) { Assert.notNull(lookup, "FullDocument not be null"); @@ -434,6 +446,7 @@ public ChangeStreamRequestBuilder<T> fullDocumentLookup(FullDocument lookup) { * @see ChangeStreamOptions#getFullDocumentBeforeChangeLookup() * @see ChangeStreamOptionsBuilder#fullDocumentBeforeChangeLookup(FullDocumentBeforeChange) */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> fullDocumentBeforeChangeLookup(FullDocumentBeforeChange lookup) { Assert.notNull(lookup, "FullDocumentBeforeChange not be null"); @@ -448,6 +461,7 @@ public ChangeStreamRequestBuilder<T> fullDocumentBeforeChangeLookup(FullDocument * @param timeout must not be {@literal null}. * @since 3.0 */ + @Contract("_ -> this") public ChangeStreamRequestBuilder<T> maxAwaitTime(Duration timeout) { Assert.notNull(timeout, "timeout not be null"); @@ -459,6 +473,7 @@ public ChangeStreamRequestBuilder<T> maxAwaitTime(Duration timeout) { /** * @return the build {@link ChangeStreamRequest}. */ + @Contract("-> new") public ChangeStreamRequest<T> 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 fc8372613b..cc4d3f0bdb 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.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.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 @@ static class ChangeStreamEventMessage<T> implements Message<ChangeStreamDocument this.messageProperties = messageProperties; } - @Nullable @Override - public ChangeStreamDocument<Document> getRaw() { + public @Nullable ChangeStreamDocument<Document> 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 41b5fed4f5..662960284d 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.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<T, R> implements Task { private State state = State.CREATED; - private MongoCursor<T> cursor; + private @Nullable MongoCursor<T> cursor; /** * @param template must not be {@literal null}. @@ -109,6 +109,7 @@ public void run() { * 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 @@ public boolean awaitStart(Duration timeout) throws InterruptedException { return awaitStart.await(timeout.toNanos(), TimeUnit.NANOSECONDS); } + @SuppressWarnings("NullAway") protected Message<T, R> createMessage(T source, Class<R> targetType, RequestOptions options) { SimpleMessage<T, T> message = new SimpleMessage<>(source, source, MessageProperties.builder() @@ -209,11 +211,10 @@ private void emitMessage(Message<T, R> message) { } } - @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 @@ private static boolean isValidCursor(@Nullable MongoCursor<?> cursor) { * @return can be {@literal null}. * @throws RuntimeException The potentially translated exception. */ - @Nullable - private <V> V execute(Supplier<V> callback) { + private <V> @Nullable V execute(Supplier<V> 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 546f3fdd33..1b24e67e07 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 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 1c934e8302..f9a9c4131d 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<S, T> implements Message<S, T> { } @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 46db068096..e7aa5b036d 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<S, T> { * @return can be {@literal null}. * @since 4.0 */ - @Nullable - default T getBodyBeforeChange() { + default @Nullable T getBodyBeforeChange() { return null; } @@ -87,8 +87,7 @@ class MessageProperties { * * @return can be {@literal null}. */ - @Nullable - public String getDatabaseName() { + public @Nullable String getDatabaseName() { return databaseName; } @@ -97,8 +96,7 @@ public String getDatabaseName() { * * @return can be {@literal null}. */ - @Nullable - public String getCollectionName() { + public @Nullable String getCollectionName() { return collectionName; } @@ -162,6 +160,7 @@ public static class MessagePropertiesBuilder { * @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 MessagePropertiesBuilder databaseName(String dbName) { * @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 MessagePropertiesBuilder collectionName(String collectionName) { /** * @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 be5308e3cf..acb7bfd8a2 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<S, T> implements Message<S, T> { } @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 287ba293b6..7b914f16f5 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 @@ 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 @@ interface RequestOptions { * @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 @@ static RequestOptions justDatabase(String database) { 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 c6caef12fb..92e23ff847 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 @@ 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 static class TailableCursorRequestOptions implements SubscriptionRequest. TailableCursorRequestOptions() {} + @SuppressWarnings("NullAway") public static TailableCursorRequestOptions of(RequestOptions options) { return builder().collection(options.getCollectionName()).build(); } @@ -136,7 +138,7 @@ public static TailableCursorRequestOptionsBuilder builder() { } @Override - public String getCollectionName() { + public @Nullable String getCollectionName() { return collectionName; } @@ -163,6 +165,7 @@ private TailableCursorRequestOptionsBuilder() {} * @param collection must not be {@literal null} nor {@literal empty}. * @return this. */ + @Contract("_ -> this") public TailableCursorRequestOptionsBuilder collection(String collection) { Assert.hasText(collection, "Collection must not be null nor empty"); @@ -177,6 +180,7 @@ public TailableCursorRequestOptionsBuilder collection(String collection) { * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public TailableCursorRequestOptionsBuilder filter(Query filter) { Assert.notNull(filter, "Filter must not be null"); @@ -188,6 +192,7 @@ public TailableCursorRequestOptionsBuilder filter(Query filter) { /** * @return the built {@link TailableCursorRequestOptions}. */ + @Contract("-> new") public TailableCursorRequestOptions build() { TailableCursorRequestOptions options = new TailableCursorRequestOptions(); @@ -220,6 +225,7 @@ private TailableCursorRequestBuilder() {} * @param collectionName must not be {@literal null} nor empty. * @return this. */ + @Contract("_ -> this") public TailableCursorRequestBuilder<T> collection(String collectionName) { Assert.hasText(collectionName, "CollectionName must not be null"); @@ -234,6 +240,7 @@ public TailableCursorRequestBuilder<T> collection(String collectionName) { * @param messageListener must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public TailableCursorRequestBuilder<T> publishTo(MessageListener<Document, ? super T> messageListener) { Assert.notNull(messageListener, "MessageListener must not be null"); @@ -248,6 +255,7 @@ public TailableCursorRequestBuilder<T> publishTo(MessageListener<Document, ? sup * @param filter the {@link Query } to apply for filtering events. Must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public TailableCursorRequestBuilder<T> filter(Query filter) { Assert.notNull(filter, "Filter must not be null"); @@ -259,6 +267,7 @@ public TailableCursorRequestBuilder<T> filter(Query filter) { /** * @return the build {@link ChangeStreamRequest}. */ + @Contract("_ -> new") public TailableCursorRequest<T> 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 35be8f2ef8..aa879cc3c3 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. * <a href="https://docs.mongodb.com/manual/changeStreams/">Change Streams</a>. */ -@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 e2f9169d0d..cae1d3df48 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 8b1620b320..fd81030275 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 @@ 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 BasicQuery(Document queryObject, Document fieldsObject) { * @param query the query to copy. * @since 4.4 */ + @SuppressWarnings("NullAway") public BasicQuery(Query query) { super(query); @@ -101,6 +103,7 @@ public BasicQuery(Query 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 12843ce622..9ca1e1f1b8 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 @@ -15,21 +15,14 @@ */ package org.springframework.data.mongodb.core.query; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; import org.bson.Document; - -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; +import org.jspecify.annotations.Nullable; +import org.springframework.lang.Contract; /** - * {@link Document}-based {@link Update} variant. - * * @author Thomas Risberg * @author John Brisbin * @author Oliver Gierke @@ -41,114 +34,83 @@ public class BasicUpdate extends Update { private final Document updateObject; public BasicUpdate(String updateString) { - this(Document.parse(updateString)); + super(); + this.updateObject = Document.parse(updateString); } public BasicUpdate(Document updateObject) { + super(); this.updateObject = updateObject; } @Override + @Contract("_, _ -> this") public Update set(String key, @Nullable Object value) { - setOperationValue("$set", key, value); + updateObject.put("$set", Collections.singletonMap(key, value)); return this; } @Override + @Contract("_ -> this") public Update unset(String key) { - setOperationValue("$unset", key, 1); + updateObject.put("$unset", Collections.singletonMap(key, 1)); return this; } @Override + @Contract("_, _ -> this") public Update inc(String key, Number inc) { - setOperationValue("$inc", key, inc); + updateObject.put("$inc", Collections.singletonMap(key, inc)); return this; } @Override + @Contract("_, _ -> this") public Update push(String key, @Nullable Object value) { - setOperationValue("$push", key, value); + updateObject.put("$push", Collections.singletonMap(key, value)); return this; } @Override + @Contract("_, _ -> this") public Update addToSet(String key, @Nullable Object value) { - setOperationValue("$addToSet", key, value); + updateObject.put("$addToSet", Collections.singletonMap(key, value)); return this; } @Override + @Contract("_, _ -> this") public Update pop(String key, Position pos) { - setOperationValue("$pop", key, (pos == Position.FIRST ? -1 : 1)); + updateObject.put("$pop", Collections.singletonMap(key, (pos == Position.FIRST ? -1 : 1))); return this; } @Override + @Contract("_, _ -> this") public Update pull(String key, @Nullable Object value) { - setOperationValue("$pull", key, value); + updateObject.put("$pull", Collections.singletonMap(key, value)); return this; } @Override + @Contract("_, _ -> this") public Update pullAll(String key, Object[] values) { - setOperationValue("$pullAll", key, List.of(values), (o, o2) -> { - - if (o instanceof List<?> prev && o2 instanceof List<?> currentValue) { - List<Object> merged = new ArrayList<>(prev.size() + currentValue.size()); - merged.addAll(prev); - merged.addAll(currentValue); - return merged; - } - - return o2; - }); + Document keyValue = new Document(); + keyValue.put(key, Arrays.copyOf(values, values.length)); + updateObject.put("$pullAll", keyValue); return this; } @Override + @Contract("_, _ -> this") public Update rename(String oldName, String newName) { - setOperationValue("$rename", oldName, newName); + updateObject.put("$rename", Collections.singletonMap(oldName, newName)); return this; } - @Override - public boolean modifies(String key) { - return super.modifies(key) || Update.fromDocument(getUpdateObject()).modifies(key); - } - @Override public Document getUpdateObject() { return updateObject; } - void setOperationValue(String operator, String key, @Nullable Object value) { - setOperationValue(operator, key, value, (o, o2) -> o2); - } - - void setOperationValue(String operator, String key, @Nullable Object value, - BiFunction<Object, Object, Object> mergeFunction) { - - if (!updateObject.containsKey(operator)) { - updateObject.put(operator, Collections.singletonMap(key, value)); - } else { - Object o = updateObject.get(operator); - if (o instanceof Map<?, ?> existing) { - Map<Object, Object> target = new LinkedHashMap<>(existing); - - if (target.containsKey(key)) { - target.put(key, mergeFunction.apply(target.get(key), value)); - } else { - target.put(key, value); - } - updateObject.put(operator, target); - } else { - throw new IllegalStateException( - "Cannot add ['%s' : { '%s' : ... }]. Operator already exists with value of type [%s] which is not suitable for appending" - .formatted(operator, key, - o != null ? ClassUtils.getShortName(o.getClass()) : "null")); - } - } - } - } 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 de24c0511d..217e669883 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.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 static Collation from(Document source) { * @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 Collation strength(int strength) { * @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 Collation strength(ComparisonLevel comparisonLevel) { * @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 Collation caseLevel(boolean caseLevel) { * @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 Collation caseFirst(String caseFirst) { * @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 Collation caseFirst(CaseFirst sort) { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation numericOrderingEnabled() { return numericOrdering(true); } @@ -248,6 +255,7 @@ public Collation numericOrderingEnabled() { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation numericOrderingDisabled() { return numericOrdering(false); } @@ -257,6 +265,7 @@ public Collation numericOrderingDisabled() { * * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation numericOrdering(boolean flag) { Collation newInstance = copy(); @@ -271,6 +280,7 @@ public Collation numericOrdering(boolean flag) { * @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 Collation alternate(String alternate) { * @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 Collation alternate(Alternate alternate) { * * @return new {@link Collation}. */ + @Contract("_ -> new") public Collation backwardDiacriticSort() { return backwards(true); } @@ -305,6 +317,7 @@ public Collation backwardDiacriticSort() { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation forwardDiacriticSort() { return backwards(false); } @@ -315,6 +328,7 @@ public Collation forwardDiacriticSort() { * @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 Collation backwards(boolean backwards) { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation normalizationEnabled() { return normalization(true); } @@ -336,6 +351,7 @@ public Collation normalizationEnabled() { * * @return new {@link Collation}. */ + @Contract("-> new") public Collation normalizationDisabled() { return normalization(false); } @@ -346,6 +362,7 @@ public Collation normalizationDisabled() { * @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 Collation normalization(boolean normalization) { * @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 Collation maxVariable(String maxVariable) { * * @return the native MongoDB {@link Document} representation of the {@link Collation}. */ + @SuppressWarnings("NullAway") public Document toDocument() { return map(toMongoDocumentConverter()); } @@ -379,7 +398,7 @@ public Document toDocument() { * * @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 com.mongodb.client.model.Collation toMongoCollation() { * @param <R> * @return the converted result. */ - public <R> R map(Converter<? super Collation, ? extends R> mapper) { + public <R> @Nullable R map(Converter<? super Collation, ? extends R> 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 8d4cb703bb..547c6965ce 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.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.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 static Criteria expr(MongoExpression expression) { * * @return new instance of {@link Criteria}. */ + @Contract("_ -> new") public Criteria and(String key) { return new Criteria(this.criteriaChain, key); } @@ -194,6 +196,7 @@ public Criteria and(String key) { * @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 Criteria is(@Nullable Object value) { * Missing Fields: Equality Filter</a> * @since 3.3 */ + @Contract("_ -> this") public Criteria isNull() { return is(null); } @@ -237,6 +241,7 @@ public Criteria isNull() { * Fields: Type Check</a> * @since 3.3 */ + @Contract("_ -> this") public Criteria isNullValue() { criteria.put("$type", BsonType.NULL.getValue()); @@ -254,6 +259,7 @@ private boolean lastOperatorWasNot() { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/ne/">MongoDB Query operator: $ne</a> */ + @Contract("_ -> this") public Criteria ne(@Nullable Object value) { criteria.put("$ne", value); return this; @@ -266,6 +272,7 @@ public Criteria ne(@Nullable Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/lt/">MongoDB Query operator: $lt</a> */ + @Contract("_ -> this") public Criteria lt(Object value) { criteria.put("$lt", value); return this; @@ -278,6 +285,7 @@ public Criteria lt(Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/lte/">MongoDB Query operator: $lte</a> */ + @Contract("_ -> this") public Criteria lte(Object value) { criteria.put("$lte", value); return this; @@ -290,6 +298,7 @@ public Criteria lte(Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/gt/">MongoDB Query operator: $gt</a> */ + @Contract("_ -> this") public Criteria gt(Object value) { criteria.put("$gt", value); return this; @@ -302,6 +311,7 @@ public Criteria gt(Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/gte/">MongoDB Query operator: $gte</a> */ + @Contract("_ -> this") public Criteria gte(Object value) { criteria.put("$gte", value); return this; @@ -314,7 +324,8 @@ public Criteria gte(Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/in/">MongoDB Query operator: $in</a> */ - 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 Criteria in(Object... values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/in/">MongoDB Query operator: $in</a> */ + @Contract("_ -> this") public Criteria in(Collection<?> values) { criteria.put("$in", values); return this; @@ -342,6 +354,7 @@ public Criteria in(Collection<?> values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nin/">MongoDB Query operator: $nin</a> */ + @Contract("_ -> this") public Criteria nin(Object... values) { return nin(Arrays.asList(values)); } @@ -353,6 +366,7 @@ public Criteria nin(Object... values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nin/">MongoDB Query operator: $nin</a> */ + @Contract("_ -> this") public Criteria nin(Collection<?> values) { criteria.put("$nin", values); return this; @@ -366,6 +380,7 @@ public Criteria nin(Collection<?> values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/mod/">MongoDB Query operator: $mod</a> */ + @Contract("_ -> this") public Criteria mod(Number value, Number remainder) { List<Object> l = new ArrayList<>(2); l.add(value); @@ -381,6 +396,7 @@ public Criteria mod(Number value, Number remainder) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/all/">MongoDB Query operator: $all</a> */ + @Contract("_ -> this") public Criteria all(Object... values) { return all(Arrays.asList(values)); } @@ -392,6 +408,7 @@ public Criteria all(Object... values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/all/">MongoDB Query operator: $all</a> */ + @Contract("_ -> this") public Criteria all(Collection<?> values) { criteria.put("$all", values); return this; @@ -404,6 +421,7 @@ public Criteria all(Collection<?> values) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/size/">MongoDB Query operator: $size</a> */ + @Contract("_ -> this") public Criteria size(int size) { criteria.put("$size", size); return this; @@ -416,6 +434,7 @@ public Criteria size(int size) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/exists/">MongoDB Query operator: $exists</a> */ + @Contract("_ -> this") public Criteria exists(boolean value) { criteria.put("$exists", value); return this; @@ -431,6 +450,7 @@ public Criteria exists(boolean value) { * $sampleRate</a> * @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 Criteria sampleRate(double sampleRate) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a> */ + @Contract("_ -> this") public Criteria type(int typeNumber) { criteria.put("$type", typeNumber); return this; @@ -460,6 +481,7 @@ public Criteria type(int typeNumber) { * @since 2.1 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a> */ + @Contract("_ -> this") public Criteria type(Type... types) { Assert.notNull(types, "Types must not be null"); @@ -476,6 +498,7 @@ public Criteria type(Type... types) { * @since 3.2 * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/type/">MongoDB Query operator: $type</a> */ + @Contract("_ -> this") public Criteria type(Collection<Type> types) { Assert.notNull(types, "Types must not be null"); @@ -490,6 +513,7 @@ public Criteria type(Collection<Type> types) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/not/">MongoDB Query operator: $not</a> */ + @Contract("-> this") public Criteria not() { return not(null); } @@ -501,6 +525,7 @@ public Criteria not() { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/not/">MongoDB Query operator: $not</a> */ + @Contract("_ -> this") private Criteria not(@Nullable Object value) { criteria.put("$not", value); return this; @@ -513,6 +538,7 @@ private Criteria not(@Nullable Object value) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/regex/">MongoDB Query operator: $regex</a> */ + @Contract("_ -> this") public Criteria regex(String regex) { return regex(regex, null); } @@ -525,6 +551,7 @@ public Criteria regex(String regex) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/regex/">MongoDB Query operator: $regex</a> */ + @Contract("_, _ -> this") public Criteria regex(String regex, @Nullable String options) { return regex(toPattern(regex, options)); } @@ -535,6 +562,7 @@ public Criteria regex(String regex, @Nullable String options) { * @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 Criteria regex(Pattern pattern) { * @param regex must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Criteria regex(BsonRegularExpression regex) { if (lastOperatorWasNot()) { @@ -581,6 +610,7 @@ private Pattern toPattern(String regex, @Nullable String options) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/centerSphere/">MongoDB Query operator: * $centerSphere</a> */ + @Contract("_ -> this") public Criteria withinSphere(Circle circle) { Assert.notNull(circle, "Circle must not be null"); @@ -597,6 +627,7 @@ public Criteria withinSphere(Circle circle) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/geoWithin/">MongoDB Query operator: * $geoWithin</a> */ + @Contract("_ -> this") public Criteria within(Shape shape) { Assert.notNull(shape, "Shape must not be null"); @@ -612,6 +643,7 @@ public Criteria within(Shape shape) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/near/">MongoDB Query operator: $near</a> */ + @Contract("_ -> this") public Criteria near(Point point) { Assert.notNull(point, "Point must not be null"); @@ -629,6 +661,7 @@ public Criteria near(Point point) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/nearSphere/">MongoDB Query operator: * $nearSphere</a> */ + @Contract("_ -> this") public Criteria nearSphere(Point point) { Assert.notNull(point, "Point must not be null"); @@ -646,6 +679,7 @@ public Criteria nearSphere(Point point) { * @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 Criteria intersects(GeoJson geoJson) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/maxDistance/">MongoDB Query operator: * $maxDistance</a> */ + @Contract("_ -> this") public Criteria maxDistance(double maxDistance) { if (createNearCriteriaForCommand("$near", "$maxDistance", maxDistance) @@ -687,6 +722,7 @@ public Criteria maxDistance(double maxDistance) { * @return this. * @since 1.7 */ + @Contract("_ -> this") public Criteria minDistance(double minDistance) { if (createNearCriteriaForCommand("$near", "$minDistance", minDistance) @@ -706,6 +742,7 @@ public Criteria minDistance(double minDistance) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/elemMatch/">MongoDB Query operator: * $elemMatch</a> */ + @Contract("_ -> this") public Criteria elemMatch(Criteria criteria) { this.criteria.put("$elemMatch", criteria.getCriteriaObject()); return this; @@ -718,6 +755,7 @@ public Criteria elemMatch(Criteria criteria) { * @return this. * @since 1.8 */ + @Contract("_ -> this") public Criteria alike(Example<?> sample) { if (StringUtils.hasText(this.getKey())) { @@ -745,6 +783,7 @@ public Criteria alike(Example<?> sample) { * @see <a href="https://docs.mongodb.com/manual/reference/operator/query/jsonSchema/">MongoDB Query operator: * $jsonSchema</a> */ + @Contract("_ -> this") public Criteria andDocumentStructureMatches(MongoJsonSchema schema) { Assert.notNull(schema, "Schema must not be null"); @@ -776,6 +815,7 @@ public BitwiseCriteriaOperators bits() { * @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 Criteria orOperator(Criteria... criteria) { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria orOperator(Collection<Criteria> criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -810,6 +851,7 @@ public Criteria orOperator(Collection<Criteria> criteria) { * @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 Criteria norOperator(Criteria... criteria) { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria norOperator(Collection<Criteria> criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -844,6 +887,7 @@ public Criteria norOperator(Collection<Criteria> criteria) { * @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 Criteria andOperator(Criteria... criteria) { * @return this. * @since 3.2 */ + @Contract("_ -> this") public Criteria andOperator(Collection<Criteria> criteria) { Assert.notNull(criteria, "Criteria must not be null"); @@ -884,8 +929,7 @@ private Criteria registerCriteriaChainElement(Criteria criteria) { * @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 @@ private boolean isEqual(@Nullable Object left, @Nullable Object right) { 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 c00b1d4b82..c75f709ab9 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 3540a5a836..9775fefdb0 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.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 FieldProjectionExpression project(MongoExpression expression) { * @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 Field projectAs(MongoExpression expression, String 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 Field include(String... fields) { * @return {@code this} field projection instance. * @since 4.4 */ + @Contract("_ -> this") public Field include(Collection<String> fields) { Assert.notNull(fields, "Keys must not be null"); @@ -149,6 +154,7 @@ public Field include(Collection<String> fields) { * @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 Field exclude(String 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 Field exclude(String... fields) { * @return {@code this} field projection instance. * @since 4.4 */ + @Contract("_ -> this") public Field exclude(Collection<String> fields) { Assert.notNull(fields, "Keys must not be null"); @@ -191,6 +199,7 @@ public Field exclude(Collection<String> fields) { * @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 Field slice(String field, int size) { * @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 Field elemMatch(String field, Criteria elemMatchCriteria) { * @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 83417c7200..19ecd94e23 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 @@ 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 5757aa94a2..5ec4af3989 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.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 @@ private enum MetaKey { private Map<String, Object> values = Collections.emptyMap(); private Set<CursorOption> flags = Collections.emptySet(); - private Integer cursorBatchSize; - private Boolean allowDiskUse; + private @Nullable Integer cursorBatchSize; + private @Nullable Boolean allowDiskUse; public Meta() {} @@ -85,8 +85,7 @@ public boolean hasMaxTime() { /** * @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 void setComment(String comment) { * @return {@literal null} if not set. * @since 2.1 */ - @Nullable - public Integer getCursorBatchSize() { + public @Nullable Integer getCursorBatchSize() { return cursorBatchSize; } @@ -285,9 +283,8 @@ void setValue(String key, @Nullable Object value) { this.values.put(key, value); } - @Nullable @SuppressWarnings("unchecked") - private <T> T getValue(String key) { + private <T> @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 571bbd275c..5625de5e93 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.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 @@ static ConversionMultiplierBuilder builder() { */ private static class ConversionMultiplierBuilder { - private Number from; - private Number to; + private @Nullable Number from; + private @Nullable Number to; ConversionMultiplierBuilder() {} @@ -177,6 +179,9 @@ ConversionMultiplierBuilder to(Metric to) { } 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 e26a61c61e..b37c088981 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 @@ 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 MatchMode { * @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 f0f3b0a4dc..6dad07b8cb 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 @@ 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.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 Metric getMetric() { * @return * @since 2.2 */ + @Contract("_ -> this") public NearQuery limit(long limit) { this.limit = limit; return this; @@ -289,6 +291,7 @@ public NearQuery limit(long limit) { * @param skip * @return */ + @Contract("_ -> this") public NearQuery skip(long skip) { this.skip = skip; return this; @@ -300,6 +303,7 @@ public NearQuery skip(long skip) { * @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 NearQuery with(Pageable pageable) { * @param maxDistance * @return */ + @Contract("_ -> this") public NearQuery maxDistance(double maxDistance) { return maxDistance(new Distance(maxDistance, getMetric())); } @@ -335,6 +340,7 @@ public NearQuery maxDistance(double maxDistance) { * @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 NearQuery maxDistance(double maxDistance, Metric metric) { * @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 NearQuery maxDistance(Distance distance) { * @return * @since 1.7 */ + @Contract("_ -> this") public NearQuery minDistance(double minDistance) { return minDistance(new Distance(minDistance, getMetric())); } @@ -392,6 +400,7 @@ public NearQuery minDistance(double minDistance) { * @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 NearQuery minDistance(double minDistance, Metric metric) { * @return * @since 1.7 */ + @Contract("_ -> this") public NearQuery minDistance(Distance distance) { Assert.notNull(distance, "Distance must not be null"); @@ -428,8 +438,7 @@ public NearQuery minDistance(Distance distance) { * * @return */ - @Nullable - public Distance getMaxDistance() { + public @Nullable Distance getMaxDistance() { return this.maxDistance; } @@ -439,8 +448,7 @@ public Distance getMaxDistance() { * @return * @since 1.7 */ - @Nullable - public Distance getMinDistance() { + public @Nullable Distance getMinDistance() { return this.minDistance; } @@ -450,6 +458,7 @@ public Distance getMinDistance() { * @param distanceMultiplier * @return */ + @Contract("_ -> this") public NearQuery distanceMultiplier(double distanceMultiplier) { this.metric = new CustomMetric(distanceMultiplier); @@ -462,6 +471,7 @@ public NearQuery distanceMultiplier(double distanceMultiplier) { * @param spherical * @return */ + @Contract("_ -> this") public NearQuery spherical(boolean spherical) { this.spherical = spherical; return this; @@ -482,6 +492,7 @@ public boolean isSpherical() { * * @return */ + @Contract("-> this") public NearQuery inKilometers() { return adaptMetric(Metrics.KILOMETERS); } @@ -492,6 +503,7 @@ public NearQuery inKilometers() { * * @return */ + @Contract("-> this") public NearQuery inMiles() { return adaptMetric(Metrics.MILES); } @@ -504,6 +516,7 @@ public NearQuery inMiles() { * passed. * @return */ + @Contract("_ -> this") public NearQuery in(@Nullable Metric metric) { return adaptMetric(metric == null ? Metrics.NEUTRAL : metric); } @@ -514,6 +527,7 @@ public NearQuery in(@Nullable Metric metric) { * * @param metric */ + @Contract("_ -> this") private NearQuery adaptMetric(Metric metric) { if (metric != Metrics.NEUTRAL) { @@ -530,6 +544,7 @@ private NearQuery adaptMetric(Metric metric) { * @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 NearQuery query(Query query) { /** * @return the number of elements to skip. */ - @Nullable - public Long getSkip() { + public @Nullable Long getSkip() { return skip; } @@ -557,8 +571,7 @@ public Long getSkip() { * @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 Collation getCollation() { * @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 NearQuery withReadConcern(ReadConcern readConcern) { * @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 NearQuery withReadPreference(ReadPreference readPreference) { * @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 ReadConcern getReadConcern() { * @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 31c6b9069f..47ce615fe3 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.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.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 Query(CriteriaDefinition criteriaDefinition) { * @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 Field fields() { * @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 Query skip(long skip) { * @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 Query limit(int limit) { * @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 Query limit(Limit limit) { * @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 Query withHint(String hint) { * @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 Query withReadConcern(ReadConcern readConcern) { * @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 boolean hasReadConcern() { } @Override - public ReadConcern getReadConcern() { + public @Nullable ReadConcern getReadConcern() { return this.readConcern; } @@ -253,7 +262,7 @@ public boolean hasReadPreference() { } @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 ReadPreference getReadPreference() { * @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 Query withHint(Document hint) { * @param pageable must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public Query with(Pageable pageable) { if (pageable.isPaged()) { @@ -299,6 +310,7 @@ public Query with(Pageable pageable) { * @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 Query with(ScrollPosition position) { * @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 Query with(OffsetScrollPosition position) { * @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 boolean hasKeyset() { return keysetScrollPosition != null; } - @Nullable - public KeysetScrollPosition getKeyset() { + public @Nullable KeysetScrollPosition getKeyset() { return keysetScrollPosition; } @@ -360,6 +373,7 @@ public KeysetScrollPosition getKeyset() { * @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 Set<Class<?>> getRestrictedTypes() { * @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 String getHint() { * @see Meta#setMaxTimeMsec(long) * @since 1.6 */ + @Contract("_ -> this") public Query maxTimeMsec(long maxTimeMsec) { meta.setMaxTimeMsec(maxTimeMsec); @@ -530,6 +546,7 @@ public Query maxTimeMsec(long maxTimeMsec) { * @see Meta#setMaxTime(Duration) * @since 2.1 */ + @Contract("_ -> this") public Query maxTime(Duration timeout) { meta.setMaxTime(timeout); @@ -544,6 +561,7 @@ public Query maxTime(Duration timeout) { * @see Meta#setComment(String) * @since 1.6 */ + @Contract("_ -> this") public Query comment(String comment) { meta.setComment(comment); @@ -562,6 +580,7 @@ public Query comment(String comment) { * @see Meta#setAllowDiskUse(Boolean) * @since 3.2 */ + @Contract("_ -> this") public Query allowDiskUse(boolean allowDiskUse) { meta.setAllowDiskUse(allowDiskUse); @@ -578,6 +597,7 @@ public Query allowDiskUse(boolean allowDiskUse) { * @see Meta#setCursorBatchSize(int) * @since 2.1 */ + @Contract("_ -> this") public Query cursorBatchSize(int batchSize) { meta.setCursorBatchSize(batchSize); @@ -589,6 +609,7 @@ public Query cursorBatchSize(int batchSize) { * @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 Query noCursorTimeout() { * @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 Query exhaust() { * @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 Query allowSecondaryReads() { * @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 void setMeta(Meta meta) { * @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 11e0f7fb24..29f8adb2c6 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.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 @@ private static void toFlatMap(String currentPath, Object source, Map<String, Obj * @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 bd6d8c3469..cc87434178 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 Term(String raw, @Nullable Type type) { * * @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 e1a7d0c4d0..5cedc2e476 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.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 static TextCriteria forDefaultLanguage() { * @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 static TextCriteria forLanguage(String language) { * @param words the words to match. * @return */ + @Contract("_ -> this") public TextCriteria matchingAny(String... words) { for (String word : words) { @@ -97,6 +100,7 @@ public TextCriteria matchingAny(String... words) { * * @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 TextCriteria matching(Term term) { * @param term * @return */ + @Contract("_ -> this") public TextCriteria matching(String term) { if (StringUtils.hasText(term)) { @@ -121,6 +126,7 @@ public TextCriteria matching(String term) { * @param term * @return */ + @Contract("_ -> this") public TextCriteria notMatching(String term) { if (StringUtils.hasText(term)) { @@ -133,6 +139,7 @@ public TextCriteria notMatching(String term) { * @param words * @return */ + @Contract("_ -> this") public TextCriteria notMatchingAny(String... words) { for (String word : words) { @@ -147,6 +154,7 @@ public TextCriteria notMatchingAny(String... words) { * @param phrase * @return */ + @Contract("_ -> this") public TextCriteria notMatchingPhrase(String phrase) { if (StringUtils.hasText(phrase)) { @@ -161,6 +169,7 @@ public TextCriteria notMatchingPhrase(String phrase) { * @param phrase * @return */ + @Contract("_ -> this") public TextCriteria matchingPhrase(String phrase) { if (StringUtils.hasText(phrase)) { @@ -176,6 +185,7 @@ public TextCriteria matchingPhrase(String phrase) { * @return never {@literal null}. * @since 1.10 */ + @Contract("_ -> this") public TextCriteria caseSensitive(boolean caseSensitive) { this.caseSensitive = caseSensitive; @@ -189,6 +199,7 @@ public TextCriteria caseSensitive(boolean caseSensitive) { * @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 a6583299d6..a9f82a857f 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.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 static TextQuery queryText(TextCriteria criteria) { * @see TextQuery#includeScore() * @return this. */ + @Contract("-> this") public TextQuery sortByScore() { this.sortByScoreIndex = getSortObject().size(); @@ -113,6 +115,7 @@ public TextQuery sortByScore() { * * @return this. */ + @Contract("-> this") public TextQuery includeScore() { this.includeScore = true; @@ -125,6 +128,7 @@ public TextQuery includeScore() { * @param fieldname must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public TextQuery includeScore(String fieldname) { setScoreFieldName(fieldname); @@ -170,9 +174,8 @@ public Document getSortObject() { 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 677575c9e4..c02425214d 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 @@ 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 32d98f5804..cfb214a5a3 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.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 static Update fromDocument(Document object, String... exclude) { * @return this. * @see <a href="https://docs.mongodb.com/manual/reference/operator/update/set/">MongoDB Update operator: $set</a> */ + @Contract("_, _ -> this") public Update set(String key, @Nullable Object value) { addMultiFieldOperation("$set", key, value); return this; @@ -128,6 +130,7 @@ public Update set(String key, @Nullable Object value) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/setOnInsert/">MongoDB Update operator: * $setOnInsert</a> */ + @Contract("_, _ -> this") public Update setOnInsert(String key, @Nullable Object value) { addMultiFieldOperation("$setOnInsert", key, value); return this; @@ -140,6 +143,7 @@ public Update setOnInsert(String key, @Nullable Object value) { * @return this. * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/unset/">MongoDB Update operator: $unset</a> */ + @Contract("_ -> this") public Update unset(String key) { addMultiFieldOperation("$unset", key, 1); return this; @@ -153,12 +157,14 @@ public Update unset(String key) { * @return this. * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/inc/">MongoDB Update operator: $inc</a> */ + @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 void inc(String key) { * @return this. * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/push/">MongoDB Update operator: $push</a> */ + @Contract("_, _ -> this") public Update push(String key, @Nullable Object value) { addMultiFieldOperation("$push", key, value); return this; @@ -207,6 +214,7 @@ public PushOperatorBuilder push(String key) { * @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 AddToSetBuilder addToSet(String key) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/addToSet/">MongoDB Update operator: * $addToSet</a> */ + @Contract("_, _ -> this") public Update addToSet(String key, @Nullable Object value) { addMultiFieldOperation("$addToSet", key, value); return this; @@ -233,6 +242,7 @@ public Update addToSet(String key, @Nullable Object value) { * @return this. * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pop/">MongoDB Update operator: $pop</a> */ + @Contract("_, _ -> this") public Update pop(String key, Position pos) { addMultiFieldOperation("$pop", key, pos == Position.FIRST ? -1 : 1); return this; @@ -246,6 +256,7 @@ public Update pop(String key, Position pos) { * @return this. * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pull/">MongoDB Update operator: $pull</a> */ + @Contract("_, _ -> this") public Update pull(String key, @Nullable Object value) { addMultiFieldOperation("$pull", key, value); return this; @@ -260,6 +271,7 @@ public Update pull(String key, @Nullable Object value) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/pullAll/">MongoDB Update operator: * $pullAll</a> */ + @Contract("_, _ -> this") public Update pullAll(String key, Object[] values) { addMultiFieldOperation("$pullAll", key, Arrays.asList(values)); return this; @@ -274,6 +286,7 @@ public Update pullAll(String key, Object[] values) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/rename/">MongoDB Update operator: * $rename</a> */ + @Contract("_, _ -> this") public Update rename(String oldName, String newName) { addMultiFieldOperation("$rename", oldName, newName); return this; @@ -288,6 +301,7 @@ public Update rename(String oldName, String newName) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator: * $currentDate</a> */ + @Contract("_ -> this") public Update currentDate(String key) { addMultiFieldOperation("$currentDate", key, true); @@ -303,6 +317,7 @@ public Update currentDate(String key) { * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/currentDate/">MongoDB Update operator: * $currentDate</a> */ + @Contract("_ -> this") public Update currentTimestamp(String key) { addMultiFieldOperation("$currentDate", key, new Document("$type", "timestamp")); @@ -318,6 +333,7 @@ public Update currentTimestamp(String key) { * @since 1.7 * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/mul/">MongoDB Update operator: $mul</a> */ + @Contract("_, _ -> this") public Update multiply(String key, Number multiplier) { Assert.notNull(multiplier, "Multiplier must not be null"); @@ -335,6 +351,7 @@ public Update multiply(String key, Number multiplier) { * @see <a href="https://docs.mongodb.com/manual/reference/bson-type-comparison-order/">Comparison/Sort Order</a> * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/max/">MongoDB Update operator: $max</a> */ + @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 Update max(String key, Object value) { * @see <a href="https://docs.mongodb.com/manual/reference/bson-type-comparison-order/">Comparison/Sort Order</a> * @see <a href="https://docs.mongodb.org/manual/reference/operator/update/min/">MongoDB Update operator: $min</a> */ + @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 Update min(String key, Object value) { * @return this. * @since 1.7 */ + @Contract("_ -> new") public BitwiseOperatorBuilder bitwise(String key) { return new BitwiseOperatorBuilder(this, key); } @@ -378,6 +397,7 @@ public BitwiseOperatorBuilder bitwise(String key) { * @return this. * @since 2.0 */ + @Contract("-> this") public Update isolated() { isolated = true; @@ -392,6 +412,7 @@ public Update isolated() { * @return this. * @since 2.2 */ + @Contract("_ -> this") public Update filterArray(CriteriaDefinition criteria) { if (arrayFilters == Collections.EMPTY_LIST) { @@ -411,6 +432,7 @@ public Update filterArray(CriteriaDefinition criteria) { * @return this. * @since 2.2 */ + @Contract("_, _ -> this") public Update filterArray(String identifier, Object expression) { if (arrayFilters == Collections.EMPTY_LIST) { @@ -815,6 +837,7 @@ public Update each(Object... values) { * @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 PushOperatorBuilder slice(int count) { * @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 PushOperatorBuilder sort(Direction direction) { * @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 PushOperatorBuilder sort(Sort sort) { * @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 PushOperatorBuilder atPosition(int position) { * @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 d3f67790a1..7c6889e45b 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 b59c20c6b6..da77a0199f 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 29cedfd6ce..3b7c3255eb 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,7 +23,7 @@ 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.schema.TypedJsonSchemaObject.ArrayJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.BooleanJsonSchemaObject; @@ -33,7 +33,7 @@ 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 +97,7 @@ public static class UntypedJsonSchemaProperty extends IdentifiableJsonSchemaProp * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -106,6 +107,7 @@ public UntypedJsonSchemaProperty possibleValues(Object... possibleValues) { * @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 +117,7 @@ public UntypedJsonSchemaProperty allOf(JsonSchemaObject... allOf) { * @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 +127,7 @@ public UntypedJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { * @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 +137,7 @@ public UntypedJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty possibleValues(Collection<Object> possibleValues) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -142,6 +147,7 @@ public UntypedJsonSchemaProperty possibleValues(Collection<Object> possibleValue * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -151,6 +157,7 @@ public UntypedJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -160,6 +167,7 @@ public UntypedJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public UntypedJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -169,6 +177,7 @@ public UntypedJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { * @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 +187,7 @@ public UntypedJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { * @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 +196,7 @@ public UntypedJsonSchemaProperty description(String description) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public UntypedJsonSchemaProperty generatedDescription() { return new UntypedJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -213,6 +224,7 @@ public static class StringJsonSchemaProperty extends IdentifiableJsonSchemaPrope * @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 +234,7 @@ public StringJsonSchemaProperty minLength(int length) { * @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 +244,7 @@ public StringJsonSchemaProperty maxLength(int length) { * @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 +254,7 @@ public StringJsonSchemaProperty matching(String pattern) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty possibleValues(String... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -249,6 +264,7 @@ public StringJsonSchemaProperty possibleValues(String... possibleValues) { * @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 +274,7 @@ public StringJsonSchemaProperty allOf(JsonSchemaObject... allOf) { * @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 +284,7 @@ public StringJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { * @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 +294,7 @@ public StringJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -285,6 +304,7 @@ public StringJsonSchemaProperty possibleValues(Collection<String> possibleValues * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -294,6 +314,7 @@ public StringJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -303,6 +324,7 @@ public StringJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public StringJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -312,6 +334,7 @@ public StringJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { * @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 +344,7 @@ public StringJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { * @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 +353,7 @@ public StringJsonSchemaProperty description(String description) { * @return new instance of {@link StringJsonSchemaProperty}. * @see StringJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public StringJsonSchemaProperty generatedDescription() { return new StringJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -355,6 +380,7 @@ public static class ObjectJsonSchemaProperty extends IdentifiableJsonSchemaPrope * @param range must not be {@literal null}. * @return new instance of {@link ObjectJsonSchemaProperty}. */ + @Contract("_ -> new") public ObjectJsonSchemaProperty propertiesCount(Range<Integer> range) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.propertiesCount(range)); } @@ -364,6 +390,7 @@ public ObjectJsonSchemaProperty propertiesCount(Range<Integer> range) { * @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 +400,7 @@ public ObjectJsonSchemaProperty minProperties(int count) { * @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 +410,7 @@ public ObjectJsonSchemaProperty maxProperties(int count) { * @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 +420,7 @@ public ObjectJsonSchemaProperty required(String... properties) { * @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 +431,7 @@ public ObjectJsonSchemaProperty additionalProperties(boolean additionalPropertie * @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 +442,7 @@ public ObjectJsonSchemaProperty additionalProperties(ObjectJsonSchemaObject addi * @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 +452,7 @@ public ObjectJsonSchemaProperty properties(JsonSchemaProperty... properties) { * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) { return possibleValues(Arrays.asList(possibleValues)); } @@ -429,6 +462,7 @@ public ObjectJsonSchemaProperty possibleValues(Object... possibleValues) { * @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 +472,7 @@ public ObjectJsonSchemaProperty allOf(JsonSchemaObject... allOf) { * @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 +482,7 @@ public ObjectJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { * @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 +492,7 @@ public ObjectJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty possibleValues(Collection<Object> possibleValues) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -465,6 +502,7 @@ public ObjectJsonSchemaProperty possibleValues(Collection<Object> possibleValues * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -474,6 +512,7 @@ public ObjectJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -483,6 +522,7 @@ public ObjectJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ObjectJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -492,6 +532,7 @@ public ObjectJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { * @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 +542,7 @@ public ObjectJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { * @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 +551,7 @@ public ObjectJsonSchemaProperty description(String description) { * @return new instance of {@link ObjectJsonSchemaProperty}. * @see ObjectJsonSchemaObject#generateDescription() */ + @Contract("_ -> new") public ObjectJsonSchemaProperty generatedDescription() { return new ObjectJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.generatedDescription()); } @@ -540,6 +583,7 @@ public NumericJsonSchemaProperty(String identifier, NumericJsonSchemaObject sche * @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 +593,7 @@ public NumericJsonSchemaProperty multipleOf(Number value) { * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#within(Range) */ + @Contract("_ -> new") public NumericJsonSchemaProperty within(Range<? extends Number> range) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.within(range)); } @@ -558,6 +603,7 @@ public NumericJsonSchemaProperty within(Range<? extends Number> range) { * @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 +613,7 @@ public NumericJsonSchemaProperty gt(Number min) { * @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 +623,7 @@ public NumericJsonSchemaProperty gte(Number min) { * @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 +633,7 @@ public NumericJsonSchemaProperty lt(Number max) { * @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 +643,7 @@ public NumericJsonSchemaProperty lte(Number max) { * @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 +653,7 @@ public NumericJsonSchemaProperty possibleValues(Number... possibleValues) { * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) { return allOf(Arrays.asList(allOf)); } @@ -612,6 +663,7 @@ public NumericJsonSchemaProperty allOf(JsonSchemaObject... allOf) { * @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 +673,7 @@ public NumericJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { * @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 +683,7 @@ public NumericJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValues) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -639,6 +693,7 @@ public NumericJsonSchemaProperty possibleValues(Collection<Number> possibleValue * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -648,6 +703,7 @@ public NumericJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -657,6 +713,7 @@ public NumericJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link NumericJsonSchemaProperty}. * @see NumericJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public NumericJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { return new NumericJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -666,6 +723,7 @@ public NumericJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { * @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 +733,7 @@ public NumericJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { * @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 +769,7 @@ public ArrayJsonSchemaProperty(String identifier, ArrayJsonSchemaObject schemaOb * @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 +779,7 @@ public ArrayJsonSchemaProperty uniqueItems(boolean uniqueItems) { * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#range(Range) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty range(Range<Integer> range) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.range(range)); } @@ -728,6 +789,7 @@ public ArrayJsonSchemaProperty range(Range<Integer> range) { * @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 +799,7 @@ public ArrayJsonSchemaProperty minItems(int count) { * @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 +809,7 @@ public ArrayJsonSchemaProperty maxItems(int count) { * @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 +819,7 @@ public ArrayJsonSchemaProperty items(JsonSchemaObject... items) { * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#items(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty items(Collection<JsonSchemaObject> items) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.items(items)); } @@ -764,6 +829,7 @@ public ArrayJsonSchemaProperty items(Collection<JsonSchemaObject> items) { * @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 +839,7 @@ public ArrayJsonSchemaProperty additionalItems(boolean additionalItemsAllowed) { * @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 +849,7 @@ public ArrayJsonSchemaProperty possibleValues(Object... possibleValues) { * @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 +859,7 @@ public ArrayJsonSchemaProperty allOf(JsonSchemaObject... allOf) { * @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 +869,7 @@ public ArrayJsonSchemaProperty anyOf(JsonSchemaObject... anyOf) { * @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 +879,7 @@ public ArrayJsonSchemaProperty oneOf(JsonSchemaObject... oneOf) { * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty possibleValues(Collection<Object> possibleValues) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.possibleValues(possibleValues)); } @@ -818,6 +889,7 @@ public ArrayJsonSchemaProperty possibleValues(Collection<Object> possibleValues) * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.allOf(allOf)); } @@ -827,6 +899,7 @@ public ArrayJsonSchemaProperty allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.anyOf(anyOf)); } @@ -836,6 +909,7 @@ public ArrayJsonSchemaProperty anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link ArrayJsonSchemaProperty}. * @see ArrayJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> new") public ArrayJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { return new ArrayJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.oneOf(oneOf)); } @@ -845,6 +919,7 @@ public ArrayJsonSchemaProperty oneOf(Collection<JsonSchemaObject> oneOf) { * @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 +929,7 @@ public ArrayJsonSchemaProperty notMatch(JsonSchemaObject notMatch) { * @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 +960,7 @@ public static class BooleanJsonSchemaProperty extends IdentifiableJsonSchemaProp * @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 +991,7 @@ public static class NullJsonSchemaProperty extends IdentifiableJsonSchemaPropert * @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 +1022,7 @@ public static class DateJsonSchemaProperty extends IdentifiableJsonSchemaPropert * @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 +1053,7 @@ public static class TimestampJsonSchemaProperty extends IdentifiableJsonSchemaPr * @return new instance of {@link TimestampJsonSchemaProperty}. * @see TimestampJsonSchemaProperty#description(String) */ + @Contract("_ -> new") public TimestampJsonSchemaProperty description(String description) { return new TimestampJsonSchemaProperty(identifier, jsonSchemaObjectDelegate.description(description)); } @@ -1073,6 +1153,7 @@ public static EncryptedJsonSchemaProperty encrypted(JsonSchemaProperty target) { * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("-> new") public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_random() { return algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Random"); } @@ -1082,6 +1163,7 @@ public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_random() { * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("-> new") public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_deterministic() { return algorithm("AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"); } @@ -1091,6 +1173,7 @@ public EncryptedJsonSchemaProperty aead_aes_256_cbc_hmac_sha_512_deterministic() * * @return new instance of {@link EncryptedJsonSchemaProperty}. */ + @Contract("_ -> new") public EncryptedJsonSchemaProperty algorithm(String algorithm) { return new EncryptedJsonSchemaProperty(targetProperty, algorithm, keyId, keyIds); } @@ -1099,6 +1182,7 @@ public EncryptedJsonSchemaProperty algorithm(String algorithm) { * @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); } @@ -1107,6 +1191,7 @@ public EncryptedJsonSchemaProperty keyId(String keyId) { * @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)); } @@ -1115,6 +1200,7 @@ public EncryptedJsonSchemaProperty keys(UUID... keyId) { * @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)); } @@ -1159,8 +1245,7 @@ public Set<Type> getTypes() { 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)); 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 a84f361d37..24a40efa5b 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.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.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/JsonSchemaProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java index 8529951db2..9a112941be 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/JsonSchemaProperty.java @@ -17,10 +17,10 @@ import java.util.Collection; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.NumericJsonSchemaObject; import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject.ObjectJsonSchemaObject; import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.*; -import org.springframework.lang.Nullable; /** * A {@literal property} or {@literal patternProperty} within a {@link JsonSchemaObject} of {@code type : 'object'}. 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 f64218cc56..87c46d63dc 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.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 @@ interface Path { /** * @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 @@ static Resolution ofValue(Path path, Object value) { * @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 @@ class MongoJsonSchemaBuilder { private ObjectJsonSchemaObject root; - @Nullable // - private Document encryptionMetadata; + private @Nullable Document encryptionMetadata; MongoJsonSchemaBuilder() { root = new ObjectJsonSchemaObject(); @@ -323,6 +323,7 @@ class MongoJsonSchemaBuilder { * @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 MongoJsonSchemaBuilder minProperties(int count) { * @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 MongoJsonSchemaBuilder maxProperties(int count) { * @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 MongoJsonSchemaBuilder required(String... properties) { * @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 MongoJsonSchemaBuilder additionalProperties(boolean additionalPropertiesA * @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 MongoJsonSchemaBuilder additionalProperties(ObjectJsonSchemaObject schema * @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 MongoJsonSchemaBuilder properties(JsonSchemaProperty... properties) { * @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 MongoJsonSchemaBuilder patternProperties(JsonSchemaProperty... properties * @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 MongoJsonSchemaBuilder property(JsonSchemaProperty property) { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see ObjectJsonSchemaObject#possibleValues(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) { root = root.possibleValues(possibleValues); @@ -422,6 +431,7 @@ public MongoJsonSchemaBuilder possibleValues(Set<Object> possibleValues) { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#allOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) { root = root.allOf(allOf); @@ -433,6 +443,7 @@ public MongoJsonSchemaBuilder allOf(Set<JsonSchemaObject> allOf) { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#anyOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) { root = root.anyOf(anyOf); @@ -444,6 +455,7 @@ public MongoJsonSchemaBuilder anyOf(Set<JsonSchemaObject> anyOf) { * @return {@code this} {@link MongoJsonSchemaBuilder}. * @see UntypedJsonSchemaObject#oneOf(Collection) */ + @Contract("_ -> this") public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) { root = root.oneOf(oneOf); @@ -455,6 +467,7 @@ public MongoJsonSchemaBuilder oneOf(Set<JsonSchemaObject> oneOf) { * @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 MongoJsonSchemaBuilder notMatch(JsonSchemaObject notMatch) { * @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 void encryptionMetadata(@Nullable Document encryptionMetadata) { * * @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 95f116619f..87bdd8c618 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.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 @@ private static String getTypeKeyToUse(String key, Document source) { return key; } - @Nullable - private static Object getUnifiedExistingType(String key, Document source) { + private static @Nullable Object getUnifiedExistingType(String key, Document source) { return source.get(getTypeKeyToUse(key, source)); } @@ -155,7 +154,7 @@ public SimplePath append(String next) { } @Override - public String currentElement() { + public @Nullable String currentElement() { return CollectionUtils.lastElement(path); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java index abf8b0b8a2..7b299fd4d2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/TypedJsonSchemaObject.java @@ -27,9 +27,10 @@ import java.util.stream.Collectors; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; -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; @@ -100,6 +101,7 @@ public Set<Type> getTypes() { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject description(String description) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions); } @@ -110,6 +112,7 @@ public TypedJsonSchemaObject description(String description) { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("-> new") public TypedJsonSchemaObject generatedDescription() { return new TypedJsonSchemaObject(types, description, true, restrictions); } @@ -121,6 +124,7 @@ public TypedJsonSchemaObject generatedDescription() { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.possibleValues(possibleValues)); @@ -133,6 +137,7 @@ public TypedJsonSchemaObject possibleValues(Collection<? extends Object> possibl * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.allOf(allOf)); } @@ -144,6 +149,7 @@ public TypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.anyOf(anyOf)); } @@ -155,6 +161,7 @@ public TypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { * @return new instance of {@link TypedJsonSchemaObject}. */ @Override + @Contract("_ -> new") public TypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) { return new TypedJsonSchemaObject(types, description, generateDescription, restrictions.oneOf(oneOf)); } @@ -166,6 +173,7 @@ public TypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) { * @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 @@ private Optional<String> getOrCreateDescription() { * * @return can be {@literal null}. */ - @Nullable - protected String generateDescription() { + protected @Nullable String generateDescription() { return null; } @@ -264,6 +271,7 @@ public ObjectJsonSchemaObject propertiesCount(Range<Integer> range) { * @param count the allowed minimal number of properties. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject minProperties(int count) { Bound<Integer> upper = this.propertiesCount != null ? this.propertiesCount.getUpperBound() : Bound.unbounded(); @@ -276,6 +284,7 @@ public ObjectJsonSchemaObject minProperties(int count) { * @param count the allowed maximum number of properties. * @return new instance of {@link ObjectJsonSchemaObject}. */ + @Contract("_ -> new") public ObjectJsonSchemaObject maxProperties(int count) { Bound<Integer> lower = this.propertiesCount != null ? this.propertiesCount.getLowerBound() : Bound.unbounded(); @@ -288,6 +297,7 @@ public ObjectJsonSchemaObject maxProperties(int count) { * @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 ObjectJsonSchemaObject required(String... properties) { * @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 ObjectJsonSchemaObject additionalProperties(boolean additionalPropertiesA * @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 ObjectJsonSchemaObject additionalProperties(ObjectJsonSchemaObject schema * @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 ObjectJsonSchemaObject properties(JsonSchemaProperty... properties) { * @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 ObjectJsonSchemaObject patternProperties(JsonSchemaProperty... regularExp * @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<? extends Object> possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public ObjectJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private NumericJsonSchemaObject(Set<Type> types, @Nullable String description, b * @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 NumericJsonSchemaObject multipleOf(Number value) { * @param range must not be {@literal null}. * @return new instance of {@link NumericJsonSchemaObject}. */ + @Contract("_ -> new") public NumericJsonSchemaObject within(Range<? extends Number> range) { Assert.notNull(range, "Range must not be null"); @@ -578,6 +602,7 @@ public NumericJsonSchemaObject within(Range<? extends Number> range) { * @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 NumericJsonSchemaObject gt(Number min) { * @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 NumericJsonSchemaObject gte(Number min) { * @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 NumericJsonSchemaObject lt(Number max) { * @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 NumericJsonSchemaObject lte(Number max) { } @Override + @Contract("_ -> new") public NumericJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public NumericJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private StringJsonSchemaObject(@Nullable String description, boolean generateDes * @param range must not be {@literal null}. * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject length(Range<Integer> range) { Assert.notNull(range, "Range must not be null"); @@ -801,6 +837,7 @@ public StringJsonSchemaObject length(Range<Integer> range) { * @param length * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject minLength(int length) { Bound<Integer> upper = this.length != null ? this.length.getUpperBound() : Bound.unbounded(); @@ -813,6 +850,7 @@ public StringJsonSchemaObject minLength(int length) { * @param length * @return new instance of {@link StringJsonSchemaObject}. */ + @Contract("_ -> new") public StringJsonSchemaObject maxLength(int length) { Bound<Integer> lower = this.length != null ? this.length.getLowerBound() : Bound.unbounded(); @@ -825,6 +863,7 @@ public StringJsonSchemaObject maxLength(int length) { * @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 StringJsonSchemaObject matching(String pattern) { } @Override + @Contract("_ -> new") public StringJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public StringJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private ArrayJsonSchemaObject(@Nullable String description, boolean generateDesc * @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 ArrayJsonSchemaObject uniqueItems(boolean uniqueItems) { * @param range must not be {@literal null}. Consider {@link Range#unbounded()} instead. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject range(Range<Integer> range) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -975,6 +1023,7 @@ public ArrayJsonSchemaObject range(Range<Integer> range) { * @param count the allowed minimal number of array items. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject minItems(int count) { Bound<Integer> upper = this.range != null ? this.range.getUpperBound() : Bound.unbounded(); @@ -987,6 +1036,7 @@ public ArrayJsonSchemaObject minItems(int count) { * @param count the allowed maximal number of array items. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject maxItems(int count) { Bound<Integer> lower = this.range != null ? this.range.getLowerBound() : Bound.unbounded(); @@ -999,6 +1049,7 @@ public ArrayJsonSchemaObject maxItems(int count) { * @param items the allowed items in the array. * @return new instance of {@link ArrayJsonSchemaObject}. */ + @Contract("_ -> new") public ArrayJsonSchemaObject items(Collection<JsonSchemaObject> items) { ArrayJsonSchemaObject newInstance = newInstance(description, generateDescription, restrictions); @@ -1013,6 +1064,7 @@ public ArrayJsonSchemaObject items(Collection<JsonSchemaObject> items) { * @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 ArrayJsonSchemaObject additionalItems(boolean additionalItemsAllowed) { } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return newInstance(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return newInstance(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return newInstance(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public ArrayJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private BooleanJsonSchemaObject(@Nullable String description, boolean generateDe } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new BooleanJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public BooleanJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private NullJsonSchemaObject(@Nullable String description, boolean generateDescr } @Override + @Contract("_ -> new") public NullJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new NullJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new NullJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new NullJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public NullJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private DateJsonSchemaObject(@Nullable String description, boolean generateDescr } @Override + @Contract("_ -> new") public DateJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new DateJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new DateJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new DateJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public DateJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 @@ private TimestampJsonSchemaObject(@Nullable String description, boolean generate } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.possibleValues(possibleValues)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.allOf(allOf)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new TimestampJsonSchemaObject(description, generateDescription, restrictions.anyOf(anyOf)); } @Override + @Contract("_ -> new") public TimestampJsonSchemaObject oneOf(Collection<JsonSchemaObject> 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 54ca29e0e3..d13f8d7985 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.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 Set<Type> getTypes() { * @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 UntypedJsonSchemaObject description(String description) { * * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("-> new") public UntypedJsonSchemaObject generatedDescription() { return new UntypedJsonSchemaObject(restrictions, description, true); } @@ -88,6 +91,7 @@ public UntypedJsonSchemaObject generatedDescription() { * @param possibleValues must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject possibleValues(Collection<? extends Object> possibleValues) { return new UntypedJsonSchemaObject(restrictions.possibleValues(possibleValues), description, generateDescription); } @@ -98,6 +102,7 @@ public UntypedJsonSchemaObject possibleValues(Collection<? extends Object> possi * @param allOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { return new UntypedJsonSchemaObject(restrictions.allOf(allOf), description, generateDescription); } @@ -108,6 +113,7 @@ public UntypedJsonSchemaObject allOf(Collection<JsonSchemaObject> allOf) { * @param anyOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { return new UntypedJsonSchemaObject(restrictions.anyOf(anyOf), description, generateDescription); } @@ -118,6 +124,7 @@ public UntypedJsonSchemaObject anyOf(Collection<JsonSchemaObject> anyOf) { * @param oneOf must not be {@literal null}. * @return new instance of {@link TypedJsonSchemaObject}. */ + @Contract("_ -> new") public UntypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) { return new UntypedJsonSchemaObject(restrictions.oneOf(oneOf), description, generateDescription); } @@ -128,6 +135,7 @@ public UntypedJsonSchemaObject oneOf(Collection<JsonSchemaObject> oneOf) { * @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 @@ private Optional<String> getOrCreateDescription() { * * @return can be {@literal null}. */ - @Nullable - protected String generateDescription() { + protected @Nullable String generateDescription() { return null; } @@ -177,14 +184,14 @@ protected String generateDescription() { */ static class Restrictions { - private final Collection<? extends Object> possibleValues; + private final Collection<?> possibleValues; private final Collection<JsonSchemaObject> allOf; private final Collection<JsonSchemaObject> anyOf; private final Collection<JsonSchemaObject> oneOf; private final @Nullable JsonSchemaObject notMatch; - Restrictions(Collection<? extends Object> possibleValues, Collection<JsonSchemaObject> allOf, - Collection<JsonSchemaObject> anyOf, Collection<JsonSchemaObject> oneOf, JsonSchemaObject notMatch) { + Restrictions(Collection<?> possibleValues, Collection<JsonSchemaObject> allOf, + Collection<JsonSchemaObject> anyOf, Collection<JsonSchemaObject> oneOf, @Nullable JsonSchemaObject notMatch) { this.possibleValues = possibleValues; this.allOf = allOf; @@ -206,7 +213,8 @@ static Restrictions empty() { * @param possibleValues must not be {@literal null}. * @return */ - Restrictions possibleValues(Collection<? extends Object> 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 @@ Restrictions possibleValues(Collection<? extends Object> possibleValues) { * @param allOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions allOf(Collection<JsonSchemaObject> allOf) { Assert.notNull(allOf, "AllOf must not be null"); @@ -226,6 +235,7 @@ Restrictions allOf(Collection<JsonSchemaObject> allOf) { * @param anyOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions anyOf(Collection<JsonSchemaObject> anyOf) { Assert.notNull(anyOf, "AnyOf must not be null"); @@ -236,6 +246,7 @@ Restrictions anyOf(Collection<JsonSchemaObject> anyOf) { * @param oneOf must not be {@literal null}. * @return */ + @Contract("_ -> new") Restrictions oneOf(Collection<JsonSchemaObject> oneOf) { Assert.notNull(oneOf, "OneOf must not be null"); @@ -246,6 +257,7 @@ Restrictions oneOf(Collection<JsonSchemaObject> oneOf) { * @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 380d92af09..cdc583e038 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 34eb8ea890..976b238fbd 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 b4550ee8de..a5b4a2aabf 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 @@ 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 boolean isLiteral() { * * @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 8869f51e09..89edd4eab2 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 @@ 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 T getCurrentNode() { * * @return */ - @Nullable - public ExpressionNode getParentNode() { + public @Nullable ExpressionNode getParentNode() { return parentNode; } @@ -81,8 +80,7 @@ public ExpressionNode getParentNode() { * @see #addToPreviousOrReturn(Object) * @return */ - @Nullable - public Document getPreviousOperationObject() { + public @Nullable Document getPreviousOperationObject() { return previousOperationObject; } @@ -110,7 +108,7 @@ public boolean parentIsSameOperation() { * @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 Document addToPreviousOperation(Object value) { * @param value * @return */ - public Object addToPreviousOrReturn(Object value) { + public @Nullable Object addToPreviousOrReturn(@Nullable Object value) { return hasPreviousOperation() ? addToPreviousOperation(value) : value; } + @SuppressWarnings("unchecked") private List<Object> extractArgumentListFrom(Document context) { - return (List<Object>) context.get(context.keySet().iterator().next()); + + Object o = context.get(context.keySet().iterator().next()); + return o instanceof List<?> l ? (List<Object>) 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 512f753042..da5748f523 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<T extends ExpressionTransformationContext * @param context will never be {@literal null}. * @return */ - Object transform(T context); + @Nullable Object transform(T context); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java index 030ef0d055..b276831860 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/LiteralNode.java @@ -17,6 +17,7 @@ import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ast.BooleanLiteral; import org.springframework.expression.spel.ast.FloatLiteral; @@ -26,7 +27,6 @@ import org.springframework.expression.spel.ast.NullLiteral; import org.springframework.expression.spel.ast.RealLiteral; import org.springframework.expression.spel.ast.StringLiteral; -import org.springframework.lang.Nullable; /** * A node representing a literal in an expression. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java index 5f1b0c4309..db2801e649 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java @@ -21,9 +21,9 @@ import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.ast.MethodReference; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -272,8 +272,7 @@ public class MethodReferenceNode extends ExpressionNode { * @return can be {@literal null}. * @since 1.10 */ - @Nullable - public AggregationMethodReference getMethodReference() { + public @Nullable AggregationMethodReference getMethodReference() { String name = getName(); String methodName = name.substring(0, name.indexOf('(')); @@ -288,7 +287,7 @@ public static final class AggregationMethodReference { private final @Nullable String mongoOperator; private final @Nullable ArgumentType argumentType; - private final @Nullable String[] argumentMap; + private final String @Nullable[] argumentMap; /** * Creates new {@link AggregationMethodReference}. @@ -298,7 +297,7 @@ public static final class AggregationMethodReference { * @param argumentMap can be {@literal null}. */ private AggregationMethodReference(@Nullable String mongoOperator, @Nullable ArgumentType argumentType, - @Nullable String[] argumentMap) { + String @Nullable[] argumentMap) { this.mongoOperator = mongoOperator; this.argumentType = argumentType; @@ -310,8 +309,7 @@ private AggregationMethodReference(@Nullable String mongoOperator, @Nullable Arg * * @return can be {@literal null}. */ - @Nullable - public String getMongoOperator() { + public @Nullable String getMongoOperator() { return this.mongoOperator; } @@ -320,8 +318,7 @@ public String getMongoOperator() { * * @return never {@literal null}. */ - @Nullable - public ArgumentType getArgumentType() { + public @Nullable ArgumentType getArgumentType() { return this.argumentType; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java index fbfa2ae78b..627cb8a04f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/package-info.java @@ -3,6 +3,6 @@ * * @since 1.4 */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.mongodb.core.spel; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java index 779ed4ec9f..d739994e89 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/CriteriaValidator.java @@ -16,10 +16,10 @@ package org.springframework.data.mongodb.core.validation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.CriteriaDefinition; import org.springframework.data.mongodb.core.query.SerializationUtils; -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/validation/DocumentValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java index 5e27b99ad6..9adf5fcf79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/DocumentValidator.java @@ -16,8 +16,8 @@ package org.springframework.data.mongodb.core.validation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.SerializationUtils; -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/validation/JsonSchemaValidator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java index 61ef8c5b4f..b04e65b1db 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/JsonSchemaValidator.java @@ -16,9 +16,9 @@ package org.springframework.data.mongodb.core.validation; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.SerializationUtils; import org.springframework.data.mongodb.core.schema.MongoJsonSchema; -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/validation/package-info.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java index 002a4ee1fb..83fe5719a3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/validation/package-info.java @@ -1,6 +1,6 @@ /** * MongoDB schema validation specifics. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked @org.springframework.lang.NonNullFields package org.springframework.data.mongodb.core.validation; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java index 54010a7c65..e0aa5bb2d5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsCriteria.java @@ -15,8 +15,8 @@ */ package org.springframework.data.mongodb.gridfs; +import org.jspecify.annotations.Nullable; import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.lang.Nullable; /** * GridFs-specific helper class to define {@link Criteria}s. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java index f73c0c943f..99807e63a2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/GridFsObject.java @@ -16,9 +16,10 @@ package org.springframework.data.mongodb.gridfs; import org.bson.Document; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import com.mongodb.client.gridfs.model.GridFSFile; +import org.springframework.lang.Contract; /** * A common interface when dealing with GridFs items using Spring Data. @@ -110,6 +111,7 @@ public static Options from(@Nullable GridFSFile gridFSFile) { * @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 Options contentType(String contentType) { * @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 Options metadata(Document metadata) { * @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 bf5a1d86e3..4878b431f4 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 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 @@ default ObjectId store(InputStream content, @Nullable String filename, @Nullable * @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 b3d3771f3c..9a5621dcbc 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 @@ 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 0873432977..db6ce9833d 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.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 InputStream getInputStream() throws IOException, IllegalStateException { } @Override + @SuppressWarnings("NullAway") public long contentLength() throws IOException { verifyExists(); @@ -122,6 +123,7 @@ public boolean exists() { } @Override + @SuppressWarnings("NullAway") public long lastModified() throws IOException { verifyExists(); @@ -139,6 +141,7 @@ public String getDescription() { * @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 Object getId() { } @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 Object getFileId() { * @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 GridFSFile getGridFSFile() { * 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 8187c7dbc3..722a57edc1 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 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 void delete(Query query) { } @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 9f8d9a47d2..6f2b9ed85b 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 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 @@ private GridFsUpload(@Nullable ID id, Lazy<InputStream> dataStream, String filen * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId() */ @Override - @Nullable - public ID getFileId() { + public @Nullable ID getFileId() { return id; } @@ -72,6 +71,7 @@ public String getFilename() { } @Override + @SuppressWarnings("NullAway") public InputStream getContent() { return dataStream.orElse(InputStream.nullInputStream()); } @@ -98,9 +98,9 @@ public static GridFsUploadBuilder<ObjectId> fromStream(InputStream stream) { */ public static class GridFsUploadBuilder<T> { - private Object id; - private Lazy<InputStream> dataStream; - private String filename; + private @Nullable Object id; + private @Nullable Lazy<InputStream> dataStream; + private @Nullable String filename; private Options options = Options.none(); private GridFsUploadBuilder() {} @@ -124,6 +124,7 @@ public GridFsUploadBuilder<T> content(InputStream stream) { * @param stream the upload content. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> content(Supplier<InputStream> stream) { Assert.notNull(stream, "InputStream Supplier must not be null"); @@ -139,6 +140,8 @@ public GridFsUploadBuilder<T> content(Supplier<InputStream> stream) { * @param <T1> * @return this. */ + @SuppressWarnings("unchecked") + @Contract("_ -> this") public <T1> GridFsUploadBuilder<T1> id(T1 id) { this.id = id; @@ -151,6 +154,7 @@ public <T1> GridFsUploadBuilder<T1> id(T1 id) { * @param filename the filename to use. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> filename(String filename) { this.filename = filename; @@ -163,6 +167,7 @@ public GridFsUploadBuilder<T> filename(String filename) { * @param options must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> options(Options options) { Assert.notNull(options, "Options must not be null"); @@ -177,6 +182,7 @@ public GridFsUploadBuilder<T> options(Options options) { * @param metadata must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> metadata(Document metadata) { this.options = this.options.metadata(metadata); @@ -189,6 +195,7 @@ public GridFsUploadBuilder<T> metadata(Document metadata) { * @param chunkSize use negative number for default. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> chunkSize(int chunkSize) { this.options = this.options.chunkSize(chunkSize); @@ -201,6 +208,7 @@ public GridFsUploadBuilder<T> chunkSize(int chunkSize) { * @param gridFSFile must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) { Assert.notNull(gridFSFile, "GridFSFile must not be null"); @@ -219,13 +227,20 @@ public GridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) { * @param contentType must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public GridFsUploadBuilder<T> contentType(String contentType) { this.options = this.options.contentType(contentType); return this; } + @Contract("-> new") public GridFsUpload<T> 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 9ee47e0bb9..f8a6bd804f 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 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 aec7cadef1..e889ec7183 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.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.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 static ReactiveGridFsResource absent(String filename) { } @Override - public Object getFileId() { + public @Nullable Object getFileId() { return id instanceof BsonValue bsonValue ? BsonUtils.toJavaType(bsonValue) : id; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java index 305e55aee4..092f81d1fa 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsTemplate.java @@ -26,6 +26,7 @@ import org.bson.BsonValue; 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.core.io.buffer.DataBufferFactory; @@ -37,7 +38,6 @@ import org.springframework.data.mongodb.core.query.SerializationUtils; 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; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java index 2f16c3b06e..09ea77798c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/gridfs/ReactiveGridFsUpload.java @@ -17,9 +17,10 @@ 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.lang.Nullable; +import org.springframework.lang.Contract; import org.springframework.util.Assert; import com.mongodb.client.gridfs.model.GridFSFile; @@ -58,8 +59,7 @@ private ReactiveGridFsUpload(@Nullable ID id, Publisher<DataBuffer> dataStream, * @see org.springframework.data.mongodb.gridfs.GridFsObject#getFileId() */ @Override - @Nullable - public ID getFileId() { + public @Nullable ID getFileId() { return id; } @@ -96,8 +96,8 @@ public static ReactiveGridFsUploadBuilder<ObjectId> fromPublisher(Publisher<Data public static class ReactiveGridFsUploadBuilder<T> { private @Nullable Object id; - private Publisher<DataBuffer> dataStream; - private String filename; + private @Nullable Publisher<DataBuffer> dataStream; + private @Nullable String filename; private Options options = Options.none(); private ReactiveGridFsUploadBuilder() {} @@ -108,6 +108,7 @@ private ReactiveGridFsUploadBuilder() {} * @param source the upload content. * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> content(Publisher<DataBuffer> source) { this.dataStream = source; return this; @@ -120,6 +121,7 @@ public ReactiveGridFsUploadBuilder<T> content(Publisher<DataBuffer> source) { * @param <T1> * @return this. */ + @Contract("_ -> this") public <T1> ReactiveGridFsUploadBuilder<T1> id(T1 id) { this.id = id; @@ -132,6 +134,7 @@ public <T1> ReactiveGridFsUploadBuilder<T1> id(T1 id) { * @param filename the filename to use. * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> filename(String filename) { this.filename = filename; @@ -144,6 +147,7 @@ public ReactiveGridFsUploadBuilder<T> filename(String filename) { * @param options must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> options(Options options) { Assert.notNull(options, "Options must not be null"); @@ -156,8 +160,9 @@ public ReactiveGridFsUploadBuilder<T> options(Options options) { * Set the file metadata. * * @param metadata must not be {@literal null}. - * @return + * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> metadata(Document metadata) { this.options = this.options.metadata(metadata); @@ -168,8 +173,9 @@ public ReactiveGridFsUploadBuilder<T> metadata(Document metadata) { * Set the upload chunk size in bytes. * * @param chunkSize use negative number for default. - * @return + * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> chunkSize(int chunkSize) { this.options = this.options.chunkSize(chunkSize); @@ -182,6 +188,7 @@ public ReactiveGridFsUploadBuilder<T> chunkSize(int chunkSize) { * @param gridFSFile must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) { Assert.notNull(gridFSFile, "GridFSFile must not be null"); @@ -200,13 +207,20 @@ public ReactiveGridFsUploadBuilder<T> gridFsFile(GridFSFile gridFSFile) { * @param contentType must not be {@literal null}. * @return this. */ + @Contract("_ -> this") public ReactiveGridFsUploadBuilder<T> contentType(String contentType) { this.options = this.options.contentType(contentType); return this; } + @Contract("-> new") public ReactiveGridFsUpload<T> 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 2f3b5af150..57726d69cc 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/AbstractMonitor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AbstractMonitor.java deleted file mode 100644 index 9572f53702..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AbstractMonitor.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import java.util.List; -import java.util.stream.Collectors; - -import org.bson.Document; - -import com.mongodb.ServerAddress; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoDatabase; -import com.mongodb.connection.ServerDescription; - -/** - * Base class to encapsulate common configuration settings when connecting to a database - * - * @author Mark Pollack - * @author Oliver Gierke - * @author Christoph Strobl - */ -public abstract class AbstractMonitor { - - private final MongoClient mongoClient; - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - protected AbstractMonitor(MongoClient mongoClient) { - this.mongoClient = mongoClient; - } - - public Document getServerStatus() { - return getDb("admin").runCommand(new Document("serverStatus", 1).append("rangeDeleter", 1).append("repl", 1)); - } - - public MongoDatabase getDb(String databaseName) { - return mongoClient.getDatabase(databaseName); - } - - protected MongoClient getMongoClient() { - return mongoClient; - } - - protected List<ServerAddress> hosts() { - - return mongoClient.getClusterDescription().getServerDescriptions().stream().map(ServerDescription::getAddress) - .collect(Collectors.toList()); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java deleted file mode 100644 index ec8186e30e..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/AssertMetrics.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for assertions - * - * @author Mark Pollack - */ -@ManagedResource(description = "Assertion Metrics") -public class AssertMetrics extends AbstractMonitor { - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - public AssertMetrics(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Regular") - public int getRegular() { - return getBtree("regular"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Warning") - public int getWarning() { - return getBtree("warning"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Msg") - public int getMsg() { - return getBtree("msg"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "User") - public int getUser() { - return getBtree("user"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Rollovers") - public int getRollovers() { - return getBtree("rollovers"); - } - - private int getBtree(String key) { - Document asserts = (Document) getServerStatus().get("asserts"); - // Class c = btree.get(key).getClass(); - return (Integer) asserts.get(key); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java deleted file mode 100644 index 67fa8f6562..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BackgroundFlushingMetrics.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import java.util.Date; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for Background Flushing - * - * @author Mark Pollack - */ -@ManagedResource(description = "Background Flushing Metrics") -public class BackgroundFlushingMetrics extends AbstractMonitor { - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - public BackgroundFlushingMetrics(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Flushes") - public int getFlushes() { - return getFlushingData("flushes", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Total ms", unit = "ms") - public int getTotalMs() { - return getFlushingData("total_ms", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Average ms", unit = "ms") - public double getAverageMs() { - return getFlushingData("average_ms", java.lang.Double.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Last Ms", unit = "ms") - public int getLastMs() { - return getFlushingData("last_ms", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Last finished") - public Date getLastFinished() { - return getLast(); - } - - @SuppressWarnings("unchecked") - private <T> T getFlushingData(String key, Class<T> targetClass) { - Document mem = (Document) getServerStatus().get("backgroundFlushing"); - return (T) mem.get(key); - } - - private Date getLast() { - Document bgFlush = (Document) getServerStatus().get("backgroundFlushing"); - Date lastFinished = (Date) bgFlush.get("last_finished"); - return lastFinished; - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java deleted file mode 100644 index 03924d88a0..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/BtreeIndexCounters.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for B-tree index counters - * - * @author Mark Pollack - */ -@ManagedResource(description = "Btree Metrics") -public class BtreeIndexCounters extends AbstractMonitor { - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - public BtreeIndexCounters(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Accesses") - public int getAccesses() { - return getBtree("accesses"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Hits") - public int getHits() { - return getBtree("hits"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Misses") - public int getMisses() { - return getBtree("misses"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Resets") - public int getResets() { - return getBtree("resets"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Miss Ratio") - public int getMissRatio() { - return getBtree("missRatio"); - } - - private int getBtree(String key) { - Document indexCounters = (Document) getServerStatus().get("indexCounters"); - if (indexCounters.get("note") != null) { - String message = (String) indexCounters.get("note"); - if (message.contains("not supported")) { - return -1; - } - } - Document btree = (Document) indexCounters.get("btree"); - // Class c = btree.get(key).getClass(); - return (Integer) btree.get(key); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java deleted file mode 100644 index beb3932ea4..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ConnectionMetrics.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for Connections - * - * @author Mark Pollack - */ -@ManagedResource(description = "Connection metrics") -public class ConnectionMetrics extends AbstractMonitor { - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - public ConnectionMetrics(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Current Connections") - public int getCurrent() { - return getConnectionData("current", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Available Connections") - public int getAvailable() { - return getConnectionData("available", java.lang.Integer.class); - } - - @SuppressWarnings("unchecked") - private <T> T getConnectionData(String key, Class<T> targetClass) { - Document mem = (Document) getServerStatus().get("connections"); - // Class c = mem.get(key).getClass(); - return (T) mem.get(key); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java deleted file mode 100644 index 096c67b1a0..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/GlobalLockMetrics.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.DBObject; -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for Global Locks - * - * @author Mark Pollack - */ -@ManagedResource(description = "Global Lock Metrics") -public class GlobalLockMetrics extends AbstractMonitor { - - /** - * @param mongoClient must not be {@literal null}. - * @since 2.2 - */ - public GlobalLockMetrics(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Total time") - public double getTotalTime() { - return getGlobalLockData("totalTime", java.lang.Double.class); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Lock time", unit = "s") - public double getLockTime() { - return getGlobalLockData("lockTime", java.lang.Double.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Lock time") - public double getLockTimeRatio() { - return getGlobalLockData("ratio", java.lang.Double.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Current Queue") - public int getCurrentQueueTotal() { - return getCurrentQueue("total"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Reader Queue") - public int getCurrentQueueReaders() { - return getCurrentQueue("readers"); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Writer Queue") - public int getCurrentQueueWriters() { - return getCurrentQueue("writers"); - } - - @SuppressWarnings("unchecked") - private <T> T getGlobalLockData(String key, Class<T> targetClass) { - DBObject globalLock = (DBObject) getServerStatus().get("globalLock"); - return (T) globalLock.get(key); - } - - private int getCurrentQueue(String key) { - Document globalLock = (Document) getServerStatus().get("globalLock"); - Document currentQueue = (Document) globalLock.get("currentQueue"); - return (Integer) currentQueue.get(key); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java deleted file mode 100644 index 75daa3de55..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/MemoryMetrics.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for Memory - * - * @author Mark Pollack - */ -@ManagedResource(description = "Memory Metrics") -public class MemoryMetrics extends AbstractMonitor { - - /** - * @param mongoClient - * @since 2.2 - */ - public MemoryMetrics(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Memory address size") - public int getBits() { - return getMemData("bits", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Resident in Physical Memory", unit = "MB") - public int getResidentSpace() { - return getMemData("resident", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Virtual Address Space", unit = "MB") - public int getVirtualAddressSpace() { - return getMemData("virtual", java.lang.Integer.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Is memory info supported on this platform") - public boolean getMemoryInfoSupported() { - return getMemData("supported", java.lang.Boolean.class); - } - - @ManagedMetric(metricType = MetricType.GAUGE, displayName = "Memory Mapped Space", unit = "MB") - public int getMemoryMappedSpace() { - return getMemData("mapped", java.lang.Integer.class); - } - - @SuppressWarnings("unchecked") - private <T> T getMemData(String key, Class<T> targetClass) { - Document mem = (Document) getServerStatus().get("mem"); - // Class c = mem.get(key).getClass(); - return (T) mem.get(key); - } - -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java deleted file mode 100644 index 35281753e6..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/OperationCounters.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import org.bson.Document; -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; -import org.springframework.util.NumberUtils; - -import com.mongodb.client.MongoClient; - -/** - * JMX Metrics for Operation counters - * - * @author Mark Pollack - */ -@ManagedResource(description = "Operation Counters") -public class OperationCounters extends AbstractMonitor { - - /** - * @param mongoClient - * @since 2.2 - */ - public OperationCounters(MongoClient mongoClient) { - super(mongoClient); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Insert operation count") - public int getInsertCount() { - return getOpCounter("insert"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Query operation count") - public int getQueryCount() { - return getOpCounter("query"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Update operation count") - public int getUpdateCount() { - return getOpCounter("update"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Delete operation count") - public int getDeleteCount() { - return getOpCounter("delete"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "GetMore operation count") - public int getGetMoreCount() { - return getOpCounter("getmore"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Command operation count") - public int getCommandCount() { - return getOpCounter("command"); - } - - private int getOpCounter(String key) { - Document opCounters = (Document) getServerStatus().get("opcounters"); - return NumberUtils.convertNumberToTargetClass((Number) opCounters.get(key), Integer.class); - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java deleted file mode 100644 index bddf62d028..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/monitor/ServerInfo.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2012-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import java.net.UnknownHostException; - -import org.springframework.jmx.export.annotation.ManagedMetric; -import org.springframework.jmx.export.annotation.ManagedOperation; -import org.springframework.jmx.export.annotation.ManagedResource; -import org.springframework.jmx.support.MetricType; -import org.springframework.util.StringUtils; - -import com.mongodb.client.MongoClient; - -/** - * Expose basic server information via JMX - * - * @author Mark Pollack - * @author Thomas Darimont - * @author Christoph Strobl - */ -@ManagedResource(description = "Server Information") -public class ServerInfo extends AbstractMonitor { - - /** - * @param mongoClient - * @since 2.2 - */ - protected ServerInfo(MongoClient mongoClient) { - super(mongoClient); - } - - /** - * Returns the hostname of the used server reported by MongoDB. - * - * @return the reported hostname can also be an IP address. - * @throws UnknownHostException - */ - @ManagedOperation(description = "Server host name") - public String getHostName() throws UnknownHostException { - - /* - * UnknownHostException is not necessary anymore, but clients could have - * called this method in a try..catch(UnknownHostException) already - */ - return StringUtils.collectionToDelimitedString(hosts(), ","); - } - - @ManagedMetric(displayName = "Uptime Estimate") - public double getUptimeEstimate() { - return (Double) getServerStatus().get("uptimeEstimate"); - } - - @ManagedOperation(description = "MongoDB Server Version") - public String getVersion() { - return (String) getServerStatus().get("version"); - } - - @ManagedOperation(description = "Local Time") - public String getLocalTime() { - return (String) getServerStatus().get("localTime"); - } - - @ManagedMetric(metricType = MetricType.COUNTER, displayName = "Server uptime in seconds", unit = "seconds") - public double getUptime() { - return (Double) getServerStatus().get("uptime"); - } -} 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 index 0d495584a9..40073d6022 100644 --- 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 @@ -1,6 +1,6 @@ /** * MongoDB specific JMX monitoring support. */ -@org.springframework.lang.NonNullApi +@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 b823ce223b..132a51ba46 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 @@ -21,6 +21,7 @@ import org.springframework.data.mongodb.observability.MongoObservation.LowCardinalityCommandKeyNames; import org.springframework.data.mongodb.util.MongoCompatibilityAdapter; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import com.mongodb.ConnectionString; @@ -67,6 +68,10 @@ public KeyValues getLowCardinalityKeyValues(MongoHandlerContext context) { .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) { @@ -111,6 +116,8 @@ public String getContextualName(MongoHandlerContext context) { 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 854e1481fc..6185c95db5 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 @@ 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 @@ public MapRequestContext(Map<Object, Object> context) { @Override public <T> 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 cc58aac56e..cab9cd5cb8 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 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<Object> { "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 MongoHandlerContext(@Nullable ConnectionString connectionString, CommandS 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 @@ private static String getCollectionName(CommandStartedEvent event) { * * @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 9360a95de2..914396ab96 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 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 @@ private void doInObservation(@Nullable RequestContext requestContext, * @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 d240e12f9e..d6319e5f4f 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 900342bbcb..989655f4a6 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 b1ba6ea3f0..00ff498731 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 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.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 9016519d9b..750cc38678 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 a2cbf659dd..db7edc05bd 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 d0d9b07081..e276d4d1e0 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 8deddfe939..799597e19c 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 4d0d604a27..e160fd879a 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,12 +20,9 @@ import org.bson.Document; import org.bson.codecs.configuration.CodecRegistry; - -import org.springframework.core.env.StandardEnvironment; +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueEvaluationContextProvider; import org.springframework.data.expression.ValueExpression; -import org.springframework.data.expression.ValueExpressionParser; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery; @@ -47,17 +44,10 @@ import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; 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.data.util.Lazy; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -84,36 +74,6 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { private final ValueExpressionDelegate valueExpressionDelegate; private final ValueEvaluationContextProvider valueEvaluationContextProvider; - /** - * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}. - * - * @param method must not be {@literal null}. - * @param operations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated use the constructor version with {@link ValueExpressionDelegate} - */ - @Deprecated(since = "4.4.0") - public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser, - QueryMethodEvaluationContextProvider evaluationContextProvider) { - - Assert.notNull(operations, "MongoOperations must not be null"); - Assert.notNull(method, "MongoQueryMethod must not be null"); - Assert.notNull(expressionParser, "SpelExpressionParser must not be null"); - Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null"); - - this.method = method; - this.operations = operations; - - MongoEntityMetadata<?> metadata = method.getEntityInformation(); - Class<?> type = metadata.getCollectionEntity().getType(); - - this.executableFind = operations.query(type); - this.executableUpdate = operations.update(type); - this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser)); - this.valueEvaluationContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters()); - } - /** * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}. * @@ -145,7 +105,7 @@ public MongoQueryMethod getQueryMethod() { } @Override - public Object execute(Object[] parameters) { + public @Nullable Object execute(Object[] parameters) { ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(operations.getConverter(), new MongoParametersParameterAccessor(method, parameters)); @@ -165,8 +125,7 @@ public Object execute(Object[] parameters) { * @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); @@ -185,7 +144,8 @@ protected Object doExecute(MongoQueryMethod method, ResultProcessor processor, C } /** - * If present apply the {@link com.mongodb.ReadPreference} from the {@link org.springframework.data.mongodb.repository.ReadPreference} annotation. + * If present apply the {@link com.mongodb.ReadPreference} from the + * {@link org.springframework.data.mongodb.repository.ReadPreference} annotation. * * @param query must not be {@literal null}. * @return never {@literal null}. @@ -200,6 +160,7 @@ private Query applyAnnotatedReadPreferenceIfPresent(Query query) { return query.withReadPreference(com.mongodb.ReadPreference.valueOf(method.getAnnotatedReadPreference())); } + @SuppressWarnings("NullAway") private MongoQueryExecution getExecution(ConvertingParameterAccessor accessor, FindWithQuery<?> operation) { if (isDeleteQuery()) { @@ -320,6 +281,7 @@ protected Query createCountQuery(ConvertingParameterAccessor accessor) { * @throws IllegalStateException if no update could be found. * @since 3.4 */ + @SuppressWarnings("NullAway") protected UpdateDefinition createUpdate(ConvertingParameterAccessor accessor) { if (accessor.getUpdate() != null) { @@ -396,20 +358,6 @@ protected ParameterBindingDocumentCodec getParameterBindingCodec() { return codec.get(); } - /** - * Obtain a the {@link EvaluationContext} suitable to evaluate expressions backed by the given dependencies. - * - * @param dependencies must not be {@literal null}. - * @param accessor must not be {@literal null}. - * @return the {@link SpELExpressionEvaluator}. - * @since 2.4 - */ - protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDependencies dependencies, - ConvertingParameterAccessor accessor) { - - return new DefaultSpELExpressionEvaluator(new SpelExpressionParser(), valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), dependencies).getEvaluationContext()); - } - /** * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions. * @@ -418,14 +366,16 @@ protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDepend * @since 4.4.0 */ protected ValueExpressionEvaluator getExpressionEvaluatorFor(MongoParameterAccessor accessor) { - return new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, (ValueExpression expression) -> - valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies())); + return new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, + (ValueExpression expression) -> valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), + expression.getExpressionDependencies())); } /** * @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 a5754a4e46..d363c93442 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,17 +24,15 @@ 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; -import org.springframework.core.env.StandardEnvironment; import org.springframework.data.expression.ReactiveValueEvaluationContextProvider; import org.springframework.data.expression.ValueEvaluationContext; import org.springframework.data.expression.ValueEvaluationContextProvider; import org.springframework.data.expression.ValueExpression; -import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.model.EntityInstantiators; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithProjection; @@ -56,15 +54,10 @@ import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; 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.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -89,45 +82,6 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { private final ValueExpressionDelegate valueExpressionDelegate; private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider; - /** - * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and - * {@link MongoOperations}. - * - * @param method must not be {@literal null}. - * @param operations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated use the constructor version with {@link ValueExpressionDelegate} - */ - @Deprecated(since = "4.4.0") - public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations, - ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { - - Assert.notNull(method, "MongoQueryMethod must not be null"); - Assert.notNull(operations, "ReactiveMongoOperations must not be null"); - Assert.notNull(expressionParser, "SpelExpressionParser must not be null"); - Assert.notNull(evaluationContextProvider, "ReactiveEvaluationContextExtension must not be null"); - - this.method = method; - this.operations = operations; - this.instantiators = new EntityInstantiators(); - this.valueExpressionDelegate = new ValueExpressionDelegate( - new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), - evaluationContextProvider.getEvaluationContextProvider()), - ValueExpressionParser.create(() -> expressionParser)); - - MongoEntityMetadata<?> metadata = method.getEntityInformation(); - Class<?> type = metadata.getCollectionEntity().getType(); - - this.findOperationWithProjection = operations.query(type); - this.updateOps = operations.update(type); - ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate - .createValueContextProvider(method.getParameters()); - Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, - "ValueEvaluationContextProvider must be reactive"); - this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider; - } - /** * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and * {@link MongoOperations}. @@ -241,6 +195,7 @@ private ReactiveMongoQueryExecution getExecution(MongoParameterAccessor accessor return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing); } + @SuppressWarnings("NullAway") private ReactiveMongoQueryExecution getExecutionToWrap(MongoParameterAccessor accessor, FindWithQuery<?> operation) { if (isDeleteQuery()) { @@ -380,6 +335,7 @@ protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) { * @throws IllegalStateException if no update could be found. * @since 3.4 */ + @SuppressWarnings("NullAway") protected Mono<UpdateDefinition> createUpdate(MongoParameterAccessor accessor) { if (accessor.getUpdate() != null) { @@ -460,26 +416,6 @@ protected Mono<ParameterBindingDocumentCodec> getParameterBindingCodec() { return getCodecRegistry().map(ParameterBindingDocumentCodec::new); } - /** - * Obtain a {@link Mono publisher} emitting the {@link SpELExpressionEvaluator} suitable to evaluate expressions - * backed by the given dependencies. - * - * @param dependencies must not be {@literal null}. - * @param accessor must not be {@literal null}. - * @return a {@link Mono} emitting the {@link SpELExpressionEvaluator} when ready. - * @since 3.4 - * @deprecated since 4.4.0, use - * {@link #getValueExpressionEvaluatorLater(ExpressionDependencies, MongoParameterAccessor)} instead - */ - @Deprecated(since = "4.4.0") - protected Mono<SpELExpressionEvaluator> getSpelEvaluatorFor(ExpressionDependencies dependencies, - MongoParameterAccessor accessor) { - return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies) - .map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator( - new SpelExpressionParser(), evaluationContext.getEvaluationContext())) - .defaultIfEmpty(DefaultSpELExpressionEvaluator.unsupported()); - } - /** * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions. * @@ -491,7 +427,7 @@ ValueExpressionEvaluator getValueExpressionEvaluator(MongoParameterAccessor acce return new ValueExpressionEvaluator() { @Override - public <T> T evaluate(String expressionString) { + public <T> @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 6eb6a5da89..639c694ef9 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.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.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 @@ static AggregationOptions computeOptions(MongoQueryMethod method, ConvertingPara * Prepares the AggregationPipeline including type discovery and calling {@link AggregationCallback} to run the * aggregation. */ - @Nullable - static <T> T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor, + static <T> @Nullable T doAggregate(AggregationPipeline pipeline, MongoQueryMethod method, ResultProcessor processor, ConvertingParameterAccessor accessor, Function<MongoParameterAccessor, ValueExpressionEvaluator> evaluatorFunction, AggregationCallback<T> callback) { @@ -308,8 +306,7 @@ static void appendLimitAndOffsetIfPresent(AggregationPipeline aggregationPipelin * @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> T extractSimpleTypeResult(@Nullable Document source, Class<T> targetType, MongoConverter converter) { + static <T> @Nullable T extractSimpleTypeResult(@Nullable Document source, Class<T> targetType, MongoConverter converter) { if (ObjectUtils.isEmpty(source)) { return null; @@ -336,9 +333,8 @@ static <T> T extractSimpleTypeResult(@Nullable Document source, Class<T> targetT String.format("o_O no entry of type %s found in %s.", targetType.getSimpleName(), source.toJson())); } - @Nullable @SuppressWarnings("unchecked") - private static <T> T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value, + private static <T> @Nullable T getPotentiallyConvertedSimpleTypeValue(MongoConverter converter, @Nullable Object value, Class<T> 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 2aac6b77a8..108c6ee796 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.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 @@ private 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 @@ static Collation computeCollation(@Nullable String collationExpression, Converti 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 dbf87f2f2e..d075b67efe 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.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.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 PotentiallyConvertingIterator iterator() { } @Override - public ScrollPosition getScrollPosition() { + public @Nullable ScrollPosition getScrollPosition() { return delegate.getScrollPosition(); } @@ -87,34 +87,34 @@ public Sort getSort() { } @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<Distance> getDistanceRange() { + public @Nullable Range<Distance> 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 Limit getLimit() { * @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 boolean hasNext() { 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 @@ private static Collection<?> asCollection(@Nullable Object source) { } @Override - public Object[] getValues() { + public Object @Nullable[] getValues() { return delegate.getValues(); } @@ -244,6 +243,6 @@ public interface PotentiallyConvertingIterator extends Iterator<Object> { * * @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/DefaultSpELExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java deleted file mode 100644 index 16a1e55226..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2020-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.repository.query; - -import org.springframework.data.mapping.model.SpELExpressionEvaluator; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.ExpressionParser; - -/** - * Simple {@link SpELExpressionEvaluator} implementation using {@link ExpressionParser} and {@link EvaluationContext}. - * - * @author Mark Paluch - * @since 3.1 - */ -class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator { - - private final ExpressionParser parser; - private final EvaluationContext context; - - DefaultSpELExpressionEvaluator(ExpressionParser parser, EvaluationContext context) { - this.parser = parser; - this.context = context; - } - - /** - * Return a {@link SpELExpressionEvaluator} that does not support expression evaluation. - * - * @return a {@link SpELExpressionEvaluator} that does not support expression evaluation. - * @since 3.1 - */ - public static SpELExpressionEvaluator unsupported() { - return NoOpExpressionEvaluator.INSTANCE; - } - - @Override - @SuppressWarnings("unchecked") - public <T> T evaluate(String expression) { - return (T) parser.parseExpression(expression).getValue(context, Object.class); - } - - /** - * {@link SpELExpressionEvaluator} that does not support SpEL evaluation. - * - * @author Mark Paluch - * @since 3.1 - */ - enum NoOpExpressionEvaluator implements SpELExpressionEvaluator { - - INSTANCE; - - @Override - public <T> T evaluate(String expression) { - throw new UnsupportedOperationException("Expression evaluation not supported"); - } - } -} 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 8678e5a74c..c54d689b52 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 @@ default boolean isVersioned() { * @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 5db853e810..00d748f8a9 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.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<Distance> getDistanceRange(); + @Nullable Range<Distance> 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 257d8d1918..3453e05617 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 @@ -19,6 +19,7 @@ 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; @@ -32,7 +33,6 @@ 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 @@ -46,9 +46,9 @@ public class MongoParameters extends Parameters<MongoParameters, MongoParameter> 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; @@ -89,9 +89,8 @@ private MongoParameters(ParametersSource parametersSource, NearIndex nearIndex) this.nearIndex = nearIndex.nearIndex; } - private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @Nullable Integer nearIndex, - @Nullable Integer fullTextIndex, int rangeIndex, @Nullable Integer collationIndex, int updateIndex, - TypeInformation<?> domainType) { + private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, int nearIndex, int fullTextIndex, + int rangeIndex, int collationIndex, int updateIndex, TypeInformation<?> domainType) { super(parameters); @@ -106,7 +105,7 @@ private MongoParameters(List<MongoParameter> parameters, int maxDistanceIndex, @ static class NearIndex { - private final @Nullable Integer nearIndex; + private final int nearIndex; public NearIndex(ParametersSource parametersSource, boolean isGeoNearMethod) { @@ -191,7 +190,7 @@ public int getNearIndex() { * @since 1.6 */ public int getFullTextParameterIndex() { - return fullTextIndex != null ? fullTextIndex : -1; + return fullTextIndex; } /** @@ -199,7 +198,7 @@ public int getFullTextParameterIndex() { * @since 1.6 */ public boolean hasFullTextParameter() { - return this.fullTextIndex != null && this.fullTextIndex >= 0; + return this.fullTextIndex >= 0; } /** @@ -217,7 +216,7 @@ public int getRangeIndex() { * @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 ac1931e10c..66529dfce9 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.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 MongoParametersParameterAccessor(MongoQueryMethod method, Object[] values this.method = method; } - public Range<Distance> getDistanceRange() { + @SuppressWarnings("NullAway") + public @Nullable Range<Distance> getDistanceRange() { MongoParameters mongoParameters = method.getParameters(); @@ -70,7 +72,7 @@ public Range<Distance> getDistanceRange() { return Range.of(Bound.unbounded(), maxDistance); } - public Point getGeoNearLocation() { + public @Nullable Point getGeoNearLocation() { int nearIndex = method.getParameters().getNearIndex(); @@ -95,14 +97,14 @@ public Point getGeoNearLocation() { 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 @@ protected TextCriteria potentiallyConvertFullText(Object fullText) { } @Override - public Collation getCollation() { + public @Nullable Collation getCollation() { if (method.getParameters().getCollationParameterIndex() == -1) { return null; @@ -134,12 +136,12 @@ public Collation getCollation() { } @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 66a8870623..7e327f4e20 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 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.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 @@ public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor, protected Criteria create(Part part, Iterator<Object> iterator) { if (isGeoNearQuery && part.getType().equals(Type.NEAR)) { - return null; + return new Criteria(); } PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty()); @@ -141,7 +141,7 @@ protected Criteria or(Criteria base, Criteria criteria) { } @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 @@ protected Query complete(Criteria criteria, Sort sort) { * @param parameters * @return */ + @SuppressWarnings("NullAway") private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria, Iterator<Object> parameters) { Type type = part.getType(); @@ -333,6 +334,7 @@ private Criteria createContainingCriteria(Part part, MongoPersistentProperty pro * @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 @@ private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, Object * @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 @@ private Streamable<?> asStreamable(Object value) { 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 dd2b78de59..abdcf62930 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 @@ 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.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 @@ public Object execute(Query query) { return isListOfGeoResult(method.getReturnType()) ? results.getContent() : results; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","NullAway"}) GeoResults<Object> 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 @@ GeoResults<Object> doExecuteQuery(Query query) { } Range<Distance> 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 @@ public DeleteExecution(MongoOperations operations, MongoQueryMethod method) { } @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 dcfef7de00..fc4ea90f49 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 @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.data.geo.GeoPage; import org.springframework.data.geo.GeoResult; @@ -48,7 +49,6 @@ 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; @@ -478,7 +478,7 @@ public boolean hasAnnotatedUpdate() { * @return the {@link Update} or {@literal null} if not present. * @since 3.4 */ - public Update getUpdateSource() { + public @Nullable Update getUpdateSource() { return lookupUpdateAnnotation().orElse(null); } @@ -488,6 +488,7 @@ public Update getUpdateSource() { * @since 3.4 * @throws IllegalStateException */ + @SuppressWarnings("NullAway") public void verify() { if (isModifyingQuery()) { @@ -526,6 +527,7 @@ public void verify() { } } + @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 afabf9c37e..6116cc5534 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 @@ -18,8 +18,6 @@ import org.bson.Document; import org.bson.json.JsonParseException; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoTemplate; @@ -29,14 +27,11 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.expression.ExpressionParser; import org.springframework.util.StringUtils; /** @@ -54,26 +49,6 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { private final MappingContext<?, MongoPersistentProperty> context; private final ResultProcessor processor; - /** - * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. - * - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead. - */ - @Deprecated(since = "4.4.0") - public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, ExpressionParser expressionParser, - QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations, expressionParser, evaluationContextProvider); - - this.processor = method.getResultProcessor(); - this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); - this.isGeoNearQuery = method.isGeoNearQuery(); - this.context = mongoOperations.getConverter().getMappingContext(); - } - /** * Creates a new {@link PartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. * @@ -103,6 +78,7 @@ public PartTree getTree() { } @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 431510f11b..4b7262749a 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 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 324f01d61f..9534a9cf4f 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 @@ public ReactiveMongoParameterAccessor(MongoQueryMethod method, Object[] values) * @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 d18c6a989c..06f946d745 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 @@ 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.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 @@ public Publisher<? extends Object> execute(Query query, Class<?> type, String co private Flux<GeoResult<Object>> 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 @@ private Flux<GeoResult<Object>> doExecuteQuery(@Nullable Query query, Class<?> t } Range<Distance> 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 @@ public ResultProcessingExecution(ReactiveMongoQueryExecution delegate, Converter } @Override + @SuppressWarnings("NullAway") public Publisher<? extends Object> 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/ReactiveMongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java index 8df1ba487e..966bbc0a26 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryMethod.java @@ -15,8 +15,6 @@ */ package org.springframework.data.mongodb.repository.query; -import static org.springframework.data.repository.util.ClassUtils.*; - import java.lang.reflect.Method; import org.springframework.dao.InvalidDataAccessApiUsageException; @@ -35,6 +33,7 @@ import org.springframework.data.repository.util.ReactiveWrapperConverters; 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.util.ClassUtils; @@ -130,7 +129,7 @@ public boolean hasReactiveWrapperParameter() { @Override public void verify() { - if (hasParameterOfType(method, Pageable.class)) { + if (ReflectionUtils.hasParameterOfType(method, Pageable.class)) { TypeInformation<?> returnType = TypeInformation.fromReturnTypeOf(method); @@ -139,7 +138,7 @@ public void verify() { && (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType()) || SLICE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())); - if (hasParameterOfType(method, Sort.class)) { + if (ReflectionUtils.hasParameterOfType(method, Sort.class)) { throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter;" + " Use sorting capabilities on Pageable instead; Offending method: %s", method)); } 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 5787cca5a5..4aa773091b 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 @@ -28,14 +28,11 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.expression.ExpressionParser; import org.springframework.util.StringUtils; /** @@ -52,26 +49,6 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { private final MappingContext<?, MongoPersistentProperty> context; private final ResultProcessor processor; - /** - * Creates a new {@link ReactivePartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. - * - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead. - */ - @Deprecated(since = "4.4.0") - public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, - ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations, expressionParser, evaluationContextProvider); - - this.processor = method.getResultProcessor(); - this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); - this.isGeoNearQuery = method.isGeoNearQuery(); - this.context = mongoOperations.getConverter().getMappingContext(); - } - /** * Creates a new {@link ReactivePartTreeMongoQuery} from the given {@link QueryMethod} and {@link MongoTemplate}. * @@ -110,6 +87,7 @@ protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) { 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 ff01d8f8a3..ebc33cef96 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 java.util.List; import org.bson.Document; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -29,12 +30,9 @@ import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.ReflectionUtils; -import org.springframework.expression.ExpressionParser; -import org.springframework.lang.Nullable; /** * A reactive {@link org.springframework.data.repository.query.RepositoryQuery} to use a plain JSON String to create an @@ -49,24 +47,6 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery { private final ReactiveMongoOperations reactiveMongoOperations; private final MongoConverter mongoConverter; - /** - * @param method must not be {@literal null}. - * @param reactiveMongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead. - */ - @Deprecated(since = "4.4.0") - public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method, - ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser, - ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { - - super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider); - - this.reactiveMongoOperations = reactiveMongoOperations; - this.mongoConverter = reactiveMongoOperations.getConverter(); - } - /** * @param method must not be {@literal null}. * @param reactiveMongoOperations must not be {@literal null}. 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 0e980fcfaf..4bfe2ca39f 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; @@ -28,13 +30,8 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.util.json.ParameterBindingContext; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; -import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.spel.ExpressionDependencies; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.NonNull; import org.springframework.util.Assert; /** @@ -50,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; @@ -59,73 +56,15 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { private final boolean isDeleteQuery; /** - * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod} and - * {@link MongoOperations}. - * - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead. - */ - @Deprecated(since = "4.4.0") - public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, - ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { - this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); - } - - /** - * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link String}, {@link MongoQueryMethod}, - * {@link MongoOperations}, {@link SpelExpressionParser} and - * {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider}. - * - * @param query must not be {@literal null}. - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead. - */ - @Deprecated(since = "4.4.0") - public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, - ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser, - ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations, expressionParser, evaluationContextProvider); - - Assert.notNull(query, "Query must not be null"); - - this.query = query; - this.expressionParser = ValueExpressionParser.create(() -> expressionParser); - this.fieldSpec = method.getFieldSpecification(); - - if (method.hasAnnotatedQuery()) { - - org.springframework.data.mongodb.repository.Query queryAnnotation = method.getQueryAnnotation(); - - this.isCountQuery = queryAnnotation.count(); - this.isExistsQuery = queryAnnotation.exists(); - this.isDeleteQuery = queryAnnotation.delete(); - - if (hasAmbiguousProjectionFlags(this.isCountQuery, this.isExistsQuery, this.isDeleteQuery)) { - throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method)); - } - - } else { - - this.isCountQuery = false; - this.isExistsQuery = false; - this.isDeleteQuery = false; - } - } - - /** - * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod}, - * {@link MongoOperations} and {@link ValueExpressionDelegate}. + * Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations} + * and {@link ValueExpressionDelegate}. * * @param method must not be {@literal null}. * @param mongoOperations must not be {@literal null}. * @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); @@ -141,7 +80,8 @@ public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMo * @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); @@ -195,7 +135,7 @@ protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) { }); } - private Mono<ParameterBindingContext> getBindingContext(String json, ConvertingParameterAccessor accessor, + private Mono<ParameterBindingContext> 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 724c8f29ef..289b953b27 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.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 7ad5d78fa6..3f6a48e84c 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.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; @@ -29,13 +29,9 @@ import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.util.ReflectionUtils; -import org.springframework.expression.ExpressionParser; -import org.springframework.lang.Nullable; /** * {@link AbstractMongoQuery} implementation to run string-based aggregations using @@ -51,30 +47,6 @@ public class StringBasedAggregation extends AbstractMongoQuery { private final MongoOperations mongoOperations; private final MongoConverter mongoConverter; - /** - * Creates a new {@link StringBasedAggregation} from the given {@link MongoQueryMethod} and {@link MongoOperations}. - * - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link QueryMethodValueEvaluationContextAccessor} instead. - */ - @Deprecated(since = "4.4.0") - public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations, - ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations, expressionParser, evaluationContextProvider); - - if (method.isPageQuery()) { - throw new InvalidMongoDbApiUsageException(String.format( - "Repository aggregation method '%s' does not support '%s' return type; Please use 'Slice' or 'List' instead", - method.getName(), method.getReturnType().getType().getSimpleName())); - } - - this.mongoOperations = mongoOperations; - this.mongoConverter = mongoOperations.getConverter(); - } - /** * Creates a new {@link StringBasedAggregation} from the given {@link MongoQueryMethod} and {@link MongoOperations}. * @@ -99,8 +71,7 @@ public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOper @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 abc158f88a..c990d3269d 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 @@ -22,11 +22,8 @@ import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; /** @@ -49,47 +46,6 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { private final boolean isExistsQuery; private final boolean isDeleteQuery; - /** - * Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations}, - * {@link SpelExpressionParser} and {@link QueryMethodEvaluationContextProvider}. - * - * @param method must not be {@literal null}. - * @param mongoOperations must not be {@literal null}. - * @param expressionParser must not be {@literal null}. - * @param evaluationContextProvider must not be {@literal null}. - * @deprecated since 4.4.0, use the constructors accepting {@link ValueExpressionDelegate} instead. - */ - @Deprecated(since = "4.4.0") - public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, - ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations, expressionParser, evaluationContextProvider); - - String query = method.getAnnotatedQuery(); - Assert.notNull(query, "Query must not be null"); - - this.query = query; - this.fieldSpec = method.getFieldSpecification(); - - if (method.hasAnnotatedQuery()) { - - org.springframework.data.mongodb.repository.Query queryAnnotation = method.getQueryAnnotation(); - - this.isCountQuery = queryAnnotation.count(); - this.isExistsQuery = queryAnnotation.exists(); - this.isDeleteQuery = queryAnnotation.delete(); - - if (hasAmbiguousProjectionFlags(this.isCountQuery, this.isExistsQuery, this.isDeleteQuery)) { - throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method)); - } - - } else { - - this.isCountQuery = false; - this.isExistsQuery = false; - this.isDeleteQuery = false; - } - } - /** * Creates a new {@link StringBasedMongoQuery} for the given {@link MongoQueryMethod}, {@link MongoOperations}, * {@link ValueExpressionDelegate}. @@ -99,6 +55,7 @@ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOpera * @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); @@ -114,6 +71,7 @@ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOpera * @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 c479f3faa9..360f5e80eb 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 @@ 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> T evaluate(String expressionString) { + public <T> @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 20c77e22aa..5f0cc21049 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 f59a995170..abd828a9f7 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 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.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 @@ static MethodInvocation currentInvocation() throws IllegalStateException { } @Override - public Object invoke(MethodInvocation invocation) throws Throwable { + public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); @@ -220,7 +220,7 @@ public boolean isStatic() { } @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 1d876289be..443108d2f0 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 boolean isVersioned() { } @Override - public Object getVersion(T entity) { + public @Nullable Object getVersion(T entity) { if (!isVersioned()) { return null; @@ -124,8 +124,7 @@ public Object getVersion(T entity) { 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 3c029ee5aa..6deee469e1 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.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 d0a3f7a1e4..1a39198757 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 baf069c3a4..e1abcdc2ab 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.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; @@ -42,10 +43,8 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.query.QueryLookupStrategy; 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; /** @@ -61,7 +60,6 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); private final MongoOperations operations; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; - @Nullable private QueryMethodValueEvaluationContextAccessor accessor; /** * Creates a new {@link MongoRepositoryFactory} with the given {@link MongoOperations}. @@ -79,14 +77,14 @@ public MongoRepositoryFactory(MongoOperations mongoOperations) { } @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 c98d38c5f5..cec54de0bb 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 @@ 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 @@ * * @author Oliver Gierke */ +@SuppressWarnings("NullAway") public class MongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends RepositoryFactoryBeanSupport<T, S, ID> { 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 ec845510ce..833ce69458 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 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 @@ protected <R> FluentQuerydsl<R> create(Predicate predicate, Sort sort, int limit } @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 3edfcdd2db..ae8561bc17 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,7 +21,7 @@ import java.lang.reflect.Method; import java.util.Optional; -import org.springframework.beans.BeansException; +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; @@ -44,10 +44,8 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; 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,19 +75,19 @@ public ReactiveMongoRepositoryFactory(ReactiveMongoOperations mongoOperations) { this.operations = mongoOperations; this.mappingContext = mongoOperations.getConverter().getMappingContext(); - setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT); addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); } @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(); } @@ -132,7 +130,9 @@ protected Object getTargetRepository(RepositoryInformation information) { return targetRepository; } - @Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key, + @Override + @SuppressWarnings("NullAway") + protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key, ValueExpressionDelegate valueExpressionDelegate) { return Optional.of(new MongoQueryLookupStrategy(operations, mappingContext, valueExpressionDelegate)); } @@ -159,8 +159,8 @@ private <T, ID> MongoEntityInformation<T, ID> getEntityInformation(Class<T> doma * @author Christoph Strobl */ private record MongoQueryLookupStrategy(ReactiveMongoOperations operations, - MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, - ValueExpressionDelegate delegate) implements QueryLookupStrategy { + MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, + ValueExpressionDelegate delegate) implements QueryLookupStrategy { @Override public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, 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 4f9c0d945c..e3d71325f9 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 @@ -16,18 +16,14 @@ package org.springframework.data.mongodb.repository.support; import java.io.Serializable; -import java.util.Optional; -import org.springframework.beans.factory.ListableBeanFactory; +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.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -82,6 +78,7 @@ public void setMappingContext(MappingContext<?, ?> mappingContext) { } @Override + @SuppressWarnings("NullAway") protected RepositoryFactorySupport createRepositoryFactory() { RepositoryFactorySupport factory = getFactoryInstance(operations); @@ -94,12 +91,6 @@ protected RepositoryFactorySupport createRepositoryFactory() { return factory; } - @Override - protected Optional<QueryMethodEvaluationContextProvider> createDefaultQueryMethodEvaluationContextProvider( - ListableBeanFactory beanFactory) { - return Optional.of(new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(beanFactory)); - } - /** * Creates and initializes a {@link RepositoryFactorySupport} instance. * 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 cf5191fd42..a86ada0aad 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.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.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 @@ static class NoMatchException extends RuntimeException { @Override public synchronized Throwable fillInStackTrace() { - return null; + return this; } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java index 2f4c30ee7a..7e6e2fc82e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SimpleMongoRepository.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; @@ -47,7 +48,6 @@ import org.springframework.data.support.PageableExecutionUtils; import org.springframework.data.util.StreamUtils; import org.springframework.data.util.Streamable; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.mongodb.ReadPreference; @@ -427,12 +427,12 @@ protected <R> FluentQueryByExample<S, R> create(Example<S> predicate, Sort sort, } @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 1c1df2c9a1..7e4a3aa665 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.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.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 0ef6c38744..809dbab8cf 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,21 +22,18 @@ 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; import org.springframework.data.domain.ScrollPosition; -import org.springframework.data.domain.Slice; import org.springframework.data.domain.Window; import org.springframework.data.mongodb.core.ExecutableFindOperation; import org.springframework.data.mongodb.core.MongoOperations; 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.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 +44,7 @@ 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} @@ -185,22 +183,8 @@ public Page<T> fetchPage(Pageable pageable) { } } - /** - * Fetch a {@link Slice}. - * - * @param pageable defines range and sort of requested slice - * @return new instance of {@link Slice} containing matching results within range. - * @since 4.5 - */ - public Slice<T> fetchSlice(Pageable pageable) { - - List<T> content = find.matching(SliceUtils.limitResult(createQuery(), pageable).with(pageable.getSort())).all(); - - return SliceUtils.sliceResult(content, pageable); - } - @Override - public T fetchFirst() { + public @Nullable T fetchFirst() { try { return find.matching(createQuery()).firstValue(); } catch (RuntimeException e) { @@ -209,7 +193,7 @@ public T fetchFirst() { } @Override - public T fetchOne() { + public @Nullable T fetchOne() { try { return find.matching(createQuery()).oneValue(); } catch (RuntimeException e) { @@ -279,7 +263,8 @@ protected List<Object> getIds(Class<?> targetType, Predicate condition) { return mongoOperations.findDistinct(query, FieldName.ID.name(), targetType, Object.class); } - private static <T> T handleException(RuntimeException e, T defaultValue) { + @Contract("_, !null -> !null") + private static <T> @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/SpringDataMongodbSerializer.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/SpringDataMongodbSerializer.java index d9a550a0f7..756d04d0c2 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.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.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 @@ * @author Christoph Strobl * @author Mark Paluch */ +@NullUnmarked class SpringDataMongodbSerializer extends MongodbDocumentSerializer { private static final String ID_KEY = FieldName.ID.name(); @@ -146,8 +148,7 @@ protected boolean isId(Path<?> arg) { } @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 @@ protected Object convert(@Nullable Path<?> path, @Nullable Constant<?> constant) 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 1d0b8beeba..42cd5a0b18 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 cbbd4a37a9..83df5f7798 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.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> T get(Bson bson, String key) { + @Contract("null, _ -> null") + public static <T> @Nullable T get(@Nullable Bson bson, String key) { return (T) asMap(bson).get(key); } @@ -85,7 +86,7 @@ public static <T> T get(Bson bson, String key) { * @param bson * @return */ - public static Map<String, Object> asMap(Bson bson) { + public static Map<String, Object> asMap(@Nullable Bson bson) { return asMap(bson, MongoClientSettings.getDefaultCodecRegistry()); } @@ -126,7 +127,7 @@ public static Map<String, Object> asMap(@Nullable Bson bson, CodecRegistry codec * @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 static Document asDocument(Bson bson) { * @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<String, Object> map = asMap(bson, codecRegistry); @@ -326,14 +327,14 @@ public static Object toJavaType(BsonValue value) { * @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 static BsonValue simpleToBsonValue(Object source) { * @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 static BsonValue simpleToBsonValue(Object source, CodecRegistry codecRegi 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 static Document toDocumentOrElse(String source, Function<String, Document * @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 static String toJson(@Nullable Document source) { * @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 static boolean isJsonDocument(@Nullable String value) { * @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 static Document parse(String json, @Nullable CodecRegistryProvider codecR * @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 static Object resolveValue(Bson bson, String key) { * @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 static Object resolveValue(Bson bson, FieldName fieldName) { * @return can be {@literal null}. * @since 4.2 */ - @Nullable - public static Object resolveValue(Map<String, Object> source, FieldName fieldName) { + public static @Nullable Object resolveValue(Map<String, Object> source, FieldName fieldName) { if (fieldName.isKey()) { return source.get(fieldName.name()); @@ -590,8 +597,7 @@ public static Object resolveValue(Map<String, Object> source, FieldName fieldNam * @return can be {@literal null}. * @since 4.1 */ - @Nullable - public static Object resolveValue(Map<String, Object> source, String key) { + public static @Nullable Object resolveValue(Map<String, Object> source, String key) { if (source.containsKey(key)) { return source.get(key); @@ -643,9 +649,9 @@ public static boolean hasValue(Bson bson, String key) { * @param source can be {@literal null}. * @return can be {@literal null}. */ - @Nullable @SuppressWarnings("unchecked") - private static Map<String, Object> getAsMap(Object source) { + @Contract("null -> null") + private static @Nullable Map<String, Object> getAsMap(@Nullable Object source) { if (source instanceof Document document) { return document; @@ -745,8 +751,8 @@ public static Document mapEntries(Document source, Function<Entry<String, Object 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 191c7d24d3..549c7ff720 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 67255b878a..78eb59a461 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 @@ 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.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 static Duration evaluate(String value, ValueEvaluationContext evaluationC public static Duration evaluate(String value, Supplier<EvaluationContext> 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 ffc97402fe..23ea9409cc 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 @@ public boolean replace(String key, Object oldValue, Object newValue) { 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 991a7292fd..fbbba59e8f 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,8 +15,8 @@ */ package org.springframework.data.mongodb.util; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Version; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.internal.build.MongoDriverVersion; @@ -91,8 +91,7 @@ private static Version getMongoDbDriverVersion(ClassLoader classLoader) { 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 { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java index f85be98c1f..ba1e32356a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/MongoCompatibilityAdapter.java @@ -20,8 +20,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -44,6 +44,7 @@ * @author Christoph Strobl * @since 4.3 */ +@SuppressWarnings("NullAway") public class MongoCompatibilityAdapter { private static final String NO_LONGER_SUPPORTED = "%s is no longer supported on Mongo Client 5 or newer"; @@ -174,8 +175,7 @@ public interface IndexOptionsAdapter { } public interface ClientSettingsAdapter { - @Nullable - <T> T getStreamFactoryFactory(); + <T> @Nullable T getStreamFactoryFactory(); } public interface ClientSettingsBuilderAdapter { @@ -199,14 +199,12 @@ public interface MongoDatabaseAdapterBuilder { MongoDatabaseAdapter forDb(com.mongodb.client.MongoDatabase db); } - @SuppressWarnings({ "unchecked", "DataFlowIssue" }) + @SuppressWarnings({ "unchecked", "DataFlowIssue", "NullAway" }) public static class MongoDatabaseAdapter { - @Nullable // - private static final Method LIST_COLLECTION_NAMES_METHOD; + private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD; - @Nullable // - private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION; + private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD_SESSION; private static final Class<?> collectionNamesReturnType; @@ -271,11 +269,9 @@ public interface ReactiveMongoDatabaseAdapterBuilder { @SuppressWarnings({ "unchecked", "DataFlowIssue" }) public static class ReactiveMongoDatabaseAdapter { - @Nullable // - private static final Method LIST_COLLECTION_NAMES_METHOD; + private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD; - @Nullable // - private static final Method LIST_COLLECTION_NAMES_METHOD_SESSION; + private static final @Nullable Method LIST_COLLECTION_NAMES_METHOD_SESSION; private static final Class<?> collectionNamesReturnType; 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 326a5c1e88..7fcc1383d5 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 23c96f9e46..8b0f4b83ce 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 @@ 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 344244717e..950f9ec797 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 @@ 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.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 9dd3f1d8fb..be9a2e1cff 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 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> evaluationContext) { + public static @Nullable Object resolveKeyId(String value, Supplier<EvaluationContext> 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 b5c26755cf..3961fafc21 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.ZoneOffset; import java.time.ZonedDateTime; +import org.jspecify.annotations.NullUnmarked; + /** * DateTimeFormatter implementation borrowed from <a href= * "https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/json/DateTimeFormatter.java">MongoDB @@ -33,6 +35,7 @@ * @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 6c31a9721f..57fecd284c 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 @@ 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> T evaluate(String expression) { + public <T> @Nullable T evaluate(String expression) { return evaluateExpression(expression, Collections.emptyMap()); } @@ -55,7 +54,7 @@ Expression getParsedExpression(String expressionString) { } @SuppressWarnings("unchecked") - <T> T evaluateExpression(String expressionString, Map<String, Object> variables) { + <T> @Nullable T evaluateExpression(String expressionString, Map<String, Object> 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 4b4b497dae..dcb9a3ff13 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 <a href= @@ -27,6 +28,7 @@ * @author Ross Lawley * @since 2.2 */ +@NullUnmarked class JsonBuffer { private final String buffer; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java index ca4fbddd60..461e52489f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/JsonScanner.java @@ -17,6 +17,7 @@ import org.bson.BsonRegularExpression; import org.bson.json.JsonParseException; +import org.jspecify.annotations.NullUnmarked; /** * Parses the string representation of a JSON object into a set of {@link JsonToken}-derived objects. <br /> @@ -32,6 +33,7 @@ * @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 293736123e..e73d57774b 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 org.bson.BsonDouble; import org.bson.json.JsonParseException; import org.bson.types.Decimal128; +import org.jspecify.annotations.NullUnmarked; /** * JsonToken implementation borrowed from <a href= @@ -30,6 +31,7 @@ * @author Ross Lawley * @since 2.2 */ +@NullUnmarked class JsonToken { private final Object value; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java index b4fd13b3af..55dccad0c9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java @@ -19,7 +19,7 @@ import java.util.function.Function; import java.util.function.Supplier; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.ValueExpressionEvaluator; import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.util.Lazy; @@ -28,8 +28,6 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; /** * Reusable context for binding parameters to a placeholder or a SpEL expression within a JSON structure. <br /> @@ -44,29 +42,6 @@ public class ParameterBindingContext { private final ValueProvider valueProvider; private final ValueExpressionEvaluator expressionEvaluator; - /** - * @param valueProvider - * @param expressionParser - * @param evaluationContext - * @deprecated since 4.4.0, use {@link #ParameterBindingContext(ValueProvider, ExpressionParser, Supplier)} instead. - */ - @Deprecated(since = "4.4.0") - public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, - EvaluationContext evaluationContext) { - this(valueProvider, expressionParser, () -> evaluationContext); - } - - /** - * @param valueProvider - * @param expressionEvaluator - * @since 3.1 - * @deprecated since 4.4.0, use {@link #ParameterBindingContext(ValueProvider, ValueExpressionEvaluator)} instead. - */ - @Deprecated(since = "4.4.0") - public ParameterBindingContext(ValueProvider valueProvider, SpELExpressionEvaluator expressionEvaluator) { - this(valueProvider, (ValueExpressionEvaluator) expressionEvaluator); - } - /** * @param valueProvider * @param expressionParser @@ -153,18 +128,15 @@ public static ParameterBindingContext forExpressions(ValueProvider valueProvider 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<String, Object> variables) { + public @Nullable Object evaluateExpression(String expressionString, Map<String, Object> 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 ffa226ab69..8138f397a6 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.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 @@ * @author Rocco Lagrotteria * @since 2.2 */ +@NullUnmarked public class ParameterBindingDocumentCodec implements CollectibleCodec<Document> { private static final String ID_FIELD_NAME = FieldName.ID.name(); @@ -170,7 +171,7 @@ public void encode(final BsonWriter writer, final Document document, final Encod public Document decode(@Nullable String json, Object[] values) { return decode(json, new ParameterBindingContext((index) -> values[index], new SpelExpressionParser(), - EvaluationContextProvider.DEFAULT.getEvaluationContext(values))); + () -> EvaluationContextProvider.DEFAULT.getEvaluationContext(values))); } public Document decode(@Nullable String json, ParameterBindingContext bindingContext) { @@ -396,9 +397,8 @@ static class DependencyCapturingExpressionEvaluator implements ValueExpressionEv this.expressionParser = expressionParser; } - @Nullable @Override - public <T> T evaluate(String expression) { + public <T> @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 8dd42e2427..c1e519e2f5 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.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 @@ * @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 @@ private BsonType bsonTypeForValue(Object value) { 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<String,Object> variables) { + private @Nullable Object evaluateExpression(String expressionString, Map<String,Object> 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 8f1d23885d..2ce22214fb 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 8a86b3522b..60e5e8c609 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 7caec410f5..a697bb7000 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 9fa66b3b2b..796f618906 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 @@ 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 static Expression detectExpression(@Nullable String potentialExpression) return expression instanceof LiteralExpression ? null : expression; } - @Nullable - public static Object evaluate(String value, Supplier<EvaluationContext> evaluationContext) { + public static @Nullable Object evaluate(String value, Supplier<EvaluationContext> 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 d132482f65..d7784a7768 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 <T> Update.pull(key: KProperty<T>, value: Any) = * @since 4.4 * @see Update.pullAll */ -fun <T> Update.pullAll(key: KProperty<Collection<T>>, values: Array<T>) = +fun <T> Update.pullAll(key: KProperty<Collection<T>>, values: Array<Any>) = pullAll(key.toDotPath(), values) /** diff --git a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas index 57920f7449..c6c28dbab1 100644 --- a/spring-data-mongodb/src/main/resources/META-INF/spring.schemas +++ b/spring-data-mongodb/src/main/resources/META-INF/spring.schemas @@ -13,7 +13,8 @@ http\://www.springframework.org/schema/data/mongo/spring-mongo-2.2.xsd=org/sprin http\://www.springframework.org/schema/data/mongo/spring-mongo-3.0.xsd=org/springframework/data/mongodb/config/spring-mongo-3.0.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-3.3.xsd=org/springframework/data/mongodb/config/spring-mongo-3.3.xsd http\://www.springframework.org/schema/data/mongo/spring-mongo-4.0.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd -http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo-5.0.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd +http\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd https\://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd=org/springframework/data/mongodb/config/spring-mongo-1.0.xsd https\://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd=org/springframework/data/mongodb/config/spring-mongo-1.1.xsd https\://www.springframework.org/schema/data/mongo/spring-mongo-1.2.xsd=org/springframework/data/mongodb/config/spring-mongo-1.2.xsd @@ -29,4 +30,5 @@ https\://www.springframework.org/schema/data/mongo/spring-mongo-2.2.xsd=org/spri https\://www.springframework.org/schema/data/mongo/spring-mongo-3.0.xsd=org/springframework/data/mongodb/config/spring-mongo-3.0.xsd https\://www.springframework.org/schema/data/mongo/spring-mongo-3.3.xsd=org/springframework/data/mongodb/config/spring-mongo-3.3.xsd https\://www.springframework.org/schema/data/mongo/spring-mongo-4.0.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd -https\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-4.0.xsd +https\://www.springframework.org/schema/data/mongo/spring-mongo-5.0.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd +https\://www.springframework.org/schema/data/mongo/spring-mongo.xsd=org/springframework/data/mongodb/config/spring-mongo-5.0.xsd diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd new file mode 100644 index 0000000000..5fae630b6b --- /dev/null +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-5.0.xsd @@ -0,0 +1,935 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + ~ Copyright 2019-2025 the original author or authors. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ https://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<xsd:schema xmlns="http://www.springframework.org/schema/data/mongo" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:beans="http://www.springframework.org/schema/beans" + xmlns:tool="http://www.springframework.org/schema/tool" + xmlns:repository="http://www.springframework.org/schema/data/repository" + targetNamespace="http://www.springframework.org/schema/data/mongo" + elementFormDefault="qualified" attributeFormDefault="unqualified"> + + <xsd:import namespace="http://www.springframework.org/schema/beans"/> + <xsd:import namespace="http://www.springframework.org/schema/tool"/> + <xsd:import namespace="http://www.springframework.org/schema/context"/> + <xsd:import namespace="http://www.springframework.org/schema/data/repository" + schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd"/> + + <xsd:element name="mongo-client" type="mongoClientType"> + <xsd:annotation> + <xsd:documentation + source="org.springframework.data.mongodb.core.MongoClientFactoryBean"> + <![CDATA[ +Defines a com.mongodb.client.MongoClient instance used for accessing MongoDB. + ]]></xsd:documentation> + <xsd:appinfo> + <tool:annotation> + <tool:exports type="com.mongodb.client.MongoClient"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:element> + + <xsd:element name="db-factory"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Defines a MongoDbFactory for connecting to a specific database + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:attribute name="id" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the MongoDatabaseFactory definition (by default "mongoDbFactory").]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="mongo-client-ref" type="mongoRef" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The reference to a com.mongodb.client.MongoClient instance. + ]]> + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="dbname" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the database to connect to. Default is 'db'. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="client-uri" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The MongoClientURI string. +@Deprecated since 3.0 - Use connection-string instead. + ]]> + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-string" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The MongoDB Connection String. Supersedes client-uri. +See https://docs.mongodb.com/manual/reference/connection-string/ for full documentation. +@Since 3.0 + ]]> + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="write-concern"> + <xsd:annotation> + <xsd:documentation> + The WriteConcern that will be the default value used when asking + the MongoDatabaseFactory for a DB object + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="writeConcernEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:attributeGroup name="mongo-repository-attributes"> + <xsd:attribute name="mongo-template-ref" type="mongoTemplateRef" + default="mongoTemplate"> + <xsd:annotation> + <xsd:documentation> + The reference to a MongoTemplate. Will default to 'mongoTemplate'. + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="create-query-indexes" default="false"> + <xsd:annotation> + <xsd:documentation> + Enables creation of indexes for queries that get derived from the + method name + and thus reference domain class properties. Defaults to false. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="booleanType xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + </xsd:attributeGroup> + + <xsd:element name="repositories"> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="repository:repositories"> + <xsd:attributeGroup ref="mongo-repository-attributes"/> + <xsd:attributeGroup ref="repository:repository-attributes"/> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="mapping-converter"> + <xsd:annotation> + <xsd:documentation> + <![CDATA[Defines a MongoConverter for getting rich mapping functionality.]]></xsd:documentation> + <xsd:appinfo> + <tool:exports + type="org.springframework.data.mongodb.core.convert.MappingMongoConverter"/> + </xsd:appinfo> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="custom-converters" minOccurs="0"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Top-level element that contains one or more custom converters to be used for mapping + domain objects to and from Mongo's Document]]> + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="converter" type="customConverterType" + minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="base-package" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="id" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the MappingMongoConverter instance (by default "mappingConverter").]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="base-package" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The base package in which to scan for entities annotated with @Document + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="db-factory-ref" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + The reference to a MongoDatabaseFactory. + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.MongoDatabaseFactory"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="type-mapper-ref" type="typeMapperRef" use="optional"> + <xsd:annotation> + <xsd:documentation> + The reference to a MongoTypeMapper to be used by this + MappingMongoConverter. + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="mapping-context-ref" type="mappingContextRef" + use="optional"> + <xsd:annotation> + <xsd:documentation + source="org.springframework.data.mapping.model.MappingContext"> + The reference to a MappingContext. Will default to + 'mappingContext'. + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="disable-validation" use="optional"> + <xsd:annotation> + <xsd:documentation + source="org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener"> + Disables JSR-303 validation on MongoDB documents before they are + saved. By default it is set to false. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="xsd:boolean xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="abbreviate-field-names" use="optional"> + <xsd:annotation> + <xsd:documentation + source="org.springframework.data.mongodb.core.mapping.CamelCaseAbbreviatingFieldNamingStrategy"> + Enables abbreviating the field names for domain class properties + to the + first character of their camel case names, e.g. fooBar -> fb. + Defaults to false. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="xsd:boolean xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="field-naming-strategy-ref" type="fieldNamingStrategyRef" + use="optional"> + <xsd:annotation> + <xsd:documentation + source="org.springframework.data.mongodb.core.mapping.FieldNamingStrategy"> + The reference to a FieldNamingStrategy. + </xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="auto-index-creation" use="optional"> + <xsd:annotation> + <xsd:documentation> + Enable/Disable index creation for annotated properties/entities. + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="xsd:boolean xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="auditing"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation> + <tool:exports + type="org.springframework.data.mongodb.core.mapping.event.AuditingEntityCallback"/> + <tool:exports + type="org.springframework.data.auditing.IsNewAwareAuditingHandler"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:complexType> + <xsd:attributeGroup ref="repository:auditing-attributes"/> + <xsd:attribute name="mapping-context-ref" type="mappingContextRef"/> + <xsd:attribute name="mongo-converter-ref" type="mongoConverterRef"/> + </xsd:complexType> + </xsd:element> + + <xsd:simpleType name="typeMapperRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.convert.MongoTypeMapper"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="mappingContextRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mapping.model.MappingContext"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="mongoConverterRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.convert.MongoConverter"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="fieldNamingStrategyRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.mapping.FieldNamingStrategy"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="mongoTemplateRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.MongoTemplate"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="mongoRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.MongoClientFactoryBean"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="sslSocketFactoryRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to type="javax.net.ssl.SSLSocketFactory"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="encryptionSettingsRef"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Reference to FactoryBean for com.mongodb.AutoEncryptionSettings - @since 2.2 + ]]></xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.MongoEncryptionSettingsFactoryBean"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="serverApiRef"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Reference to FactoryBean for com.mongodb.MongoServerApiFactoryBean - @since 3.3 + ]]></xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.MongoServerApiFactoryBean"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:simpleType name="readConcernEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="DEFAULT"/> + <xsd:enumeration value="LOCAL"/> + <xsd:enumeration value="MAJORITY"/> + <xsd:enumeration value="LINEARIZABLE"/> + <xsd:enumeration value="AVAILABLE"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="writeConcernEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="ACKNOWLEDGED"/> + <xsd:enumeration value="W1"/> + <xsd:enumeration value="W2"/> + <xsd:enumeration value="W3"/> + <xsd:enumeration value="UNACKNOWLEDGED"/> + <xsd:enumeration value="JOURNALED"/> + <xsd:enumeration value="MAJORITY"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="readPreferenceEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="PRIMARY"/> + <xsd:enumeration value="PRIMARY_PREFERRED"/> + <xsd:enumeration value="SECONDARY"/> + <xsd:enumeration value="SECONDARY_PREFERRED"/> + <xsd:enumeration value="NEAREST"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="uuidRepresentationEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="UNSPECIFIED"/> + <xsd:enumeration value="STANDARD"/> + <xsd:enumeration value="C_SHARP_LEGACY"/> + <xsd:enumeration value="JAVA_LEGACY"/> + <xsd:enumeration value="PYTHON_LEGACY"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="clusterConnectionModeEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="SINGLE"/> + <xsd:enumeration value="MULTIPLE"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="clusterTypeEnumeration"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="STANDALONE"/> + <xsd:enumeration value="REPLICA_SET"/> + <xsd:enumeration value="SHARDED"/> + <xsd:enumeration value="UNKNOWN"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="booleanType"> + <xsd:restriction base="xsd:token"> + <xsd:enumeration value="true"/> + <xsd:enumeration value="false"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:complexType name="mongoClientType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Configuration options for 'com.mongodb.client.MongoClient' + ]]></xsd:documentation> + </xsd:annotation> + <xsd:sequence minOccurs="0" maxOccurs="1"> + <xsd:element name="client-settings" type="clientSettingsType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The Mongo driver settings + ]]></xsd:documentation> + <xsd:appinfo> + <tool:annotation> + <tool:exports type="com.mongodb.MongoClientSettings"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="id" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the MongoClient definition (by default "mongoClient").]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-string" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The mongodb connection string. E.g. 'mongodb://localhost:27017?replicaSet=rs0' +See https://docs.mongodb.com/manual/reference/connection-string/ for full documentation. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="port" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The port to connect to MongoDB server. Default is 27017 + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="host" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The host to connect to a MongoDB server. Default is localhost + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="replica-set" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The replica set name when connecting to a cluster. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="credential" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +NOTE: Best way of setting up a connection is by providing the 'connection-string'. +The comma delimited list of username:password@database entries to use for authentication. Appending ?uri.authMechanism allows to specify the authentication challenge mechanism. If the credential you're trying to pass contains a comma itself, quote it with single quotes: '…'. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:complexType name="clientSettingsType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Configuration options for 'MongoClientSettings' - @since 3.0 + ]]></xsd:documentation> + </xsd:annotation> + <xsd:attribute name="application-name" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The application name to use when connecting to MongoDB. Mainly used to identify an operation in server logs, query logs and other profiling features. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="uuid-representation" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The storage format of UUID types. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="uuidRepresentationEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="read-preference" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The read preference to use for quries, map-reduce, aggregation and count operations. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="readPreferenceEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="read-concern" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set the global read isolation level. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="readConcernEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="write-concern"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set the default 'WriteConcern' that is controls the acknowledgment of write operations. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="writeConcernEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="retry-reads" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets whether reads should be retried if they fail due to a network error. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="booleanType xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="retry-writes" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets whether writes should be retried if they fail due to a network error. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="booleanType xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="socket-connect-timeout" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the socket connect timeout (msec). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="socket-read-timeout" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the socket read timeoutn (msec). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="socket-receive-buffer-size" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the receive buffer size. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="socket-send-buffer-size" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the send buffer size. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="server-heartbeat-frequency" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +This is the frequency that the driver will attempt to determine the current state of each server in the cluster. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="server-min-heartbeat-frequency" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +In the event that the driver has to frequently re-check a server's availability, it will wait at least this long since the previous check to avoid wasted effort. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="cluster-srv-host" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the host name to use in order to look up an SRV DNS record to find the MongoDB hosts. +NOTE: do not use along with cluster-hosts. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="cluster-hosts" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the hosts for the cluster. Any duplicate server addresses are removed from the list. +NOTE: do not use along with cluster-srv-host + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="cluster-connection-mode" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the cluster connection mode to either single node direct or multiple servers. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="clusterConnectionModeEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="cluster-type" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the cluster type (eg. SHARDED, REPLICA_SET,...). + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="clusterTypeEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="cluster-local-threshold" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the local threshold when selecting a server based on fastes ping time. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="cluster-server-selection-timeout" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the timeout to apply when selecting a server. +Zero indicates an immediate timeout while a negative value means indefinitely wait. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-max-size" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the maximum number of connections allowed. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-min-size" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the minimum number of connections. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-max-wait-time" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the maximum time a thread may wait for a connection to become available. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-max-connection-life-time" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The maximum time a pooled connection can live for. +Zero indicates no limit. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-max-connection-idle-time" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the maximum idle time of a pooled connection. +Zero indicates no limit. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-maintenance-initial-delay" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the period of time to wait before running the first maintenance job on the connection pool. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="connection-pool-maintenance-frequency" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Sets the time period between runs of the maintenance job. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="ssl-enabled" default="false"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set whether SSL should be enabled or not. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="booleanType xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="ssl-invalid-host-name-allowed" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set whether invalid host names should be allowed. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="ssl-provider" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set the SSL Context instance provider. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="encryption-settings-ref" type="encryptionSettingsRef" + use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +AutoEncryptionSettings for MongoDB 4.2+ + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="server-api-version" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Set the server API version for MongoDB 5.0+. Use server-api-ref if required to set additional modes like 'strict',... + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="server-api-ref" type="serverApiRef" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +ServerAPI for MongoDB 5.0+ + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:group name="beanElementGroup"> + <xsd:choice> + <xsd:element ref="beans:bean"/> + <xsd:element ref="beans:ref"/> + </xsd:choice> + </xsd:group> + + <xsd:complexType name="customConverterType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Element defining a custom converter. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:group ref="beanElementGroup" minOccurs="0" maxOccurs="1"/> + <xsd:attribute name="ref" type="xsd:string"> + <xsd:annotation> + <xsd:documentation> + A reference to a custom converter. + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"/> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:simpleType name="converterRef"> + <xsd:annotation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.convert.MongoConverter"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + <xsd:union memberTypes="xsd:string"/> + </xsd:simpleType> + + <xsd:element name="template"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Defines a MongoTemplate. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:attribute name="id" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the MongoTemplate definition (by default "mongoTemplate").]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="converter-ref" type="converterRef" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The reference to a MappingMongoConverter instance. + ]]> + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.convert.MongoConverter"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="db-factory-ref" type="xsd:string" + use="optional"> + <xsd:annotation> + <xsd:documentation> + The reference to a MongoDatabaseFactory. + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.MongoDatabaseFactory"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="write-concern"> + <xsd:annotation> + <xsd:documentation> + The WriteConcern that will be the default value used when asking + the MongoDatabaseFactory for a DB object + </xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:union memberTypes="writeConcernEnumeration xsd:string"/> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="gridFsTemplate"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +Defines a GridFsTemplate. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:attribute name="id" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The name of the GridFsTemplate definition (by default "gridFsTemplate").]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="converter-ref" type="converterRef" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The reference to a MappingMongoConverter instance. + ]]> + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.core.convert.MongoConverter"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="db-factory-ref" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation> + The reference to a MongoDatabaseFactory. + </xsd:documentation> + <xsd:appinfo> + <tool:annotation kind="ref"> + <tool:assignable-to + type="org.springframework.data.mongodb.MongoDatabaseFactory"/> + </tool:annotation> + </xsd:appinfo> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="bucket" type="xsd:string" use="optional"> + <xsd:annotation> + <xsd:documentation><![CDATA[ +The GridFs bucket string.]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> +</xsd:schema> 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 0448ad936c..c05122873c 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 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 CapturingTransactionOptionsResolver(MongoTransactionOptionsResolver deleg 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 44692348a0..d89edc6206 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 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 @@ void testEquals() { 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/JmxServer.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java deleted file mode 100644 index cb8a8dcf45..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/JmxServer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.core; - -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * Server application than can be run as an app or unit test. - * - * @author Mark Pollack - * @author Oliver Gierke - */ -public class JmxServer { - - public static void main(String[] args) { - new JmxServer().run(); - } - - @SuppressWarnings("resource") - public void run() { - new ClassPathXmlApplicationContext(new String[] { "infrastructure.xml", "server-jmx.xml" }); - } -} 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 9730e61e51..bdc151ec63 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 @@ 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.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 51b3b005a5..9a6bbb4f29 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 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.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 String toString() { static class ReferencableConverter implements Converter<ReferenceAble, DocumentPointer<Object>> { - @Nullable @Override - public DocumentPointer<Object> convert(ReferenceAble source) { + public @Nullable DocumentPointer<Object> convert(ReferenceAble source) { return source::toReference; } } @@ -1947,9 +1946,8 @@ public DocumentPointer<Object> convert(ReferenceAble source) { static class DocumentToSimpleObjectRefWithReadingConverter implements Converter<DocumentPointer<Document>, SimpleObjectRefWithReadingConverter> { - @Nullable @Override - public SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> source) { + public @Nullable SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> 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 SimpleObjectRefWithReadingConverter convert(DocumentPointer<Document> sou static class SimpleObjectRefWithReadingConverterToDocumentConverter implements Converter<SimpleObjectRefWithReadingConverter, DocumentPointer<Document>> { - @Nullable @Override - public DocumentPointer<Document> convert(SimpleObjectRefWithReadingConverter source) { + public @Nullable DocumentPointer<Document> 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 766929c732..772392f037 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.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.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 83d4e30cc5..ee01de7ebc 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 @@ -35,6 +35,7 @@ import java.util.stream.Stream; 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; @@ -48,7 +49,7 @@ import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.annotation.Version; import org.springframework.data.auditing.IsNewAwareAuditingHandler; import org.springframework.data.domain.PageRequest; @@ -90,7 +91,6 @@ 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; @@ -4469,7 +4469,7 @@ static class TestClass { LocalDateTime myDate; - @PersistenceConstructor + @PersistenceCreator TestClass(LocalDateTime myDate) { this.myDate = myDate; } @@ -4726,7 +4726,7 @@ static class DocumentWithLazyDBrefUsedInPresistenceConstructor { @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocUsedInCtor; @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) Document refToDocNotUsedInCtor; - @PersistenceConstructor + @PersistenceCreator public DocumentWithLazyDBrefUsedInPresistenceConstructor(Document refToDocUsedInCtor) { this.refToDocUsedInCtor = refToDocUsedInCtor; } 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 79a0bb1fcb..b5892c2ca0 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.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.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 List<T> getValues() { 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 18da8c516d..fd1b70f3c7 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.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.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 SimpleBean(@Nullable String nonNullString, @Nullable Integer rangedIntege 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 bc126e05f0..7b07cd9448 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 609a456912..b5a40f5738 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 @@ void usesExtractedCollectionName() { 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 @@ void usesExplicitCollectionName() { .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 @@ void usesQueryWhenPresent() { 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 @@ void usesCriteriaWhenPresent() { .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 @@ void usesProjectionWhenPresent() { 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 f89b2fa8c1..5ba0d947fe 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.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.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 8968f53a74..b8cc9cc972 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 @@ 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 T saveWithinMaxCommitTime(T entity) { 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 d4c2f37f63..61cd3ecce4 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 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.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 25fbbbcb83..e9eae082e3 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/Venue.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java index 09a0605ed7..13aba70afa 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/Venue.java @@ -19,7 +19,7 @@ import java.util.Date; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.mongodb.core.mapping.Document; @Document("newyork") @@ -30,7 +30,7 @@ public class Venue { private double[] location; private Date openingDate; - @PersistenceConstructor + @PersistenceCreator Venue(String name, double[] location) { super(); this.name = name; 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 32c6d43220..3256b889d4 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 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 47176fd8ab..d830a44582 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 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.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 9496a51c03..1b9aba1ba0 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 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.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 311496ba8d..18980f6a06 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 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.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 1174507e1c..a463b72cff 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.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 24566089e7..d29e32a988 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 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.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 093d4af7a0..82ad4c223f 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,6 +20,7 @@ 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; @@ -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.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 b5f5f596e6..18eb659cd0 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 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.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 e47fea289e..ef514ca882 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.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 2f081cc9fc..c406b89626 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.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.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 b53531f301..71c395e822 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 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; @@ -46,7 +47,7 @@ import org.springframework.data.annotation.AccessType; import org.springframework.data.annotation.AccessType.Type; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mongodb.MongoDatabaseFactory; @@ -55,7 +56,6 @@ 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; @@ -774,7 +774,7 @@ static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget { public LazyDbRefTargetWithPeristenceConstructor() {} - @PersistenceConstructor + @PersistenceCreator LazyDbRefTargetWithPeristenceConstructor(String id, String value) { super(id, value); this.persistenceConstructorCalled = true; @@ -790,7 +790,7 @@ static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor e boolean persistenceConstructorCalled; - @PersistenceConstructor + @PersistenceCreator LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor(String id, String value) { super(id, value); this.persistenceConstructorCalled = true; 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 cf6d69c6c3..5bd7e06b97 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.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; @@ -63,7 +65,7 @@ import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.convert.ConverterBuilder; @@ -103,10 +105,7 @@ 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.ClassTypeInformation; 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; @@ -1061,7 +1060,7 @@ void convertsSetToBasicDBList() { address.city = "London"; address.street = "Foo"; - Object result = converter.convertToMongoType(Collections.singleton(address), ClassTypeInformation.OBJECT); + Object result = converter.convertToMongoType(Collections.singleton(address), TypeInformation.OBJECT); assertThat(result).isInstanceOf(List.class); Set<?> readResult = converter.read(Set.class, (org.bson.Document) result); @@ -1393,7 +1392,7 @@ void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() { address.street = "Foo"; Object result = converter.convertToMongoType(Collections.singletonList(address), - ClassTypeInformation.from(InterfaceType.class)); + TypeInformation.of(InterfaceType.class)); assertThat(result).isInstanceOf(List.class); @@ -1421,7 +1420,7 @@ void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() { address.city = "London"; address.street = "Foo"; - Object result = converter.convertToMongoType(new Address[] { address }, ClassTypeInformation.OBJECT); + Object result = converter.convertToMongoType(new Address[] { address }, TypeInformation.OBJECT); assertThat(result).isInstanceOf(List.class); @@ -1712,7 +1711,7 @@ void shouldIncludeTextScorePropertyWhenReading() { } @Test // DATAMONGO-1001, DATAMONGO-1509 - void shouldWriteCglibProxiedClassTypeInformationCorrectly() { + void shouldWriteCglibProxiedTypeInformationCorrectly() { ProxyFactory factory = new ProxyFactory(); factory.setTargetClass(GenericType.class); @@ -2318,7 +2317,7 @@ void readAndConvertDBRefNestedByMapCorrectly() { Mockito.doReturn(cluster).when(spyConverter).readRef(dbRef); Map<Object, Object> result = spyConverter.readMap(spyConverter.getConversionContext(ObjectPath.ROOT), data, - ClassTypeInformation.MAP); + TypeInformation.MAP); assertThat(((Map) result.get("cluster")).get("_id")).isEqualTo(100L); } @@ -3168,15 +3167,14 @@ void beanConverter() { registrar.registerConverter(WithValueConverters.class, "viaRegisteredConverter", new PropertyValueConverter<String, org.bson.Document, MongoConversionContext>() { - @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); } }); @@ -3522,7 +3520,7 @@ static class Person implements Contact { } - @PersistenceConstructor + @PersistenceCreator public Person(Set<Address> addresses) { this.addresses = addresses; } @@ -3802,7 +3800,7 @@ static class PrimitiveContainer { @Field("property") private int m_property; - @PersistenceConstructor + @PersistenceCreator public PrimitiveContainer(@Value("#root.property") int a_property) { m_property = a_property; } @@ -3817,7 +3815,7 @@ static class ObjectContainer { @Field("property") private PrimitiveContainer m_property; - @PersistenceConstructor + @PersistenceCreator public ObjectContainer(@Value("#root.property") PrimitiveContainer a_property) { m_property = a_property; } @@ -4215,9 +4213,9 @@ public SubTypeOfGenericType convert(org.bson.Document source) { @WritingConverter static class TypeImplementingMapToDocumentConverter implements Converter<TypeImplementingMap, org.bson.Document> { - @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); } } @@ -4225,9 +4223,8 @@ public org.bson.Document convert(TypeImplementingMap source) { @ReadingConverter static class DocumentToTypeImplementingMapConverter implements Converter<org.bson.Document, TypeImplementingMap> { - @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")); } } @@ -4413,30 +4410,28 @@ enum Converter2 implements MongoValueConverter<String, org.bson.Document> { 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<String, org.bson.Document> { - @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/ObjectPathUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java index b772772444..9e58693faa 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ObjectPathUnitTests.java @@ -23,7 +23,7 @@ import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; /** * Unit tests for {@link ObjectPath}. @@ -39,9 +39,9 @@ public class ObjectPathUnitTests { @BeforeEach public void setUp() { - one = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityOne.class)); - two = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityTwo.class)); - three = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(EntityThree.class)); + one = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityOne.class)); + two = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityTwo.class)); + three = new BasicMongoPersistentEntity<>(TypeInformation.of(EntityThree.class)); } @Test // DATAMONGO-1703 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 eb3b1aba1a..0fe791784d 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/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index d8e36c8f67..c646af5539 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -544,7 +544,7 @@ void doesNotConvertRawDocuments() { } @Test // DATAMONG0-471 - void testUpdateShouldRetainClassTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() { + void testUpdateShouldRetainTypeInformationWhenUsing$addToSetWith$eachForCustomTypes() { Update update = new Update().addToSet("models").each(new ModelImpl(2014), new ModelImpl(1), new ModelImpl(28)); Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java index b81b51abd5..96c685275f 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java @@ -33,7 +33,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataAccessException; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Metrics; import org.springframework.data.geo.Point; @@ -536,7 +536,7 @@ static class Venue2DSphere { private String name; private @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) double[] location; - @PersistenceConstructor + @PersistenceCreator public Venue2DSphere(String name, double[] location) { this.name = name; this.location = location; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java index aa26445f2d..d1fa2b1b9a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java @@ -53,7 +53,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.Unwrapped; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; /** * Tests for {@link MongoPersistentEntityIndexResolver}. @@ -1186,7 +1186,7 @@ public void shouldNotDetectCycleWhenTypeIsUsedMoreThanOnce() { @SuppressWarnings({ "rawtypes", "unchecked" }) public void shouldCatchCyclicReferenceExceptionOnRoot() { - MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Object.class)); + MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(TypeInformation.of(Object.class)); MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class); when(propertyMock.isEntity()).thenReturn(true); @@ -1195,7 +1195,7 @@ public void shouldCatchCyclicReferenceExceptionOnRoot() { new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar")); MongoPersistentEntity<SelfCyclingViaCollectionType> selfCyclingEntity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(SelfCyclingViaCollectionType.class)); + TypeInformation.of(SelfCyclingViaCollectionType.class)); new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class)) .resolveIndexForEntity(selfCyclingEntity); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java index 116505143e..1037ba4f19 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java @@ -39,7 +39,7 @@ import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.util.ReflectionUtils; /** @@ -56,7 +56,7 @@ public class BasicMongoPersistentPropertyUnitTests { @BeforeEach void setup() { - entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class)); + entity = new BasicMongoPersistentEntity<>(TypeInformation.of(Person.class)); } @Test @@ -90,7 +90,7 @@ void preventsNegativeOrder() { void usesPropertyAccessForThrowableCause() { BasicMongoPersistentEntity<Throwable> entity = new BasicMongoPersistentEntity<>( - ClassTypeInformation.from(Throwable.class)); + TypeInformation.of(Throwable.class)); MongoPersistentProperty property = getPropertyFor(entity, "cause"); assertThat(property.usePropertyAccess()).isTrue(); @@ -99,7 +99,7 @@ void usesPropertyAccessForThrowableCause() { @Test // DATAMONGO-607 void usesCustomFieldNamingStrategyByDefault() throws Exception { - ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class); + TypeInformation<Person> type = TypeInformation.of(Person.class); Field field = ReflectionUtils.findField(Person.class, "lastname"); MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity, @@ -116,7 +116,7 @@ void usesCustomFieldNamingStrategyByDefault() throws Exception { @Test // DATAMONGO-607 void rejectsInvalidValueReturnedByFieldNamingStrategy() { - ClassTypeInformation<Person> type = ClassTypeInformation.from(Person.class); + TypeInformation<Person> type = TypeInformation.of(Person.class); Field field = ReflectionUtils.findField(Person.class, "lastname"); MongoPersistentProperty property = new BasicMongoPersistentProperty(Property.of(type, field), entity, @@ -255,7 +255,7 @@ private MongoPersistentProperty getPropertyFor(Field field) { } private static <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) { - return getPropertyFor(new BasicMongoPersistentEntity<>(ClassTypeInformation.from(type)), fieldname); + return getPropertyFor(new BasicMongoPersistentEntity<>(TypeInformation.of(type)), fieldname); } private static MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> entity, String fieldname) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java index 06f0db6c35..eaed01fc3b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/Person.java @@ -18,7 +18,7 @@ import java.util.List; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.annotation.Transient; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.index.CompoundIndexes; @@ -44,7 +44,7 @@ public Person(Integer ssn) { this.ssn = ssn; } - @PersistenceConstructor + @PersistenceCreator public Person(Integer ssn, String firstName, String lastName, Integer age, T address) { this.ssn = ssn; this.firstName = firstName; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java index a68fe0d531..4a4f7fb126 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/PersonCustomIdName.java @@ -16,7 +16,7 @@ package org.springframework.data.mongodb.core.mapping; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; /** * @author Jon Brisbin <jbrisbin@vmware.com> @@ -30,7 +30,7 @@ public PersonCustomIdName(Integer ssn, String firstName) { this.firstName = firstName; } - @PersistenceConstructor + @PersistenceCreator public PersonCustomIdName(Integer ssn, String firstName, String lastName) { this.ssn = ssn; this.firstName = firstName; 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 6ea0f5aa9c..b12b83fe3a 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 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.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/monitor/MongoMonitorIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java deleted file mode 100644 index e70b398f7f..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/MongoMonitorIntegrationTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import static org.assertj.core.api.Assertions.*; - -import java.net.UnknownHostException; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.data.mongodb.test.util.Client; -import org.springframework.data.mongodb.test.util.MongoClientExtension; - -import com.mongodb.client.MongoClient; - -/** - * This test class assumes that you are already running the MongoDB server. - * - * @author Mark Pollack - * @author Thomas Darimont - * @author Mark Paluch - */ -@ExtendWith(MongoClientExtension.class) -public class MongoMonitorIntegrationTests { - - static @Client MongoClient mongoClient; - - @Test - public void serverInfo() { - ServerInfo serverInfo = new ServerInfo(mongoClient); - serverInfo.getVersion(); - } - - @Test // DATAMONGO-685 - public void getHostNameShouldReturnServerNameReportedByMongo() throws UnknownHostException { - - ServerInfo serverInfo = new ServerInfo(mongoClient); - - String hostName = null; - try { - hostName = serverInfo.getHostName(); - } catch (UnknownHostException e) { - throw e; - } - - assertThat(hostName).isNotNull(); - assertThat(hostName).isEqualTo("127.0.0.1:27017"); - } - - @Test - public void operationCounters() { - OperationCounters operationCounters = new OperationCounters(mongoClient); - operationCounters.getInsertCount(); - } -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java deleted file mode 100644 index 1fdbb1f188..0000000000 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/monitor/Resumeable.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2018-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.monitor; - -import java.util.function.Supplier; - -/** - * @author Christoph Strobl - * @since 2018/01 - */ -interface Resumeable<T> { - - void resumeAt(Supplier<T> token); -} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java index e815cc6e7c..fb8cedd9b1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/performance/PerformanceTests.java @@ -28,7 +28,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.Constants; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; @@ -454,7 +454,7 @@ public Address(String zipCode, String city) { this(zipCode, city, new HashSet<AddressType>(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values())))); } - @PersistenceConstructor + @PersistenceCreator public Address(String zipCode, String city, Set<AddressType> types) { this.zipCode = zipCode; this.city = city; @@ -512,7 +512,7 @@ public Order(List<LineItem> lineItems, Date createdAt) { this.status = Status.ORDERED; } - @PersistenceConstructor + @PersistenceCreator public Order(List<LineItem> lineItems, Date createdAt, Status status) { this.lineItems = lineItems; this.createdAt = createdAt; 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 edda1aad01..a7fba9a046 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,11 +28,12 @@ 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; import org.springframework.core.Constants; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; @@ -48,7 +49,6 @@ 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 void setUp() throws Exception { 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; } @@ -513,7 +512,7 @@ public Address(String zipCode, String city) { this(zipCode, city, new HashSet<AddressType>(pickRandomNumerOfItemsFrom(Arrays.asList(AddressType.values())))); } - @PersistenceConstructor + @PersistenceCreator public Address(String zipCode, String city, Set<AddressType> types) { this.zipCode = zipCode; this.city = city; @@ -571,7 +570,7 @@ public Order(List<LineItem> lineItems, Date createdAt) { this.status = Status.ORDERED; } - @PersistenceConstructor + @PersistenceCreator public Order(List<LineItem> lineItems, Date createdAt, Status status) { this.lineItems = lineItems; this.createdAt = createdAt; 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 534f44c8fb..be5be2d9ba 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 c41abf4aa1..e0c2caee31 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 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.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 3dace8928b..4e589d5892 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 @@ 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 664b5279c8..eeca60bc3e 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.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.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/PersonAggregate.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java index 16b2157bc8..da22801ba6 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonAggregate.java @@ -22,7 +22,7 @@ import java.util.Set; import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.data.annotation.PersistenceCreator; /** * @author Christoph Strobl @@ -37,7 +37,7 @@ public PersonAggregate(String lastname, String name) { this(lastname, Collections.singletonList(name)); } - @PersistenceConstructor + @PersistenceCreator public PersonAggregate(String lastname, Collection<String> names) { this.lastname = lastname; 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 c66b554078..93a293ecff 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.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.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 0af684b9c1..b2b350dc4d 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 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.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 @@ private AfterTransactionAssertion assertAfterTransaction(Person person) { AfterTransactionAssertion assertion = new AfterTransactionAssertion<>(new Persistable<Object>() { - @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/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java index e89dec21bd..1a481b49ed 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java @@ -72,7 +72,6 @@ import org.springframework.data.mongodb.test.util.ReactiveMongoClientClosingTestConfiguration; import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; import org.springframework.data.repository.Repository; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.test.context.junit.jupiter.SpringExtension; /** @@ -111,7 +110,6 @@ ReactiveMongoRepositoryFactory factory(ReactiveMongoOperations template, BeanFac factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); factory.setBeanClassLoader(beanFactory.getClass().getClassLoader()); factory.setBeanFactory(beanFactory); - factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT); return factory; } 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 44235c54ef..751fc51ded 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.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,8 +49,6 @@ 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.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; -import org.springframework.lang.Nullable; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.TransactionDefinition; @@ -96,7 +95,6 @@ void setUp() { factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class); factory.setBeanClassLoader(classLoader); factory.setBeanFactory(beanFactory); - factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT); repository = factory.getRepository(ReactivePersonRepository.class); immutableRepository = factory.getRepository(ReactiveImmutablePersonRepository.class); 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 606cca8647..c3bb9cb724 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 294e4ea501..4f1adc714e 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 @@ 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 String getFirstname() { 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/config/MongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java index f613beb6d5..a222deca39 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/MongoRepositoryConfigurationExtensionUnitTests.java @@ -27,7 +27,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.repository.Repository; @@ -43,13 +43,13 @@ */ public class MongoRepositoryConfigurationExtensionUnitTests { - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); + AnnotationMetadata metadata = AnnotationMetadata.introspect(Config.class); ResourceLoader loader = new PathMatchingResourcePatternResolver(); Environment environment = new StandardEnvironment(); BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableMongoRepositories.class, loader, environment, registry); + EnableMongoRepositories.class, loader, environment, registry, null); @Test // DATAMONGO-1009 public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java index 45ecba992f..2b52204f74 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/config/ReactiveMongoRepositoryConfigurationExtensionUnitTests.java @@ -27,7 +27,7 @@ import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; @@ -43,13 +43,13 @@ */ public class ReactiveMongoRepositoryConfigurationExtensionUnitTests { - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); + AnnotationMetadata metadata = AnnotationMetadata.introspect(Config.class); ResourceLoader loader = new PathMatchingResourcePatternResolver(); Environment environment = new StandardEnvironment(); BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, - EnableReactiveMongoRepositories.class, loader, environment, registry); + EnableReactiveMongoRepositories.class, loader, environment, registry, null); @Test // DATAMONGO-1444 public void isStrictMatchIfDomainTypeIsAnnotatedWithDocument() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java index 74ff20b148..326ccf5f3a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java @@ -56,8 +56,7 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.util.ReflectionUtils; import com.mongodb.client.result.DeleteResult; @@ -81,15 +80,13 @@ class MongoQueryExecutionUnitTests { @Mock TerminatingFindNear<Object> terminatingGeoMock; @Mock DbRefResolver dbRefResolver; - private SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); private Point POINT = new Point(10, 20); private Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); private RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class); private MongoMappingContext context = new MongoMappingContext(); private ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); private Method method = ReflectionUtils.findMethod(PersonRepository.class, "findByLocationNear", Point.class, - Distance.class, - Pageable.class); + Distance.class, Pageable.class); private MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, factory, context); private MappingMongoConverter converter; @@ -152,8 +149,8 @@ void pagingGeoExecutionShouldUseCountFromResultWithOffsetAndResultsWithinPageSiz ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(0, 10) })); - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, - QueryMethodEvaluationContextProvider.DEFAULT); + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, + ValueExpressionDelegate.create()); PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); execution.execute(new Query()); @@ -173,8 +170,8 @@ void pagingGeoExecutionRetrievesObjectsForPageableOutOfRange() { ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(2, 10) })); - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, - QueryMethodEvaluationContextProvider.DEFAULT); + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, + ValueExpressionDelegate.create()); PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); execution.execute(new Query()); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java index e0b9b77099..07c10592d9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java @@ -45,8 +45,7 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.data.repository.query.ValueExpressionDelegate; /** * Unit tests for {@link PartTreeMongoQuery}. @@ -206,8 +205,7 @@ private PartTreeMongoQuery createQueryForMethod(String methodName, Class<?>... p MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory, mappingContext); - return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, new SpelExpressionParser(), - QueryMethodEvaluationContextProvider.DEFAULT); + return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, ValueExpressionDelegate.create()); } catch (Exception e) { throw new IllegalArgumentException(e.getMessage(), e); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java index 21d5dc71fb..d7a3430048 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveMongoQueryExecutionUnitTests.java @@ -46,7 +46,7 @@ import org.springframework.data.mongodb.repository.Person; import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.DeleteExecution; import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.GeoNearExecution; -import org.springframework.data.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.util.ClassUtils; import com.mongodb.client.result.DeleteResult; @@ -74,7 +74,7 @@ public void geoNearExecutionShouldApplyQuerySettings() throws Exception { .thenReturn(Range.from(Bound.inclusive(new Distance(10))).to(Bound.inclusive(new Distance(15)))); when(parameterAccessor.getPageable()).thenReturn(PageRequest.of(1, 10)); - new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query, + new GeoNearExecution(operations, parameterAccessor, TypeInformation.fromReturnTypeOf(geoNear)).execute(query, Person.class, "person"); ArgumentCaptor<NearQuery> queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class); @@ -96,7 +96,7 @@ public void geoNearExecutionShouldApplyMinimalSettings() throws Exception { when(parameterAccessor.getGeoNearLocation()).thenReturn(new Point(1, 2)); when(parameterAccessor.getDistanceRange()).thenReturn(Range.unbounded()); - new GeoNearExecution(operations, parameterAccessor, ClassTypeInformation.fromReturnTypeOf(geoNear)).execute(query, + new GeoNearExecution(operations, parameterAccessor, TypeInformation.fromReturnTypeOf(geoNear)).execute(query, Person.class, "person"); ArgumentCaptor<NearQuery> queryArgumentCaptor = ArgumentCaptor.forClass(NearQuery.class); 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 c6047ce30d..b55ee77732 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.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; @@ -34,6 +35,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.reactivestreams.Publisher; + import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -54,10 +56,8 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import com.mongodb.ReadPreference; @@ -71,8 +71,6 @@ @ExtendWith(MockitoExtension.class) public class ReactiveStringBasedAggregationUnitTests { - SpelExpressionParser PARSER = new SpelExpressionParser(); - @Mock ReactiveMongoOperations operations; @Mock DbRefResolver dbRefResolver; MongoConverter converter; @@ -226,8 +224,7 @@ private ReactiveStringBasedAggregation createAggregationForMethod(String name, C ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext()); - return new ReactiveStringBasedAggregation(queryMethod, operations, PARSER, - ReactiveQueryMethodEvaluationContextProvider.DEFAULT); + return new ReactiveStringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create()); } private List<Document> pipelineOf(AggregationInvocation invocation) { @@ -242,27 +239,24 @@ private Class<?> inputTypeOf(AggregationInvocation invocation) { 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) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null) + private @Nullable Object hintOf(AggregationInvocation invocation) { + return invocation.aggregation.getOptions() != null + ? invocation.aggregation.getOptions().getHintObject().orElse(null) : null; } private Boolean skipResultsOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() - : false; + return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() : false; } @Nullable private ReadPreference readPreferenceOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() - : null; + return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() : null; } private Class<?> targetTypeOf(AggregationInvocation invocation) { @@ -284,7 +278,7 @@ private interface SampleRepository extends ReactiveCrudRepository<Person, Long> @Aggregation(GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER) Mono<PersonAggregate> spelParameterReplacementAggregation(String arg0); - @Aggregation(pipeline = {RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER}) + @Aggregation(pipeline = { RAW_GROUP_BY_LASTNAME_STRING, GROUP_BY_LASTNAME_STRING_WITH_SPEL_PARAMETER_PLACEHOLDER }) Mono<PersonAggregate> multiOperationPipeline(String arg0); @Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT") diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java index 72f9626a57..7358bf4ce6 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQueryUnitTests.java @@ -56,8 +56,6 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor; -import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.data.spel.spi.EvaluationContextExtension; import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension; @@ -248,8 +246,8 @@ public void shouldSupportNonQuotedBinaryDataReplacement() throws Exception { ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class); org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block(); - org.springframework.data.mongodb.core.query.Query reference = new BasicQuery( - "{'lastname' : { '$binary' : '" + Base64.getEncoder().encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}"); + org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : { '$binary' : '" + + Base64.getEncoder().encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}"); assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); } @@ -266,16 +264,14 @@ void shouldConsiderReactiveSpelExtension() throws Exception { assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson()); } - private ReactiveStringBasedMongoQuery createQueryForMethod( - String name, Class<?>... parameters) - throws Exception { + private ReactiveStringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception { Method method = SampleRepository.class.getMethod(name, parameters); ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext()); - QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor( - environment, Collections.singletonList(ReactiveSpelExtension.INSTANCE)); + QueryMethodValueEvaluationContextAccessor accessor = new QueryMethodValueEvaluationContextAccessor(environment, + Collections.singletonList(ReactiveSpelExtension.INSTANCE)); return new ReactiveStringBasedMongoQuery(queryMethod, operations, new ValueExpressionDelegate(accessor, PARSER)); } 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 85a8650b26..827168007e 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.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; @@ -35,6 +36,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -62,9 +64,7 @@ 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.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; +import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.util.ClassUtils; import com.mongodb.MongoClientSettings; @@ -81,8 +81,6 @@ @MockitoSettings(strictness = Strictness.LENIENT) public class StringBasedAggregationUnitTests { - private SpelExpressionParser PARSER = new SpelExpressionParser(); - @Mock MongoOperations operations; @Mock DbRefResolver dbRefResolver; @Mock AggregationResults aggregationResults; @@ -254,8 +252,7 @@ void aggregateRaisesErrorOnInvalidReturnType() { factory, converter.getMappingContext()); assertThatExceptionOfType(InvalidMongoDbApiUsageException.class) // - .isThrownBy(() -> new StringBasedAggregation(queryMethod, operations, PARSER, - QueryMethodEvaluationContextProvider.DEFAULT)) // + .isThrownBy(() -> new StringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create())) // .withMessageContaining("pageIsUnsupported") // .withMessageContaining("Page"); } @@ -311,7 +308,7 @@ private StringBasedAggregation createAggregationForMethod(String name, Class<?>. ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext()); - return new StringBasedAggregation(queryMethod, operations, PARSER, QueryMethodEvaluationContextProvider.DEFAULT); + return new StringBasedAggregation(queryMethod, operations, ValueExpressionDelegate.create()); } private List<Document> pipelineOf(AggregationInvocation invocation) { @@ -326,27 +323,24 @@ private Class<?> inputTypeOf(AggregationInvocation invocation) { 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) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null) + private @Nullable Object hintOf(AggregationInvocation invocation) { + return invocation.aggregation.getOptions() != null + ? invocation.aggregation.getOptions().getHintObject().orElse(null) : null; } private Boolean skipResultsOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() - : false; + return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().isSkipResults() : false; } @Nullable private ReadPreference readPreferenceOf(AggregationInvocation invocation) { - return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() - : null; + return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getReadPreference() : null; } private Class<?> targetTypeOf(AggregationInvocation invocation) { 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 1927378e80..3ed7ace0f9 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 @@ 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.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 @@ public Collation getCollation() { * @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 15a0538600..eda1e501a0 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.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 09149c02ef..8300690ccd 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.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.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 diff --git a/spring-data-mongodb/src/test/resources/server-jmx.xml b/spring-data-mongodb/src/test/resources/server-jmx.xml deleted file mode 100644 index 54f985f4cb..0000000000 --- a/spring-data-mongodb/src/test/resources/server-jmx.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:mongo="http://www.springframework.org/schema/data/mongo" - xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd - http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> - - <mongo:jmx/> - - <context:mbean-export/> - - <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean" - p:port="1099"/> - - <!-- Expose JMX over RMI --> - <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry" - p:objectName="connector:name=rmi" - p:serviceUrl="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/> - -</beans> \ No newline at end of file diff --git a/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc b/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc index d76266c36a..3b5b4e49fe 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/mapping/mapping.adoc @@ -465,7 +465,7 @@ This can be a single value (the _id_ by default), or a `Document` provided via a * `@Transient`: By default, all fields are mapped to the document. This annotation excludes the field where it is applied from being stored in the database. Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument. -* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. +* `@PersistenceCreator`: Marks a given constructor or a `static` factory method - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved Document. * `@Value`: This annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object. @@ -513,7 +513,7 @@ public class Person<T extends Address> { this.ssn = ssn; } - @PersistenceConstructor + @PersistenceCreator public Person(Integer ssn, String firstName, String lastName, Integer age, T address) { this.ssn = ssn; this.firstName = firstName; @@ -673,7 +673,7 @@ Increased levels of nesting increase the complexity of the aggregation expressio [[mapping-custom-object-construction]] === Customized Object Construction -The mapping subsystem allows the customization of the object construction by annotating a constructor with the `@PersistenceConstructor` annotation. +The mapping subsystem allows the customization of the object construction by annotating a constructor with the `@PersistenceCreator` annotation. The values to be used for the constructor parameters are resolved in the following way: * If a parameter is annotated with the `@Value` annotation, the given expression is evaluated and the result is used as the parameter value. @@ -706,7 +706,7 @@ OrderItem item = converter.read(OrderItem.class, input); NOTE: The SpEL expression in the `@Value` annotation of the `quantity` parameter falls back to the value `0` if the given property path cannot be resolved. -Additional examples for using the `@PersistenceConstructor` annotation can be found in the https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java[MappingMongoConverterUnitTests] test suite. +Additional examples for using the `@PersistenceCreator` annotation can be found in the https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java[MappingMongoConverterUnitTests] test suite. [[mapping-usage-events]] === Mapping Framework Events diff --git a/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc b/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc index a424748205..697af23a9e 100644 --- a/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc +++ b/src/main/antora/modules/ROOT/pages/mongodb/template-query-operations.adoc @@ -342,7 +342,7 @@ public class Venue { private String name; private double[] location; - @PersistenceConstructor + @PersistenceCreator Venue(String name, double[] location) { super(); this.name = name; diff --git a/src/main/resources/notice.txt b/src/main/resources/notice.txt index 61b472b23b..776445bac4 100644 --- a/src/main/resources/notice.txt +++ b/src/main/resources/notice.txt @@ -1,4 +1,4 @@ -Spring Data MongoDB 4.5 M2 (2025.0.0) +Spring Data MongoDB 5.0 M1 (2025.1.0) Copyright (c) [2010-2019] Pivotal Software, Inc. This product is licensed to you under the Apache License, Version 2.0 (the "License"). @@ -58,6 +58,5 @@ conditions of the subcomponent's license, as noted in the LICENSE file. -