4848import ai .timefold .solver .core .preview .api .move .Move ;
4949import ai .timefold .solver .core .preview .api .move .SolutionView ;
5050
51- import org .jspecify .annotations .NonNull ;
5251import org .jspecify .annotations .NullMarked ;
5352import org .jspecify .annotations .Nullable ;
5453import org .slf4j .Logger ;
6564 * <li>after* method: first statement should be a call to the super method</li>
6665 * </ul>
6766 */
67+ @ NullMarked
6868public abstract class AbstractScoreDirector <Solution_ , Score_ extends Score <Score_ >, Factory_ extends AbstractScoreDirectorFactory <Solution_ , Score_ , Factory_ >>
69- implements InnerScoreDirector <Solution_ , Score_ >, Cloneable {
69+ implements InnerScoreDirector <Solution_ , Score_ > {
7070
7171 private static final int CONSTRAINT_MATCH_DISPLAY_LIMIT = 8 ;
7272 protected final Logger logger = LoggerFactory .getLogger (getClass ());
@@ -79,7 +79,7 @@ public abstract class AbstractScoreDirector<Solution_, Score_ extends Score<Scor
7979 */
8080 private final NeighborhoodNotifier <Solution_ > neighborhoodsElementUpdateNotifier ;
8181 private final boolean lookUpEnabled ;
82- private final LookUpManager lookUpManager ;
82+ private final @ Nullable LookUpManager lookUpManager ;
8383 protected final ConstraintMatchPolicy constraintMatchPolicy ;
8484 private boolean expectShadowVariablesInCorrectState ;
8585 private final VariableDescriptorCache <Solution_ > variableDescriptorCache ;
@@ -93,14 +93,14 @@ public abstract class AbstractScoreDirector<Solution_, Score_ extends Score<Scor
9393 * and operations which do not perform moves do not require them.
9494 */
9595 private final ValueRangeManager <Solution_ > valueRangeManager ;
96- private final ListVariableStateSupply <Solution_ , Object , Object > listVariableStateSupply ; // Null when no list variable.
96+ private final @ Nullable ListVariableStateSupply <Solution_ , Object , Object > listVariableStateSupply ; // Null when no list variable.
9797 private final MoveDirector <Solution_ , Score_ > moveDirector = new MoveDirector <>(this );
9898
9999 private long workingEntityListRevision = 0L ;
100100 private int workingGenuineEntityCount = 0 ;
101101 private boolean allChangesWillBeUndoneBeforeStepEnds = false ;
102102 private long calculationCount = 0L ;
103- protected Solution_ workingSolution ;
103+ protected @ Nullable Solution_ workingSolution ;
104104 private int workingInitScore = 0 ;
105105
106106 private final boolean isStepAssertOrMore ;
@@ -178,7 +178,7 @@ public boolean expectShadowVariablesInCorrectState() {
178178 }
179179
180180 @ Override
181- public @ NonNull Solution_ getWorkingSolution () {
181+ public Solution_ getWorkingSolution () {
182182 return workingSolution ;
183183 }
184184
@@ -251,7 +251,8 @@ public NeighborhoodNotifier<Solution_> getNeighborhoodNotifier() {
251251 * @param workingSolution the working solution to set
252252 * @param entityAndFactVisitor maybe null; a function to apply to all problem facts and problem entities
253253 */
254- protected void setWorkingSolutionWithoutUpdatingShadows (Solution_ workingSolution , Consumer <Object > entityAndFactVisitor ) {
254+ protected void setWorkingSolutionWithoutUpdatingShadows (Solution_ workingSolution ,
255+ @ Nullable Consumer <Object > entityAndFactVisitor ) {
255256 this .workingSolution = requireNonNull (workingSolution );
256257 var solutionDescriptor = getSolutionDescriptor ();
257258
@@ -443,15 +444,6 @@ protected void setCalculatedScore(Score_ score) {
443444 calculationCount ++;
444445 }
445446
446- /**
447- * @deprecated Unused, but kept for backward compatibility.
448- */
449- @ Deprecated (forRemoval = true , since = "1.14.0" )
450- @ Override
451- public AbstractScoreDirector <Solution_ , Score_ , Factory_ > clone () {
452- throw new UnsupportedOperationException ("Cloning score directors is not supported." );
453- }
454-
455447 @ Override
456448 public InnerScoreDirector <Solution_ , Score_ > createChildThreadScoreDirector (ChildThreadType childThreadType ) {
457449 // Most score directors don't need derived status; CS will override this.
@@ -885,7 +877,7 @@ private void assertValueRangeForListVariable(Object entity, List<Object> valueLi
885877 }
886878
887879 private static <Solution_ > void assertValueRangeForBasicVariables (InnerScoreDirector <Solution_ , ?> scoreDirector ,
888- List <BasicVariableDescriptor <Solution_ >> basicVariableDescriptorList , Object entity ) {
880+ @ Nullable List <BasicVariableDescriptor <Solution_ >> basicVariableDescriptorList , Object entity ) {
889881 if (basicVariableDescriptorList == null || basicVariableDescriptorList .isEmpty ()) {
890882 return ;
891883 }
@@ -905,7 +897,7 @@ private static <Solution_> void assertValueRangeForBasicVariables(InnerScoreDire
905897 }
906898
907899 private static <Solution_ > void assertValueRangeForListVariable (InnerScoreDirector <Solution_ , ?> scoreDirector ,
908- ListVariableDescriptor <Solution_ > variableDescriptor , Object entity , List <Object > valueList ) {
900+ ListVariableDescriptor <Solution_ > variableDescriptor , Object entity , List <@ Nullable Object > valueList ) {
909901 if (valueList .isEmpty ()) {
910902 return ;
911903 }
@@ -923,36 +915,6 @@ private static <Solution_> void assertValueRangeForListVariable(InnerScoreDirect
923915 }
924916 }
925917
926- public SolutionTracker .SolutionCorruptionResult getSolutionCorruptionAfterUndo (Move <Solution_ > move ,
927- InnerScore <Score_ > undoInnerScore ) {
928- var trackingWorkingSolution = solutionTracker != null ;
929- if (trackingWorkingSolution ) {
930- solutionTracker .setAfterUndoSolution (workingSolution );
931- }
932- // Precondition: assert that there are probably no corrupted constraints
933- var undoMoveToString = "Undo(%s)" .formatted (move );
934- assertWorkingScoreFromScratch (undoInnerScore , undoMoveToString );
935- // Precondition: assert that shadow variables aren't stale after doing the undoMove
936- assertShadowVariablesAreNotStale (undoInnerScore , undoMoveToString );
937- if (trackingWorkingSolution ) {
938- // Recalculate all shadow variables from scratch.
939- // We cannot set all shadow variables to null, since some variable listeners
940- // may expect them to be non-null.
941- // Instead, we just simulate a change to all genuine variables.
942- variableListenerSupport .forceTriggerAllVariableListeners (workingSolution );
943- solutionTracker .setUndoFromScratchSolution (workingSolution );
944-
945- // Also calculate from scratch for the before solution, since it might
946- // have been corrupted but was only detected now
947- solutionTracker .restoreBeforeSolution ();
948- variableListenerSupport .forceTriggerAllVariableListeners (workingSolution );
949- solutionTracker .setBeforeFromScratchSolution (workingSolution );
950-
951- return solutionTracker .buildSolutionCorruptionResult ();
952- }
953- return SolutionTracker .SolutionCorruptionResult .untracked ();
954- }
955-
956918 /**
957919 * @param uncorruptedScoreDirector never null
958920 * @param predicted true if the score was predicted and might have been calculated on another thread
@@ -1028,11 +990,15 @@ Maybe there is a bug in the score constraints of those ConstraintMatch(s).
1028990 }
1029991
1030992 private static <Score_ extends Score <Score_ >> List <MatchAnalysis <Score_ >>
1031- emptyMatchAnalysisIfNull (ConstraintAnalysis <Score_ > constraintAnalysis ) {
993+ emptyMatchAnalysisIfNull (@ Nullable ConstraintAnalysis <Score_ > constraintAnalysis ) {
1032994 if (constraintAnalysis == null ) {
1033995 return Collections .emptyList ();
1034996 }
1035- return Objects .requireNonNullElse (constraintAnalysis .matches (), Collections .emptyList ());
997+ var matches = constraintAnalysis .matches ();
998+ if (matches == null ) {
999+ return Collections .emptyList ();
1000+ }
1001+ return matches ;
10361002 }
10371003
10381004 private void appendAnalysis (StringBuilder analysis , String workingLabel , String suffix ,
0 commit comments