From d3ab5c21b35f45085b6b9527fba3f0188fe740ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Tue, 14 Apr 2026 14:28:13 +0200 Subject: [PATCH] chore: make constraint metadata more flexible --- .../solution/ConstraintWeightOverrides.java | 17 +- .../score/analysis/ConstraintAnalysis.java | 8 +- .../api/score/analysis/ScoreAnalysis.java | 2 +- .../core/api/score/stream/Constraint.java | 25 +-- .../api/score/stream/ConstraintBuilder.java | 38 +--- .../api/score/stream/ConstraintMetaModel.java | 29 +-- .../api/score/stream/ConstraintMetadata.java | 27 +++ .../core/api/score/stream/ConstraintRef.java | 12 +- .../common/BavetAbstractConstraintStream.java | 9 +- .../core/impl/bavet/visual/NodeGraph.java | 2 +- .../DefaultConstraintWeightOverrides.java | 6 +- .../descriptor/ConstraintWeightSupplier.java | 12 +- .../localsearch/DefaultLocalSearchPhase.java | 4 +- .../score/constraint/ConstraintMatch.java | 2 +- .../score/director/InnerScoreDirector.java | 2 +- .../stream/DefaultConstraintMetaModel.java | 38 +--- .../score/stream/bavet/BavetConstraint.java | 18 +- .../bi/BavetAbstractBiConstraintStream.java | 7 +- .../BavetAbstractQuadConstraintStream.java | 7 +- .../tri/BavetAbstractTriConstraintStream.java | 7 +- .../uni/BavetAbstractUniConstraintStream.java | 7 +- .../stream/common/AbstractConstraint.java | 61 ++--- .../common/AbstractConstraintBuilder.java | 6 +- .../stream/common/ConstraintConstructor.java | 3 +- .../common/DefaultConstraintMetadata.java | 9 + .../AbstractSingleConstraintAssertion.java | 22 +- .../ConstraintWeightOverridesTest.java | 4 +- .../DefaultConstraintMetaModelTest.java | 16 +- .../stream/AbstractConstraintBuilderTest.java | 44 +--- ...etAdvancedGroupByConstraintStreamTest.java | 36 +-- .../stream/bavet/BavetRegressionTest.java | 20 +- ...ctAdvancedGroupByConstraintStreamTest.java | 38 ++-- .../common/AbstractConstraintStreamTest.java | 8 +- .../stream/common/AbstractConstraintTest.java | 45 ---- ...tractBiConstraintStreamPrecomputeTest.java | 10 +- .../bi/AbstractBiConstraintStreamTest.java | 184 +++++++-------- .../common/bi/BiConstraintBuilderTest.java | 9 +- ...actQuadConstraintStreamPrecomputeTest.java | 4 +- .../AbstractQuadConstraintStreamTest.java | 150 ++++++------- .../quad/QuadConstraintBuilderTest.java | 9 +- ...ractTriConstraintStreamPrecomputeTest.java | 4 +- .../tri/AbstractTriConstraintStreamTest.java | 166 +++++++------- .../common/tri/TriConstraintBuilderTest.java | 9 +- ...ractUniConstraintStreamPrecomputeTest.java | 6 +- .../uni/AbstractUniConstraintStreamTest.java | 212 +++++++++--------- .../common/uni/UniConstraintBuilderTest.java | 9 +- .../core/testconstraint/TestConstraint.java | 15 +- .../constraint-configuration.adoc | 3 + .../score-calculation.adoc | 65 ++++-- .../upgrade-from-v1.adoc | 37 ++- .../running-the-solver.adoc | 4 +- ...ConstraintWeightOverridesDeserializer.java | 4 +- .../ConstraintWeightOverridesSerializer.java | 6 +- .../ConstraintRefJacksonSerializer.java | 2 +- ...ConstraintWeightOverridesDeserializer.java | 4 +- .../ConstraintWeightOverridesSerializer.java | 6 +- .../ConstraintRefJacksonSerializer.java | 2 +- .../TimefoldDevUIPropertiesRPCService.java | 2 +- .../impl/statistic/StatisticRegistry.java | 10 +- ...aintMatchTotalBestScoreStatisticPoint.java | 2 +- ...MatchTotalBestScoreSubSingleStatistic.java | 2 +- ...aintMatchTotalStepScoreStatisticPoint.java | 2 +- ...MatchTotalStepScoreSubSingleStatistic.java | 2 +- ...hTotalBestScoreSubSingleStatisticTest.java | 2 +- ...hTotalStepScoreSubSingleStatisticTest.java | 2 +- .../solver/migration/ToLatestRecipe.java | 2 + .../v2/ConstraintMetadataMigrationRecipe.java | 67 ++++++ ...eneralMethodChangeNameMigrationRecipe.java | 2 +- ...ConstraintMetadataMigrationRecipeTest.java | 122 ++++++++++ ...alMethodChangeNameMigrationRecipeTest.java | 2 +- 70 files changed, 919 insertions(+), 811 deletions(-) create mode 100644 core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetadata.java create mode 100644 core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/DefaultConstraintMetadata.java delete mode 100644 core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintTest.java create mode 100644 tools/migration/src/main/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipe.java create mode 100644 tools/migration/src/test/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipeTest.java diff --git a/core/src/main/java/ai/timefold/solver/core/api/domain/solution/ConstraintWeightOverrides.java b/core/src/main/java/ai/timefold/solver/core/api/domain/solution/ConstraintWeightOverrides.java index 4573ceb007b..b1ebd1785e5 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/domain/solution/ConstraintWeightOverrides.java +++ b/core/src/main/java/ai/timefold/solver/core/api/domain/solution/ConstraintWeightOverrides.java @@ -6,6 +6,7 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; +import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.api.score.stream.uni.UniConstraintStream; import ai.timefold.solver.core.api.solver.change.ProblemChange; import ai.timefold.solver.core.impl.domain.solution.DefaultConstraintWeightOverrides; @@ -44,16 +45,24 @@ static > ConstraintWeightOverrides of(Map getKnownConstraintNames(); + Set getKnownConstraintIds(); } diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ConstraintAnalysis.java b/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ConstraintAnalysis.java index 2b56f5de00d..f33026270da 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ConstraintAnalysis.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ConstraintAnalysis.java @@ -52,11 +52,13 @@ public interface ConstraintAnalysis> { int matchCount(); /** - * Return name of the constraint that this analysis is for. + * Return id of the constraint that this analysis is for. * - * @return equal to {@code constraintRef.constraintName()} + * @return equal to {@code constraintRef.id()} */ - String constraintName(); + default String constraintId() { + return constraintRef().id(); + } /** * Returns a diagnostic text that explains part of the score quality through the {@link ConstraintAnalysis} API. diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ScoreAnalysis.java b/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ScoreAnalysis.java index 22ee39b2ea4..08af3488674 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ScoreAnalysis.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/analysis/ScoreAnalysis.java @@ -101,7 +101,7 @@ public interface ScoreAnalysis> { * @return null if no constraint matches of such constraint are present */ @Nullable - ConstraintAnalysis getConstraintAnalysis(String constraintName); + ConstraintAnalysis getConstraintAnalysis(String constraintId); /** * Compare this {@link ScoreAnalysis} to another {@link ScoreAnalysis} diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/stream/Constraint.java b/core/src/main/java/ai/timefold/solver/core/api/score/stream/Constraint.java index 87428051cdb..9c6e13c8ce8 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/stream/Constraint.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/stream/Constraint.java @@ -2,7 +2,7 @@ import ai.timefold.solver.core.api.score.Score; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** @@ -11,25 +11,20 @@ * It is defined in {@link ConstraintProvider#defineConstraints(ConstraintFactory)} * by calling {@link ConstraintFactory#forEach(Class)}. */ +@NullMarked public interface Constraint { - String DEFAULT_CONSTRAINT_GROUP = "default"; - ConstraintRef getConstraintRef(); /** - * Returns a human-friendly description of the constraint. - * The format of the description is left unspecified and will not be parsed in any way. + * Returns the metadata for this constraint, as provided to + * {@link ConstraintBuilder#asConstraint(ConstraintMetadata)}. + * The constraint's identity ({@link ConstraintMetadata#id()}) is fixed at build time; + * any later mutation of the returned object does not affect the constraint's identity. * - * @return may be left empty + * @return never null */ - default @NonNull String getDescription() { - return ""; - } - - default @NonNull String getConstraintGroup() { - return DEFAULT_CONSTRAINT_GROUP; - } + ConstraintMetadata getConstraintMetadata(); /** * Returns the weight of the constraint as defined in the {@link ConstraintProvider}, @@ -37,8 +32,6 @@ public interface Constraint { * * @return null if the constraint does not have a weight defined */ - default > @Nullable Score_ getConstraintWeight() { - return null; - } + > @Nullable Score_ getConstraintWeight(); } diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintBuilder.java b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintBuilder.java index 853fae9dd26..7610428f95f 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintBuilder.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintBuilder.java @@ -1,6 +1,7 @@ package ai.timefold.solver.core.api.score.stream; import ai.timefold.solver.core.api.score.analysis.ScoreAnalysis; +import ai.timefold.solver.core.impl.score.stream.common.DefaultConstraintMetadata; import org.jspecify.annotations.NullMarked; @@ -9,40 +10,23 @@ public interface ConstraintBuilder { /** * Builds a {@link Constraint} from the constraint stream. - * The constraint will be placed in the {@link Constraint#DEFAULT_CONSTRAINT_GROUP default constraint group}. + * Shorthand for {@link #asConstraint(ConstraintMetadata)}. * - * @param constraintName shows up in {@link ScoreAnalysis} + * @param id shows up in {@link ScoreAnalysis} */ - default Constraint asConstraint(String constraintName) { - return asConstraintDescribed(constraintName, ""); - } - - /** - * As defined by {@link #asConstraintDescribed(String, String, String)}, - * placing the constraint in the {@link Constraint#DEFAULT_CONSTRAINT_GROUP default constraint group}. - * - * @param constraintName shows up in {@link ScoreAnalysis} - * @param constraintDescription can contain any character, but it is recommended to keep it short and concise. - */ - default Constraint asConstraintDescribed(String constraintName, String constraintDescription) { - return asConstraintDescribed(constraintName, constraintDescription, Constraint.DEFAULT_CONSTRAINT_GROUP); + default Constraint asConstraint(String id) { + return asConstraint(new DefaultConstraintMetadata(id)); } /** * Builds a {@link Constraint} from the constraint stream. - * Both the constraint name and the constraint group are only allowed - * to contain alphanumeric characters, " ", "-" or "_". - * The constraint description can contain any character, but it is recommended to keep it short and concise. - *

- * Unlike the constraint name and group, - * the constraint description is unlikely to be used externally as an identifier, - * and therefore doesn't need to be URL-friendly, or protected against injection attacks. + * {@link ConstraintMetadata#id()} is called exactly once at this point; + * the returned value is validated and snapshotted as the constraint's permanent identity. + * Subsequent changes to the description's {@link ConstraintMetadata#id()} return value are ignored. * - * @param constraintName shows up in {@link ScoreAnalysis} - * @param constraintDescription can contain any character, but it is recommended to keep it short and concise. - * @param constraintGroup not used by the solver directly, but may be used by external tools to group constraints together, - * such as by their source or by their purpose + * @param metadata identifies and describes the constraint; + * {@link ConstraintMetadata#id()} shows up in {@link ScoreAnalysis} */ - Constraint asConstraintDescribed(String constraintName, String constraintDescription, String constraintGroup); + Constraint asConstraint(ConstraintMetadata metadata); } diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetaModel.java b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetaModel.java index ae7816bdc10..b40349fa191 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetaModel.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetaModel.java @@ -1,15 +1,15 @@ package ai.timefold.solver.core.api.score.stream; import java.util.Collection; -import java.util.Set; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; /** * Provides information about the known constraints. * Works in combination with {@link ConstraintProvider}. */ +@NullMarked public interface ConstraintMetaModel { /** @@ -18,30 +18,23 @@ public interface ConstraintMetaModel { * @return null if such constraint does not exist */ @Nullable - Constraint getConstraint(@NonNull ConstraintRef constraintRef); + Constraint getConstraint(ConstraintRef constraintRef); /** - * Returns all constraints defined in the {@link ConstraintProvider}. + * Returns the constraint with the given id. + * Convenience shorthand for {@link #getConstraint(ConstraintRef)}. * - * @return iteration order is undefined - */ - @NonNull - Collection getConstraints(); - - /** - * Returns all constraints from {@link #getConstraints()} that belong to the given group. - * - * @return iteration order is undefined + * @return null if such constraint does not exist */ - @NonNull - Collection getConstraintsPerGroup(@NonNull String constraintGroup); + default @Nullable Constraint getConstraint(String id) { + return getConstraint(ConstraintRef.of(id)); + } /** - * Returns constraint groups with at least one constraint in it. + * Returns all constraints defined in the {@link ConstraintProvider}. * * @return iteration order is undefined */ - @NonNull - Set getConstraintGroups(); + Collection getConstraints(); } diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetadata.java b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetadata.java new file mode 100644 index 00000000000..6d6868c62ca --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintMetadata.java @@ -0,0 +1,27 @@ +package ai.timefold.solver.core.api.score.stream; + +import org.jspecify.annotations.NullMarked; + +/** + * Identifies a {@link Constraint} and optionally carries metadata about it. + * Users implement this interface, adding any fields they require. + *

+ * Immutability contract: {@link #id()} must return the same value + * for the lifetime of the object. + * The first value returned is snapshotted + * when the constraint is built via {@link ConstraintBuilder#asConstraint(ConstraintMetadata)}; + * any later change to the return value of {@link #id()} will be silently ignored + * and will NOT affect the constraint's identity. + */ +@NullMarked +public interface ConstraintMetadata { + + /** + * Returns the unique identifier of the constraint. + * Must be non-null, non-empty, and stable (see class Javadoc). + * + * @return the constraint's unique identifier + */ + String id(); + +} diff --git a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintRef.java b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintRef.java index fe13b131cd6..6dac6edb8b2 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintRef.java +++ b/core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintRef.java @@ -9,24 +9,24 @@ *

* If you need an instance created, use {@link ConstraintRef#of(String)} and not the record's constructors. * - * @param constraintName The constraint name. It must be unique. + * @param id The constraint id. It must be unique. */ @NullMarked -public record ConstraintRef(String constraintName) +public record ConstraintRef(String id) implements Comparable { - public static ConstraintRef of(String constraintName) { - return new ConstraintRef(constraintName); + public static ConstraintRef of(String id) { + return new ConstraintRef(id); } public ConstraintRef { - constraintName = AbstractConstraintBuilder.sanitize("constraintName", constraintName); + id = AbstractConstraintBuilder.sanitize("id", id); } @Override public int compareTo(ConstraintRef other) { - return constraintName.compareTo(other.constraintName); + return id.compareTo(other.id); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/BavetAbstractConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/BavetAbstractConstraintStream.java index e8a81af31d6..4432c6ada9a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/BavetAbstractConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/BavetAbstractConstraintStream.java @@ -14,7 +14,7 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.api.score.stream.ConstraintFactory; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; import ai.timefold.solver.core.api.score.stream.ConstraintStream; import ai.timefold.solver.core.api.score.stream.bi.BiConstraintStream; import ai.timefold.solver.core.api.score.stream.quad.QuadConstraintStream; @@ -104,14 +104,13 @@ public boolean guaranteesDistinct() { } } - protected > Constraint buildConstraint(String constraintName, String description, - String constraintGroup, Score_ constraintWeight, ScoreImpactType impactType, Object justificationFunction, + protected > Constraint buildConstraint(ConstraintMetadata description, + Score_ constraintWeight, ScoreImpactType impactType, Object justificationFunction, BavetScoringConstraintStream stream) { var resolvedJustificationMapping = Objects.requireNonNullElseGet(justificationFunction, this::getDefaultJustificationMapping); var isConstraintWeightConfigurable = constraintWeight == null; - var constraintRef = ConstraintRef.of(constraintName); - var constraint = new BavetConstraint<>(constraintFactory, constraintRef, description, constraintGroup, + var constraint = new BavetConstraint<>(constraintFactory, description, isConstraintWeightConfigurable ? null : constraintWeight, impactType, resolvedJustificationMapping, stream); stream.setConstraint(constraint); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/NodeGraph.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/NodeGraph.java index 425239f5a9b..99691257457 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/NodeGraph.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/visual/NodeGraph.java @@ -145,7 +145,7 @@ private static String getMetadata(GraphSink sink, Solutio var constraint = sink.constraint(); var metadata = getBaseDOTProperties("#3423a6", true); metadata.put("label", "%s
(Weight: %s)" - .formatted(constraint.getConstraintRef().constraintName(), constraint.extractConstraintWeight(solution))); + .formatted(constraint.getConstraintRef().id(), constraint.extractConstraintWeight(solution))); return mergeMetadata(metadata); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/DefaultConstraintWeightOverrides.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/DefaultConstraintWeightOverrides.java index 878d711061d..97ddaa45891 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/DefaultConstraintWeightOverrides.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/DefaultConstraintWeightOverrides.java @@ -21,12 +21,12 @@ public DefaultConstraintWeightOverrides(Map constraintWeightMap) } @Override - public @Nullable Score_ getConstraintWeight(String constraintName) { - return constraintWeightMap.get(constraintName); + public @Nullable Score_ getConstraintWeight(String constraintId) { + return constraintWeightMap.get(constraintId); } @Override - public Set getKnownConstraintNames() { + public Set getKnownConstraintIds() { return Collections.unmodifiableSet(constraintWeightMap.keySet()); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/ConstraintWeightSupplier.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/ConstraintWeightSupplier.java index 59994930f1b..c5bb5ebc68a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/ConstraintWeightSupplier.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/ConstraintWeightSupplier.java @@ -62,19 +62,19 @@ private ConstraintWeightSupplier(SolutionDescriptor solutionDescripto * @param userDefinedConstraints never null */ public void validate(@Nullable Solution_ workingSolution, Set userDefinedConstraints) { - var userDefinedConstraintNames = - userDefinedConstraints.stream().map(ConstraintRef::constraintName).collect(Collectors.toSet()); + var userDefinedConstraintIds = + userDefinedConstraints.stream().map(ConstraintRef::id).collect(Collectors.toSet()); // Constraint verifier is known to cause null here. var overrides = workingSolution == null ? ConstraintWeightOverrides.none() : Objects.requireNonNull(getConstraintWeights(workingSolution)); - var supportedConstraints = overrides.getKnownConstraintNames(); + var supportedConstraints = overrides.getKnownConstraintIds(); var excessiveConstraints = supportedConstraints.stream() - .filter(constraintName -> !userDefinedConstraintNames.contains(constraintName)).collect(Collectors.toSet()); + .filter(constraintId -> !userDefinedConstraintIds.contains(constraintId)).collect(Collectors.toSet()); if (!excessiveConstraints.isEmpty()) { throw new IllegalStateException(""" The constraint weight overrides contain the following constraints (%s) \ that are not in the user-defined constraints (%s). - Maybe check your %s for missing constraints.""".formatted(excessiveConstraints, userDefinedConstraintNames, + Maybe check your %s for missing constraints.""".formatted(excessiveConstraints, userDefinedConstraintIds, ConstraintProvider.class.getSimpleName())); } // Constraints are allowed to be missing; the default value provided by the ConstraintProvider will be used. @@ -107,7 +107,7 @@ public Class> getProblemFactClass() if (workingSolution == null) { // ConstraintVerifier is known to cause null here. return null; } - var weight = getConstraintWeights(workingSolution).getConstraintWeight(constraintRef.constraintName()); + var weight = getConstraintWeights(workingSolution).getConstraintWeight(constraintRef.id()); if (weight == null) { // This is fine; use default value from ConstraintProvider. return null; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhase.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhase.java index 4eb16153e11..b0ca9722402 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhase.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhase.java @@ -188,8 +188,8 @@ private void collectMetrics(LocalSearchStepScope stepScope) { if (scoreDirector.getConstraintMatchPolicy().isEnabled()) { for (ConstraintMatchTotal constraintMatchTotal : scoreDirector.getConstraintMatchTotalMap() .values()) { - var tags = solverScope.getMonitoringTags().and("constraint.name", - constraintMatchTotal.getConstraintRef().constraintName()); + var tags = solverScope.getMonitoringTags().and("constraint.id", + constraintMatchTotal.getConstraintRef().id()); collectConstraintMatchTotalMetrics(SolverMetric.CONSTRAINT_MATCH_TOTAL_BEST_SCORE, tags, constraintMatchTotalTagsToBestCount, constraintMatchTotalBestScoreMap, constraintMatchTotal, scoreDefinition, solverScope); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java index 083431b4e98..25a9746b816 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java @@ -89,7 +89,7 @@ public int compareTo(ConstraintMatch other) { @Override public String toString() { - return "%s/%s=%s".formatted(getConstraintRef().constraintName(), justification, score); + return "%s/%s=%s".formatted(getConstraintRef().id(), justification, score); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java index 11cf8804fca..300153e72f8 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java @@ -96,7 +96,7 @@ public interface InnerScoreDirector> * Call {@link #calculateScore()} before calling this method, * unless that method has already been called since the last {@link PlanningVariable} changes. * - * @return never null, the key is the constraint name. + * @return never null, the key is the constraint reference. * If a constraint is present in the problem but resulted in no matches, * it will still be in the map with a {@link ConstraintMatchTotal#getConstraintMatchSet()} size of 0. * @throws IllegalStateException if {@link #getConstraintMatchPolicy()} returns {@link ConstraintMatchPolicy#DISABLED}. diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModel.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModel.java index d3f58cf17b7..28c53f5531a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModel.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModel.java @@ -1,13 +1,11 @@ package ai.timefold.solver.core.impl.score.director.stream; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.util.stream.Collectors; import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.api.score.stream.ConstraintMetaModel; @@ -16,27 +14,15 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -record DefaultConstraintMetaModel( - Map constraintPerRefMap, - Map> constraintPerGroupMap) implements ConstraintMetaModel { +record DefaultConstraintMetaModel(Map constraintPerRefMap) + implements + ConstraintMetaModel { public static ConstraintMetaModel of(List constraints) { - var constraintCount = constraints.size(); // Preserve iteration order by using LinkedHashMap. - - var perRefMap = LinkedHashMap. newLinkedHashMap(constraintCount); - var perGroupMap = new TreeMap>(); - for (var constraint : constraints) { - perRefMap.put(constraint.getConstraintRef(), constraint); - // The list is used to preserve iteration order of the constraints. - // Constraint groups are an optional feature, therefore most people won't use them, - // therefore sizing the list assuming all constraints end up in the default group. - perGroupMap.computeIfAbsent(constraint.getConstraintGroup(), k -> new ArrayList<>(constraintCount)) - .add(constraint); - } - return new DefaultConstraintMetaModel( - Collections.unmodifiableMap(perRefMap), - Collections.unmodifiableMap(perGroupMap)); + var perRefMap = constraints.stream() + .collect(Collectors.toMap(Constraint::getConstraintRef, v -> v, (a, b) -> a, LinkedHashMap::new)); + return new DefaultConstraintMetaModel(Collections.unmodifiableMap(perRefMap)); } @Override @@ -44,16 +30,6 @@ public static ConstraintMetaModel of(List constraints) { return constraintPerRefMap.get(constraintRef); } - @Override - public @NonNull Collection getConstraintsPerGroup(@NonNull String constraintGroup) { - return constraintPerGroupMap.getOrDefault(constraintGroup, Collections.emptyList()); - } - - @Override - public @NonNull Set getConstraintGroups() { - return constraintPerGroupMap.keySet(); - } - @Override public @NonNull Collection getConstraints() { return constraintPerRefMap.values(); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraint.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraint.java index 7ac5cffa49f..d54d76e8fca 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraint.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraint.java @@ -1,24 +1,27 @@ package ai.timefold.solver.core.impl.score.stream.bavet; +import java.util.Objects; import java.util.Set; import ai.timefold.solver.core.api.score.Score; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; import ai.timefold.solver.core.impl.bavet.common.BavetAbstractConstraintStream; import ai.timefold.solver.core.impl.score.stream.bavet.common.BavetScoringConstraintStream; import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraint; import ai.timefold.solver.core.impl.score.stream.common.ScoreImpactType; +import org.jspecify.annotations.NullMarked; + +@NullMarked public final class BavetConstraint extends AbstractConstraint, BavetConstraintFactory> { private final BavetScoringConstraintStream scoringConstraintStream; - public BavetConstraint(BavetConstraintFactory constraintFactory, ConstraintRef constraintRef, - String description, String constraintGroup, Score constraintWeight, ScoreImpactType scoreImpactType, + public BavetConstraint(BavetConstraintFactory constraintFactory, ConstraintMetadata constraintMetadata, + Score constraintWeight, ScoreImpactType scoreImpactType, Object justificationMapping, BavetScoringConstraintStream scoringConstraintStream) { - super(constraintFactory, constraintRef, description, constraintGroup, constraintWeight, scoreImpactType, - justificationMapping); + super(constraintFactory, constraintMetadata, constraintWeight, scoreImpactType, justificationMapping); this.scoringConstraintStream = scoringConstraintStream; } @@ -26,6 +29,11 @@ public BavetScoringConstraintStream getScoringConstraintStream() { return scoringConstraintStream; } + @Override + public JustificationMapping_ getJustificationMapping() { + return Objects.requireNonNull(super.getJustificationMapping()); + } + @Override public String toString() { return "BavetConstraint(" + getConstraintRef() + ")"; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetAbstractBiConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetAbstractBiConstraintStream.java index 8563c6986fd..5def67662be 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetAbstractBiConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetAbstractBiConstraintStream.java @@ -475,10 +475,9 @@ public > BiConstraintBuilder innerImp private > BiConstraintBuilderImpl newTerminator( BavetScoringConstraintStream stream, ScoreImpactType impactType, Score_ constraintWeight) { return new BiConstraintBuilderImpl<>( - (constraintName, constraintDescription, constraintGroup, constraintWeight_, impactType_, - justificationMapping) -> buildConstraint(constraintName, - constraintDescription, constraintGroup, constraintWeight_, impactType_, justificationMapping, - stream), + (description, constraintWeight_, impactType_, + justificationMapping) -> buildConstraint(description, + constraintWeight_, impactType_, justificationMapping, stream), impactType, constraintWeight); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetAbstractQuadConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetAbstractQuadConstraintStream.java index a6f6aae1e07..1aed934886b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetAbstractQuadConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetAbstractQuadConstraintStream.java @@ -420,10 +420,9 @@ public > QuadConstraintBuilder private > QuadConstraintBuilderImpl newTerminator( BavetScoringConstraintStream stream, Score_ constraintWeight, ScoreImpactType impactType) { return new QuadConstraintBuilderImpl<>( - (constraintName, constraintDescription, constraintGroup, constraintWeight_, impactType_, - justificationMapping) -> buildConstraint(constraintName, - constraintDescription, constraintGroup, constraintWeight_, impactType_, justificationMapping, - stream), + (description, constraintWeight_, impactType_, + justificationMapping) -> buildConstraint(description, + constraintWeight_, impactType_, justificationMapping, stream), impactType, constraintWeight); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetAbstractTriConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetAbstractTriConstraintStream.java index e208953fdf7..81163afe871 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetAbstractTriConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetAbstractTriConstraintStream.java @@ -471,10 +471,9 @@ public > TriConstraintBuilder inne private > TriConstraintBuilderImpl newTerminator(BavetScoringConstraintStream stream, Score_ constraintWeight, ScoreImpactType impactType) { return new TriConstraintBuilderImpl<>( - (constraintName, constraintDescription, constraintGroup, constraintWeight_, impactType_, - justificationMapping) -> buildConstraint(constraintName, - constraintDescription, constraintGroup, constraintWeight_, impactType_, justificationMapping, - stream), + (description, constraintWeight_, impactType_, + justificationMapping) -> buildConstraint(description, + constraintWeight_, impactType_, justificationMapping, stream), impactType, constraintWeight); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetAbstractUniConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetAbstractUniConstraintStream.java index ac8104be37c..0b28b2e6d08 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetAbstractUniConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetAbstractUniConstraintStream.java @@ -488,10 +488,9 @@ public > UniConstraintBuilder innerImpac private > UniConstraintBuilderImpl newTerminator( BavetScoringConstraintStream stream, Score_ constraintWeight, ScoreImpactType impactType) { return new UniConstraintBuilderImpl<>( - (constraintName, constraintDescription, constraintGroup, constraintWeight_, impactType_, - justificationMapping) -> buildConstraint(constraintName, - constraintDescription, constraintGroup, constraintWeight_, impactType_, justificationMapping, - stream), + (description, constraintWeight_, impactType_, + justificationMapping) -> buildConstraint(description, + constraintWeight_, impactType_, justificationMapping, stream), impactType, constraintWeight); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java index 7d856de09db..2da51dbbf06 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java @@ -2,56 +2,43 @@ import java.math.BigDecimal; import java.util.Objects; -import java.util.regex.Pattern; import ai.timefold.solver.core.api.score.IBendableScore; import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.Constraint; +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor; import ai.timefold.solver.core.impl.score.definition.AbstractBendableScoreDefinition; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@NullMarked public abstract class AbstractConstraint, ConstraintFactory_ extends InnerConstraintFactory> implements Constraint { - private static final String CONSTRAINT_GROUP_REGEX = "^[\\p{L}][\\p{L}\\p{N}\\p{M}_-]*$"; - private static final Pattern CONSTRAINT_GROUP_REGEX_PATTERN = Pattern.compile(CONSTRAINT_GROUP_REGEX); - private final ConstraintFactory_ constraintFactory; private final ConstraintRef constraintRef; - private final String description; - private final String constraintGroup; + private final ConstraintMetadata constraintMetadata; private final Score defaultConstraintWeight; private final ScoreImpactType scoreImpactType; // Constraint is not generic in uni/bi/..., therefore these can not be typed. - private final Object justificationMapping; + private final @Nullable Object justificationMapping; /** * * @param constraintFactory never null - * @param constraintRef never null - * @param description never null - * @param constraintGroup never null + * @param constraintMetadata never null * @param scoreImpactType never null * @param justificationMapping never null */ - protected AbstractConstraint(ConstraintFactory_ constraintFactory, ConstraintRef constraintRef, String description, - String constraintGroup, Score defaultConstraintWeight, ScoreImpactType scoreImpactType, - Object justificationMapping) { + protected AbstractConstraint(ConstraintFactory_ constraintFactory, ConstraintMetadata constraintMetadata, + Score defaultConstraintWeight, ScoreImpactType scoreImpactType, + @Nullable Object justificationMapping) { this.constraintFactory = Objects.requireNonNull(constraintFactory); - this.constraintRef = Objects.requireNonNull(constraintRef); - this.description = Objects.requireNonNull(description); - this.constraintGroup = Objects.requireNonNull(constraintGroup); - var matcher = CONSTRAINT_GROUP_REGEX_PATTERN.matcher(constraintGroup); - if (!matcher.matches()) { - throw new IllegalArgumentException(""" - The constraintGroup (%s) contains invalid characters. - Only alphanumeric characters, "-" and "_" are allowed. - The string must start with an alphabetic character. - """.formatted(constraintGroup)); - } + this.constraintMetadata = Objects.requireNonNull(constraintMetadata); + this.constraintRef = ConstraintRef.of(constraintMetadata.id()); this.defaultConstraintWeight = defaultConstraintWeight; this.scoreImpactType = Objects.requireNonNull(scoreImpactType); this.justificationMapping = justificationMapping; // May be omitted in test code. @@ -111,15 +98,11 @@ public ConstraintRef getConstraintRef() { } @Override - public @NonNull String getDescription() { - return description; - } - - @Override - public @NonNull String getConstraintGroup() { - return constraintGroup; + public ConstraintMetadata getConstraintMetadata() { + return constraintMetadata; } + @SuppressWarnings("unchecked") @Override public > Score_ getConstraintWeight() { return adjustConstraintWeight((Score_) defaultConstraintWeight); @@ -129,19 +112,19 @@ public final ScoreImpactType getScoreImpactType() { return scoreImpactType; } - public JustificationMapping_ getJustificationMapping() { + /** + * + * @return maybe null, in test code + * @param user-defined type + */ + @SuppressWarnings("unchecked") + public @Nullable JustificationMapping_ getJustificationMapping() { // It is the job of the code constructing the constraint to ensure that this cast is correct. return (JustificationMapping_) justificationMapping; } public static > void validateWeight( SolutionDescriptor solutionDescriptor, ConstraintRef constraintRef, Score_ constraintWeight) { - if (constraintWeight == null) { - throw new IllegalArgumentException(""" - The constraintWeight (null) for constraint (%s) must not be null. - Maybe check your constraint implementation.""" - .formatted(constraintRef)); - } var scoreDescriptor = solutionDescriptor. getScoreDescriptor(); if (!constraintWeight.getClass().isAssignableFrom(constraintWeight.getClass())) { throw new IllegalArgumentException(""" diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintBuilder.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintBuilder.java index 6b6381b5a26..fe1c7ab91ff 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintBuilder.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintBuilder.java @@ -5,6 +5,7 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.api.score.stream.ConstraintBuilder; +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -28,9 +29,8 @@ protected AbstractConstraintBuilder(ConstraintConstructor constraintConstructor, @SuppressWarnings("unchecked") @Override - public final Constraint asConstraintDescribed(String constraintName, String constraintDescription, String constraintGroup) { - return constraintConstructor.apply(sanitize("constraintName", constraintName), constraintDescription, - sanitize("constraintGroup", constraintGroup), constraintWeight, impactType, getJustificationMapping()); + public final Constraint asConstraint(ConstraintMetadata metadata) { + return constraintConstructor.apply(metadata, constraintWeight, impactType, getJustificationMapping()); } public static String sanitize(String fieldName, String fieldValue) { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintConstructor.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintConstructor.java index 571b91abf32..e5a7fc108f3 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintConstructor.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintConstructor.java @@ -2,11 +2,12 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.Constraint; +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; @FunctionalInterface public interface ConstraintConstructor, JustificationMapping_> { - Constraint apply(String constraintName, String constraintDescription, String constraintGroup, Score_ constraintWeight, + Constraint apply(ConstraintMetadata description, Score_ constraintWeight, ScoreImpactType impactType, JustificationMapping_ justificationMapping); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/DefaultConstraintMetadata.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/DefaultConstraintMetadata.java new file mode 100644 index 00000000000..42ce19d968d --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/DefaultConstraintMetadata.java @@ -0,0 +1,9 @@ +package ai.timefold.solver.core.impl.score.stream.common; + +import ai.timefold.solver.core.api.score.stream.ConstraintMetadata; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public record DefaultConstraintMetadata(String id) implements ConstraintMetadata { +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/test/AbstractSingleConstraintAssertion.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/test/AbstractSingleConstraintAssertion.java index 4c86bf3844d..2cad8849988 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/test/AbstractSingleConstraintAssertion.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/test/AbstractSingleConstraintAssertion.java @@ -297,7 +297,7 @@ private void assertImpact(ScoreImpactType scoreImpactType, Number matchWeightTot return; } var assertionMessage = buildAssertionErrorMessage(scoreImpactType, matchWeightTotal, actualScoreImpactType, - impact, constraint.getConstraintRef().constraintName(), message); + impact, constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -321,7 +321,7 @@ private void assertMoreThanImpact(ScoreImpactType scoreImpactType, Number matchW return; } var assertionMessage = buildMoreThanAssertionErrorMessage(scoreImpactType, matchWeightTotal, actualScoreImpactType, - impact, constraint.getConstraintRef().constraintName(), message); + impact, constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -345,7 +345,7 @@ private void assertLessThanImpact(ScoreImpactType scoreImpactType, Number matchW return; } var assertionMessage = buildLessThanAssertionErrorMessage(scoreImpactType, matchWeightTotal, actualScoreImpactType, - impact, constraint.getConstraintRef().constraintName(), message); + impact, constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -363,7 +363,7 @@ private void assertNoImpact(String message) { } var assertionMessage = - buildNoImpactAssertionErrorMessage(impact, constraint.getConstraintRef().constraintName(), message); + buildNoImpactAssertionErrorMessage(impact, constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -376,14 +376,14 @@ private void assertJustification(String message, boolean completeValidation, Con // No justifications if (emptyJustifications) { - var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().constraintName(), + var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().id(), justificationCollection, emptyList(), emptyList(), justificationCollection, message); throw new AssertionError(assertionMessage); } // Empty justifications if (justificationCollection.isEmpty()) { - var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().constraintName(), + var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().id(), emptyList(), Arrays.asList(justifications), Arrays.asList(justifications), emptyList(), message); throw new AssertionError(assertionMessage); } @@ -404,7 +404,7 @@ private void assertJustification(String message, boolean completeValidation, Con if (expectedNotFound.isEmpty() && unexpectedFound.isEmpty()) { return; } - var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().constraintName(), + var assertionMessage = buildAssertionErrorMessage("Justification", constraint.getConstraintRef().id(), unexpectedFound, expectedNotFound, Arrays.asList(justifications), justificationCollection, message); throw new AssertionError(assertionMessage); } @@ -460,7 +460,7 @@ private void assertMatchCount(ScoreImpactType scoreImpactType, long expectedMatc } var assertionMessage = buildAssertionErrorMessage(scoreImpactType, expectedMatchCount, actualMatchCount, - constraint.getConstraintRef().constraintName(), message); + constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -470,7 +470,7 @@ private void assertMoreThanMatchCount(ScoreImpactType scoreImpactType, long expe return; } var assertionMessage = buildMoreThanAssertionErrorMessage(scoreImpactType, expectedMatchCount, actualMatchCount, - constraint.getConstraintRef().constraintName(), message); + constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -480,7 +480,7 @@ private void assertLessThanMatchCount(ScoreImpactType scoreImpactType, long expe return; } var assertionMessage = buildLessThanAssertionErrorMessage(scoreImpactType, expectedMatchCount, actualMatchCount, - constraint.getConstraintRef().constraintName(), message); + constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } @@ -489,7 +489,7 @@ private void assertMatch(ScoreImpactType scoreImpactType, String message) { return; } var assertionMessage = - buildAssertionErrorMessage(scoreImpactType, constraint.getConstraintRef().constraintName(), message); + buildAssertionErrorMessage(scoreImpactType, constraint.getConstraintRef().id(), message); throw new AssertionError(assertionMessage); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/domain/solution/ConstraintWeightOverridesTest.java b/core/src/test/java/ai/timefold/solver/core/impl/domain/solution/ConstraintWeightOverridesTest.java index a99923144a5..753661a615d 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/domain/solution/ConstraintWeightOverridesTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/domain/solution/ConstraintWeightOverridesTest.java @@ -34,8 +34,8 @@ void consistentOrderOfConstraints() { var secondAndFirst = ConstraintWeightOverrides.of(Map.of( SECOND_WEIGHT, SimpleScore.ONE, FIRST_WEIGHT, SimpleScore.ZERO)); - assertThat(firstAndSecond.getKnownConstraintNames()) - .containsExactly(secondAndFirst.getKnownConstraintNames().toArray(new String[0])); + assertThat(firstAndSecond.getKnownConstraintIds()) + .containsExactly(secondAndFirst.getKnownConstraintIds().toArray(new String[0])); } @Test diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModelTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModelTest.java index 12c35239267..5f9597603f6 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModelTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/director/stream/DefaultConstraintMetaModelTest.java @@ -5,12 +5,10 @@ import java.util.List; import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.testconstraint.TestConstraint; import ai.timefold.solver.core.testconstraint.TestConstraintFactory; import ai.timefold.solver.core.testdomain.TestdataSolution; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; class DefaultConstraintMetaModelTest { @@ -21,8 +19,8 @@ void test() { var constraintFactory = new TestConstraintFactory(solutionDescriptor); var constraint1 = new TestConstraint<>(constraintFactory, "Test Constraint 1", SimpleScore.of(1)); var constraint2 = new TestConstraint<>(constraintFactory, "Test Constraint 2", SimpleScore.of(10)); - var constraint3 = new TestConstraint<>(constraintFactory, "Test Constraint 3", "test", SimpleScore.of(100)); - var constraint4 = new TestConstraint<>(constraintFactory, "Test Constraint 4", "another-test", SimpleScore.ZERO); + var constraint3 = new TestConstraint<>(constraintFactory, "Test Constraint 3", SimpleScore.of(100)); + var constraint4 = new TestConstraint<>(constraintFactory, "Test Constraint 4", SimpleScore.ZERO); var metaModel = DefaultConstraintMetaModel.of(List.of(constraint1, constraint2, constraint3, constraint4)); assertSoftly(softly -> { @@ -31,16 +29,6 @@ void test() { softly.assertThat(metaModel.getConstraint(constraint3.getConstraintRef())).isSameAs(constraint3); softly.assertThat(metaModel.getConstraint(constraint4.getConstraintRef())).isSameAs(constraint4); }); - - Assertions.assertThat(metaModel.getConstraintGroups()) - .containsExactly("another-test", Constraint.DEFAULT_CONSTRAINT_GROUP, "test"); - - assertSoftly(softly -> { - softly.assertThat(metaModel.getConstraintsPerGroup(Constraint.DEFAULT_CONSTRAINT_GROUP)) - .containsExactly(constraint1, constraint2); - softly.assertThat(metaModel.getConstraintsPerGroup("test")).containsExactly(constraint3); - softly.assertThat(metaModel.getConstraintsPerGroup("another-test")).containsExactly(constraint4); - }); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/AbstractConstraintBuilderTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/AbstractConstraintBuilderTest.java index 87b67eadcab..39ab311812e 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/AbstractConstraintBuilderTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/AbstractConstraintBuilderTest.java @@ -3,8 +3,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.util.UUID; - import ai.timefold.solver.core.api.score.SimpleScore; import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraintBuilder; @@ -13,7 +11,7 @@ public abstract class AbstractConstraintBuilderTest { - protected abstract AbstractConstraintBuilder of(String constraintName, String constraintGroup); + protected abstract AbstractConstraintBuilder of(String constraintId); @CsvSource(""" name, true @@ -36,46 +34,14 @@ public abstract class AbstractConstraintBuilderTest { -, false _, false""") @ParameterizedTest - void nameSanitized(String name, boolean correct) { - if (correct) { - var constraint = of(name, "group") - .asConstraintDescribed(name, UUID.randomUUID().toString()); - assertThat(constraint) - .isNotNull(); - } else { - assertThatThrownBy(() -> of(name, "group").asConstraintDescribed(name, UUID.randomUUID().toString())) - .isExactlyInstanceOf(IllegalArgumentException.class); - } - } - - @CsvSource(""" - name, true - Name and spaces, true - Name and numb3rs, true - name_and_numb3r5, true - name-and-numb3r5, true - Let's allow a complete sentence., true - name${something}name, false - name$1name, false - name%23name, false - name/name, false - name//name, false - name\\/name, false - name\\/\\/name, false - null, false - nil, false - , false - -, false - _, false""") - @ParameterizedTest - void groupSanitized(String group, boolean correct) { + void idSanitized(String id, boolean correct) { if (correct) { - var constraint = of("name", group) - .asConstraintDescribed(group, UUID.randomUUID().toString()); + var constraint = of(id) + .asConstraint(id); assertThat(constraint) .isNotNull(); } else { - assertThatThrownBy(() -> of("name", group).asConstraintDescribed(group, UUID.randomUUID().toString())) + assertThatThrownBy(() -> of(id).asConstraint(id)) .isExactlyInstanceOf(IllegalArgumentException.class); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetAdvancedGroupByConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetAdvancedGroupByConstraintStreamTest.java index 6af9b4b8ffb..d2387082705 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetAdvancedGroupByConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetAdvancedGroupByConstraintStreamTest.java @@ -50,7 +50,7 @@ void collectedDowngradedAndFiltered() { .groupBy(Pair::new) .filter(pair -> !pair.key().equals("G")) .penalize(SimpleScore.ONE, Pair::value) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -81,7 +81,7 @@ void collectedAndFiltered() { .groupBy(count()) .filter(count -> count == 10) .penalize(SimpleScore.ONE, i -> i) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -105,7 +105,7 @@ void collectedFilteredRecollected() { .groupBy(toSet()) .groupBy(sum(Set::size)) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -128,7 +128,7 @@ void uniGroupByRecollected() { .groupBy(TestdataLavishEntity::getEntityGroup) .groupBy(toSet()) .penalize(SimpleScore.ONE, Set::size) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity1 = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -157,7 +157,7 @@ void biGroupByRecollected() { .groupBy((a, b) -> a.getEntityGroup(), countBi()) .groupBy(ConstraintCollectors.toList((a, b) -> a)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -189,7 +189,7 @@ void triGroupByRecollected() { .groupBy((a, b, c) -> a.getEntityGroup(), countTri()) .groupBy(toMap((g, c) -> g, (g, c) -> c, Long::sum)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -225,7 +225,7 @@ void quadGroupByRecollected() { .groupBy((a, b, c, d) -> a.getEntityGroup(), countQuad()) .groupBy(toMap((g, c) -> g, (g, c) -> c, Long::sum)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -253,7 +253,7 @@ void biGroupByRegrouped() { .groupBy((a, b) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -285,7 +285,7 @@ void triGroupByRegrouped() { .groupBy((a, b, c) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -321,7 +321,7 @@ void quadGroupByRegrouped() { .groupBy((a, b, c, d) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -350,7 +350,7 @@ void biGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -383,7 +383,7 @@ void triGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -420,7 +420,7 @@ void quadGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -456,7 +456,7 @@ void existsAfterGroupBy() { .groupBy(TestdataLavishEntity::getEntityGroup, count()) .ifExists(TestdataLavishEntityGroup.class, equal((groupA, count) -> groupA, Function.identity())) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -491,7 +491,7 @@ void groupByAfterExists() { equal(TestdataLavishEntity::getEntityGroup, Function.identity())) .groupBy(TestdataLavishEntity::getEntityGroup, count()) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -526,7 +526,7 @@ void groupByAfterExistsBi() { equal((e1, e2) -> e1.getEntityGroup(), Function.identity())) .groupBy((e1, e2) -> e1.getEntityGroup(), countBi()) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -560,7 +560,7 @@ void filteredFromUniquePair() { Joiners.equal(TestdataLavishEntity::getEntityGroup), Joiners.filtering((e1, e2) -> !e1.getCode().contains("My"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -583,7 +583,7 @@ void groupByThenJoinThenGroupBy() { (group, value, entity) -> entity, sum((group, count, entity) -> 1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))).doesNotThrowAnyException(); + .asConstraint(TEST_CONSTRAINT_ID))).doesNotThrowAnyException(); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java index 589ff330c60..45c9e8a473f 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java @@ -34,7 +34,7 @@ void joinWithNullKeyFromRight() { .join(factory.forEachIncludingUnassigned(TestdataEntity.class), Joiners.equal(TestdataEntity::getValue)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -95,7 +95,7 @@ void filteringJoinNullConflict() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -148,7 +148,7 @@ void filteringIfExistsNullConflict() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -195,7 +195,7 @@ void filteringIfNotExistsNullConflict() { .ifNotExists(TestdataEntity.class, filtering((a, b) -> (a.getValue() != b.getValue()))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -257,7 +257,7 @@ void filteringJoinNullConflictDifferentNodes() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -314,7 +314,7 @@ void filteringIfExistsNullConflictDifferentNodes() { return a.getValue() != b.getValue(); })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -368,7 +368,7 @@ void filteringIfNotExistsNullConflictDifferentNodes() { return a.getValue() != b.getValue(); })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -417,7 +417,7 @@ void mapPlanningEntityChanges() { .map(Function.identity()) .filter(e -> e.getValue() != null) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(1, 2); @@ -462,7 +462,7 @@ void concatSameTupleDeadAndAlive() { .filter(e -> e.getValue().getCode().equals("A")) .concat(factory.forEach(TestdataEntity.class)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataSolution.generateSolution(2, 2); @@ -531,7 +531,7 @@ void clearEvents() { factory -> new Constraint[] { factory.forEach(TestdataListMultipleShadowVariableValue.class) .penalize(SimpleScore.ONE, TestdataListMultipleShadowVariableValue::getCascadeValue) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); var solution = TestdataListMultipleShadowVariableSolution.generateSolution(2, 1); // We don't want to update shadows for this test! diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java index eb0f4f9f88c..20e4b52146e 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java @@ -48,7 +48,7 @@ void collectedDowngradedAndFiltered() { .groupBy(Pair::new) .filter(pair -> !pair.key().equals("G")) .penalize(SimpleScore.ONE, Pair::value) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -79,7 +79,7 @@ void collectedAndFiltered() { .groupBy(count()) .filter(count -> count == 10) .penalize(SimpleScore.ONE, i -> i) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -103,7 +103,7 @@ void collectedFilteredRecollected() { .groupBy(toSet()) .groupBy(sum(Set::size)) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -126,7 +126,7 @@ void uniGroupByRecollected() { .groupBy(TestdataLavishEntity::getEntityGroup) .groupBy(toSet()) .penalize(SimpleScore.ONE, Set::size) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity1 = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -155,7 +155,7 @@ void biGroupByRecollected() { .groupBy((a, b) -> a.getEntityGroup(), countBi()) .groupBy(toMap((g, c) -> g, (g, c) -> c, Long::sum)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -187,7 +187,7 @@ void triGroupByRecollected() { .groupBy((a, b, c) -> a.getEntityGroup(), countTri()) .groupBy(toMap((g, c) -> g, (g, c) -> c, Long::sum)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -223,7 +223,7 @@ void quadGroupByRecollected() { .groupBy((a, b, c, d) -> a.getEntityGroup(), countQuad()) .groupBy(toMap((g, c) -> g, (g, c) -> c, Long::sum)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -251,7 +251,7 @@ void biGroupByRegrouped() { .groupBy((a, b) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -283,7 +283,7 @@ void triGroupByRegrouped() { .groupBy((a, b, c) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -319,7 +319,7 @@ void quadGroupByRegrouped() { .groupBy((a, b, c, d) -> a.getEntityGroup()) .groupBy(Function.identity(), count()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -348,7 +348,7 @@ void biGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -382,7 +382,7 @@ void triGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -419,7 +419,7 @@ void quadGroupByRegroupedDouble() { .groupBy(Function.identity(), count()) .groupBy((group, count) -> group.toString(), countBi()) .penalize(SimpleScore.ONE, (groupName, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); @@ -455,7 +455,7 @@ void existsAfterGroupBy() { .groupBy(TestdataLavishEntity::getEntityGroup, count()) .ifExists(TestdataLavishEntityGroup.class, equal((groupA, count) -> groupA, Function.identity())) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -490,7 +490,7 @@ void groupByAfterExists() { equal(TestdataLavishEntity::getEntityGroup, Function.identity())) .groupBy(TestdataLavishEntity::getEntityGroup, count()) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -525,7 +525,7 @@ void groupByAfterExistsBi() { equal((e1, e2) -> e1.getEntityGroup(), Function.identity())) .groupBy((e1, e2) -> e1.getEntityGroup(), countBi()) .penalize(SimpleScore.ONE, (groupA, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -559,7 +559,7 @@ void filteredFromUniquePair() { equal(TestdataLavishEntity::getEntityGroup), filtering((e1, e2) -> !e1.getCode().contains("My"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -582,7 +582,7 @@ void groupByThenJoinThenGroupBy() { (group, value, entity) -> entity, sum((group, count, entity) -> 1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))).doesNotThrowAnyException(); + .asConstraint(TEST_CONSTRAINT_ID))).doesNotThrowAnyException(); } @TestTemplate @@ -595,7 +595,7 @@ void reusedStreamsInJoin() { return new Constraint[] { reusedStream.join(reusedStream, equal(TestdataLavishEntity::getEntityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }; }; diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java index 3da087f0880..5ca7df22bf0 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java @@ -33,8 +33,8 @@ @Execution(ExecutionMode.CONCURRENT) public abstract class AbstractConstraintStreamTest { - protected static final String TEST_CONSTRAINT_NAME = "testConstraintName"; - protected static final ConstraintRef TEST_CONSTRAINT_REF = new ConstraintRef(TEST_CONSTRAINT_NAME); + protected static final String TEST_CONSTRAINT_ID = "testConstraintId"; + protected static final ConstraintRef TEST_CONSTRAINT_REF = new ConstraintRef(TEST_CONSTRAINT_ID); protected final ConstraintStreamImplSupport implSupport; @@ -110,8 +110,8 @@ protected static AssertableMatch assertMatch(Object... justifications) { return assertMatchWithScore(-1, justifications); } - protected static AssertableMatch assertMatch(String constraintName, Object... justifications) { - return assertMatchWithScore(-1, ConstraintRef.of(constraintName), justifications); + protected static AssertableMatch assertMatch(String constraintId, Object... justifications) { + return assertMatchWithScore(-1, ConstraintRef.of(constraintId), justifications); } protected static AssertableMatch assertMatchWithScore(int score, Object... justifications) { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintTest.java deleted file mode 100644 index 353c5d5494d..00000000000 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package ai.timefold.solver.core.impl.score.stream.common; - -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.testconstraint.TestConstraint; -import ai.timefold.solver.core.testconstraint.TestConstraintFactory; -import ai.timefold.solver.core.testdomain.TestdataSolution; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -class AbstractConstraintTest { - - @ParameterizedTest - @CsvSource({ - "Hello123, true", - "こんにちは123, true", - "你好123, true", - "Hello_123, true", - "Hello-123, true", - "Hello_123😊, false", - "_Hello123, false", - "-Hello123, false", - "😊Hello123, false", - "123Hello, false", - "Hello 123, false", - "Hello@123, false" }) - void constraintGroupValidation(String constraintGroup, boolean isValid) { - var solutionDescriptor = TestdataSolution.buildSolutionDescriptor(); - var constraintFactory = new TestConstraintFactory(solutionDescriptor); - if (isValid) { - assertThatCode( - () -> new TestConstraint<>(constraintFactory, "Test Constraint", constraintGroup, SimpleScore.ZERO)) - .doesNotThrowAnyException(); - } else { - assertThatThrownBy( - () -> new TestConstraint<>(constraintFactory, "Test Constraint", constraintGroup, SimpleScore.ZERO)) - .hasMessageContaining("invalid characters") - .isInstanceOf(IllegalArgumentException.class); - } - } - -} diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java index e1db877f0ac..032a4493f05 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java @@ -58,7 +58,7 @@ public void filter_0_changed() { && value.getValueGroup() == valueGroup)) .filter((entity, value) -> entity.getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch Mockito.reset(entity1); @@ -140,7 +140,7 @@ public void filter_1_changed() { && value.getValueGroup() == valueGroup)) .filter((value, entity) -> entity.getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch Mockito.reset(entity1); @@ -220,7 +220,7 @@ public void filter_0_changed_forEachUnfilteredUniquePair() { Joiners.equal(TestdataLavishEntity::getEntityGroup))) .filter((a, b) -> a.getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch Mockito.reset(entity1); @@ -301,7 +301,7 @@ public void filter_1_changed_forEachUnfilteredUniquePair() { Joiners.equal(TestdataLavishEntity::getEntityGroup))) .filter((a, b) -> b.getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch Mockito.reset(entity2); @@ -361,7 +361,7 @@ private void assertPrecompute(TestdataLavishSolution solution, buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java index 7d9795acdf6..3e19822edc2 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java @@ -79,7 +79,7 @@ public void filter_entity() { .join(TestdataLavishValue.class, equal(TestdataLavishEntity::getValue, Function.identity())) .filter((entity, value) -> value.getCode().equals("MyValue 1")) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -119,7 +119,7 @@ public void filter_consecutive() { .filter((entityA, entityB) -> !Objects.equals(entityA, entity2)) .filter((entityA, entityB) -> !Objects.equals(entityA, entity3)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -153,7 +153,7 @@ public void join_filterOnAssignedValue_unassignOne() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) })) { scoreDirector.setWorkingSolution(solution); @@ -220,7 +220,7 @@ public void join_filterOnAssignedValue_unassignOneReassignOther() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) })) { scoreDirector.setWorkingSolution(solution); @@ -286,7 +286,7 @@ public void join_0() { .join(TestdataLavishValue.class, equal(TestdataLavishEntity::getValue, Function.identity())) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -344,7 +344,7 @@ public void join_1Equal() { .join(TestdataLavishExtra.class, equal((entity, value) -> entity.getStringProperty(), TestdataLavishExtra::getStringProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -397,7 +397,7 @@ public void join_1Filtering() { .join(TestdataLavishExtra.class, equal((entity, value) -> entity.getStringProperty(), TestdataLavishExtra::getStringProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -457,7 +457,7 @@ public void join_2Equal() { equal((entity, value) -> entity.getStringProperty(), TestdataLavishExtra::getStringProperty), equal((entity, value) -> entity.getIntegerProperty(), TestdataLavishExtra::getIntegerProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -485,7 +485,7 @@ public void join_filtering_comesLast() { equal(TestdataLavishEntity::getValue, Function.identity())) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalStateException.class); } @@ -509,7 +509,7 @@ public void join_mixedEqualsAndFiltering() { equal(TestdataLavishEntity::getValue, Function.identity()), filtering((entity, value) -> value.getCode().contains("1"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -553,7 +553,7 @@ public void joinAfterGroupBy() { countDistinct(TestdataLavishEntity::getValue)) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -584,7 +584,7 @@ public void ifExists_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .ifExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -611,7 +611,7 @@ public void ifExists_filterOnAssignedValue_unassignOne() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) })) { scoreDirector.setWorkingSolution(solution); @@ -674,7 +674,7 @@ public void ifExists_filterOnAssignedValue_unassignOneReassignOther() { return true; })) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) })) { scoreDirector.setWorkingSolution(solution); @@ -727,7 +727,7 @@ public void ifExists_0Joiner0Filter() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishValueGroup.class) .ifExists(TestdataLavishEntityGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -760,7 +760,7 @@ public void ifExists_0Join1Filter() { filtering((entityA, entityB, group) -> Objects.equals(group, entityA.getEntityGroup()) && Objects.equals(group, entityB.getEntityGroup()))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -792,7 +792,7 @@ public void ifExists_1Join0Filter() { .ifExists(TestdataLavishEntityGroup.class, equal((entityA, entityB) -> entityA.getEntityGroup(), Function.identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -829,7 +829,7 @@ public void ifExists_1Join1Filter() { filtering((entityA, entityB, group) -> entityA.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -860,7 +860,7 @@ public void ifExistsDoesNotIncludeUnassigned() { .ifExists(TestdataLavishEntity.class, filtering((a, b, c) -> a != c && b != c)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector); @@ -872,7 +872,7 @@ public void ifNotExists_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .ifNotExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -889,7 +889,7 @@ public void ifNotExists_0Joiner0Filter() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishValueGroup.class) .ifNotExists(TestdataLavishEntityGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -922,7 +922,7 @@ public void ifNotExists_0Join1Filter() { filtering((entityA, entityB, group) -> Objects.equals(group, entityA.getEntityGroup()) && Objects.equals(group, entityB.getEntityGroup()))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -958,7 +958,7 @@ public void ifNotExists_1Join0Filter() { .ifNotExists(TestdataLavishEntityGroup.class, equal((entityA, entityB) -> entityA.getEntityGroup(), Function.identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -991,7 +991,7 @@ public void ifNotExists_1Join1Filter() { filtering((entityA, entityB, group) -> entityA.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1026,7 +1026,7 @@ public void ifNotExistsDoesNotIncludeUnassigned() { .ifNotExists(TestdataLavishEntity.class, filtering((a, b, c) -> a != c && b != c)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1064,7 +1064,7 @@ public void ifExistsAfterGroupBy() { countDistinct(TestdataLavishEntity::getValue)) .ifExists(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1104,7 +1104,7 @@ public void groupBy_1Mapping0Collector() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getEntityGroup)) .groupBy((entityA, entityB) -> entityA.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1131,7 +1131,7 @@ public void groupBy_1Mapping1Collector() { .groupBy((entityA, entityB) -> entityA.toString(), countBi()) .filter((entity, count) -> count > 4) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1159,7 +1159,7 @@ public void groupBy_1Mapping2Collector() { countBi(), toSet((entityA, entityB) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1191,7 +1191,7 @@ public void groupBy_1Mapping3Collector() { max((TestdataLavishEntity entityA, TestdataLavishEntity entityB) -> entityA.getLongProperty()), toSet((entityA, entityB) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(Long.MAX_VALUE); @@ -1225,7 +1225,7 @@ public void groupBy_0Mapping1Collector() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .groupBy(countBi()) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1248,7 +1248,7 @@ public void groupBy_0Mapping2Collector() { .groupBy(countBi(), countDistinct((e, e2) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); @@ -1273,7 +1273,7 @@ public void groupBy_0Mapping3Collector() { min((TestdataLavishEntity e, TestdataLavishEntity e2) -> e.getLongProperty()), max((TestdataLavishEntity e, TestdataLavishEntity e2) -> e.getLongProperty())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -1306,7 +1306,7 @@ public void groupBy_0Mapping4Collector() { max((TestdataLavishEntity e, TestdataLavishEntity e2) -> e.getLongProperty()), toSet((e, e2) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -1336,7 +1336,7 @@ public void groupBy_2Mapping0Collector() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1367,7 +1367,7 @@ public void groupBy_2Mapping1Collector() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup(), countBi()) .penalize(SimpleScore.ONE, (entityGroup1, entityGroup2, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1401,7 +1401,7 @@ public void groupBy_2Mapping2Collector() { .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup(), countBi(), countBi()) .penalize(SimpleScore.ONE, (entityGroup1, entityGroup2, count, sameCount) -> count + sameCount) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1433,7 +1433,7 @@ public void groupBy_3Mapping0Collector() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup(), (a, b) -> a.getValue()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1466,7 +1466,7 @@ public void groupBy_3Mapping1Collector() { .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup(), (a, b) -> a.getValue(), ConstraintCollectors.countBi()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1499,7 +1499,7 @@ public void groupBy_4Mapping0Collector() { .groupBy((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup(), (a, b) -> a.getValue(), (a, b) -> b.getValue()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1531,7 +1531,7 @@ public void distinct() { // On a distinct stream, this is a no-op. buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1553,7 +1553,7 @@ public void mapToUniWithDuplicates() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .map((a, b) -> asSet(a.getEntityGroup(), b.getEntityGroup())) // 3 entities, 2 groups => duplicates. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1583,7 +1583,7 @@ public void mapToUniWithoutDuplicates() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .map((a, b) -> asSet(a.getEntityGroup(), b.getEntityGroup())) // 3 entities, 3 groups => no duplicates. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1615,7 +1615,7 @@ public void mapToUniAndDistinctWithDuplicates() { .map((a, b) -> asSet(a.getEntityGroup(), b.getEntityGroup())) // 3 entities, 2 groups => duplicates. .distinct() // Duplicate copies removed here. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1645,7 +1645,7 @@ public void mapToUniAndDistinctWithoutDuplicates() { .map((a, b) -> asSet(a.getEntityGroup(), b.getEntityGroup())) // 3 entities, 3 groups => no duplicates. .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1677,7 +1677,7 @@ public void mapToBi() { .map((a, b) -> a.getEntityGroup(), (a, b) -> b.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1709,7 +1709,7 @@ public void mapToTri() { (a, b) -> b.getEntityGroup(), (a, b) -> a.getLongProperty() + b.getLongProperty()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1745,7 +1745,7 @@ public void mapToQuad() { (a, b) -> a.getValue(), (a, b) -> b.getValue()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstValue(); @@ -1777,7 +1777,7 @@ public void expandToTri() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .expand((a, b) -> a.getLongProperty() + b.getLongProperty()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var sum01 = solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(1).getLongProperty(); var sum02 = solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(2).getLongProperty(); @@ -1809,7 +1809,7 @@ public void expandToQuad() { .expand((a, b) -> a.getLongProperty() + b.getLongProperty(), (a, b) -> a.getEntityGroup().getCode() + b.getEntityGroup().getCode()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var sum01 = solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(1).getLongProperty(); var sum02 = solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(2).getLongProperty(); @@ -1850,7 +1850,7 @@ public void flatten() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .flatten((a, b) -> asList(a.getEntityGroup(), b.getEntityGroup(), group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1879,7 +1879,7 @@ public void flattenLastWithDuplicates() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .flattenLast(b -> asList(b.getEntityGroup(), group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1917,7 +1917,7 @@ public void flattenLastWithoutDuplicates() { buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .flattenLast(b -> singleton(b.getEntityGroup())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1948,7 +1948,7 @@ public void flattenLastAndDistinctWithDuplicates() { .flattenLast(b -> asList(b.getEntityGroup(), group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1981,7 +1981,7 @@ public void flattenLastAndDistinctWithoutDuplicates() { .flattenLast(b -> singleton(b.getEntityGroup())) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2023,7 +2023,7 @@ public void concatUniWithoutValueDuplicates() { .concat(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2070,7 +2070,7 @@ public void concatAndDistinctUniWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2118,7 +2118,7 @@ public void concatBiWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value3))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2166,7 +2166,7 @@ public void concatBiWithValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2215,7 +2215,7 @@ public void concatAndDistinctBiWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value3))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2264,7 +2264,7 @@ public void concatAndDistinctBiWithValueDuplicates() { .filter(entity -> entity.getValue() == value2))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2312,7 +2312,7 @@ public void concatTriWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2363,7 +2363,7 @@ public void concatAndDistinctTriWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value1))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2415,7 +2415,7 @@ public void concatQuadWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2468,7 +2468,7 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2520,7 +2520,7 @@ public void concatAfterGroupBy() { (e1, e2) -> e2.getValue(), ConstraintCollectors.countBi())) .penalize(SimpleScore.ONE, (v1, v2, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2572,7 +2572,7 @@ public void complement() { .filter((entity, index) -> index == 0) .complement(TestdataLavishEntity.class, e -> Integer.MAX_VALUE) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2599,7 +2599,7 @@ public void penalizeUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-21)); @@ -2616,7 +2616,7 @@ public void penalizeUnweightedBigDecimal() { factory -> new Constraint[] { factory.forEachUniquePair(TestdataEntity.class) .penalizeBigDecimal(SimpleBigDecimalScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2656,7 +2656,7 @@ public void penalize() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .penalize(SimpleScore.ONE, (entity, entity2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-42)); @@ -2674,7 +2674,7 @@ public void penalizeBigDecimal() { factory.forEachUniquePair(TestdataEntity.class) .penalizeBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2690,7 +2690,7 @@ public void rewardUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .reward(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(21)); @@ -2705,7 +2705,7 @@ public void reward() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .reward(SimpleScore.ONE, (entity, entity2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(42)); @@ -2723,7 +2723,7 @@ public void rewardBigDecimal() { factory.forEachUniquePair(TestdataEntity.class) .rewardBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2739,7 +2739,7 @@ public void impactPositiveUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(21)); @@ -2754,7 +2754,7 @@ public void impactPositive() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE, (entity, entity2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(42)); @@ -2772,7 +2772,7 @@ public void impactPositiveBigDecimal() { factory.forEachUniquePair(TestdataEntity.class) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2788,7 +2788,7 @@ public void impactNegative() { var scoreDirector = buildScoreDirector( factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE, (entity, entity2) -> -2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-42)); @@ -2806,7 +2806,7 @@ public void impactNegativeBigDecimal() { factory.forEachUniquePair(TestdataEntity.class) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(-2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2823,7 +2823,7 @@ public void penalizeUnweightedCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-21)); @@ -2865,7 +2865,7 @@ public void penalizeCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .penalize(SimpleScore.ONE, (entity, entity2) -> 2) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-42)); @@ -2884,7 +2884,7 @@ public void penalizeBigDecimalCustomJustifications() { .penalizeBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2901,7 +2901,7 @@ public void rewardUnweightedCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .reward(SimpleScore.ONE) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(21)); @@ -2917,7 +2917,7 @@ public void rewardCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .reward(SimpleScore.ONE, (entity, entity2) -> 2) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(42)); @@ -2936,7 +2936,7 @@ public void rewardBigDecimalCustomJustifications() { .rewardBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2953,7 +2953,7 @@ public void impactPositiveUnweightedCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(21)); @@ -2969,7 +2969,7 @@ public void impactPositiveCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE, (entity, entity2) -> 2) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(42)); @@ -2988,7 +2988,7 @@ public void impactPositiveBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(2)) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3005,7 +3005,7 @@ public void impactNegativeCustomJustifications() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .impact(SimpleScore.ONE, (entity, entity2) -> -2) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-42)); @@ -3024,7 +3024,7 @@ public void impactNegativeBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2) -> BigDecimal.valueOf(-2)) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3040,7 +3040,7 @@ public void failWithMultipleJustifications() { .penalize(SimpleScore.ONE, (entity, entity2) -> 2) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) .justifyWith((a, b, score) -> new TestConstraintJustification<>(score, a, b)) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .hasMessageContaining("Maybe the constraint calls justifyWith() twice?"); } @@ -3065,7 +3065,7 @@ public void joinerEqualsAndSameness() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .join(TestdataLavishEntity.class, equal(TestdataLavishEntity::getBigDecimalProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/BiConstraintBuilderTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/BiConstraintBuilderTest.java index c8826aaee44..824aa22719f 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/BiConstraintBuilderTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/BiConstraintBuilderTest.java @@ -1,7 +1,6 @@ package ai.timefold.solver.core.impl.score.stream.common.bi; import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.impl.score.stream.AbstractConstraintBuilderTest; import ai.timefold.solver.core.impl.score.stream.bavet.BavetConstraint; @@ -16,12 +15,10 @@ class BiConstraintBuilderTest extends AbstractConstraintBuilderTest { new BavetConstraintFactory<>(TestdataSolution.buildSolutionDescriptor(), EnvironmentMode.FULL_ASSERT); @Override - protected AbstractConstraintBuilder of(String constraintName, String constraintGroup) { + protected AbstractConstraintBuilder of(String constraintId) { return new BiConstraintBuilderImpl<>( - (constraintName1, constraintDescription, constraintGroup1, constraintWeight, impactType, - objectSimpleScoreObjectBiFunction) -> new BavetConstraint<>(CONSTRAINT_FACTORY, - ConstraintRef.of(constraintName1), constraintDescription, constraintGroup1, constraintWeight, - impactType, objectSimpleScoreObjectBiFunction, null), + (constraintMetadata, constraintWeight, impactType, justificationMapping) -> new BavetConstraint<>( + CONSTRAINT_FACTORY, constraintMetadata, constraintWeight, impactType, justificationMapping, null), ScoreImpactType.PENALTY, SimpleScore.ONE); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java index fe249c480d8..06d6b9df156 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java @@ -64,7 +64,7 @@ private void assertPrecomputeFilterChanged( buildScoreDirector(factory -> factory.precompute(pf -> precomputeStream.apply(pf, entityGroup, valueGroup)) .filter((a, b, c, d) -> entityPicker.apply(a, b, c, d).getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch var createMatch = @@ -193,7 +193,7 @@ private void assertPrecompute(TestdataLavishSolution solution, buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java index 9eeeddc1cf4..7673d675b98 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java @@ -80,7 +80,7 @@ public void filter_entity() { .filter((e1, e2, value, extra) -> value.getCode().equals("MyValue 1") && extra.getCode().equals("MyExtra 1")) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -125,7 +125,7 @@ public void filter_consecutive() { .filter((entityA, entityB, entityC, entityD) -> !Objects.equals(entityA, entity2)) .filter((entityA, entityB, entityC, entityD) -> !Objects.equals(entityA, entity3)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -148,7 +148,7 @@ public void ifExists_unknownClass() { TestdataLavishEntity::getEntityGroup)) .ifExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -168,7 +168,7 @@ public void ifExists_0Joiner0Filter() { TestdataLavishEntity::getEntityGroup)) .ifExists(TestdataLavishValueGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch var valueGroup = solution.getFirstValueGroup(); @@ -204,7 +204,7 @@ public void ifExists_0Join1Filter() { filtering((entityA, entityB, entityAGroup, value, valueGroup) -> Objects .equals(value.getValueGroup(), valueGroup))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -240,7 +240,7 @@ public void ifExists_1Join0Filter() { .ifExists(TestdataLavishEntityGroup.class, equal((entityA, entityB, groupA, valueA) -> entityA.getEntityGroup(), identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -279,7 +279,7 @@ public void ifExists_1Join1Filter() { filtering((entityA, entityB, groupA, valueA, groupB) -> entityA.getCode().contains("MyEntity") || groupA.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -310,7 +310,7 @@ public void ifNotExists_unknownClass() { TestdataLavishEntity::getEntityGroup)) .ifNotExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -330,7 +330,7 @@ public void ifNotExists_0Joiner0Filter() { TestdataLavishEntity::getEntityGroup)) .ifNotExists(TestdataLavishValueGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch var valueGroup = solution.getFirstValueGroup(); @@ -366,7 +366,7 @@ public void ifNotExists_0Join1Filter() { filtering((entityA, entityB, entityAGroup, value, valueGroup) -> Objects .equals(value.getValueGroup(), valueGroup))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -402,7 +402,7 @@ public void ifNotExists_1Join0Filter() { .ifNotExists(TestdataLavishEntityGroup.class, equal((entityA, entityB, groupA, valueA) -> entityB.getEntityGroup(), identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -438,7 +438,7 @@ public void ifNotExists_1Join1Filter() { groupB) -> !(entityA.getCode().contains("MyEntity") && groupB.getCode().contains("MyEntity")))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -484,7 +484,7 @@ public void ifExistsAfterGroupBy() { countDistinct(TestdataLavishEntity::getValue)) .ifExists(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -524,7 +524,7 @@ public void groupBy_0Mapping1Collector() { TestdataLavishEntity::getEntityGroup)) .groupBy(countQuad()) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -551,7 +551,7 @@ public void groupBy_0Mapping2Collector() { .groupBy(countQuad(), countDistinct((e, e2, e3, e4) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); @@ -580,7 +580,7 @@ public void groupBy_0Mapping3Collector() { max((TestdataLavishEntity e, TestdataLavishEntity e2, TestdataLavishEntity e3, TestdataLavishEntity e4) -> e.getLongProperty())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -617,7 +617,7 @@ public void groupBy_0Mapping4Collector() { TestdataLavishEntity e4) -> e.getLongProperty()), toSet((e, e2, e3, e4) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -652,7 +652,7 @@ public void groupBy_1Mapping0Collector() { TestdataLavishEntity::getEntityGroup)) .groupBy((entity1, group, value, entity2) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); @@ -676,7 +676,7 @@ public void groupBy_1Mapping1Collector() { TestdataLavishEntity::getEntityGroup)) .groupBy((entity1, group, value, entity2) -> value, countQuad()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); @@ -710,7 +710,7 @@ public void groupBy_1Mapping2Collector() { countQuad(), toSet((entityA, entityB, entityC, entityD) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -746,7 +746,7 @@ public void groupBy_1Mapping3Collector() { TestdataLavishEntity entityC, TestdataLavishEntity entityD) -> entityA.getLongProperty()), toSet((entityA, entityB, entityC, entityD) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(Long.MAX_VALUE); @@ -783,7 +783,7 @@ public void groupBy_2Mapping0Collector() { TestdataLavishEntity::getEntityGroup)) .groupBy((entity1, group, value, entity2) -> group, (entity1, group, value, entity2) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -820,7 +820,7 @@ public void groupBy_2Mapping1Collector() { .groupBy((entity1, group, value, entity2) -> group, (entity1, group, value, entity2) -> value, countQuad()) .penalize(SimpleScore.ONE, (group, value, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -857,7 +857,7 @@ public void groupBy_2Mapping2Collector() { .groupBy((entity1, group, value, entity2) -> group, (entity1, group, value, entity2) -> value, countQuad(), countQuad()) .penalize(SimpleScore.ONE, (group, value, count, sameCount) -> count + sameCount) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -895,7 +895,7 @@ public void groupBy_3Mapping0Collector() { (entity1, group, value, entity2) -> entity2.getEntityGroup(), (entity1, group, value, entity2) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -933,7 +933,7 @@ public void groupBy_3Mapping1Collector() { (entity1, group, value, entity2) -> entity2.getEntityGroup(), (entity1, group, value, entity2) -> group, ConstraintCollectors.countQuad()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -970,7 +970,7 @@ public void groupBy_4Mapping0Collector() { (entity1, group, value, entity2) -> group, (entity1, group, value, entity2) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1005,7 +1005,7 @@ public void distinct() { // On a distinct stream, this is a no-op. Joiners.equal((entity1, entity2, group) -> entity2.getEntityGroup(), Function.identity())) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1033,7 +1033,7 @@ public void mapToUniWithDuplicates() { Joiners.equal((entity1, entity2, group) -> entity2.getEntityGroup(), Function.identity())) .map((entity1, entity2, group1, group2) -> asSet(group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity = solution.getFirstEntity(); var group1 = solution.getFirstEntityGroup(); @@ -1066,7 +1066,7 @@ public void mapToUniWithoutDuplicates() { Joiners.equal((entity1, entity2, group) -> entity2.getEntityGroup(), Function.identity())) .map((entity1, entity2, group1, group2) -> asSet(group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1098,7 +1098,7 @@ public void mapToUniAndDistinctWithDuplicates() { .map((entity1, entity2, group1, group2) -> asSet(group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity = solution.getFirstEntity(); var group1 = solution.getFirstEntityGroup(); @@ -1131,7 +1131,7 @@ public void mapToUniAndDistinctWithoutDuplicates() { .map((entity1, entity2, group1, group2) -> asSet(group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1163,7 +1163,7 @@ public void mapToBi() { .map((entity1, entity2, group1, group2) -> entity1, (entity1, entity2, group1, group2) -> entity2) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1193,7 +1193,7 @@ public void mapToTri() { (entity1, entity2, group1, group2) -> entity2, (entity1, entity2, group1, group2) -> entity1.getLongProperty() + entity2.getLongProperty()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1225,7 +1225,7 @@ public void mapToQuad() { (entity1, entity2, group1, group2) -> group1.getCode(), (entity1, entity2, group1, group2) -> group2.getCode()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1259,7 +1259,7 @@ public void flattenLastWithDuplicates() { .join(TestdataLavishEntity.class, filtering((a, b, c, d) -> a != d && b != d)) .flattenLast(d -> asList(group1, group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1297,7 +1297,7 @@ public void flattenLastWithoutDuplicates() { .join(TestdataLavishEntity.class, filtering((a, b, c, d) -> a != d && b != d)) .flattenLast(d -> asList(group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1333,7 +1333,7 @@ public void flattenLastAndDistinctWithDuplicates() { .flattenLast(d -> asList(group1, group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1369,7 +1369,7 @@ public void flattenLastAndDistinctWithoutDuplicates() { .flattenLast(d -> asList(group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1417,7 +1417,7 @@ public void concatUniWithoutValueDuplicates() { .concat(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1468,7 +1468,7 @@ public void concatAndDistinctUniWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1520,7 +1520,7 @@ public void concatBiWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value3))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1573,7 +1573,7 @@ public void concatAndDistinctBiWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value3))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1627,7 +1627,7 @@ public void concatTriWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1682,7 +1682,7 @@ public void concatAndDistinctTriWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value1))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1738,7 +1738,7 @@ public void concatQuadWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1794,7 +1794,7 @@ public void concatQuadWithValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1851,7 +1851,7 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1908,7 +1908,7 @@ public void concatAndDistinctQuadWithValueDuplicates() { .filter(entity -> entity.getValue() == value1))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1966,7 +1966,7 @@ public void concatAfterGroupBy() { (e1, e2, e3, e4) -> e3.getValue().getCode() + e4.getValue().getCode(), ConstraintCollectors.countQuad())) .penalize(SimpleScore.ONE, (v1, v2, v3, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2019,7 +2019,7 @@ public void complement() { .filter((entity, index, index2, index3) -> index == 0) .complement(TestdataLavishEntity.class, e -> Integer.MAX_VALUE, e -> -1, e -> 0) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2048,7 +2048,7 @@ public void penalizeUnweighted() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-2)); @@ -2067,7 +2067,7 @@ public void penalizeUnweightedBigDecimal() { .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) .penalizeBigDecimal(SimpleBigDecimalScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2111,7 +2111,7 @@ public void penalize() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .penalize(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2131,7 +2131,7 @@ public void penalizeBigDecimal() { .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) .penalizeBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2149,7 +2149,7 @@ public void rewardUnweighted() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .reward(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2166,7 +2166,7 @@ public void reward() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .reward(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2186,7 +2186,7 @@ public void rewardBigDecimal() { .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) .rewardBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2205,7 +2205,7 @@ public void impactPositiveUnweighted() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2222,7 +2222,7 @@ public void impactPositive() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2242,7 +2242,7 @@ public void impactPositiveBigDecimal() { .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2260,7 +2260,7 @@ public void impactNegative() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE, (entity, entity2, value, value2) -> -2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2280,7 +2280,7 @@ public void impactNegativeBigDecimal() { .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value, value2) -> BigDecimal.valueOf(-2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2299,7 +2299,7 @@ public void penalizeUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .penalize(SimpleScore.ONE) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-2)); @@ -2343,7 +2343,7 @@ public void penalizeCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .penalize(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2365,7 +2365,7 @@ public void penalizeBigDecimalCustomJustifications() { (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) .justifyWith( (a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2384,7 +2384,7 @@ public void rewardUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .reward(SimpleScore.ONE) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2402,7 +2402,7 @@ public void rewardCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .reward(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2424,7 +2424,7 @@ public void rewardBigDecimalCustomJustifications() { (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) .justifyWith( (a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2443,7 +2443,7 @@ public void impactPositiveUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2461,7 +2461,7 @@ public void impactPositiveCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2483,7 +2483,7 @@ public void impactPositiveBigDecimalCustomJustifications() { (entity, entity2, value, value2) -> BigDecimal.valueOf(2)) .justifyWith( (a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2502,7 +2502,7 @@ public void impactNegativeCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2, value) -> value, identity())) .impact(SimpleScore.ONE, (entity, entity2, value, value2) -> -2) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2524,7 +2524,7 @@ public void impactNegativeBigDecimalCustomJustifications() { (entity, entity2, value, value2) -> BigDecimal.valueOf(-2)) .justifyWith( (a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2542,7 +2542,7 @@ public void failWithMultipleJustifications() { .penalize(SimpleScore.ONE, (entity, entity2, value, value2) -> 2) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) .justifyWith((a, b, c, d, score) -> new TestConstraintJustification<>(score, a, b, c, d)) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .hasMessageContaining("Maybe the constraint calls justifyWith() twice?"); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/QuadConstraintBuilderTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/QuadConstraintBuilderTest.java index 9ff29e56217..141c8b6d22d 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/QuadConstraintBuilderTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/QuadConstraintBuilderTest.java @@ -1,7 +1,6 @@ package ai.timefold.solver.core.impl.score.stream.common.quad; import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.impl.score.stream.AbstractConstraintBuilderTest; import ai.timefold.solver.core.impl.score.stream.bavet.BavetConstraint; @@ -16,12 +15,10 @@ class QuadConstraintBuilderTest extends AbstractConstraintBuilderTest { new BavetConstraintFactory<>(TestdataSolution.buildSolutionDescriptor(), EnvironmentMode.FULL_ASSERT); @Override - protected AbstractConstraintBuilder of(String constraintName, String constraintGroup) { + protected AbstractConstraintBuilder of(String constraintId) { return new QuadConstraintBuilderImpl<>( - (constraintName1, constraintDescription, constraintGroup1, constraintWeight, impactType, - objectSimpleScoreObjectBiFunction) -> new BavetConstraint<>(CONSTRAINT_FACTORY, - ConstraintRef.of(constraintName1), constraintDescription, constraintGroup1, constraintWeight, - impactType, objectSimpleScoreObjectBiFunction, null), + (constraintMetadata, constraintWeight, impactType, justificationMapping) -> new BavetConstraint<>( + CONSTRAINT_FACTORY, constraintMetadata, constraintWeight, impactType, justificationMapping, null), ScoreImpactType.PENALTY, SimpleScore.ONE); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java index 225d767a403..d80cfab093a 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java @@ -63,7 +63,7 @@ private void assertPrecomputeFilterChanged( buildScoreDirector(factory -> factory.precompute(pf -> precomputeStream.apply(pf, entityGroup, valueGroup)) .filter((a, b, c) -> entityPicker.apply(a, b, c).getValue() == value1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch var createMatch = @@ -169,7 +169,7 @@ private void assertPrecompute(TestdataLavishSolution solution, buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java index 80441cdcfa3..90724a2057f 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java @@ -81,7 +81,7 @@ public void filter_entity() { .filter((entity, value, extra) -> value.getCode().equals("MyValue 1") && extra.getCode().equals("MyExtra 1")) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME); + .asConstraint(TEST_CONSTRAINT_ID); }); // From scratch @@ -125,7 +125,7 @@ public void filter_consecutive() { .filter((entityA, entityB, entityC) -> !Objects.equals(entityA, entity2)) .filter((entityA, entityB, entityC) -> !Objects.equals(entityA, entity3)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -170,7 +170,7 @@ public void join_0() { .orElseThrow(IllegalStateException::new), TestdataObject::getCode)) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -231,7 +231,7 @@ public void join_1Equal() { .join(TestdataLavishExtra.class, equal((e1, e2, e3) -> e1.getStringProperty(), TestdataLavishExtra::getStringProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -285,7 +285,7 @@ public void join_2Equal() { equal((e1, e2, value) -> e1.getStringProperty(), TestdataLavishExtra::getStringProperty), equal((e1, e2, value) -> e1.getIntegerProperty(), TestdataLavishExtra::getIntegerProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -323,7 +323,7 @@ public void joinAfterGroupBy() { countDistinct(TestdataLavishEntity::getValue)) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -355,7 +355,7 @@ public void ifExists_unknownClass() { .join(TestdataLavishEntityGroup.class) .ifExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -373,7 +373,7 @@ public void ifExists_0Joiner0Filter() { .join(TestdataLavishEntityGroup.class) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -409,7 +409,7 @@ public void ifExists_0Join1Filter() { filtering((entityA, entityB, entityC, group) -> Objects.equals(group, entityA.getEntityGroup()) && Objects.equals(group, entityB.getEntityGroup()))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -444,7 +444,7 @@ public void ifExists_1Join0Filter() { .ifExists(TestdataLavishEntityGroup.class, equal((entityA, entityB, entityC) -> entityA.getEntityGroup(), identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -484,7 +484,7 @@ public void ifExists_1Join1Filter() { filtering((entityA, entityB, entityC, group) -> entityA.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -521,7 +521,7 @@ public void ifExistsDoesNotIncludeUnassigned() { filtering((entityA, entityB, entityC, entityD) -> entityA != entityD && entityB != entityD && entityC != entityD)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -535,7 +535,7 @@ public void ifNotExists_unknownClass() { .join(TestdataLavishEntityGroup.class) .ifNotExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -553,7 +553,7 @@ public void ifNotExists_0Joiner0Filter() { .join(TestdataLavishEntityGroup.class) .ifNotExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -589,7 +589,7 @@ public void ifNotExists_0Join1Filter() { filtering((entityA, entityB, entityC, group) -> Objects.equals(group, entityA.getEntityGroup()) && Objects.equals(group, entityB.getEntityGroup()))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -628,7 +628,7 @@ public void ifNotExists_1Join0Filter() { .ifNotExists(TestdataLavishEntityGroup.class, equal((entityA, entityB, entityC) -> entityA.getEntityGroup(), identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -664,7 +664,7 @@ public void ifNotExists_1Join1Filter() { filtering((entityA, entityB, entityC, group) -> entityA.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -705,7 +705,7 @@ public void ifNotExistsDoesNotIncludeUnassigned() { filtering((entityA, entityB, entityC, entityD) -> entityA != entityD && entityB != entityD && entityC != entityD)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -748,7 +748,7 @@ public void ifExistsAfterGroupBy() { countDistinct(TestdataLavishEntity::getValue)) .ifExists(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -781,7 +781,7 @@ public void groupBy_0Mapping1Collector() { .join(TestdataLavishValue.class, equal((entity, group) -> entity.getValue(), identity())) .groupBy(countTri()) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -807,7 +807,7 @@ public void groupBy_0Mapping2Collector() { .groupBy(countTri(), countDistinct((e, e2, e3) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); @@ -835,7 +835,7 @@ public void groupBy_0Mapping3Collector() { max((TestdataLavishEntity e, TestdataLavishEntity e2, TestdataLavishEntity e3) -> e .getLongProperty())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -871,7 +871,7 @@ public void groupBy_0Mapping4Collector() { .getLongProperty()), toSet((e, e2, e3) -> e)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(0L); @@ -904,7 +904,7 @@ public void groupBy_1Mapping0Collector() { .join(TestdataLavishValue.class, equal((entity, group) -> entity.getValue(), identity())) .groupBy((entity, group, value) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); @@ -926,7 +926,7 @@ public void groupBy_1Mapping1Collector() { .join(TestdataLavishValue.class, equal((entity, group) -> entity.getValue(), identity())) .groupBy((entity, group, value) -> value, countTri()) .penalize(SimpleScore.ONE, (group, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); @@ -959,7 +959,7 @@ public void groupBy_1Mapping2Collector() { countTri(), toSet((entityA, entityB, entityC) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -994,7 +994,7 @@ public void groupBy_1Mapping3Collector() { TestdataLavishEntity entityC) -> entityA.getLongProperty()), toSet((entityA, entityB, entityC) -> entityA)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setLongProperty(Long.MAX_VALUE); @@ -1029,7 +1029,7 @@ public void groupBy_2Mapping0Collector() { .join(TestdataLavishValue.class, equal((entity, group) -> entity.getValue(), identity())) .groupBy((entity, group, value) -> group, (entity, group, value) -> value) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1063,7 +1063,7 @@ public void groupBy_2Mapping1Collector() { .join(TestdataLavishValue.class, equal((entity, group) -> entity.getValue(), identity())) .groupBy((entity, group, value) -> group, (entity, group, value) -> value, countTri()) .penalize(SimpleScore.ONE, (group, value, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1098,7 +1098,7 @@ public void groupBy_2Mapping2Collector() { .groupBy((entity, group, value) -> group, (entity, group, value) -> value, countTri(), countTri()) .penalize(SimpleScore.ONE, (group, value, count, sameCount) -> count + sameCount) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1131,7 +1131,7 @@ public void groupBy_3Mapping0Collector() { filtering((a, b, c) -> a != c && b != c)) .groupBy((a, b, c) -> a.getEntityGroup(), (a, b, c) -> b.getEntityGroup(), (a, b, c) -> c.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1163,7 +1163,7 @@ public void groupBy_3Mapping1Collector() { .groupBy((a, b, c) -> a.getEntityGroup(), (a, b, c) -> b.getEntityGroup(), (a, b, c) -> c.getEntityGroup(), ConstraintCollectors.countTri()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1195,7 +1195,7 @@ public void groupBy_4Mapping0Collector() { .groupBy((a, b, c) -> a.getEntityGroup(), (a, b, c) -> b.getEntityGroup(), (a, b, c) -> c.getEntityGroup(), (a, b, c) -> a.getValue()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); @@ -1227,7 +1227,7 @@ public void distinct() { // On a distinct stream, this is a no-op. .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1250,7 +1250,7 @@ public void mapToUniWithDuplicates() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .map((a, b, c) -> asSet(a.getEntityGroup(), b.getEntityGroup(), c.getEntityGroup())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1280,7 +1280,7 @@ public void mapToUniWithoutDuplicates() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .map((a, b, c) -> asSet(a.getEntityGroup(), b.getEntityGroup())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1312,7 +1312,7 @@ public void mapToUniAndDistinctWithDuplicates() { .map((a, b, c) -> asSet(a.getEntityGroup(), b.getEntityGroup(), c.getEntityGroup())) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1341,7 +1341,7 @@ public void mapToUniAndDistinctWithoutDuplicates() { .map((a, b, c) -> asSet(a.getEntityGroup(), b.getEntityGroup())) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1373,7 +1373,7 @@ public void mapToBi() { .map((a, b, c) -> a.getEntityGroup(), (a, b, c) -> b.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1406,7 +1406,7 @@ public void mapToTri() { (a, b, c) -> b.getEntityGroup(), (a, b, c) -> c.getEntityGroup()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1440,7 +1440,7 @@ public void mapToQuad() { (a, b, c) -> c.getEntityGroup(), (a, b, c) -> a.getLongProperty() + b.getLongProperty() + c.getLongProperty()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -1474,7 +1474,7 @@ public void expandToQuad() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .expand((a, b, c) -> a.getLongProperty() + b.getLongProperty() + c.getLongProperty()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1514,7 +1514,7 @@ public void flatten() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .flatten((a, b, c) -> asList(a.getEntityGroup(), b.getEntityGroup(), c.getEntityGroup())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1551,7 +1551,7 @@ public void flattenLastWithDuplicates() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .flattenLast(c -> asList(c.getEntityGroup(), group1, group2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1588,7 +1588,7 @@ public void flattenLastWithoutDuplicates() { .join(TestdataLavishEntity.class, Joiners.filtering((a, b, c) -> a != c && b != c)) .flattenLast(c -> asSet(c.getEntityGroup(), c.getEntityGroup() == group1 ? group2 : group1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1623,7 +1623,7 @@ public void flattenLastAndDistinctWithDuplicates() { .flattenLast(c -> asList(c.getEntityGroup(), group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1658,7 +1658,7 @@ public void flattenLastAndDistinctWithoutDuplicates() { .flattenLast(c -> asSet(c.getEntityGroup(), c.getEntityGroup() == group1 ? group2 : group1)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1704,7 +1704,7 @@ public void concatUniWithoutValueDuplicates() { .concat(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1753,7 +1753,7 @@ public void concatAndDistinctUniWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1803,7 +1803,7 @@ public void concatBiWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value3))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1854,7 +1854,7 @@ public void concatAndDistinctBiWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value3))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1906,7 +1906,7 @@ public void concatTriWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1958,7 +1958,7 @@ public void concatTriWithValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value3))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2011,7 +2011,7 @@ public void concatAndDistinctTriWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value1))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2064,7 +2064,7 @@ public void concatAndDistinctTriWithValueDuplicates() { .filter(entity -> entity.getValue() == value3))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2116,7 +2116,7 @@ public void concatQuadWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2171,7 +2171,7 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2228,7 +2228,7 @@ public void concatAfterGroupBy() { (e1, e2, e3) -> e3.getValue(), ConstraintCollectors.countTri())) .penalize(SimpleScore.ONE, (v1, v2, v3, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2280,7 +2280,7 @@ public void complement() { .filter((entity, index, index2) -> index == 0) .complement(TestdataLavishEntity.class, e -> Integer.MAX_VALUE, e -> -1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2308,7 +2308,7 @@ public void penalizeUnweighted() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-2)); @@ -2326,7 +2326,7 @@ public void penalizeUnweightedBigDecimal() { factory.forEachUniquePair(TestdataEntity.class, equal(TestdataEntity::getValue)) .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalizeBigDecimal(SimpleBigDecimalScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2369,7 +2369,7 @@ public void penalize() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalize(SimpleScore.ONE, (entity, entity2, value) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2388,7 +2388,7 @@ public void penalizeBigDecimal() { .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalizeBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2405,7 +2405,7 @@ public void rewardUnweighted() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .reward(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2421,7 +2421,7 @@ public void reward() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .reward(SimpleScore.ONE, (entity, entity2, value) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2440,7 +2440,7 @@ public void rewardBigDecimal() { .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .rewardBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2457,7 +2457,7 @@ public void impactPositiveUnweighted() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2473,7 +2473,7 @@ public void impactPositive() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE, (entity, entity2, value) -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2492,7 +2492,7 @@ public void impactPositiveBigDecimal() { .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2509,7 +2509,7 @@ public void impactNegative() { factory -> factory.forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE, (entity, entity2, value) -> -2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2528,7 +2528,7 @@ public void impactNegativeBigDecimal() { .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(-2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2546,7 +2546,7 @@ public void penalizeUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalize(SimpleScore.ONE) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-2)); @@ -2587,7 +2587,7 @@ public void penalizeCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .penalize(SimpleScore.ONE, (entity, entity2, value) -> 2) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2607,7 +2607,7 @@ public void penalizeBigDecimalCustomJustifications() { .penalizeBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2625,7 +2625,7 @@ public void rewardUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .reward(SimpleScore.ONE) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2642,7 +2642,7 @@ public void rewardCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .reward(SimpleScore.ONE, (entity, entity2, value) -> 2) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2662,7 +2662,7 @@ public void rewardBigDecimalCustomJustifications() { .rewardBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2680,7 +2680,7 @@ public void impactPositiveUnweightedCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(2)); @@ -2697,7 +2697,7 @@ public void impactPositiveCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE, (entity, entity2, value) -> 2) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(4)); @@ -2717,7 +2717,7 @@ public void impactPositiveBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(2)) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2735,7 +2735,7 @@ public void impactNegativeCustomJustifications() { .join(TestdataLavishValue.class, equal((entity, entity2) -> entity.getValue(), identity())) .impact(SimpleScore.ONE, (entity, entity2, value) -> -2) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-4)); @@ -2755,7 +2755,7 @@ public void impactNegativeBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, (entity, entity2, value) -> BigDecimal.valueOf(-2)) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -2772,7 +2772,7 @@ public void failWithMultipleJustifications() { .penalize(SimpleScore.ONE, (entity, entity2, value) -> 2) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) .justifyWith((a, b, c, score) -> new TestConstraintJustification<>(score, a, b, c)) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .hasMessageContaining("Maybe the constraint calls justifyWith() twice?"); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/TriConstraintBuilderTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/TriConstraintBuilderTest.java index fbf4f847714..2b71df02314 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/TriConstraintBuilderTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/TriConstraintBuilderTest.java @@ -1,7 +1,6 @@ package ai.timefold.solver.core.impl.score.stream.common.tri; import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.impl.score.stream.AbstractConstraintBuilderTest; import ai.timefold.solver.core.impl.score.stream.bavet.BavetConstraint; @@ -16,12 +15,10 @@ class TriConstraintBuilderTest extends AbstractConstraintBuilderTest { new BavetConstraintFactory<>(TestdataSolution.buildSolutionDescriptor(), EnvironmentMode.FULL_ASSERT); @Override - protected AbstractConstraintBuilder of(String constraintName, String constraintGroup) { + protected AbstractConstraintBuilder of(String constraintId) { return new TriConstraintBuilderImpl<>( - (constraintName1, constraintDescription, constraintGroup1, constraintWeight, impactType, - objectSimpleScoreObjectBiFunction) -> new BavetConstraint<>(CONSTRAINT_FACTORY, - ConstraintRef.of(constraintName1), constraintDescription, constraintGroup1, constraintWeight, - impactType, objectSimpleScoreObjectBiFunction, null), + (constraintMetadata, constraintWeight, impactType, justificationMapping) -> new BavetConstraint<>( + CONSTRAINT_FACTORY, constraintMetadata, constraintWeight, impactType, justificationMapping, null), ScoreImpactType.PENALTY, SimpleScore.ONE); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java index aa572d63911..f75db61baa3 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java @@ -51,7 +51,7 @@ public void filter_0_changed() { buildScoreDirector(factory -> factory.precompute(data -> data.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() == entityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch Mockito.reset(entity1); @@ -105,7 +105,7 @@ private void assertPrecompute(TestdataLavishSolution solution, buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -200,7 +200,7 @@ private void assertPrecomputeBi(TestdataLavishSolution solution, List factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java index d117d0f96e9..883b8856886 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java @@ -90,7 +90,7 @@ public void filter_problemFact() { buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .filter(valueGroup -> valueGroup.getCode().startsWith("MyValueGroup")) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -124,7 +124,7 @@ public void filter_entity() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() == entityGroup) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -174,7 +174,7 @@ public void filter_consecutive() { .filter(entity -> !Objects.equals(entity, entity2)) .filter(entity -> !Objects.equals(entity, entity3)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -192,7 +192,7 @@ public void join_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .join(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -211,7 +211,7 @@ public void join_0() { buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .join(TestdataLavishEntityGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -250,7 +250,7 @@ public void join_1Equal() { .join(TestdataLavishEntity.class, equal(TestdataLavishEntity::getValue)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -360,7 +360,7 @@ public void join_2Equal() { equal(TestdataLavishEntity::getEntityGroup), equal(TestdataLavishEntity::getIntegerProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -407,7 +407,7 @@ public void joinAfterGroupBy() { .groupBy(countDistinct(TestdataLavishEntity::getValue)) .join(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -438,7 +438,7 @@ public void ifExists_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .ifExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -457,7 +457,7 @@ public void ifExists_0Joiner0Filter() { buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .ifExists(TestdataLavishEntityGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -491,7 +491,7 @@ public void ifExists_0Join1Filter() { .ifExists(TestdataLavishEntityGroup.class, filtering((entity, group) -> Objects.equals(entity.getEntityGroup(), group))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -525,7 +525,7 @@ public void ifExists_1Join0Filter() { .forEach(TestdataLavishEntity.class) .ifExists(TestdataLavishEntityGroup.class, equal(TestdataLavishEntity::getEntityGroup, Function.identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -562,7 +562,7 @@ public void ifExists_1Join1Filter() { filtering((entity, group) -> entity.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -593,7 +593,7 @@ public void ifExistsOther_1Join0Filter() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .ifExistsOther(TestdataLavishEntity.class, equal(TestdataLavishEntity::getEntityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -629,7 +629,7 @@ public void ifExistsDoesNotIncludeUnassigned() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .ifExistsOther(TestdataLavishEntity.class, equal(TestdataLavishEntity::getEntityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -652,7 +652,7 @@ public void ifNotExists_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .ifNotExists(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -671,7 +671,7 @@ public void ifNotExists_0Joiner0Filter() { buildScoreDirector(factory -> factory.forEach(TestdataLavishValueGroup.class) .ifNotExists(TestdataLavishEntityGroup.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -701,7 +701,7 @@ public void ifNotExists_0Join1Filter() { .ifNotExists(TestdataLavishEntityGroup.class, filtering((entity, group) -> Objects.equals(entity.getEntityGroup(), group))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -732,7 +732,7 @@ public void ifNotExists_1Join0Filter() { .ifNotExists(TestdataLavishEntityGroup.class, equal(TestdataLavishEntity::getEntityGroup, Function.identity())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -765,7 +765,7 @@ public void ifNotExists_1Join1Filter() { filtering((entity, group) -> entity.getCode().contains("MyEntity") || group.getCode().contains("MyEntity"))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -796,7 +796,7 @@ public void ifNotExistsDoesNotIncludeUnassigned() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .ifNotExistsOther(TestdataLavishEntity.class, equal(TestdataLavishEntity::getEntityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -828,7 +828,7 @@ public void ifNotExistsOther_1Join0Filter() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .ifNotExistsOther(TestdataLavishEntity.class, equal(TestdataLavishEntity::getEntityGroup)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -865,7 +865,7 @@ public void ifExistsAfterGroupBy() { .groupBy(countDistinct(TestdataLavishEntity::getValue)) .ifExists(TestdataLavishExtra.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -891,7 +891,7 @@ public void ifExistsAfterGroupBy() { public void forEach_unknownClass() { assertThatThrownBy(() -> buildScoreDirector(factory -> factory.forEach(Integer.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining(Integer.class.getCanonicalName()) .hasMessageContaining("assignable from"); @@ -945,7 +945,7 @@ public void forEach_basicVarUninitialized() { factory -> new Constraint[] { factory.forEach(TestdataAllowsUnassignedEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // From scratch @@ -987,7 +987,7 @@ public void forEach_listVarNotAllowsUnassignedValues() { factory -> new Constraint[] { factory.forEach(TestdataListValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // v2 is not assigned, so it should not be matched @@ -1030,7 +1030,7 @@ public void forEach_listVarNotAllowsUnassignedValues_noInverseVar() { factory -> new Constraint[] { factory.forEach(TestdataPinnedNoShadowsListValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // v2 is not assigned, so it should not be matched @@ -1074,7 +1074,7 @@ public void forEach_listVarAllowsUnassignedValues() { factory -> new Constraint[] { factory.forEach(TestdataAllowsUnassignedValuesListValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // v2 is not assigned, so it should not be matched @@ -1116,7 +1116,7 @@ public void forEachIncludingUnassigned_basicVarUninitialized() { factory -> new Constraint[] { factory.forEachIncludingUnassigned(TestdataAllowsUnassignedEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // From scratch @@ -1141,7 +1141,7 @@ public void forEachIncludingUnassigned_listVarNotAllowsUnassignedValues() { factory -> new Constraint[] { factory.forEachIncludingUnassigned(TestdataListValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // Even though only one value is assigned, both are matched. @@ -1169,7 +1169,7 @@ public void forEachIncludingUnassigned_listVarAllowsUnassignedValues() { factory -> new Constraint[] { factory.forEachIncludingUnassigned(TestdataAllowsUnassignedValuesListValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // Even though only one value is assigned, both are matched. @@ -1198,7 +1198,7 @@ public void forEachIncludingUnassigned_excludeInconsistentEntities() { factory -> new Constraint[] { factory.forEachIncludingUnassigned(TestdataDependencyValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // From scratch @@ -1247,7 +1247,7 @@ public void forEach_excludeInconsistentEntities() { factory -> new Constraint[] { factory.forEach(TestdataDependencyValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // From scratch @@ -1294,7 +1294,7 @@ public void forEachUnfiltered_includeUnassignedAndInconsistentEntities() { factory -> new Constraint[] { factory.forEachUnfiltered(TestdataDependencyValue.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); // From scratch @@ -1342,7 +1342,7 @@ public void forEachUniquePair_0() { var scoreDirector = buildScoreDirector(factory -> factory.forEachUniquePair(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1371,7 +1371,7 @@ public void forEachUniquePair_1Equals() { var scoreDirector = buildScoreDirector(factory -> factory .forEachUniquePair(TestdataLavishEntity.class, equal(TestdataLavishEntity::getIntegerProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1404,7 +1404,7 @@ public void groupBy_1Mapping0Collect_filtered() { .groupBy(TestdataLavishEntity::getEntityGroup) .filter(entityGroup -> Objects.equals(entityGroup, entityGroup1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, entityGroup1)); @@ -1428,7 +1428,7 @@ public void groupBy_1Mapping1Collect_filtered() { .groupBy(TestdataLavishEntity::getEntityGroup, count()) .filter((entityGroup, count) -> count > 1) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, @@ -1455,7 +1455,7 @@ public void groupBy_joinedAndFiltered() { .join(TestdataLavishEntity.class, equal(Function.identity(), TestdataLavishEntity::getEntityGroup)) .filter((group, entity) -> group.equals(entityGroup1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, @@ -1481,7 +1481,7 @@ public void groupBy_1Mapping0Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getEntityGroup) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1506,7 +1506,7 @@ public void groupBy_1Mapping1Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getEntityGroup, count()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1535,7 +1535,7 @@ public void groupBy_1Mapping2Collector() { count(), toSet()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1567,7 +1567,7 @@ public void groupBy_1Mapping3Collector() { countDistinct(), toSet()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1606,7 +1606,7 @@ public void groupBy_0Mapping1Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(count()) .penalize(SimpleScore.ONE, count -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1628,7 +1628,7 @@ public void groupBy_0Mapping2Collector() { .groupBy(count(), countDistinct()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); @@ -1653,7 +1653,7 @@ public void groupBy_0Mapping3Collector() { min(TestdataLavishEntity::getIntegerProperty), max(TestdataLavishEntity::getIntegerProperty)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setIntegerProperty(0); @@ -1686,7 +1686,7 @@ public void groupBy_0Mapping4Collector() { max(TestdataLavishEntity::getIntegerProperty), toSet()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); entity1.setIntegerProperty(0); @@ -1728,7 +1728,7 @@ public void groupBy_1Mapping1Collector_groupingOnPrimitives() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getIntegerProperty, count()) .penalize(SimpleScore.ONE, (integerProperty, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1764,7 +1764,7 @@ public void groupBy_2Mapping0Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1821,7 +1821,7 @@ public void groupBy_2Mapping1Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, count()) .penalize(SimpleScore.ONE, (entityGroup, value, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1858,7 +1858,7 @@ public void groupBy_2Mapping2Collector() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .groupBy(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, count(), count()) .penalize(SimpleScore.ONE, (entityGroup, value, count, sameCount) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -1886,7 +1886,7 @@ public void groupBy_3Mapping0Collector() { .groupBy(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, TestdataLavishEntity::getCode) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1929,7 +1929,7 @@ public void groupBy_3Mapping1Collector() { .groupBy(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, TestdataLavishEntity::getCode, ConstraintCollectors.toSet()) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -1972,7 +1972,7 @@ public void groupBy_4Mapping0Collector() { .groupBy(Function.identity(), TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, TestdataLavishEntity::getCode) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -2013,7 +2013,7 @@ public void distinct() { // On a distinct stream, this is a no-op. buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); @@ -2033,7 +2033,7 @@ public void mapToUniWithDuplicates() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .map(TestdataLavishEntity::getEntityGroup) // Two entities, just one group => duplicates. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group = solution.getFirstEntityGroup(); @@ -2061,7 +2061,7 @@ public void mapToUniWithoutDuplicates() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .map(TestdataLavishEntity::getEntityGroup) // Two entities, two groups => no duplicates. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -2091,7 +2091,7 @@ public void mapToUniAndDistinctWithDuplicates() { .map(TestdataLavishEntity::getEntityGroup) // Two entities, just one group => duplicates. .distinct() // Duplicate copies removed here. .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group = solution.getFirstEntityGroup(); @@ -2119,7 +2119,7 @@ public void mapToUniAndDistinctWithoutDuplicates() { .map(TestdataLavishEntity::getEntityGroup) // Two entities, two groups => no duplicates. .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -2149,7 +2149,7 @@ public void mapToBi() { .map(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstEntity().getValue(); @@ -2182,7 +2182,7 @@ public void mapToTri() { TestdataLavishEntity::getValue, TestdataLavishEntity::getCode) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstEntity().getValue(); @@ -2218,7 +2218,7 @@ public void mapToQuad() { TestdataLavishEntity::getCode, TestdataLavishEntity::getLongProperty) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstEntity().getValue(); @@ -2253,7 +2253,7 @@ public void expandToBi() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .expand(TestdataLavishEntity::getEntityGroup) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -2282,7 +2282,7 @@ public void expandToTri() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .expand(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstEntity().getValue(); @@ -2314,7 +2314,7 @@ public void expandToQuad() { .expand(TestdataLavishEntity::getEntityGroup, TestdataLavishEntity::getValue, TestdataLavishEntity::getCode) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var value1 = solution.getFirstEntity().getValue(); @@ -2356,7 +2356,7 @@ public void flattenLastWithDuplicates() { : Arrays.asList(group1, group2)) .filter(flatten -> flatten == group1 || flatten == group2) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch // group1 is used instead of group1Dup because it equals to it @@ -2397,7 +2397,7 @@ public void flattenLastWithoutDuplicates() { buildScoreDirector(factory -> factory.forEach(TestdataLavishEntity.class) .flattenLast(entity -> Collections.singletonList(entity.getEntityGroup())) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -2432,7 +2432,7 @@ public void flatten() { .flatten(entity -> Arrays.asList(group1, group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2463,7 +2463,7 @@ public void flattenLastAndDistinctWithDuplicates() { .flattenLast(entity -> Arrays.asList(group1, group1, group2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2491,7 +2491,7 @@ public void flattenLastAndDistinctWithoutDuplicates() { .flattenLast(entity -> Collections.singletonList(entity.getEntityGroup())) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); @@ -2535,7 +2535,7 @@ public void concatUniWithoutValueDuplicates() { .concat(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2579,7 +2579,7 @@ public void concatUniWithValueDuplicates() { .concat(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1)) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2624,7 +2624,7 @@ public void concatAndDistinctUniWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2669,7 +2669,7 @@ public void concatAndDistinctUniWithValueDuplicates() { .filter(entity -> entity.getValue() == value1)) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2713,7 +2713,7 @@ public void concatBiWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value3))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2760,7 +2760,7 @@ public void concatAndDistinctBiWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value3))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2808,7 +2808,7 @@ public void concatTriWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value1))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2857,7 +2857,7 @@ public void concatAndDistinctTriWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value1))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2907,7 +2907,7 @@ public void concatQuadWithoutValueDuplicates() { .join(factory.forEach(TestdataLavishEntity.class) .filter(entity -> entity.getValue() == value2))) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -2958,7 +2958,7 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { .filter(entity -> entity.getValue() == value2))) .distinct() .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -3004,7 +3004,7 @@ public void concatAfterGroupBy() { .filter(entity -> entity.getValue() == value2) .groupBy(TestdataLavishEntity::getValue, ConstraintCollectors.count())) .penalize(SimpleScore.ONE, (value, count) -> count) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -3052,7 +3052,7 @@ public void complement() { .filter(entity -> entity.getValue() == value1) .complement(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); // From scratch scoreDirector.setWorkingSolution(solution); @@ -3079,7 +3079,7 @@ public void penalizeUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-7)); @@ -3095,7 +3095,7 @@ public void penalizeUnweightedBigDecimal() { buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), factory -> new Constraint[] { factory.forEach(TestdataEntity.class) .penalizeBigDecimal(SimpleBigDecimalScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME) }); + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-7))); @@ -3135,7 +3135,7 @@ public void penalize() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .penalize(SimpleScore.ONE, entity -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-14)); @@ -3151,7 +3151,7 @@ public void penalizeBigDecimal() { buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), factory -> new Constraint[] { factory.forEach(TestdataEntity.class) .penalizeBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) }); + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-14))); @@ -3166,7 +3166,7 @@ public void rewardUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .reward(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(7)); @@ -3181,7 +3181,7 @@ public void reward() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .reward(SimpleScore.ONE, entity -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(14)); @@ -3198,7 +3198,7 @@ public void rewardBigDecimal() { factory -> new Constraint[] { factory.forEach(TestdataEntity.class) .rewardBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3214,7 +3214,7 @@ public void impactPositiveUnweighted() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(7)); @@ -3229,7 +3229,7 @@ public void impactPositive() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE, entity -> 2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(14)); @@ -3247,7 +3247,7 @@ public void impactPositiveBigDecimal() { factory.forEach(TestdataEntity.class) .impactBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3263,7 +3263,7 @@ public void impactNegative() { var scoreDirector = buildScoreDirector( factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE, entity -> -2) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-14)); @@ -3281,7 +3281,7 @@ public void impactNegativeBigDecimal() { factory.forEach(TestdataEntity.class) .impactBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(-2)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3298,7 +3298,7 @@ public void penalizeUnweightedCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .penalize(SimpleScore.ONE) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-7)); @@ -3340,7 +3340,7 @@ public void penalizeCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .penalize(SimpleScore.ONE, entity -> 2) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-14)); @@ -3357,7 +3357,7 @@ public void penalizeBigDecimalCustomJustifications() { factory -> new Constraint[] { factory.forEach(TestdataEntity.class) .penalizeBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME) }); + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-14))); @@ -3373,7 +3373,7 @@ public void rewardUnweightedCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .reward(SimpleScore.ONE) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(7)); @@ -3389,7 +3389,7 @@ public void rewardCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .reward(SimpleScore.ONE, entity -> 2) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(14)); @@ -3407,7 +3407,7 @@ public void rewardBigDecimalCustomJustifications() { factory.forEach(TestdataEntity.class) .rewardBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3424,7 +3424,7 @@ public void impactPositiveUnweightedCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(7)); @@ -3440,7 +3440,7 @@ public void impactPositiveCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE, entity -> 2) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(14)); @@ -3459,7 +3459,7 @@ public void impactPositiveBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(2)) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3476,7 +3476,7 @@ public void impactNegativeCustomJustifications() { factory -> factory.forEach(TestdataLavishEntity.class) .impact(SimpleScore.ONE, entity -> -2) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME)); + .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); assertThat(scoreDirector.calculateScore().raw()).isEqualTo(SimpleScore.of(-14)); @@ -3495,7 +3495,7 @@ public void impactNegativeBigDecimalCustomJustifications() { .impactBigDecimal(SimpleBigDecimalScore.ONE, entity -> BigDecimal.valueOf(-2)) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME) + .asConstraint(TEST_CONSTRAINT_ID) }); scoreDirector.setWorkingSolution(solution); @@ -3511,7 +3511,7 @@ public void failWithMultipleJustifications() { .penalize(SimpleScore.ONE, entity -> 2) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) .justifyWith((a, score) -> new TestConstraintJustification<>(score, a)) - .asConstraint(TEST_CONSTRAINT_NAME))) + .asConstraint(TEST_CONSTRAINT_ID))) .hasMessageContaining("Maybe the constraint calls justifyWith() twice?"); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/UniConstraintBuilderTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/UniConstraintBuilderTest.java index b6f66fbda88..ee5576e24ed 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/UniConstraintBuilderTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/UniConstraintBuilderTest.java @@ -1,7 +1,6 @@ package ai.timefold.solver.core.impl.score.stream.common.uni; import ai.timefold.solver.core.api.score.SimpleScore; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.impl.score.stream.AbstractConstraintBuilderTest; import ai.timefold.solver.core.impl.score.stream.bavet.BavetConstraint; @@ -16,12 +15,10 @@ class UniConstraintBuilderTest extends AbstractConstraintBuilderTest { new BavetConstraintFactory<>(TestdataSolution.buildSolutionDescriptor(), EnvironmentMode.FULL_ASSERT); @Override - protected AbstractConstraintBuilder of(String constraintName, String constraintGroup) { + protected AbstractConstraintBuilder of(String constraintId) { return new UniConstraintBuilderImpl<>( - (constraintName1, constraintDescription, constraintGroup1, constraintWeight, impactType, - objectSimpleScoreObjectBiFunction) -> new BavetConstraint<>(CONSTRAINT_FACTORY, - ConstraintRef.of(constraintName1), constraintDescription, constraintGroup1, constraintWeight, - impactType, objectSimpleScoreObjectBiFunction, null), + (constraintMetadata, constraintWeight, impactType, justificationMapping) -> new BavetConstraint<>( + CONSTRAINT_FACTORY, constraintMetadata, constraintWeight, impactType, justificationMapping, null), ScoreImpactType.PENALTY, SimpleScore.ONE); } } diff --git a/core/src/test/java/ai/timefold/solver/core/testconstraint/TestConstraint.java b/core/src/test/java/ai/timefold/solver/core/testconstraint/TestConstraint.java index 5ef6003ea38..b990e90c909 100644 --- a/core/src/test/java/ai/timefold/solver/core/testconstraint/TestConstraint.java +++ b/core/src/test/java/ai/timefold/solver/core/testconstraint/TestConstraint.java @@ -1,22 +1,19 @@ package ai.timefold.solver.core.testconstraint; import ai.timefold.solver.core.api.score.Score; -import ai.timefold.solver.core.api.score.stream.Constraint; -import ai.timefold.solver.core.api.score.stream.ConstraintRef; import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraint; +import ai.timefold.solver.core.impl.score.stream.common.DefaultConstraintMetadata; import ai.timefold.solver.core.impl.score.stream.common.ScoreImpactType; +import org.jspecify.annotations.NullMarked; + +@NullMarked public final class TestConstraint> extends AbstractConstraint, TestConstraintFactory> { - public TestConstraint(TestConstraintFactory constraintFactory, String constraintName, + public TestConstraint(TestConstraintFactory constraintFactory, String constraintId, Score_ constraintWeight) { - this(constraintFactory, constraintName, Constraint.DEFAULT_CONSTRAINT_GROUP, constraintWeight); - } - - public TestConstraint(TestConstraintFactory constraintFactory, String constraintName, - String constraintGroup, Score_ constraintWeight) { - super(constraintFactory, ConstraintRef.of(constraintName), "", constraintGroup, constraintWeight, + super(constraintFactory, new DefaultConstraintMetadata(constraintId), constraintWeight, ScoreImpactType.REWARD, null); } diff --git a/docs/src/modules/ROOT/pages/constraints-and-score/constraint-configuration.adoc b/docs/src/modules/ROOT/pages/constraints-and-score/constraint-configuration.adoc index 91c2359a8d1..47e1a0f7e82 100644 --- a/docs/src/modules/ROOT/pages/constraints-and-score/constraint-configuration.adoc +++ b/docs/src/modules/ROOT/pages/constraints-and-score/constraint-configuration.adoc @@ -135,6 +135,9 @@ as opposed to its original `1hard`. The `Service finished after max end time` constraint has a weight of `0hard`, and therefore will be disabled entirely. +NOTE: The string keys passed to `ConstraintWeightOverrides.of(...)` must match the constraint ID, +which is the value given to `asConstraint(...)` when building your constraints. + In this way, you can solve the same problem by applying different constraint weights to each instance. Once solved, you can compare the results diff --git a/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc b/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc index 055dafd0675..c372c5a1e1c 100644 --- a/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc +++ b/docs/src/modules/ROOT/pages/constraints-and-score/score-calculation.adoc @@ -314,23 +314,7 @@ The purpose of constraint streams is to build up a xref:constraints-and-score/ov To do this, every constraint stream must contain a call to either a `penalize()` or a `reward()` building block. The `penalize()` building block makes the score worse and the `reward()` building block improves the score. - Each constraint stream is then terminated by calling `asConstraint()` method, which finally builds the constraint. -Constraints have several components: - -- Constraint name is the human-readable descriptive name for the constraint, -which must be unique within the entire `ConstraintProvider` implementation. -- Constraint weight is a constant score value indicating how much every breach of the constraint affects the score. -Valid examples include `SimpleScore.ONE`, `HardSoftScore.ONE_HARD` and `HardMediumSoftScore.of(1, 2, 3)`. -- Constraint match weigher is an optional function indicating how many times the constraint weight should be applied in -the score. -The penalty or reward score impact is the constraint weight multiplied by the match weight. -The default value is `1`. - -[NOTE] -==== -Constraints with zero constraint weight are automatically disabled and do not impose any performance penalty. -==== The Constraint Streams API supports many different types of penalties. Browse the API in your IDE for the full list of method overloads. @@ -341,16 +325,53 @@ constraint stream. The score type must be the same type as used on the `@PlanningScore` annotated member on the planning solution. - Dynamic penalty (`penalize(SimpleScore.ONE, Shift::getHours)`) makes the score worse by the number of hours in every matching `Shift` in the constraint stream. -This is an example of using a constraint match weigher. -- Configurable penalty (`penalizeConfigurable()`) makes the score worse using constraint weights -defined in xref:constraints-and-score/constraint-configuration.adoc#constraintConfiguration[constraint configuration]. -- Configurable dynamic penalty(`penalizeConfigurable(Shift::getHours)`) makes the score worse using -constraint weights defined in xref:constraints-and-score/constraint-configuration.adoc#constraintConfiguration[constraint configuration], multiplied by the number of hours in -every matching `Shift` in the constraint stream. +This is an example of using a constraint match weight. By replacing the keyword `penalize` by `reward` in the name of these building blocks, you get operations that affect score in the opposite direction. +[#constraintAnatomy] +==== Anatomy of a constraint + +Constraints have several components: + +- Constraint ID is a short(ish) name for the constraint, +which must be unique within the entire `ConstraintProvider` implementation. +It is validated to not include most special characters. +- Constraint weight is a constant score value indicating how much every breach of the constraint affects the score. +Valid examples include `SimpleScore.ONE`, `HardSoftScore.ONE_HARD` and `HardMediumSoftScore.of(1, 2, 3)`. +The penalty or reward score impact is the constraint weight multiplied by the match weight. +The default value is `1`. + +NOTE: Constraints with zero weight are automatically disabled and do not impose any performance penalty. + +[#constraintMetadata] +===== Attaching metadata to constraints + +`asConstraint(String id)` is a shorthand that creates a simple `ConstraintMetadata` carrying only the constraint ID. +For richer metadata, implement the `ConstraintMetadata` interface and pass it to `asConstraint(ConstraintMetadata)`: + +[tabs] +==== +Java:: ++ +[source,java,options="nowrap"] +---- + public record MyConstraintMetadata(String id, String displayName) implements ConstraintMetadata { + } + + private Constraint penalizeEveryShift(ConstraintFactory factory) { + return factory.forEach(Shift.class) + .penalize(HardSoftScore.ONE_SOFT) + .asConstraint(new MyConstraintMetadata("penalizeEveryShift", "Penalize every shift")); + } +---- +==== + +The ID is snapshotted at build time via `ConstraintMetadata#id()`; +any subsequent mutation of the ID does not affect the constraint's identity. +The full metadata are accessible at runtime via `Constraint#getConstraintMetadata()`. + [#constraintStreamsCustomizingJustifications] [#constraintStreamsCustomizingJustificationsAndIndictments] diff --git a/docs/src/modules/ROOT/pages/upgrading-timefold-solver/upgrade-from-v1.adoc b/docs/src/modules/ROOT/pages/upgrading-timefold-solver/upgrade-from-v1.adoc index f9df8ea24fd..d06b1c8a565 100644 --- a/docs/src/modules/ROOT/pages/upgrading-timefold-solver/upgrade-from-v1.adoc +++ b/docs/src/modules/ROOT/pages/upgrading-timefold-solver/upgrade-from-v1.adoc @@ -86,10 +86,9 @@ Applies to all built-in score types and `IBendableScore`. * `ConstraintFactory.getDefaultConstraintPackage()` removed. * `Constraint.getConstraintPackage()` removed. -* `ConstraintRef.of(packageName, constraintName)` → `ConstraintRef.of(constraintName)` (package argument removed). -* `Constraint.getConstraintId()` replaced by `getConstraintRef().constraintId()`. -* `Constraint.getConstraintName()` replaced by `getConstraintRef().constraintName()`. -* Same renames apply to `ConstraintMatch` and `ConstraintMatchTotal`. +* `ConstraintRef.of(packageName, constraintName)` → `ConstraintRef.of(constraintId)` (package argument removed). +* `Constraint.getConstraintId()` replaced by `getConstraintRef().id()`. +* `Constraint.getConstraintName()` removed. ==== Constraint Stream terminal methods refactored @@ -97,12 +96,15 @@ Applies to all built-in score types and `IBendableScore`. (and their `Configurable`/`Long`/`BigDecimal` variants, across Uni/Bi/Tri/Quad streams) are replaced by a two-call chain: `penalize(score).asConstraint(constraintName)`, moving the constraint name out of the scoring method and into a dedicated -`asConstraint()` call. +`asConstraint()`. * Where the old form passed both a package and a name (`penalize(pkg, name, score)`), the package argument is no longer available for `asConstraint()`: `penalize(score).asConstraint(name)`. * `ConstraintBuilder.asConstraint(packageName, constraintName)` → `asConstraint(constraintName)` (two-arg form collapsed to one). * `penalizeLong(..)`, `rewardLong(..)`, `impactLong(..)` on all stream types (Uni/Bi/Tri/Quad) renamed to `penalize(..)`, `reward(..)`, `impact(..)`. +* "Constraint name" was renamed to "Constraint ID". +* "Constraint description" and "Constraint group" were removed, +replaced by a more flexible `ConstraintMetadata`. ==== `SolverManager` API: Builder pattern replaces overloaded `solve()` methods @@ -407,12 +409,12 @@ to allow the solver to access them via reflection. ''' -.icon:info-circle[role=yellow] Constraint name now strictly validated +.icon:info-circle[role=yellow] Constraint ID now strictly validated [%collapsible%open] ==== -Constraint names are now validated more strictly. -Constraint names must be non-null, non-empty, and must not contain slashes and other special characters. -If your constraint names do not comply, an exception will be thrown at solver initialization. +Constraint IDs are now validated more strictly. +They must be non-null, non-empty, and must not contain slashes and other special characters. +If your constraint IDs do not comply, an exception will be thrown at solver initialization. ==== ''' @@ -942,6 +944,23 @@ most notably from `ConstraintAnalysis` and `Constraint`. ''' +.icon:eye[] `ConstraintMetadata` replaces string-based constraint description and group APIs +[%collapsible%closed] +==== +The old `asConstraintDescribed(String name, String description)` and `asConstraintDescribed(String name, String group, String description)` methods have been removed. +Use `asConstraint(String id)` (description dropped) or implement the `ConstraintMetadata` interface and call `asConstraint(ConstraintMetadata)` to carry additional metadata. + +The following methods have also been renamed or removed: + +* `ConstraintRef.constraintName()` → `ConstraintRef.id()` +* `ConstraintAnalysis.constraintName()` → `ConstraintAnalysis.constraintId()` +* `Constraint.getConstraintGroup()` removed — constraint groups are no longer supported. +* `Constraint.getDescription()` returning `String` removed — use `Constraint.getConstraintMetadata()` returning `ConstraintMetadata` instead. +* `ConstraintMetaModel.getConstraintsPerGroup(...)` and `ConstraintMetaModel.getConstraintGroups()` removed. +==== + +''' + .icon:eye[] Deprecated `ConstraintCollectors` methods removed [%collapsible%closed] ==== diff --git a/docs/src/modules/ROOT/pages/using-timefold-solver/running-the-solver.adoc b/docs/src/modules/ROOT/pages/using-timefold-solver/running-the-solver.adoc index 22d12f0188b..80d2f0738f4 100644 --- a/docs/src/modules/ROOT/pages/using-timefold-solver/running-the-solver.adoc +++ b/docs/src/modules/ROOT/pages/using-timefold-solver/running-the-solver.adoc @@ -890,13 +890,13 @@ This does not measure the amount of memory used by a solver; two solvers on the Measures the score impact of each constraint on the best solution Timefold Solver found so far. There are separate meters for each level of the score, with tags for each constraint. For instance, for a `HardSoftScore` for a constraint "Minimize Cost", -there are `timefold.solver.constraint.match.best.score.hard.score` and `timefold.solver.constraint.match.best.score.soft.score` meters with a tag "constraint.name=Minimize Cost". +there are `timefold.solver.constraint.match.best.score.hard.score` and `timefold.solver.constraint.match.best.score.soft.score` meters with a tag "constraint.id=Minimize Cost". - `CONSTRAINT_MATCH_TOTAL_STEP_SCORE` (Micrometer meter id: "timefold.solver.constraint.match.step.score.*"): Measures the score impact of each constraint on the current step. There are separate meters for each level of the score, with tags for each constraint. For instance, for a `HardSoftScore` for a constraint "Minimize Cost", -there are `timefold.solver.constraint.match.step.score.hard.score` and `timefold.solver.constraint.match.step.score.soft.score` meters with a tag "constraint.name=Minimize Cost". +there are `timefold.solver.constraint.match.step.score.hard.score` and `timefold.solver.constraint.match.step.score.soft.score` meters with a tag "constraint.id=Minimize Cost". - `PICKED_MOVE_TYPE_BEST_SCORE_DIFF` (Micrometer meter id: "timefold.solver.move.type.best.score.diff.*"): Measures how much a particular move type improves the best solution. diff --git a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/AbstractConstraintWeightOverridesDeserializer.java b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/AbstractConstraintWeightOverridesDeserializer.java index 391627bf447..89dd83e6f38 100644 --- a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/AbstractConstraintWeightOverridesDeserializer.java +++ b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/AbstractConstraintWeightOverridesDeserializer.java @@ -25,9 +25,9 @@ public final ConstraintWeightOverrides deserialize(JsonParser p, Deseria var resultMap = new LinkedHashMap(); JsonNode node = p.readValueAsTree(); node.properties().iterator().forEachRemaining(entry -> { - var constraintName = entry.getKey(); + var constraintId = entry.getKey(); var weight = parseScore(entry.getValue().asString()); - resultMap.put(constraintName, weight); + resultMap.put(constraintId, weight); }); return ConstraintWeightOverrides.of(resultMap); } diff --git a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/ConstraintWeightOverridesSerializer.java b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/ConstraintWeightOverridesSerializer.java index 0fbf64b2b86..b40c9a434c7 100644 --- a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/ConstraintWeightOverridesSerializer.java +++ b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/domain/solution/ConstraintWeightOverridesSerializer.java @@ -17,9 +17,9 @@ public final class ConstraintWeightOverridesSerializer constraintWeightOverrides, JsonGenerator generator, SerializationContext serializerProvider) throws JacksonException { generator.writeStartObject(); - for (var constraintName : constraintWeightOverrides.getKnownConstraintNames()) { - var weight = Objects.requireNonNull(constraintWeightOverrides.getConstraintWeight(constraintName)); - generator.writeStringProperty(constraintName, weight.toString()); + for (var constraintId : constraintWeightOverrides.getKnownConstraintIds()) { + var weight = Objects.requireNonNull(constraintWeightOverrides.getConstraintWeight(constraintId)); + generator.writeStringProperty(constraintId, weight.toString()); } generator.writeEndObject(); } diff --git a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/score/constraint/ConstraintRefJacksonSerializer.java b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/score/constraint/ConstraintRefJacksonSerializer.java index 2cbe108837f..0a5fec6f572 100644 --- a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/score/constraint/ConstraintRefJacksonSerializer.java +++ b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/api/score/constraint/ConstraintRefJacksonSerializer.java @@ -12,6 +12,6 @@ public final class ConstraintRefJacksonSerializer extends ValueSerializer deserialize(JsonParser p, Deseria var resultMap = new LinkedHashMap(); JsonNode node = p.readValueAsTree(); node.properties().forEach(entry -> { - var constraintName = entry.getKey(); + var constraintId = entry.getKey(); var weight = parseScore(entry.getValue().asText()); - resultMap.put(constraintName, weight); + resultMap.put(constraintId, weight); }); return ConstraintWeightOverrides.of(resultMap); } diff --git a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/domain/solution/ConstraintWeightOverridesSerializer.java b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/domain/solution/ConstraintWeightOverridesSerializer.java index 81ce5400f66..313007c709b 100644 --- a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/domain/solution/ConstraintWeightOverridesSerializer.java +++ b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/domain/solution/ConstraintWeightOverridesSerializer.java @@ -17,9 +17,9 @@ public final class ConstraintWeightOverridesSerializer constraintWeightOverrides, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException { generator.writeStartObject(); - for (var constraintName : constraintWeightOverrides.getKnownConstraintNames()) { - var weight = Objects.requireNonNull(constraintWeightOverrides.getConstraintWeight(constraintName)); - generator.writeStringField(constraintName, weight.toString()); + for (var constraintId : constraintWeightOverrides.getKnownConstraintIds()) { + var weight = Objects.requireNonNull(constraintWeightOverrides.getConstraintWeight(constraintId)); + generator.writeStringField(constraintId, weight.toString()); } generator.writeEndObject(); } diff --git a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/score/constraint/ConstraintRefJacksonSerializer.java b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/score/constraint/ConstraintRefJacksonSerializer.java index 2771e8fa527..41e2263ac6b 100644 --- a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/score/constraint/ConstraintRefJacksonSerializer.java +++ b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/score/constraint/ConstraintRefJacksonSerializer.java @@ -13,6 +13,6 @@ public final class ConstraintRefJacksonSerializer extends JsonSerializer out.put(key, JsonArray.of(value.getConstraintList() .stream() - .map(ConstraintRef::constraintName) + .map(ConstraintRef::id) .toArray()))); return out; } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/StatisticRegistry.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/StatisticRegistry.java index 650c19a20a6..f3fd163180a 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/StatisticRegistry.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/StatisticRegistry.java @@ -30,7 +30,7 @@ public class StatisticRegistry extends SimpleMeterRegistry implements PhaseLifecycleListener { - private static final String CONSTRAINT_NAME_TAG = "constraint.name"; + private static final String CONSTRAINT_ID_TAG = "constraint.id"; List>> solverMeterListenerList = new ArrayList<>(); List>> stepMeterListenerList = new ArrayList<>(); @@ -108,13 +108,13 @@ public void extractConstraintSummariesFromMeters(SolverMetric metric, Tags runId // Add the constraint ids from the meter ids getMeterIds(metric, runId) .stream() - .map(meterId -> ConstraintRef.of(meterId.getTag(CONSTRAINT_NAME_TAG))) + .map(meterId -> ConstraintRef.of(meterId.getTag(CONSTRAINT_ID_TAG))) .distinct() .forEach(constraintRef -> { - var constraintMatchTotalRunId = runId.and(CONSTRAINT_NAME_TAG, constraintRef.constraintName()); - // Get the score from the corresponding constraint name meters + var constraintMatchTotalRunId = runId.and(CONSTRAINT_ID_TAG, constraintRef.id()); + // Get the score from the corresponding constraint ID meters extractScoreFromMeters(metric, constraintMatchTotalRunId, - // Get the count gauge (add constraint name to the run tags) + // Get the count gauge (add constraint ID to the run tags) score -> { var count = SolverMetricUtil.getGaugeValue(this, SolverMetricUtil.getGaugeName(metric, "count"), constraintMatchTotalRunId); diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreStatisticPoint.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreStatisticPoint.java index 3d0a6228f0a..90111eea7f8 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreStatisticPoint.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreStatisticPoint.java @@ -37,7 +37,7 @@ public Score getScoreTotal() { @Override public String toCsvLine() { - return buildCsvLineWithStrings(timeMillisSpent, constraintRef.constraintName(), Integer.toString(constraintMatchCount), + return buildCsvLineWithStrings(timeMillisSpent, constraintRef.id(), Integer.toString(constraintMatchCount), scoreTotal.toString()); } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatistic.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatistic.java index 5936ef06146..eb926a38c44 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatistic.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatistic.java @@ -62,7 +62,7 @@ protected List> generateCharts(BenchmarkReport benchmark builderList.add(new LineChart.Builder<>()); } LineChart.Builder builder = builderList.get(i); - String seriesLabel = point.getConstraintRef().constraintName() + " weight"; + String seriesLabel = point.getConstraintRef().id() + " weight"; // Only add changes double lastValue = (builder.count(seriesLabel) == 0) ? 0.0 : builder.getLastValue(seriesLabel); if (levelValues[i] != lastValue) { diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreStatisticPoint.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreStatisticPoint.java index 78cf8cf229f..e2f5db8bbb0 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreStatisticPoint.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreStatisticPoint.java @@ -37,7 +37,7 @@ public Score getScoreTotal() { @Override public String toCsvLine() { - return buildCsvLineWithStrings(timeMillisSpent, constraintRef.constraintName(), Integer.toString(constraintMatchCount), + return buildCsvLineWithStrings(timeMillisSpent, constraintRef.id(), Integer.toString(constraintMatchCount), scoreTotal.toString()); } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatistic.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatistic.java index 9afbed266d9..9b2ed979bea 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatistic.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatistic.java @@ -62,7 +62,7 @@ protected List> generateCharts(BenchmarkReport benchmark builderList.add(new LineChart.Builder<>()); } LineChart.Builder builder = builderList.get(i); - String seriesLabel = point.getConstraintRef().constraintName() + " weight"; + String seriesLabel = point.getConstraintRef().id() + " weight"; // Only add changes double lastValue = (builder.count(seriesLabel) == 0) ? 0.0 : builder.getLastValue(seriesLabel); if (levelValues[i] != lastValue) { diff --git a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatisticTest.java b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatisticTest.java index b32e71489e4..dc2580d5ae5 100644 --- a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatisticTest.java +++ b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalbestscore/ConstraintMatchTotalBestScoreSubSingleStatisticTest.java @@ -36,7 +36,7 @@ protected void runTest(SoftAssertions assertions, List Objects.equals(s.getConstraintRef().constraintName(), "CP"), "Constraint names do not match.") + .matches(s -> Objects.equals(s.getConstraintRef().id(), "CP"), "Constraint IDs do not match.") .matches(s -> s.getConstraintMatchCount() == Integer.MAX_VALUE, "Constraint match counts do not match.") .matches(s -> s.getScoreTotal().equals(SimpleScore.of(Integer.MAX_VALUE)), "Scores do not match.") .matches(s -> s.getTimeMillisSpent() == Long.MAX_VALUE, "Millis do not match."); diff --git a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatisticTest.java b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatisticTest.java index 6aea13a81a9..25185eda9b8 100644 --- a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatisticTest.java +++ b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/impl/statistic/subsingle/constraintmatchtotalstepscore/ConstraintMatchTotalStepScoreSubSingleStatisticTest.java @@ -36,7 +36,7 @@ protected void runTest(SoftAssertions assertions, List Objects.equals(s.getConstraintRef().constraintName(), "CP"), "Constraint names do not match.") + .matches(s -> Objects.equals(s.getConstraintRef().id(), "CP"), "Constraint IDs do not match.") .matches(s -> s.getConstraintMatchCount() == Integer.MAX_VALUE, "Constraint match counts do not match.") .matches(s -> s.getScoreTotal().equals(SimpleScore.of(Integer.MAX_VALUE)), "Scores do not match.") .matches(s -> s.getTimeMillisSpent() == Long.MAX_VALUE, "Millis do not match."); diff --git a/tools/migration/src/main/java/ai/timefold/solver/migration/ToLatestRecipe.java b/tools/migration/src/main/java/ai/timefold/solver/migration/ToLatestRecipe.java index 3ad94ae81ed..aa2db210f72 100644 --- a/tools/migration/src/main/java/ai/timefold/solver/migration/ToLatestRecipe.java +++ b/tools/migration/src/main/java/ai/timefold/solver/migration/ToLatestRecipe.java @@ -3,6 +3,7 @@ import java.util.List; import ai.timefold.solver.migration.v2.ConstraintArgRemovalMigrationRecipe; +import ai.timefold.solver.migration.v2.ConstraintMetadataMigrationRecipe; import ai.timefold.solver.migration.v2.GeneralDependencyDeleteMigrationRecipe; import ai.timefold.solver.migration.v2.GeneralMethodChangeNameMigrationRecipe; import ai.timefold.solver.migration.v2.GeneralMethodDeleteInvocationMigrationRecipe; @@ -39,6 +40,7 @@ public List getRecipeList() { new ToLatestV1Recipe(), new ChangeVersionRecipe(), new ConstraintArgRemovalMigrationRecipe(), + new ConstraintMetadataMigrationRecipe(), new PlanningSolutionAnnotationCleanupMigrationRecipe(), new GeneralMethodDeleteInvocationMigrationRecipe(), new GeneralMethodChangeNameMigrationRecipe(), diff --git a/tools/migration/src/main/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipe.java b/tools/migration/src/main/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipe.java new file mode 100644 index 00000000000..4b2b8a4f95d --- /dev/null +++ b/tools/migration/src/main/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipe.java @@ -0,0 +1,67 @@ +package ai.timefold.solver.migration.v2; + +import java.util.List; + +import ai.timefold.solver.migration.AbstractRecipe; + +import org.openrewrite.Recipe; +import org.openrewrite.java.ChangeMethodName; +import org.openrewrite.java.DeleteMethodArgument; +import org.openrewrite.java.RemoveMethodInvocations; + +public final class ConstraintMetadataMigrationRecipe extends AbstractRecipe { + + @Override + public String getDisplayName() { + return "Migrate constraint description and group APIs"; + } + + @Override + public String getDescription() { + return "Renames asConstraintDescribed to asConstraint, updates ConstraintRef.constraintName() to id(), " + + "ConstraintAnalysis.constraintName() to constraintId(), and removes deleted group/description methods."; + } + + @Override + public List getRecipeList() { + return List.of( + // asConstraintDescribed(String name, String description) -> asConstraint(String name), drop arg 1 + new ChangeMethodName( + "ai.timefold.solver.core.api.score.stream.ConstraintBuilder asConstraintDescribed(String, String)", + "asConstraint", true, false), + new DeleteMethodArgument( + "ai.timefold.solver.core.api.score.stream.ConstraintBuilder asConstraint(String, String)", + 1), + // asConstraintDescribed(String name, String group, String description) -> asConstraint(String name), drop args 1 and 2 + new ChangeMethodName( + "ai.timefold.solver.core.api.score.stream.ConstraintBuilder asConstraintDescribed(String, String, String)", + "asConstraint", true, false), + new DeleteMethodArgument( + "ai.timefold.solver.core.api.score.stream.ConstraintBuilder asConstraint(String, String, String)", + 2), + new DeleteMethodArgument( + "ai.timefold.solver.core.api.score.stream.ConstraintBuilder asConstraint(String, String)", + 1), + // ConstraintRef.constraintName() -> id() + new ChangeMethodName( + "ai.timefold.solver.core.api.score.stream.ConstraintRef constraintName()", + "id", true, false), + // ConstraintAnalysis.constraintName() -> constraintId() + new ChangeMethodName( + "ai.timefold.solver.core.api.score.analysis.ConstraintAnalysis constraintName()", + "constraintId", true, false), + // Constraint.getConstraintGroup() removed + new RemoveMethodInvocations( + "ai.timefold.solver.core.api.score.stream.Constraint getConstraintGroup()"), + // Constraint.getDescription() returning String removed + new RemoveMethodInvocations( + "ai.timefold.solver.core.api.score.stream.Constraint getDescription()"), + // ConstraintMetaModel.getConstraintsPerGroup(..) removed + new RemoveMethodInvocations( + "ai.timefold.solver.core.api.score.stream.ConstraintMetaModel getConstraintsPerGroup(..)"), + // ConstraintMetaModel.getConstraintGroups() removed + new RemoveMethodInvocations( + "ai.timefold.solver.core.api.score.stream.ConstraintMetaModel getConstraintGroups()")); + } + +} diff --git a/tools/migration/src/main/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipe.java b/tools/migration/src/main/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipe.java index c887826dd53..cc5fc6e38b0 100644 --- a/tools/migration/src/main/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipe.java +++ b/tools/migration/src/main/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipe.java @@ -29,7 +29,7 @@ public List getRecipeList() { // Constraint new CustomChangeMethodRecipe("ai.timefold.solver.core.api.score.stream.Constraint", "getConstraintName()", - ".getConstraintRef().constraintName()"), + ".getConstraintRef().id()"), // Constraint new ChangeMethodName("ai.timefold.solver.core.api.solver.Solver isEveryProblemFactChangeProcessed()", "isEveryProblemChangeProcessed", true, false), diff --git a/tools/migration/src/test/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipeTest.java b/tools/migration/src/test/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipeTest.java new file mode 100644 index 00000000000..f454147eb95 --- /dev/null +++ b/tools/migration/src/test/java/ai/timefold/solver/migration/v2/ConstraintMetadataMigrationRecipeTest.java @@ -0,0 +1,122 @@ +package ai.timefold.solver.migration.v2; + +import static org.openrewrite.java.Assertions.java; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +@Execution(ExecutionMode.CONCURRENT) +class ConstraintMetadataMigrationRecipeTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipes(new ConstraintMetadataMigrationRecipe()) + .parser(JavaParser.fromJavaVersion() + .dependsOn( + """ + package ai.timefold.solver.core.api.score.stream; + public interface ConstraintBuilder { + Object asConstraintDescribed(String name, String description); + Object asConstraintDescribed(String name, String group, String description); + }""", + """ + package ai.timefold.solver.core.api.score.stream; + public interface ConstraintRef { + String constraintName(); + }""", + """ + package ai.timefold.solver.core.api.score.analysis; + public interface ConstraintAnalysis { + String constraintName(); + }""")); + } + + @Test + void asConstraintDescribedTwoArgs() { + rewriteRun(java( + """ + import ai.timefold.solver.core.api.score.stream.ConstraintBuilder; + + class Test { + void method(ConstraintBuilder builder) { + builder.asConstraintDescribed("myConstraint", "Some description."); + } + }""", + """ + import ai.timefold.solver.core.api.score.stream.ConstraintBuilder; + + class Test { + void method(ConstraintBuilder builder) { + builder.asConstraint("myConstraint"); + } + }""")); + } + + @Test + void asConstraintDescribedThreeArgs() { + rewriteRun(java( + """ + import ai.timefold.solver.core.api.score.stream.ConstraintBuilder; + + class Test { + void method(ConstraintBuilder builder) { + builder.asConstraintDescribed("myConstraint", "myGroup", "Some description."); + } + }""", + """ + import ai.timefold.solver.core.api.score.stream.ConstraintBuilder; + + class Test { + void method(ConstraintBuilder builder) { + builder.asConstraint("myConstraint"); + } + }""")); + } + + @Test + void constraintRefConstraintName() { + rewriteRun(java( + """ + import ai.timefold.solver.core.api.score.stream.ConstraintRef; + + class Test { + void method(ConstraintRef ref) { + String name = ref.constraintName(); + } + }""", + """ + import ai.timefold.solver.core.api.score.stream.ConstraintRef; + + class Test { + void method(ConstraintRef ref) { + String name = ref.id(); + } + }""")); + } + + @Test + void constraintAnalysisConstraintName() { + rewriteRun(java( + """ + import ai.timefold.solver.core.api.score.analysis.ConstraintAnalysis; + + class Test { + void method(ConstraintAnalysis analysis) { + String name = analysis.constraintName(); + } + }""", + """ + import ai.timefold.solver.core.api.score.analysis.ConstraintAnalysis; + + class Test { + void method(ConstraintAnalysis analysis) { + String name = analysis.constraintId(); + } + }""")); + } + +} diff --git a/tools/migration/src/test/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipeTest.java b/tools/migration/src/test/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipeTest.java index b248818fecc..6511f4c8d9e 100644 --- a/tools/migration/src/test/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipeTest.java +++ b/tools/migration/src/test/java/ai/timefold/solver/migration/v2/GeneralMethodChangeNameMigrationRecipeTest.java @@ -174,7 +174,7 @@ public class Test { UnionMoveSelectorConfig unionMoveSelectorConfig; public void test() { benchmark.benchmark(); - constraint.getConstraintRef().constraintName(); + constraint.getConstraintRef().id(); solver.isEveryProblemChangeProcessed(); bestSolutionChangedEvent.isEveryProblemChangeProcessed(); cartesianProductMoveSelectorConfig.getMoveSelectorList();