11package ai .timefold .solver .core .impl .domain .variable .declarative ;
22
3- import java .util .ArrayList ;
43import java .util .BitSet ;
54import java .util .List ;
6- import java .util .Map ;
75import java .util .Objects ;
86import java .util .PriorityQueue ;
97import java .util .Set ;
10- import java .util .function .BiConsumer ;
118import java .util .function .Consumer ;
129import java .util .function .Function ;
1310
@@ -29,7 +26,6 @@ final class AffectedEntitiesUpdater<Solution_>
2926 private final BitSet visited ;
3027 private final PriorityQueue <BaseTopologicalOrderGraph .NodeTopologicalOrder > changeQueue ;
3128
32- @ SuppressWarnings ("unchecked" )
3329 AffectedEntitiesUpdater (BaseTopologicalOrderGraph graph , List <EntityVariablePair <Solution_ >> instanceList ,
3430 Function <Object , List <EntityVariablePair <Solution_ >>> entityVariablePairFunction ,
3531 ChangedVariableNotifier <Solution_ > changedVariableNotifier ) {
@@ -38,11 +34,7 @@ final class AffectedEntitiesUpdater<Solution_>
3834 this .entityVariablePairFunction = entityVariablePairFunction ;
3935 this .changedVariableNotifier = changedVariableNotifier ;
4036 var instanceCount = instanceList .size ();
41- this .affectedEntities = new AffectedEntities <>(this ::updateLoopedStatusOfAffectedEntity , instanceList .stream ()
42- .map (e -> e .variableReference ().shadowVariableLoopedDescriptor ())
43- .filter (Objects ::nonNull )
44- .distinct ()
45- .toArray (ShadowVariableLoopedVariableDescriptor []::new ));
37+ this .affectedEntities = new AffectedEntities <>(this ::updateLoopedStatusOfAffectedEntity );
4638 this .loopedTracker = new LoopedTracker (instanceCount );
4739 this .visited = new BitSet (instanceCount );
4840 this .changeQueue = new PriorityQueue <>(instanceCount );
@@ -98,42 +90,48 @@ private void initializeChangeQueue(BitSet changed) {
9890 changed .clear ();
9991 }
10092
101- private void updateLoopedStatusOfAffectedEntity (Object affectedEntity ,
102- ShadowVariableLoopedVariableDescriptor <Solution_ > shadowVariableLoopedDescriptor ) {
93+ private void updateLoopedStatusOfAffectedEntity (Object affectedEntity ) {
94+ ShadowVariableLoopedVariableDescriptor <Solution_ > shadowVariableLoopedDescriptor = null ;
10395 var isEntityLooped = false ;
10496 for (var node : entityVariablePairFunction .apply (affectedEntity )) {
97+ // All variables come from the same entity,
98+ // therefore all have the same looped marker.
99+ shadowVariableLoopedDescriptor = node .variableReference ().shadowVariableLoopedDescriptor ();
105100 if (graph .isLooped (loopedTracker , node .graphNodeId ())) {
106101 isEntityLooped = true ;
107102 break ;
108103 }
109104 }
105+ if (shadowVariableLoopedDescriptor == null ) {
106+ // At this point, affectedEntity is guaranteed to have looped marker.
107+ // Otherwise AffectedEntities would not have sent it here.
108+ throw new IllegalStateException ("Impossible state: loop marker descriptor does not exist." );
109+ }
110110 var oldValue = shadowVariableLoopedDescriptor .getValue (affectedEntity );
111111 if (!Objects .equals (oldValue , isEntityLooped )) {
112112 changeShadowVariableAndNotify (shadowVariableLoopedDescriptor , affectedEntity , isEntityLooped );
113113 }
114114
115115 }
116116
117- private boolean updateShadowVariable (EntityVariablePair <Solution_ > shadowVariable , boolean isLooped ) {
118- var entity = shadowVariable .entity ();
119- var shadowVariableReference = shadowVariable .variableReference ();
120- var shadowVariableLoopedDescriptor = shadowVariableReference .shadowVariableLoopedDescriptor ();
117+ private boolean updateShadowVariable (EntityVariablePair <Solution_ > entityVariable , boolean isLooped ) {
118+ var entity = entityVariable .entity ();
119+ var shadowVariableReference = entityVariable .variableReference ();
121120 var oldValue = shadowVariableReference .memberAccessor ().executeGetter (entity );
122121
123122 if (isLooped ) {
124123 // null might be a valid value, and thus it could be the case
125124 // that is was not looped and null, then turned to looped and null,
126125 // which is still considered a change.
127- affectedEntities .add (entity , shadowVariableLoopedDescriptor );
126+ affectedEntities .add (entityVariable );
128127 if (oldValue != null ) {
129128 changeShadowVariableAndNotify (shadowVariableReference , entity , null );
130129 }
131130 return true ;
132131 } else {
133132 var newValue = shadowVariableReference .calculator ().apply (entity );
134-
135133 if (!Objects .equals (oldValue , newValue )) {
136- affectedEntities .add (entity , shadowVariableLoopedDescriptor );
134+ affectedEntities .add (entityVariable );
137135 changeShadowVariableAndNotify (shadowVariableReference , entity , newValue );
138136 return true ;
139137 }
@@ -156,36 +154,27 @@ private void changeShadowVariableAndNotify(VariableDescriptor<Solution_> variabl
156154
157155 private static final class AffectedEntities <Solution_ > {
158156
159- private final BiConsumer <Object , ShadowVariableLoopedVariableDescriptor < Solution_ > > consumer ;
160- private final Map < ShadowVariableLoopedVariableDescriptor < Solution_ >, Set <Object > > entitiesForLoopedVarUpdateSet ;
157+ private final Consumer <Object > consumer ;
158+ private final Set <Object > entitiesForLoopedVarUpdateSet ;
161159
162- @ SuppressWarnings ("unchecked" )
163- public AffectedEntities (BiConsumer <Object , ShadowVariableLoopedVariableDescriptor <Solution_ >> consumer ,
164- ShadowVariableLoopedVariableDescriptor <Solution_ >... shadowVariableLoopedDescriptors ) {
160+ public AffectedEntities (Consumer <Object > consumer ) {
165161 this .consumer = consumer ;
166-
167- var entryList = new ArrayList <Map .Entry <ShadowVariableLoopedVariableDescriptor <Solution_ >, Set <Object >>>();
168- for (var shadowVariableLoopedDescriptor : shadowVariableLoopedDescriptors ) {
169- entryList .add (Map .entry (shadowVariableLoopedDescriptor , new LinkedIdentityHashSet <>()));
170- }
171- this .entitiesForLoopedVarUpdateSet = Map .ofEntries (entryList .toArray (new Map .Entry [0 ]));
162+ this .entitiesForLoopedVarUpdateSet = new LinkedIdentityHashSet <>();
172163 }
173164
174- public void add (Object entity , ShadowVariableLoopedVariableDescriptor <Solution_ > shadowVariableLoopedDescriptor ) {
165+ public void add (EntityVariablePair <Solution_ > shadowVariable ) {
166+ var shadowVariableLoopedDescriptor = shadowVariable .variableReference ().shadowVariableLoopedDescriptor ();
175167 if (shadowVariableLoopedDescriptor == null ) {
176168 return ;
177169 }
178- entitiesForLoopedVarUpdateSet .get ( shadowVariableLoopedDescriptor ). add (entity );
170+ entitiesForLoopedVarUpdateSet .add (shadowVariable . entity () );
179171 }
180172
181173 public void processAndClear () {
182- for (var affectedEntitiesPerDescriptor : entitiesForLoopedVarUpdateSet .entrySet ()) {
183- var affectedEntitySet = affectedEntitiesPerDescriptor .getValue ();
184- for (var affectedEntity : affectedEntitySet ) {
185- consumer .accept (affectedEntity , affectedEntitiesPerDescriptor .getKey ());
186- }
187- affectedEntitySet .clear (); // Keep the set, to not have to recreate and resize later.
174+ for (var entity : entitiesForLoopedVarUpdateSet ) {
175+ consumer .accept (entity );
188176 }
177+ entitiesForLoopedVarUpdateSet .clear ();
189178 }
190179
191180 }
0 commit comments