99import ai .timefold .solver .core .impl .heuristic .selector .move .AbstractMoveSelector ;
1010import ai .timefold .solver .core .impl .heuristic .selector .move .MoveSelector ;
1111import ai .timefold .solver .core .impl .phase .scope .AbstractPhaseScope ;
12+ import ai .timefold .solver .core .impl .solver .termination .PhaseTermination ;
1213
1314public final class FilteringMoveSelector <Solution_ > extends AbstractMoveSelector <Solution_ > {
1415
@@ -24,6 +25,7 @@ public static <Solution_> FilteringMoveSelector<Solution_> of(MoveSelector<Solut
2425 private final MoveSelector <Solution_ > childMoveSelector ;
2526 private final SelectionFilter <Solution_ , Move <Solution_ >> filter ;
2627 private final boolean bailOutEnabled ;
28+ private AbstractPhaseScope <Solution_ > phaseScope ;
2729
2830 private ScoreDirector <Solution_ > scoreDirector = null ;
2931
@@ -42,13 +44,15 @@ private FilteringMoveSelector(MoveSelector<Solution_> childMoveSelector,
4244 @ Override
4345 public void phaseStarted (AbstractPhaseScope <Solution_ > phaseScope ) {
4446 super .phaseStarted (phaseScope );
45- scoreDirector = phaseScope .getScoreDirector ();
47+ this .scoreDirector = phaseScope .getScoreDirector ();
48+ this .phaseScope = phaseScope ;
4649 }
4750
4851 @ Override
4952 public void phaseEnded (AbstractPhaseScope <Solution_ > phaseScope ) {
5053 super .phaseEnded (phaseScope );
51- scoreDirector = null ;
54+ this .scoreDirector = null ;
55+ this .phaseScope = null ;
5256 }
5357
5458 @ Override
@@ -68,17 +72,22 @@ public long getSize() {
6872
6973 @ Override
7074 public Iterator <Move <Solution_ >> iterator () {
71- return new JustInTimeFilteringMoveIterator (childMoveSelector .iterator (), determineBailOutSize ());
75+ return new JustInTimeFilteringMoveIterator (childMoveSelector .iterator (), determineBailOutSize (), phaseScope );
7276 }
7377
7478 private class JustInTimeFilteringMoveIterator extends UpcomingSelectionIterator <Move <Solution_ >> {
7579
7680 private final Iterator <Move <Solution_ >> childMoveIterator ;
7781 private final long bailOutSize ;
82+ private final AbstractPhaseScope <Solution_ > phaseScope ;
83+ private final PhaseTermination <Solution_ > termination ;
7884
79- public JustInTimeFilteringMoveIterator (Iterator <Move <Solution_ >> childMoveIterator , long bailOutSize ) {
85+ public JustInTimeFilteringMoveIterator (Iterator <Move <Solution_ >> childMoveIterator , long bailOutSize ,
86+ AbstractPhaseScope <Solution_ > phaseScope ) {
8087 this .childMoveIterator = childMoveIterator ;
8188 this .bailOutSize = bailOutSize ;
89+ this .phaseScope = phaseScope ;
90+ this .termination = phaseScope .getTermination ();
8291 }
8392
8493 @ Override
@@ -95,6 +104,11 @@ protected Move<Solution_> createUpcomingSelection() {
95104 logger .trace ("Bailing out of neverEnding selector ({}) after ({}) attempts to avoid infinite loop." ,
96105 FilteringMoveSelector .this , bailOutSize );
97106 return noUpcomingSelection ();
107+ } else if (termination != null && termination .isPhaseTerminated (phaseScope )) {
108+ logger .trace (
109+ "Bailing out of neverEnding selector ({}) because the termination setting has been triggered." ,
110+ FilteringMoveSelector .this );
111+ return noUpcomingSelection ();
98112 }
99113 attemptsBeforeBailOut --;
100114 }
0 commit comments