Skip to content

Commit 291955c

Browse files
committed
Simplify processor logic
1 parent 1933d7b commit 291955c

4 files changed

Lines changed: 100 additions & 107 deletions

File tree

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/DefaultShadowVariableSessionFactory.java

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package ai.timefold.solver.core.impl.domain.variable.declarative;
22

33
import java.util.ArrayList;
4-
import java.util.Collections;
54
import java.util.HashMap;
65
import java.util.LinkedHashSet;
76
import java.util.List;
@@ -41,33 +40,34 @@ public static <Solution_> VariableReferenceGraph<Solution_> buildGraph(
4140
}
4241
var variableIdToUpdater = new HashMap<VariableMetaModel<?, ?, ?>, VariableUpdaterInfo<Solution_>>();
4342

43+
// Create graph node for each entity/declarative shadow variable pair.
4444
// Maps a variable id to it source aliases;
4545
// For instance, "previousVisit.startTime" is a source alias of "startTime"
4646
// One way to view this concept is "previousVisit.startTime" is a pointer
4747
// to "startTime" of some visit, and thus alias it.
48-
Map<VariableMetaModel<?, ?, ?>, Set<VariableSourceReference>> declarativeShadowVariableToAliasMap = new HashMap<>();
49-
50-
// Create graph node for each entity/declarative shadow variable pair
51-
createGraphNodes(variableReferenceGraphBuilder, entities, declarativeShadowVariableDescriptors, variableIdToUpdater,
52-
declarativeShadowVariableToAliasMap);
48+
var declarativeShadowVariableToAliasMap = createGraphNodes(variableReferenceGraphBuilder, entities,
49+
declarativeShadowVariableDescriptors, variableIdToUpdater);
5350

5451
// Create variable processors for each declarative shadow variable descriptor
5552
for (var declarativeShadowVariable : declarativeShadowVariableDescriptors) {
56-
final var fromVariableId = declarativeShadowVariable.getVariableMetaModel();
53+
var fromVariableId = declarativeShadowVariable.getVariableMetaModel();
5754
createSourceChangeProcessors(variableReferenceGraphBuilder, declarativeShadowVariable, fromVariableId);
58-
createAliasToVariableChangeProcessors(variableReferenceGraphBuilder, declarativeShadowVariableToAliasMap,
59-
fromVariableId);
55+
var aliasSet = declarativeShadowVariableToAliasMap.get(fromVariableId);
56+
if (aliasSet != null) {
57+
createAliasToVariableChangeProcessors(variableReferenceGraphBuilder, aliasSet, fromVariableId);
58+
}
6059
}
6160

6261
// Create the fixed edges in the graph
6362
createFixedVariableRelationEdges(variableReferenceGraphBuilder, entities, declarativeShadowVariableDescriptors);
6463
return variableReferenceGraphBuilder.build(graphCreator);
6564
}
6665

67-
private static <Solution_> void createGraphNodes(VariableReferenceGraphBuilder<Solution_> graph, Object[] entities,
66+
private static <Solution_> Map<VariableMetaModel<?, ?, ?>, Set<VariableSourceReference>> createGraphNodes(
67+
VariableReferenceGraphBuilder<Solution_> graph, Object[] entities,
6868
List<DeclarativeShadowVariableDescriptor<Solution_>> declarativeShadowVariableDescriptors,
69-
Map<VariableMetaModel<?, ?, ?>, VariableUpdaterInfo<Solution_>> variableIdToUpdater,
70-
Map<VariableMetaModel<?, ?, ?>, Set<VariableSourceReference>> declarativeShadowVariableToAliasMap) {
69+
Map<VariableMetaModel<?, ?, ?>, VariableUpdaterInfo<Solution_>> variableIdToUpdater) {
70+
var result = new HashMap<VariableMetaModel<?, ?, ?>, Set<VariableSourceReference>>();
7171
for (var entity : entities) {
7272
for (var declarativeShadowVariableDescriptor : declarativeShadowVariableDescriptors) {
7373
var entityClass = declarativeShadowVariableDescriptor.getEntityDescriptor().getEntityClass();
@@ -83,16 +83,16 @@ private static <Solution_> void createGraphNodes(VariableReferenceGraphBuilder<S
8383
for (var sourceRoot : declarativeShadowVariableDescriptor.getSources()) {
8484
for (var source : sourceRoot.variableSourceReferences()) {
8585
if (source.downstreamDeclarativeVariableMetamodel() != null) {
86-
declarativeShadowVariableToAliasMap
87-
.computeIfAbsent(source.downstreamDeclarativeVariableMetamodel(),
88-
ignored -> new LinkedHashSet<>())
86+
result.computeIfAbsent(source.downstreamDeclarativeVariableMetamodel(),
87+
ignored -> new LinkedHashSet<>())
8988
.add(source);
9089
}
9190
}
9291
}
9392
}
9493
}
9594
}
95+
return result;
9696
}
9797

9898
private static <Solution_> void createSourceChangeProcessors(
@@ -127,44 +127,50 @@ private static <Solution_> void createSourceChangeProcessors(
127127
}
128128

129129
private static <Solution_> void createAliasToVariableChangeProcessors(
130-
VariableReferenceGraphBuilder<Solution_> variableReferenceGraphBuilder,
131-
Map<VariableMetaModel<?, ?, ?>, Set<VariableSourceReference>> declarativeShadowVariableToAliasMap,
130+
VariableReferenceGraphBuilder<Solution_> variableReferenceGraphBuilder, Set<VariableSourceReference> aliasSet,
132131
VariableMetaModel<Solution_, ?, ?> fromVariableId) {
133-
for (var alias : declarativeShadowVariableToAliasMap.getOrDefault(fromVariableId, Collections.emptySet())) {
132+
for (var alias : aliasSet) {
134133
var toVariableId = alias.targetVariableMetamodel();
135134
var sourceVariableId = alias.variableMetaModel();
136135

137136
if (!alias.isDeclarative() && alias.affectGraphEdges()) {
138137
// Exploit the same fact as above
139138
variableReferenceGraphBuilder.addBeforeProcessor(sourceVariableId,
140-
(graph, toEntity) -> alias.targetEntityFunctionStartingFromVariableEntity()
141-
.accept(toEntity, fromEntity -> {
142-
// from/to can be null in extended models
143-
// ex: previous is used as a source, but only an extended class
144-
// has the to variable
145-
var from = graph.lookupOrNull(fromVariableId, fromEntity);
146-
if (from == null) {
147-
return;
148-
}
149-
var to = graph.lookupOrNull(toVariableId, toEntity);
150-
if (to == null) {
151-
return;
152-
}
153-
graph.removeEdge(from, to);
154-
}));
139+
(graph, toEntity) -> {
140+
// from/to can be null in extended models
141+
// ex: previous is used as a source, but only an extended class
142+
// has the to variable
143+
var to = graph.lookupOrNull(toVariableId, toEntity);
144+
if (to == null) {
145+
return;
146+
}
147+
var fromEntity = alias.targetEntityFunctionStartingFromVariableEntity()
148+
.apply(toEntity);
149+
if (fromEntity == null) {
150+
return;
151+
}
152+
var from = graph.lookupOrNull(fromVariableId, fromEntity);
153+
if (from == null) {
154+
return;
155+
}
156+
graph.removeEdge(from, to);
157+
});
155158
variableReferenceGraphBuilder.addAfterProcessor(sourceVariableId,
156-
(graph, toEntity) -> alias.targetEntityFunctionStartingFromVariableEntity()
157-
.accept(toEntity, fromEntity -> {
158-
var from = graph.lookupOrNull(fromVariableId, fromEntity);
159-
if (from == null) {
160-
return;
161-
}
162-
var to = graph.lookupOrNull(toVariableId, toEntity);
163-
if (to == null) {
164-
return;
165-
}
166-
graph.addEdge(from, to);
167-
}));
159+
(graph, toEntity) -> {
160+
var to = graph.lookupOrNull(toVariableId, toEntity);
161+
if (to == null) {
162+
return;
163+
}
164+
var fromEntity = alias.findTargetEntity(toEntity);
165+
if (fromEntity == null) {
166+
return;
167+
}
168+
var from = graph.lookupOrNull(fromVariableId, fromEntity);
169+
if (from == null) {
170+
return;
171+
}
172+
graph.addEdge(from, to);
173+
});
168174
}
169175
// Note: it is impossible to have a declarative variable affect graph edges,
170176
// since accessing a declarative variable from another declarative variable is prohibited.
@@ -178,21 +184,21 @@ private static <Solution_> void createFixedVariableRelationEdges(
178184
for (var entity : entities) {
179185
for (var declarativeShadowVariableDescriptor : declarativeShadowVariableDescriptors) {
180186
var entityClass = declarativeShadowVariableDescriptor.getEntityDescriptor().getEntityClass();
181-
if (entityClass.isInstance(entity)) {
182-
var toVariableId = declarativeShadowVariableDescriptor.getVariableMetaModel();
183-
for (var sourceRoot : declarativeShadowVariableDescriptor.getSources()) {
184-
for (var source : sourceRoot.variableSourceReferences()) {
185-
if (source.isTopLevel() && source.isDeclarative()) {
186-
var fromVariableId = source.variableMetaModel();
187-
188-
sourceRoot.valueEntityFunction()
189-
.accept(entity, fromEntity -> variableReferenceGraphBuilder.addFixedEdge(
190-
variableReferenceGraphBuilder
191-
.lookupOrError(fromVariableId, fromEntity),
192-
variableReferenceGraphBuilder
193-
.lookupOrError(toVariableId, entity)));
194-
break;
195-
}
187+
if (!entityClass.isInstance(entity)) {
188+
continue;
189+
}
190+
var toVariableId = declarativeShadowVariableDescriptor.getVariableMetaModel();
191+
var to = variableReferenceGraphBuilder.lookupOrError(toVariableId, entity);
192+
for (var sourceRoot : declarativeShadowVariableDescriptor.getSources()) {
193+
for (var source : sourceRoot.variableSourceReferences()) {
194+
if (source.isTopLevel() && source.isDeclarative()) {
195+
var fromVariableId = source.variableMetaModel();
196+
sourceRoot.valueEntityFunction()
197+
.accept(entity, fromEntity -> {
198+
var from = variableReferenceGraphBuilder.lookupOrError(fromVariableId, fromEntity);
199+
variableReferenceGraphBuilder.addFixedEdge(from, to);
200+
});
201+
break;
196202
}
197203
}
198204
}

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/RootVariableSource.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import ai.timefold.solver.core.preview.api.domain.variable.declarative.ShadowSources;
2020

2121
import org.jspecify.annotations.NonNull;
22+
import org.jspecify.annotations.Nullable;
2223

2324
public record RootVariableSource<Entity_, Value_>(
2425
Class<? extends Entity_> rootEntity,
@@ -32,18 +33,18 @@ private record VariablePath(Class<?> variableEntityClass,
3233
String variableName,
3334
List<MemberAccessor> memberAccessorsBeforeEntity,
3435
List<MemberAccessor> memberAccessorsAfterEntity) {
35-
public BiConsumer<Object, Consumer<Object>> getValueVisitorFromVariableEntity() {
36-
return (entity, consumer) -> {
37-
var currentEntity = entity;
38-
for (var member : memberAccessorsAfterEntity) {
39-
currentEntity = member.executeGetter(currentEntity);
40-
if (currentEntity == null) {
41-
return;
42-
}
36+
37+
public @Nullable Object findTargetEntity(Object entity) {
38+
var currentEntity = entity;
39+
for (var member : memberAccessorsAfterEntity) {
40+
currentEntity = member.executeGetter(currentEntity);
41+
if (currentEntity == null) {
42+
return null;
4343
}
44-
consumer.accept(currentEntity);
45-
};
44+
}
45+
return currentEntity;
4646
}
47+
4748
}
4849

4950
public static Iterator<PathPart> pathIterator(Class<?> rootEntity, String path) {
@@ -215,7 +216,7 @@ public static <Entity_, Value_> RootVariableSource<Entity_, Value_> from(
215216
isDeclarativeShadowVariable(variableMemberAccessor),
216217
solutionMetaModel.entity(rootEntityClass).variable(targetVariableName),
217218
downstreamDeclarativeVariable,
218-
sourceVariablePath.getValueVisitorFromVariableEntity());
219+
sourceVariablePath::findTargetEntity);
219220
}
220221

221222
private static void assertIsValidVariableReference(Class<?> rootEntityClass, String variablePath,

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/VariableSourceReference.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package ai.timefold.solver.core.impl.domain.variable.declarative;
22

33
import java.util.List;
4-
import java.util.function.BiConsumer;
5-
import java.util.function.Consumer;
4+
import java.util.function.Function;
65

76
import ai.timefold.solver.core.impl.domain.common.accessor.MemberAccessor;
87
import ai.timefold.solver.core.preview.api.domain.metamodel.VariableMetaModel;
@@ -18,8 +17,14 @@ public record VariableSourceReference(VariableMetaModel<?, ?, ?> variableMetaMod
1817
boolean isDeclarative,
1918
VariableMetaModel<?, ?, ?> targetVariableMetamodel,
2019
@Nullable VariableMetaModel<?, ?, ?> downstreamDeclarativeVariableMetamodel,
21-
BiConsumer<Object, Consumer<Object>> targetEntityFunctionStartingFromVariableEntity) {
20+
Function<Object, @Nullable Object> targetEntityFunctionStartingFromVariableEntity) {
21+
2222
public boolean affectGraphEdges() {
2323
return downstreamDeclarativeVariableMetamodel != null;
2424
}
25+
26+
public @Nullable Object findTargetEntity(Object entity) {
27+
return targetEntityFunctionStartingFromVariableEntity.apply(entity);
28+
}
29+
2530
}

0 commit comments

Comments
 (0)