Skip to content

Commit 39aa1f4

Browse files
committed
refactor: remove Solution_ generic from SolverConfigOverride
1 parent 858855a commit 39aa1f4

16 files changed

Lines changed: 146 additions & 73 deletions

File tree

core/src/main/java/ai/timefold/solver/core/api/solver/SolverConfigOverride.java

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import java.time.Duration;
44
import java.util.Objects;
55

6-
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
76
import ai.timefold.solver.core.config.solver.termination.TerminationConfig;
87

9-
import org.jspecify.annotations.NonNull;
8+
import org.jspecify.annotations.NullMarked;
9+
import org.jspecify.annotations.Nullable;
1010

1111
/**
1212
* Includes settings to override default {@link ai.timefold.solver.core.api.solver.Solver} configuration.
@@ -22,15 +22,14 @@
2222
* it must be called before any specific termination methods like {@link #withTerminationSpentLimit(Duration)}
2323
* or {@link #withTerminationUnimprovedSpentLimit(Duration)}. This prevents accidental override of previously
2424
* set specific configurations.
25-
*
26-
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
2725
*/
28-
public final class SolverConfigOverride<Solution_> {
26+
@NullMarked
27+
public final class SolverConfigOverride {
2928

30-
private TerminationConfig terminationConfig = null;
29+
private @Nullable TerminationConfig terminationConfig = null;
3130
private boolean hasSpecificTerminationSettings = false;
3231

33-
public TerminationConfig getTerminationConfig() {
32+
public @Nullable TerminationConfig getTerminationConfig() {
3433
return terminationConfig;
3534
}
3635

@@ -40,33 +39,30 @@ public TerminationConfig getTerminationConfig() {
4039
* <p>
4140
* After calling this method, additional specific termination methods can be chained to further
4241
* customize the configuration:
43-
*
44-
* <pre>{@code
45-
* new SolverConfigOverride<MySolution>()
42+
* {@snippet :
43+
* new SolverConfigOverride()
4644
* .withTerminationConfig(new TerminationConfig())
4745
* .withTerminationSpentLimit(Duration.ofMinutes(5))
4846
* .withTerminationUnimprovedSpentLimit(Duration.ofMinutes(2));
49-
* }</pre>
47+
* }
5048
*
5149
* <p>
5250
* <strong>Important:</strong> This method must be called before any specific termination methods
5351
* like {@link #withTerminationSpentLimit(Duration)} or {@link #withTerminationUnimprovedSpentLimit(Duration)}.
5452
* <p>
5553
* Calling this method after specific termination settings have been applied will throw an exception
5654
* to prevent accidental override of those settings.
57-
*
58-
*
59-
* <pre>{@code
60-
* new SolverConfigOverride<MySolution>()
55+
* {@snippet :
56+
* new SolverConfigOverride()
6157
* .withTerminationSpentLimit(Duration.ofMinutes(5))
6258
* .withTerminationConfig(new TerminationConfig()); // Will throw exception
63-
* }</pre>
59+
* }
6460
*
6561
* @param terminationConfig allows overriding the default termination config of {@link Solver}
6662
* @return this
6763
* @throws IllegalStateException if specific termination settings have already been applied
6864
*/
69-
public @NonNull SolverConfigOverride<Solution_> withTerminationConfig(@NonNull TerminationConfig terminationConfig) {
65+
public SolverConfigOverride withTerminationConfig(TerminationConfig terminationConfig) {
7066
if (hasSpecificTerminationSettings) {
7167
throw new IllegalStateException("""
7268
Cannot set terminationConfig after specific termination settings
@@ -89,27 +85,26 @@ public TerminationConfig getTerminationConfig() {
8985
* update its spent limit setting.
9086
* <p>
9187
* Usage examples:
92-
*
93-
* <pre>{@code
88+
* {@snippet :
9489
* // Set only spent limit
95-
* new SolverConfigOverride<MySolution>()
90+
* new SolverConfigOverride()
9691
* .withTerminationSpentLimit(Duration.ofMinutes(10));
9792
*
9893
* // Combine with unimproved spent limit
99-
* new SolverConfigOverride<MySolution>()
94+
* new SolverConfigOverride()
10095
* .withTerminationSpentLimit(Duration.ofMinutes(10))
10196
* .withTerminationUnimprovedSpentLimit(Duration.ofMinutes(3));
10297
*
10398
* // Use with base config
104-
* new SolverConfigOverride<MySolution>()
99+
* new SolverConfigOverride()
105100
* .withTerminationConfig(new TerminationConfig())
106101
* .withTerminationSpentLimit(Duration.ofMinutes(10));
107-
* }</pre>
102+
* }
108103
*
109104
* @param spentLimit the maximum duration the solver is allowed to run
110105
* @return this
111106
*/
112-
public @NonNull SolverConfigOverride<Solution_> withTerminationSpentLimit(@NonNull Duration spentLimit) {
107+
public SolverConfigOverride withTerminationSpentLimit(Duration spentLimit) {
113108
if (this.terminationConfig == null) {
114109
this.terminationConfig = new TerminationConfig();
115110
}
@@ -134,28 +129,26 @@ public TerminationConfig getTerminationConfig() {
134129
* update its unimproved spent limit setting.
135130
* <p>
136131
* Usage examples:
137-
*
138-
* <pre>{@code
132+
* {@snippet :
139133
* // Set only unimproved spent limit
140-
* new SolverConfigOverride<MySolution>()
134+
* new SolverConfigOverride()
141135
* .withTerminationUnimprovedSpentLimit(Duration.ofMinutes(2));
142136
*
143137
* // Combine with total spent limit
144-
* new SolverConfigOverride<MySolution>()
138+
* new SolverConfigOverride()
145139
* .withTerminationSpentLimit(Duration.ofMinutes(10))
146140
* .withTerminationUnimprovedSpentLimit(Duration.ofMinutes(2));
147141
*
148142
* // Use with base config
149-
* new SolverConfigOverride<MySolution>()
143+
* new SolverConfigOverride()
150144
* .withTerminationConfig(new TerminationConfig())
151145
* .withTerminationUnimprovedSpentLimit(Duration.ofMinutes(2));
152-
* }</pre>
146+
* }
153147
*
154148
* @param unimprovedSpentLimit the maximum duration the solver is allowed to run without improvement
155149
* @return this
156150
*/
157-
public @NonNull SolverConfigOverride<Solution_>
158-
withTerminationUnimprovedSpentLimit(@NonNull Duration unimprovedSpentLimit) {
151+
public SolverConfigOverride withTerminationUnimprovedSpentLimit(Duration unimprovedSpentLimit) {
159152
if (this.terminationConfig == null) {
160153
this.terminationConfig = new TerminationConfig();
161154
}

core/src/main/java/ai/timefold/solver/core/api/solver/SolverFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,13 @@ static <Solution_> SolverFactory<Solution_> create(SolverConfig solverConfig) {
116116
* Creates a new {@link Solver} instance.
117117
*/
118118
default Solver<Solution_> buildSolver() {
119-
return this.buildSolver(new SolverConfigOverride<>());
119+
return this.buildSolver(new SolverConfigOverride());
120120
}
121121

122122
/**
123123
* As defined by {@link #buildSolver()}.
124124
*
125125
* @param configOverride includes settings that override the default configuration
126126
*/
127-
Solver<Solution_> buildSolver(SolverConfigOverride<Solution_> configOverride);
127+
Solver<Solution_> buildSolver(SolverConfigOverride configOverride);
128128
}

core/src/main/java/ai/timefold/solver/core/api/solver/SolverJobBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ SolverJobBuilder<Solution_> withFirstInitializedSolutionEventConsumer(
115115
* @param solverConfigOverride allows overriding the default behavior of {@link Solver}
116116
* @return this
117117
*/
118-
SolverJobBuilder<Solution_> withConfigOverride(SolverConfigOverride<Solution_> solverConfigOverride);
118+
SolverJobBuilder<Solution_> withConfigOverride(SolverConfigOverride solverConfigOverride);
119119

120120
/**
121121
* Submits a planning problem to solve and returns immediately. The planning problem is solved on a solver {@link Thread},

core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public <Score_ extends Score<Score_>> ScoreDirectorFactory<Solution_, Score_> ge
9696
}
9797

9898
@Override
99-
public Solver<Solution_> buildSolver(SolverConfigOverride<Solution_> configOverride) {
99+
public Solver<Solution_> buildSolver(SolverConfigOverride configOverride) {
100100
Objects.requireNonNull(configOverride, "Invalid configOverride (null) given to SolverFactory.");
101101
var isDaemon = Objects.requireNonNullElse(solverConfig.getDaemon(), false);
102102

@@ -178,7 +178,7 @@ public Solver<Solution_> buildSolver(SolverConfigOverride<Solution_> configOverr
178178
}
179179

180180
private SolverTermination<Solution_> buildTermination(BasicPlumbingTermination<Solution_> basicPlumbingTermination,
181-
HeuristicConfigPolicy<Solution_> configPolicy, SolverConfigOverride<Solution_> solverConfigOverride) {
181+
HeuristicConfigPolicy<Solution_> configPolicy, SolverConfigOverride solverConfigOverride) {
182182
var terminationConfig = Objects.requireNonNullElseGet(solverConfigOverride.getTerminationConfig(),
183183
() -> Objects.requireNonNullElseGet(solverConfig.getTerminationConfig(), TerminationConfig::new));
184184
return TerminationFactory.<Solution_> create(terminationConfig)

core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverJobBuilder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public final class DefaultSolverJobBuilder<Solution_> implements SolverJobBuilde
3131
private @Nullable Consumer<FirstInitializedSolutionEvent<Solution_>> initializedSolutionConsumer;
3232
private @Nullable Consumer<SolverJobStartedEvent<Solution_>> solverJobStartedConsumer;
3333
private @Nullable BiConsumer<? super Object, ? super Throwable> exceptionHandler;
34-
private @Nullable SolverConfigOverride<Solution_> solverConfigOverride;
34+
private @Nullable SolverConfigOverride solverConfigOverride;
3535

3636
public DefaultSolverJobBuilder(DefaultSolverManager<Solution_> solverManager) {
3737
this.solverManager = Objects.requireNonNull(solverManager, "The SolverManager (" + solverManager + ") cannot be null.");
@@ -93,8 +93,7 @@ public SolverJobBuilder<Solution_> withProblemId(Object problemId) {
9393
}
9494

9595
@Override
96-
public SolverJobBuilder<Solution_>
97-
withConfigOverride(SolverConfigOverride<Solution_> solverConfigOverride) {
96+
public SolverJobBuilder<Solution_> withConfigOverride(SolverConfigOverride solverConfigOverride) {
9897
this.solverConfigOverride =
9998
Objects.requireNonNull(solverConfigOverride, "Invalid solverConfigOverride (null) given to SolverJobBuilder.");
10099
return this;
@@ -110,7 +109,7 @@ public SolverJob<Solution_> run() {
110109
}
111110
if (solverConfigOverride == null) {
112111
// The config is required by SolverFactory and it must be initialized
113-
this.solverConfigOverride = new SolverConfigOverride<>();
112+
this.solverConfigOverride = new SolverConfigOverride();
114113
}
115114

116115
if (this.bestSolutionConsumer == null) {

core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ SolverJob<Solution_> solveAndListen(Object problemId, Function<? super Object, ?
8585
@Nullable Consumer<FirstInitializedSolutionEvent<Solution_>> initializedSolutionConsumer,
8686
@Nullable Consumer<SolverJobStartedEvent<Solution_>> solverJobStartedConsumer,
8787
@Nullable BiConsumer<? super Object, ? super Throwable> exceptionHandler,
88-
SolverConfigOverride<Solution_> solverConfigOverride) {
88+
SolverConfigOverride solverConfigOverride) {
8989
return solve(problemId, problemFinder, bestSolutionConsumer, finalBestSolutionConsumer, initializedSolutionConsumer,
9090
solverJobStartedConsumer, exceptionHandler, solverConfigOverride);
9191
}
@@ -96,7 +96,7 @@ SolverJob<Solution_> solve(Object problemId, Function<? super Object, ? extends
9696
@Nullable Consumer<FirstInitializedSolutionEvent<Solution_>> initializedSolutionConsumer,
9797
@Nullable Consumer<SolverJobStartedEvent<Solution_>> solverJobStartedConsumer,
9898
@Nullable BiConsumer<? super Object, ? super Throwable> exceptionHandler,
99-
SolverConfigOverride<Solution_> configOverride) {
99+
SolverConfigOverride configOverride) {
100100
var solver = solverFactory.buildSolver(configOverride);
101101
((DefaultSolver<Solution_>) solver).setMonitorTagMap(Map.of("problem.id", problemId.toString()));
102102
BiConsumer<? super Object, ? super Throwable> finalExceptionHandler =

core/src/test/java/ai/timefold/solver/core/api/solver/SolverFactoryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ void create() {
144144
void createAndOverrideSettings() {
145145
var solverConfig = PlannerTestUtils.buildSolverConfig(TestdataSolution.class, TestdataEntity.class);
146146
SolverFactory<TestdataSolution> solverFactory = SolverFactory.create(solverConfig);
147-
SolverConfigOverride<TestdataSolution> configOverride = mock(SolverConfigOverride.class);
147+
SolverConfigOverride configOverride = mock(SolverConfigOverride.class);
148148
var terminationConfig = new TerminationConfig();
149149
terminationConfig.withSpentLimit(Duration.ofSeconds(60));
150150
doReturn(terminationConfig).when(configOverride).getTerminationConfig();

core/src/test/java/ai/timefold/solver/core/api/solver/SolverManagerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ void solveWithOverride() {
591591
assertThat(solverJob.getSolverTermination().calculateSolverTimeGradient(solverScope)).isEqualTo(0.05);
592592

593593
// Spent limit overridden by 100L
594-
var configOverride = new SolverConfigOverride<TestdataSolution>()
594+
var configOverride = new SolverConfigOverride()
595595
.withTerminationConfig(new TerminationConfig().withSpentLimit(Duration.ofMillis(100L)));
596596
solverJob = (DefaultSolverJob<TestdataSolution>) solverManager.solveBuilder()
597597
.withProblemId(2L)
@@ -617,7 +617,7 @@ void solveWithTerminationSpentLimit() {
617617
doReturn(50L).when(solverScope).calculateTimeMillisSpentUpToNow();
618618

619619
// Override spent limit to 100 milliseconds
620-
var configOverride = new SolverConfigOverride<TestdataSolution>()
620+
var configOverride = new SolverConfigOverride()
621621
.withTerminationSpentLimit(Duration.ofMillis(100L));
622622
var solverJob = (DefaultSolverJob<TestdataSolution>) solverManager.solveBuilder()
623623
.withProblemId(1L)
@@ -642,7 +642,7 @@ void solveWithTerminationUnimprovedSpentLimit() {
642642
try (var solverManager = createDefaultSolverManager(solverConfig)) {
643643
var problem = PlannerTestUtils.generateTestdataSolution("s1");
644644
// Override unimproved spent limit to 500 milliseconds, keep the longer spent limit
645-
var configOverride = new SolverConfigOverride<TestdataSolution>()
645+
var configOverride = new SolverConfigOverride()
646646
.withTerminationUnimprovedSpentLimit(Duration.ofMillis(500L));
647647

648648
// create a job so we can see the passed termination

core/src/test/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactoryTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ void testInvalidRandomConfiguration() {
127127
SolverConfig.createFromXmlResource("ai/timefold/solver/core/config/solver/testdataSolverConfig.xml")
128128
.withRandomFactoryClass(RandomFactory.class)
129129
.withRandomSeed(1000L);
130-
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride<>()))
130+
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride()))
131131
.hasMessageContaining("The solverConfig with randomFactoryClass ")
132132
.hasMessageContaining("has a non-null randomType (null) or a non-null randomSeed (1000).");
133133
}
@@ -137,7 +137,7 @@ void testInvalidMoveThreadCountConfiguration() {
137137
SolverConfig solverConfig =
138138
SolverConfig.createFromXmlResource("ai/timefold/solver/core/config/solver/testdataSolverConfig.xml")
139139
.withMoveThreadCount("-1");
140-
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride<>()))
140+
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride()))
141141
.hasMessageContaining("The moveThreadCount")
142142
.hasMessageContaining("resulted in a resolvedMoveThreadCount")
143143
.hasMessageContaining("that is lower than 1.");
@@ -151,7 +151,7 @@ void testInvalidConstraintProfilingWithoutEnterprise() {
151151
.withScoreDirectorFactory(new ScoreDirectorFactoryConfig()
152152
.withConstraintProviderClass(TestdataConstraintProvider.class)
153153
.withConstraintStreamProfilingEnabled(true));
154-
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride<>()))
154+
assertThatCode(() -> new DefaultSolverFactory<>(solverConfig).buildSolver(new SolverConfigOverride()))
155155
.hasMessageContainingAll("Constraint profiling",
156156
"remove constraintStreamProfilingEnabled from the solver configuration");
157157
}

0 commit comments

Comments
 (0)