Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
*
* <ul>
* <li>
* "variableName", for referring any variable on the same planning entity.
* "variableName", for referring any variable on the same planning entity,
* except a {@link PlanningListVariable}, which cannot be used as a source.
* </li>
* <li>
* A list of names seperated by ".", such as "variableOrFact.fact.entity.variable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

/**
* Specifies that a bean property (or a field) is a custom shadow variable of 1 or more source variables.
* The source variable may be a genuine {@link PlanningVariable}, {@link PlanningListVariable},
* or another shadow variable.
* The source variable may be a genuine {@link PlanningVariable} or another shadow variable.
* A genuine {@link PlanningListVariable} cannot be a source.
* <p>
* It is specified on a getter of a java bean property (or a field) of a {@link PlanningEntity} class.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import ai.timefold.solver.core.api.domain.variable.InverseRelationShadowVariable;
import ai.timefold.solver.core.api.domain.variable.NextElementShadowVariable;
import ai.timefold.solver.core.api.domain.variable.PlanningListVariable;
import ai.timefold.solver.core.api.domain.variable.PlanningVariable;
import ai.timefold.solver.core.api.domain.variable.PreviousElementShadowVariable;
import ai.timefold.solver.core.api.domain.variable.ShadowSources;
Expand Down Expand Up @@ -126,6 +127,16 @@ public static <Entity_, Value_> RootVariableSource<Entity_, Value_> from(
}

var isVariable = isVariable(solutionMetaModel, memberAccessor.getDeclaringClass(), pathPart.name());
if (isVariable
&& getAnnotation(memberAccessor.getDeclaringClass(), pathPart.name(),
PlanningListVariable.class) != null) {
throw new IllegalArgumentException(
"""
The source path (%s) starting from root class (%s) accesses a planning list variable (%s), which is not allowed.
Maybe remove the source path (%s) from the @%s?"""
.formatted(variablePath, rootEntityClass.getSimpleName(), pathPart.name(),
variablePath, ShadowSources.class.getSimpleName()));
}
chainToVariable.add(memberAccessor);
for (var chain : chainStartingFromSourceVariableList) {
chain.add(memberAccessor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,26 @@ void invalidPathUsingGroupAfterGroup() {
" after another collection (group), which is not allowed.");
}

@Test
void invalidPathUsingBareListVariable() {
assertThatCode(() -> RootVariableSource.from(
planningSolutionMetaModel,
TestdataInvalidDeclarativeEntity.class,
"shadow",
"values",
DEFAULT_MEMBER_ACCESSOR_FACTORY,
DEFAULT_DESCRIPTOR_POLICY))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContainingAll(
"""
The source path (values) starting from root class (TestdataInvalidDeclarativeEntity) \
accesses a planning list variable (values), which is not allowed.""",
"""
The shadow variable would not be updated when the list variable \
or the shadow variables of its elements change.""",
"Maybe remove the source path (values) from the @ShadowSources?");
}

@Test
void invalidPathUsingGroupAfterVariable() {
assertThatCode(() -> RootVariableSource.from(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

@PlanningEntity
public class TestdataInvalidDeclarativeEntity extends TestdataObject {
TestdataInvalidDeclarativeEntity fact;

@PlanningListVariable
List<TestdataInvalidDeclarativeValue> values;

Expand All @@ -23,9 +25,17 @@ public TestdataInvalidDeclarativeEntity(String code) {
super(code);
}

@ShadowSources("values")
@ShadowSources("fact.shadow")
public Integer shadowSupplier() {
return values.size();
return fact == null ? 0 : fact.getShadow();
}

public TestdataInvalidDeclarativeEntity getFact() {
return fact;
}

public void setFact(TestdataInvalidDeclarativeEntity fact) {
this.fact = fact;
}

public List<TestdataInvalidDeclarativeValue> getValues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1800,7 +1800,7 @@ These paths must follow 1 of 3 syntactic forms:

|Simple Variable Name
|"variableName"
|Refers to a variable (genuine or shadow) on the same planning entity.
|Refers to a variable (genuine or shadow) on the same planning entity. A planning list variable cannot be used as a source.

|Chained Property Path
|"a.b.c"
Expand Down
Loading