From 3e826a58214861db1dd21f186f778a150f257d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Tue, 12 Nov 2024 12:23:18 +0100 Subject: [PATCH] chore: Address Sonar issues post-nullness (Part 3) --- .../entity/EntitySelectorFactory.java | 144 +++++++++--------- .../move/AbstractMoveSelectorFactory.java | 65 ++++---- .../composite/UnionMoveSelectorFactory.java | 14 +- .../factory/MoveIteratorFactoryFactory.java | 10 +- .../move/factory/MoveListFactoryFactory.java | 9 +- .../generic/ChangeMoveSelectorFactory.java | 28 +++- .../SubListChangeMoveSelectorFactory.java | 14 +- .../selector/value/ValueSelectorFactory.java | 112 +++++++------- .../DefaultLocalSearchPhaseFactory.java | 42 ++--- .../forager/LocalSearchForagerFactory.java | 2 +- .../custom/DefaultCustomPhaseFactory.java | 6 +- .../easy/EasyScoreDirectorFactory.java | 7 +- ...tConstraintStreamScoreDirectorFactory.java | 13 +- .../impl/solver/DefaultSolverFactory.java | 22 +-- .../impl/solver/DefaultSolverManager.java | 17 +-- .../jpyinterpreter/types/PythonLikeType.java | 8 +- .../jpyinterpreter/types/PythonString.java | 8 +- .../types/collections/PythonLikeDict.java | 6 +- .../collections/PythonLikeFrozenSet.java | 6 +- .../types/collections/PythonLikeList.java | 5 +- .../types/collections/PythonLikeSet.java | 3 +- .../types/collections/PythonLikeTuple.java | 5 +- .../types/collections/view/DictItemView.java | 3 +- .../types/wrappers/JavaObjectWrapper.java | 8 +- .../types/wrappers/PythonObjectWrapper.java | 9 +- .../util/ConcurrentWeakIdentityHashMap.java | 4 +- 26 files changed, 287 insertions(+), 283 deletions(-) diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/EntitySelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/EntitySelectorFactory.java index 72d21c4b26..6e9368be49 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/EntitySelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/EntitySelectorFactory.java @@ -2,8 +2,8 @@ import java.util.ArrayList; import java.util.Comparator; -import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Stream; import ai.timefold.solver.core.api.domain.solution.PlanningSolution; @@ -44,6 +44,7 @@ public EntitySelectorFactory(EntitySelectorConfig entitySelectorConfig) { public EntityDescriptor extractEntityDescriptor(HeuristicConfigPolicy configPolicy) { var entityClass = config.getEntityClass(); + var mimicSelectorRef = config.getMimicSelectorRef(); if (entityClass != null) { var solutionDescriptor = configPolicy.getSolutionDescriptor(); var entityDescriptor = solutionDescriptor.getEntityDescriptorStrict(entityClass); @@ -56,8 +57,8 @@ Check your solver configuration. If that class (%s) is not in the entityClassSet solutionDescriptor.getEntityClassSet(), PlanningSolution.class.getSimpleName())); } return entityDescriptor; - } else if (config.getMimicSelectorRef() != null) { - return configPolicy.getEntityMimicRecorder(config.getMimicSelectorRef()).getEntityDescriptor(); + } else if (mimicSelectorRef != null) { + return configPolicy.getEntityMimicRecorder(mimicSelectorRef).getEntityDescriptor(); } else { return null; } @@ -118,15 +119,15 @@ protected EntitySelector buildMimicReplaying(HeuristicConfigPolicy(entityMimicRecorder); } @@ -134,8 +135,7 @@ protected EntitySelector buildMimicReplaying(HeuristicConfigPolicy entityDescriptor, SelectionCacheType resolvedCacheType, SelectionOrder resolvedSelectionOrder) { return switch (resolvedSelectionOrder) { - case ORIGINAL -> false; - case SORTED, SHUFFLED, PROBABILISTIC -> + case ORIGINAL, SORTED, SHUFFLED, PROBABILISTIC -> // baseValueSelector and lower should be ORIGINAL if they are going to get cached completely false; case RANDOM -> @@ -156,8 +156,8 @@ private EntitySelector buildBaseEntitySelector(EntityDescriptor(entityDescriptor, minimumCacheType, randomSelection); @@ -179,10 +179,11 @@ private EntitySelector applyFiltering(EntitySelector entit ClassInstanceCache instanceCache) { var entityDescriptor = entitySelector.getEntityDescriptor(); if (hasFiltering(entityDescriptor)) { - List> filterList = new ArrayList<>(config.getFilterClass() == null ? 1 : 2); - if (config.getFilterClass() != null) { + var filterClass = config.getFilterClass(); + var filterList = new ArrayList>(filterClass == null ? 1 : 2); + if (filterClass != null) { SelectionFilter selectionFilter = - instanceCache.newInstance(config, "filterClass", config.getFilterClass()); + instanceCache.newInstance(config, "filterClass", filterClass); filterList.add(selectionFilter); } // Filter out pinned entities @@ -198,58 +199,52 @@ private EntitySelector applyFiltering(EntitySelector entit } protected void validateSorting(SelectionOrder resolvedSelectionOrder) { - if ((config.getSorterManner() != null || config.getSorterComparatorClass() != null - || config.getSorterWeightFactoryClass() != null - || config.getSorterOrder() != null || config.getSorterClass() != null) - && resolvedSelectionOrder != SelectionOrder.SORTED) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") with sorterManner (" + config.getSorterManner() - + ") and sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") and sorterOrder (" + config.getSorterOrder() - + ") and sorterClass (" + config.getSorterClass() - + ") has a resolvedSelectionOrder (" + resolvedSelectionOrder - + ") that is not " + SelectionOrder.SORTED + "."); - } - if (config.getSorterManner() != null && config.getSorterComparatorClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterComparatorClass (" + config.getSorterComparatorClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterWeightFactoryClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterClass (" + config.getSorterClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterOrder() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") with sorterManner (" + config.getSorterManner() - + ") has a non-null sorterOrder (" + config.getSorterOrder() + ")."); + var sorterManner = config.getSorterManner(); + var sorterComparatorClass = config.getSorterComparatorClass(); + var sorterWeightFactoryClass = config.getSorterWeightFactoryClass(); + var sorterOrder = config.getSorterOrder(); + var sorterClass = config.getSorterClass(); + if ((sorterManner != null || sorterComparatorClass != null || sorterWeightFactoryClass != null || sorterOrder != null + || sorterClass != null) && resolvedSelectionOrder != SelectionOrder.SORTED) { + throw new IllegalArgumentException(""" + The entitySelectorConfig (%s) with sorterManner (%s) \ + and sorterComparatorClass (%s) and sorterWeightFactoryClass (%s) and sorterOrder (%s) and sorterClass (%s) \ + has a resolvedSelectionOrder (%s) that is not %s.""" + .formatted(config, sorterManner, sorterComparatorClass, sorterWeightFactoryClass, sorterOrder, sorterClass, + resolvedSelectionOrder, SelectionOrder.SORTED)); } - if (config.getSorterComparatorClass() != null && config.getSorterWeightFactoryClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() + ")."); - } - if (config.getSorterComparatorClass() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and a sorterClass (" + config.getSorterClass() + ")."); + assertNotSorterMannerAnd(config, "sorterComparatorClass", EntitySelectorConfig::getSorterComparatorClass); + assertNotSorterMannerAnd(config, "sorterWeightFactoryClass", EntitySelectorConfig::getSorterWeightFactoryClass); + assertNotSorterMannerAnd(config, "sorterClass", EntitySelectorConfig::getSorterClass); + assertNotSorterMannerAnd(config, "sorterOrder", EntitySelectorConfig::getSorterOrder); + assertNotSorterClassAnd(config, "sorterComparatorClass", EntitySelectorConfig::getSorterComparatorClass); + assertNotSorterClassAnd(config, "sorterWeightFactoryClass", EntitySelectorConfig::getSorterWeightFactoryClass); + assertNotSorterClassAnd(config, "sorterOrder", EntitySelectorConfig::getSorterOrder); + if (sorterComparatorClass != null && sorterWeightFactoryClass != null) { + throw new IllegalArgumentException( + "The entitySelectorConfig (%s) has both a sorterComparatorClass (%s) and a sorterWeightFactoryClass (%s)." + .formatted(config, sorterComparatorClass, sorterWeightFactoryClass)); } - if (config.getSorterWeightFactoryClass() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") has both a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") and a sorterClass (" + config.getSorterClass() + ")."); + } + + private static void assertNotSorterMannerAnd(EntitySelectorConfig config, String propertyName, + Function propertyAccessor) { + var sorterManner = config.getSorterManner(); + var property = propertyAccessor.apply(config); + if (sorterManner != null && property != null) { + throw new IllegalArgumentException("The entitySelectorConfig (%s) has both a sorterManner (%s) and a %s (%s)." + .formatted(config, sorterManner, propertyName, property)); } - if (config.getSorterClass() != null && config.getSorterOrder() != null) { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") with sorterClass (" + config.getSorterClass() - + ") has a non-null sorterOrder (" + config.getSorterOrder() + ")."); + } + + private static void assertNotSorterClassAnd(EntitySelectorConfig config, String propertyName, + Function propertyAccessor) { + var sorterClass = config.getSorterClass(); + var property = propertyAccessor.apply(config); + if (sorterClass != null && property != null) { + throw new IllegalArgumentException( + "The entitySelectorConfig (%s) with sorterClass (%s) has a non-null %s (%s)." + .formatted(config, sorterClass, propertyName, property)); } } @@ -257,12 +252,13 @@ protected EntitySelector applySorting(SelectionCacheType resolvedCach SelectionOrder resolvedSelectionOrder, EntitySelector entitySelector, ClassInstanceCache instanceCache) { if (resolvedSelectionOrder == SelectionOrder.SORTED) { SelectionSorter sorter; - if (config.getSorterManner() != null) { + var sorterManner = config.getSorterManner(); + if (sorterManner != null) { var entityDescriptor = entitySelector.getEntityDescriptor(); - if (!EntitySelectorConfig.hasSorter(config.getSorterManner(), entityDescriptor)) { + if (!EntitySelectorConfig.hasSorter(sorterManner, entityDescriptor)) { return entitySelector; } - sorter = EntitySelectorConfig.determineSorter(config.getSorterManner(), entityDescriptor); + sorter = EntitySelectorConfig.determineSorter(sorterManner, entityDescriptor); } else if (config.getSorterComparatorClass() != null) { Comparator sorterComparator = instanceCache.newInstance(config, "sorterComparatorClass", config.getSorterComparatorClass()); @@ -276,12 +272,12 @@ protected EntitySelector applySorting(SelectionCacheType resolvedCach } else if (config.getSorterClass() != null) { sorter = instanceCache.newInstance(config, "sorterClass", config.getSorterClass()); } else { - throw new IllegalArgumentException("The entitySelectorConfig (" + config - + ") with resolvedSelectionOrder (" + resolvedSelectionOrder - + ") needs a sorterManner (" + config.getSorterManner() - + ") or a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") or a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") or a sorterClass (" + config.getSorterClass() + ")."); + throw new IllegalArgumentException(""" + The entitySelectorConfig (%s) with resolvedSelectionOrder (%s) needs \ + a sorterManner (%s) or a sorterComparatorClass (%s) or a sorterWeightFactoryClass (%s) \ + or a sorterClass (%s).""" + .formatted(config, resolvedSelectionOrder, sorterManner, config.getSorterComparatorClass(), + config.getSorterWeightFactoryClass(), config.getSorterClass())); } entitySelector = new SortingEntitySelector<>(entitySelector, resolvedCacheType, sorter); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/AbstractMoveSelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/AbstractMoveSelectorFactory.java index 927e52b671..ef3f58f0dc 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/AbstractMoveSelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/AbstractMoveSelectorFactory.java @@ -109,13 +109,12 @@ private void validateResolvedCacheType(SelectionCacheType resolvedCacheType, Mov protected boolean determineBaseRandomSelection(SelectionCacheType resolvedCacheType, SelectionOrder resolvedSelectionOrder) { return switch (resolvedSelectionOrder) { - case ORIGINAL -> false; - case SORTED, SHUFFLED, PROBABILISTIC -> + case ORIGINAL, SORTED, SHUFFLED, PROBABILISTIC -> // baseValueSelector and lower should be ORIGINAL if they are going to get cached completely false; case RANDOM -> // Predict if caching will occur - resolvedCacheType.isNotCached() || (isBaseInherentlyCached() && !hasFiltering()); + resolvedCacheType.isNotCached() || isBaseInherentlyCached() && config.getFilterClass() == null; default -> throw new IllegalStateException("The selectionOrder (" + resolvedSelectionOrder + ") is not implemented."); }; @@ -125,10 +124,6 @@ protected boolean isBaseInherentlyCached() { return false; } - private boolean hasFiltering() { - return config.getFilterClass() != null; - } - private MoveSelector applyFiltering(MoveSelector moveSelector, boolean skipNonDoableMoves) { /* * Do not filter out pointless moves in Construction Heuristics and Exhaustive Search, @@ -139,9 +134,10 @@ private MoveSelector applyFiltering(MoveSelector moveSelec SelectionFilter> baseFilter = skipNonDoableMoves ? DoableMoveSelectionFilter.INSTANCE : null; - if (hasFiltering()) { + var filterClass = config.getFilterClass(); + if (filterClass != null) { SelectionFilter> selectionFilter = - ConfigUtils.newInstance(config, "filterClass", config.getFilterClass()); + ConfigUtils.newInstance(config, "filterClass", filterClass); SelectionFilter> finalFilter = baseFilter == null ? selectionFilter : SelectionFilter.compose(baseFilter, selectionFilter); return FilteringMoveSelector.of(moveSelector, finalFilter); @@ -190,25 +186,26 @@ protected MoveSelector applySorting(SelectionCacheType resolvedCacheT SelectionOrder resolvedSelectionOrder, MoveSelector moveSelector) { if (resolvedSelectionOrder == SelectionOrder.SORTED) { SelectionSorter> sorter; - if (config.getSorterComparatorClass() != null) { - Comparator> sorterComparator = ConfigUtils.newInstance(config, - "sorterComparatorClass", config.getSorterComparatorClass()); + var sorterComparatorClass = config.getSorterComparatorClass(); + var sorterWeightFactoryClass = config.getSorterWeightFactoryClass(); + var sorterClass = config.getSorterClass(); + if (sorterComparatorClass != null) { + Comparator> sorterComparator = + ConfigUtils.newInstance(config, "sorterComparatorClass", sorterComparatorClass); sorter = new ComparatorSelectionSorter<>(sorterComparator, SelectionSorterOrder.resolve(config.getSorterOrder())); - } else if (config.getSorterWeightFactoryClass() != null) { + } else if (sorterWeightFactoryClass != null) { SelectionSorterWeightFactory> sorterWeightFactory = - ConfigUtils.newInstance(config, "sorterWeightFactoryClass", - config.getSorterWeightFactoryClass()); + ConfigUtils.newInstance(config, "sorterWeightFactoryClass", sorterWeightFactoryClass); sorter = new WeightFactorySelectionSorter<>(sorterWeightFactory, SelectionSorterOrder.resolve(config.getSorterOrder())); - } else if (config.getSorterClass() != null) { - sorter = ConfigUtils.newInstance(config, "sorterClass", config.getSorterClass()); + } else if (sorterClass != null) { + sorter = ConfigUtils.newInstance(config, "sorterClass", sorterClass); } else { - throw new IllegalArgumentException("The moveSelectorConfig (" + config - + ") with resolvedSelectionOrder (" + resolvedSelectionOrder - + ") needs a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") or a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") or a sorterClass (" + config.getSorterClass() + ")."); + throw new IllegalArgumentException( + "The moveSelectorConfig (%s) with resolvedSelectionOrder (%s) needs a sorterComparatorClass (%s) or a sorterWeightFactoryClass (%s) or a sorterClass (%s)." + .formatted(config, resolvedSelectionOrder, sorterComparatorClass, sorterWeightFactoryClass, + sorterClass)); } moveSelector = new SortingMoveSelector<>(moveSelector, resolvedCacheType, sorter); } @@ -216,26 +213,26 @@ protected MoveSelector applySorting(SelectionCacheType resolvedCacheT } private void validateProbability(SelectionOrder resolvedSelectionOrder) { - if (config.getProbabilityWeightFactoryClass() != null && resolvedSelectionOrder != SelectionOrder.PROBABILISTIC) { - throw new IllegalArgumentException("The moveSelectorConfig (" + config - + ") with probabilityWeightFactoryClass (" + config.getProbabilityWeightFactoryClass() - + ") has a resolvedSelectionOrder (" + resolvedSelectionOrder - + ") that is not " + SelectionOrder.PROBABILISTIC + "."); + var probabilityWeightFactoryClass = config.getProbabilityWeightFactoryClass(); + if (probabilityWeightFactoryClass != null && resolvedSelectionOrder != SelectionOrder.PROBABILISTIC) { + throw new IllegalArgumentException( + "The moveSelectorConfig (%s) with probabilityWeightFactoryClass (%s) has a resolvedSelectionOrder (%s) that is not %s." + .formatted(config, probabilityWeightFactoryClass, resolvedSelectionOrder, + SelectionOrder.PROBABILISTIC)); } } private MoveSelector applyProbability(SelectionCacheType resolvedCacheType, SelectionOrder resolvedSelectionOrder, MoveSelector moveSelector) { if (resolvedSelectionOrder == SelectionOrder.PROBABILISTIC) { - if (config.getProbabilityWeightFactoryClass() == null) { - throw new IllegalArgumentException("The moveSelectorConfig (" + config - + ") with resolvedSelectionOrder (" + resolvedSelectionOrder - + ") needs a probabilityWeightFactoryClass (" - + config.getProbabilityWeightFactoryClass() + ")."); + var probabilityWeightFactoryClass = config.getProbabilityWeightFactoryClass(); + if (probabilityWeightFactoryClass == null) { + throw new IllegalArgumentException( + "The moveSelectorConfig (%s) with resolvedSelectionOrder (%s) needs a probabilityWeightFactoryClass (%s)." + .formatted(config, resolvedSelectionOrder, probabilityWeightFactoryClass)); } SelectionProbabilityWeightFactory> probabilityWeightFactory = - ConfigUtils.newInstance(config, "probabilityWeightFactoryClass", - config.getProbabilityWeightFactoryClass()); + ConfigUtils.newInstance(config, "probabilityWeightFactoryClass", probabilityWeightFactoryClass); moveSelector = new ProbabilityMoveSelector<>(moveSelector, resolvedCacheType, probabilityWeightFactory); } return moveSelector; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorFactory.java index 0e3d03182f..58ed734d1c 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorFactory.java @@ -53,15 +53,15 @@ The selector configuration (%s) already includes the Nearby Selection setting, m buildInnerMoveSelectors(moveSelectorConfigList, configPolicy, minimumCacheType, randomSelection); SelectionProbabilityWeightFactory> selectorProbabilityWeightFactory; - if (config.getSelectorProbabilityWeightFactoryClass() != null) { + var selectorProbabilityWeightFactoryClass = config.getSelectorProbabilityWeightFactoryClass(); + if (selectorProbabilityWeightFactoryClass != null) { if (!randomSelection) { - throw new IllegalArgumentException("The moveSelectorConfig (" + config - + ") with selectorProbabilityWeightFactoryClass (" - + config.getSelectorProbabilityWeightFactoryClass() - + ") has non-random randomSelection (" + randomSelection + ")."); + throw new IllegalArgumentException( + "The moveSelectorConfig (%s) with selectorProbabilityWeightFactoryClass (%s) has non-random randomSelection (%s)." + .formatted(configPolicy, selectorProbabilityWeightFactoryClass, randomSelection)); } - selectorProbabilityWeightFactory = ConfigUtils.newInstance(config, - "selectorProbabilityWeightFactoryClass", config.getSelectorProbabilityWeightFactoryClass()); + selectorProbabilityWeightFactory = ConfigUtils.newInstance(config, "selectorProbabilityWeightFactoryClass", + selectorProbabilityWeightFactoryClass); } else if (randomSelection) { Map, Double> fixedProbabilityWeightMap = new HashMap<>(moveSelectorConfigList.size()); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveIteratorFactoryFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveIteratorFactoryFactory.java index 725c947938..585a46c2d5 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveIteratorFactoryFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveIteratorFactoryFactory.java @@ -17,12 +17,12 @@ public MoveIteratorFactoryFactory(MoveIteratorFactoryConfig moveSelectorConfig) @Override public MoveSelector buildBaseMoveSelector(HeuristicConfigPolicy configPolicy, SelectionCacheType minimumCacheType, boolean randomSelection) { - if (config.getMoveIteratorFactoryClass() == null) { - throw new IllegalArgumentException("The moveIteratorFactoryConfig (" + config - + ") lacks a moveListFactoryClass (" + config.getMoveIteratorFactoryClass() + ")."); + var moveIteratorFactoryClass = config.getMoveIteratorFactoryClass(); + if (moveIteratorFactoryClass == null) { + throw new IllegalArgumentException("The moveIteratorFactoryConfig (%s) lacks a moveListFactoryClass (%s)." + .formatted(config, moveIteratorFactoryClass)); } - MoveIteratorFactory moveIteratorFactory = ConfigUtils.newInstance(config, - "moveIteratorFactoryClass", config.getMoveIteratorFactoryClass()); + var moveIteratorFactory = ConfigUtils.newInstance(config, "moveIteratorFactoryClass", moveIteratorFactoryClass); ConfigUtils.applyCustomProperties(moveIteratorFactory, "moveIteratorFactoryClass", config.getMoveIteratorFactoryCustomProperties(), "moveIteratorFactoryCustomProperties"); return new MoveIteratorFactoryToMoveSelectorBridge<>(moveIteratorFactory, randomSelection); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveListFactoryFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveListFactoryFactory.java index f22651d1c6..59140fe21e 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveListFactoryFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/factory/MoveListFactoryFactory.java @@ -17,12 +17,13 @@ public MoveListFactoryFactory(MoveListFactoryConfig moveSelectorConfig) { @Override public MoveSelector buildBaseMoveSelector(HeuristicConfigPolicy configPolicy, SelectionCacheType minimumCacheType, boolean randomSelection) { - if (config.getMoveListFactoryClass() == null) { - throw new IllegalArgumentException("The moveListFactoryConfig (" + config - + ") lacks a moveListFactoryClass (" + config.getMoveListFactoryClass() + ")."); + var moveListFactoryClass = config.getMoveListFactoryClass(); + if (moveListFactoryClass == null) { + throw new IllegalArgumentException("The moveListFactoryConfig (%s) lacks a moveListFactoryClass (%s)." + .formatted(config, moveListFactoryClass)); } MoveListFactory moveListFactory = - ConfigUtils.newInstance(config, "moveListFactoryClass", config.getMoveListFactoryClass()); + ConfigUtils.newInstance(config, "moveListFactoryClass", moveListFactoryClass); ConfigUtils.applyCustomProperties(moveListFactory, "moveListFactoryClass", config.getMoveListFactoryCustomProperties(), "moveListFactoryCustomProperties"); // MoveListFactoryToMoveSelectorBridge caches by design, so it uses the minimumCacheType diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/ChangeMoveSelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/ChangeMoveSelectorFactory.java index 1d7482998d..f7d18cb3c2 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/ChangeMoveSelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/ChangeMoveSelectorFactory.java @@ -131,13 +131,33 @@ The changeMoveSelectorConfig ({}) is being used for a list variable. Please update your solver config to use {} now.""", config, ListChangeMoveSelectorConfig.class.getSimpleName()); ListChangeMoveSelectorConfig listChangeMoveSelectorConfig = ListChangeMoveSelectorFactory.buildChildMoveSelectorConfig( - variableDescriptor, config.getValueSelectorConfig(), - new DestinationSelectorConfig() - .withEntitySelectorConfig(config.getEntitySelectorConfig()) - .withValueSelectorConfig(config.getValueSelectorConfig())); + variableDescriptor, config.getValueSelectorConfig(), createDestinationSelectorConfig()); if (inheritFoldedConfig) { listChangeMoveSelectorConfig.inheritFolded(config); } return listChangeMoveSelectorConfig; } + + private DestinationSelectorConfig createDestinationSelectorConfig() { + var entitySelectorConfig = config.getEntitySelectorConfig(); + var valueSelectorConfig = config.getValueSelectorConfig(); + if (entitySelectorConfig == null) { + if (valueSelectorConfig == null) { + return new DestinationSelectorConfig(); + } else { + return new DestinationSelectorConfig() + .withValueSelectorConfig(valueSelectorConfig); + } + } else { + if (valueSelectorConfig == null) { + return new DestinationSelectorConfig() + .withEntitySelectorConfig(entitySelectorConfig); + } else { + return new DestinationSelectorConfig() + .withEntitySelectorConfig(entitySelectorConfig) + .withValueSelectorConfig(valueSelectorConfig); + } + } + } + } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SubListChangeMoveSelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SubListChangeMoveSelectorFactory.java index 9401c21e4e..940832dcbd 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SubListChangeMoveSelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SubListChangeMoveSelectorFactory.java @@ -131,29 +131,31 @@ The subListChangeMoveSelector (%s) cannot unfold when there are multiple entitie } private SubListChangeMoveSelectorConfig buildChildMoveSelectorConfig(ListVariableDescriptor variableDescriptor) { + var subListSelectorConfig = config.getSubListSelectorConfig(); + var destinationSelectorConfig = config.getDestinationSelectorConfig(); var subListChangeMoveSelectorConfig = config.copyConfig() - .withSubListSelectorConfig(new SubListSelectorConfig(config.getSubListSelectorConfig()) - .withValueSelectorConfig(Optional.ofNullable(config.getSubListSelectorConfig()) + .withSubListSelectorConfig(new SubListSelectorConfig(subListSelectorConfig) + .withValueSelectorConfig(Optional.ofNullable(subListSelectorConfig) .map(SubListSelectorConfig::getValueSelectorConfig) .map(ValueSelectorConfig::new) // use copy constructor if inherited not null .orElseGet(ValueSelectorConfig::new))) - .withDestinationSelectorConfig(new DestinationSelectorConfig(config.getDestinationSelectorConfig()) + .withDestinationSelectorConfig(new DestinationSelectorConfig(destinationSelectorConfig) .withEntitySelectorConfig( - Optional.ofNullable(config.getDestinationSelectorConfig()) + Optional.ofNullable(destinationSelectorConfig) .map(DestinationSelectorConfig::getEntitySelectorConfig) .map(EntitySelectorConfig::new) // use copy constructor if inherited not null .orElseGet(EntitySelectorConfig::new) // otherwise create new instance // override entity class (destination entity selector is never replaying) .withEntityClass(variableDescriptor.getEntityDescriptor().getEntityClass())) .withValueSelectorConfig( - Optional.ofNullable(config.getDestinationSelectorConfig()) + Optional.ofNullable(destinationSelectorConfig) .map(DestinationSelectorConfig::getValueSelectorConfig) .map(ValueSelectorConfig::new) // use copy constructor if inherited not null .orElseGet(ValueSelectorConfig::new) // otherwise create new instance // override variable name (destination value selector is never replaying) .withVariableName(variableDescriptor.getVariableName()))); - var subListSelectorConfig = Objects.requireNonNull(subListChangeMoveSelectorConfig.getSubListSelectorConfig()); + subListSelectorConfig = Objects.requireNonNull(subListChangeMoveSelectorConfig.getSubListSelectorConfig()); SubListConfigUtil.transferDeprecatedMinimumSubListSize( subListChangeMoveSelectorConfig, SubListChangeMoveSelectorConfig::getMinimumSubListSize, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/value/ValueSelectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/value/ValueSelectorFactory.java index dd5ef87a43..6de40c9662 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/value/ValueSelectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/value/ValueSelectorFactory.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.function.Function; import ai.timefold.solver.core.api.domain.solution.PlanningSolution; import ai.timefold.solver.core.api.domain.valuerange.ValueRangeProvider; @@ -256,58 +257,52 @@ protected ValueSelector applyInitializedChainedValueFilter(HeuristicC } protected void validateSorting(SelectionOrder resolvedSelectionOrder) { - if ((config.getSorterManner() != null || config.getSorterComparatorClass() != null - || config.getSorterWeightFactoryClass() != null - || config.getSorterOrder() != null || config.getSorterClass() != null) - && resolvedSelectionOrder != SelectionOrder.SORTED) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") with sorterManner (" + config.getSorterManner() - + ") and sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") and sorterOrder (" + config.getSorterOrder() - + ") and sorterClass (" + config.getSorterClass() - + ") has a resolvedSelectionOrder (" + resolvedSelectionOrder - + ") that is not " + SelectionOrder.SORTED + "."); - } - if (config.getSorterManner() != null && config.getSorterComparatorClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterComparatorClass (" + config.getSorterComparatorClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterWeightFactoryClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterManner (" + config.getSorterManner() - + ") and a sorterClass (" + config.getSorterClass() + ")."); - } - if (config.getSorterManner() != null && config.getSorterOrder() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") with sorterManner (" + config.getSorterManner() - + ") has a non-null sorterOrder (" + config.getSorterOrder() + ")."); - } - if (config.getSorterComparatorClass() != null && config.getSorterWeightFactoryClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() + ")."); + var sorterManner = config.getSorterManner(); + var sorterComparatorClass = config.getSorterComparatorClass(); + var sorterWeightFactoryClass = config.getSorterWeightFactoryClass(); + var sorterOrder = config.getSorterOrder(); + var sorterClass = config.getSorterClass(); + if ((sorterManner != null || sorterComparatorClass != null || sorterWeightFactoryClass != null || sorterOrder != null + || sorterClass != null) && resolvedSelectionOrder != SelectionOrder.SORTED) { + throw new IllegalArgumentException(""" + The valueSelectorConfig (%s) with sorterManner (%s) \ + and sorterComparatorClass (%s) and sorterWeightFactoryClass (%s) and sorterOrder (%s) and sorterClass (%s) \ + has a resolvedSelectionOrder (%s) that is not %s.""" + .formatted(config, sorterManner, sorterComparatorClass, sorterWeightFactoryClass, sorterOrder, sorterClass, + resolvedSelectionOrder, SelectionOrder.SORTED)); } - if (config.getSorterComparatorClass() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") and a sorterClass (" + config.getSorterClass() + ")."); + assertNotSorterMannerAnd(config, "sorterComparatorClass", ValueSelectorConfig::getSorterComparatorClass); + assertNotSorterMannerAnd(config, "sorterWeightFactoryClass", ValueSelectorConfig::getSorterWeightFactoryClass); + assertNotSorterMannerAnd(config, "sorterClass", ValueSelectorConfig::getSorterClass); + assertNotSorterMannerAnd(config, "sorterOrder", ValueSelectorConfig::getSorterOrder); + assertNotSorterClassAnd(config, "sorterComparatorClass", ValueSelectorConfig::getSorterComparatorClass); + assertNotSorterClassAnd(config, "sorterWeightFactoryClass", ValueSelectorConfig::getSorterWeightFactoryClass); + assertNotSorterClassAnd(config, "sorterOrder", ValueSelectorConfig::getSorterOrder); + if (sorterComparatorClass != null && sorterWeightFactoryClass != null) { + throw new IllegalArgumentException( + "The valueSelectorConfig (%s) has both a sorterComparatorClass (%s) and a sorterWeightFactoryClass (%s)." + .formatted(config, sorterComparatorClass, sorterWeightFactoryClass)); } - if (config.getSorterWeightFactoryClass() != null && config.getSorterClass() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") has both a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") and a sorterClass (" + config.getSorterClass() + ")."); + } + + private static void assertNotSorterMannerAnd(ValueSelectorConfig config, String propertyName, + Function propertyAccessor) { + var sorterManner = config.getSorterManner(); + var property = propertyAccessor.apply(config); + if (sorterManner != null && property != null) { + throw new IllegalArgumentException("The entitySelectorConfig (%s) has both a sorterManner (%s) and a %s (%s)." + .formatted(config, sorterManner, propertyName, property)); } - if (config.getSorterClass() != null && config.getSorterOrder() != null) { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") with sorterClass (" + config.getSorterClass() - + ") has a non-null sorterOrder (" + config.getSorterOrder() + ")."); + } + + private static void assertNotSorterClassAnd(ValueSelectorConfig config, String propertyName, + Function propertyAccessor) { + var sorterClass = config.getSorterClass(); + var property = propertyAccessor.apply(config); + if (sorterClass != null && property != null) { + throw new IllegalArgumentException( + "The entitySelectorConfig (%s) with sorterClass (%s) has a non-null %s (%s)." + .formatted(config, sorterClass, propertyName, property)); } } @@ -315,12 +310,13 @@ protected ValueSelector applySorting(SelectionCacheType resolvedCache ValueSelector valueSelector, ClassInstanceCache instanceCache) { if (resolvedSelectionOrder == SelectionOrder.SORTED) { SelectionSorter sorter; - if (config.getSorterManner() != null) { + var sorterManner = config.getSorterManner(); + if (sorterManner != null) { var variableDescriptor = valueSelector.getVariableDescriptor(); - if (!ValueSelectorConfig.hasSorter(config.getSorterManner(), variableDescriptor)) { + if (!ValueSelectorConfig.hasSorter(sorterManner, variableDescriptor)) { return valueSelector; } - sorter = ValueSelectorConfig.determineSorter(config.getSorterManner(), variableDescriptor); + sorter = ValueSelectorConfig.determineSorter(sorterManner, variableDescriptor); } else if (config.getSorterComparatorClass() != null) { Comparator sorterComparator = instanceCache.newInstance(config, "sorterComparatorClass", config.getSorterComparatorClass()); @@ -334,12 +330,12 @@ protected ValueSelector applySorting(SelectionCacheType resolvedCache } else if (config.getSorterClass() != null) { sorter = instanceCache.newInstance(config, "sorterClass", config.getSorterClass()); } else { - throw new IllegalArgumentException("The valueSelectorConfig (" + config - + ") with resolvedSelectionOrder (" + resolvedSelectionOrder - + ") needs a sorterManner (" + config.getSorterManner() - + ") or a sorterComparatorClass (" + config.getSorterComparatorClass() - + ") or a sorterWeightFactoryClass (" + config.getSorterWeightFactoryClass() - + ") or a sorterClass (" + config.getSorterClass() + ")."); + throw new IllegalArgumentException(""" + The valueSelectorConfig (%s) with resolvedSelectionOrder (%s) needs \ + a sorterManner (%s) or a sorterComparatorClass (%s) or a sorterWeightFactoryClass (%s) \ + or a sorterClass (%s).""" + .formatted(config, resolvedSelectionOrder, sorterManner, config.getSorterComparatorClass(), + config.getSorterWeightFactoryClass(), config.getSorterClass())); } if (!valueSelector.getVariableDescriptor().isValueRangeEntityIndependent() && resolvedCacheType == SelectionCacheType.STEP) { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhaseFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhaseFactory.java index 1b573b0ca5..1bcd67e5ea 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhaseFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhaseFactory.java @@ -20,7 +20,6 @@ import ai.timefold.solver.core.config.localsearch.decider.acceptor.LocalSearchAcceptorConfig; import ai.timefold.solver.core.config.localsearch.decider.forager.LocalSearchForagerConfig; import ai.timefold.solver.core.config.localsearch.decider.forager.LocalSearchPickEarlyType; -import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.enterprise.TimefoldSolverEnterpriseService; import ai.timefold.solver.core.impl.domain.variable.descriptor.BasicVariableDescriptor; import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy; @@ -47,12 +46,13 @@ public DefaultLocalSearchPhaseFactory(LocalSearchPhaseConfig phaseConfig) { public LocalSearchPhase buildPhase(int phaseIndex, boolean triggerFirstInitializedSolutionEvent, HeuristicConfigPolicy solverConfigPolicy, BestSolutionRecaller bestSolutionRecaller, Termination solverTermination) { - HeuristicConfigPolicy phaseConfigPolicy = solverConfigPolicy.createPhaseConfigPolicy(); - Termination phaseTermination = buildPhaseTermination(phaseConfigPolicy, solverTermination); - DefaultLocalSearchPhase.Builder builder = - new DefaultLocalSearchPhase.Builder<>(phaseIndex, solverConfigPolicy.getLogIndentation(), phaseTermination, + var phaseConfigPolicy = solverConfigPolicy.createPhaseConfigPolicy(); + var phaseTermination = buildPhaseTermination(phaseConfigPolicy, solverTermination); + var builder = + new DefaultLocalSearchPhase.Builder(phaseIndex, solverConfigPolicy.getLogIndentation(), + phaseTermination, buildDecider(phaseConfigPolicy, phaseTermination)); - EnvironmentMode environmentMode = phaseConfigPolicy.getEnvironmentMode(); + var environmentMode = phaseConfigPolicy.getEnvironmentMode(); if (environmentMode.isNonIntrusiveFullAsserted()) { builder.setAssertStepScoreFromScratch(true); } @@ -65,9 +65,9 @@ public LocalSearchPhase buildPhase(int phaseIndex, boolean triggerFir private LocalSearchDecider buildDecider(HeuristicConfigPolicy configPolicy, Termination termination) { - MoveSelector moveSelector = buildMoveSelector(configPolicy); - Acceptor acceptor = buildAcceptor(configPolicy); - LocalSearchForager forager = buildForager(configPolicy); + var moveSelector = buildMoveSelector(configPolicy); + var acceptor = buildAcceptor(configPolicy); + var forager = buildForager(configPolicy); if (moveSelector.isNeverEnding() && !forager.supportsNeverEndingMoveSelector()) { throw new IllegalStateException("The moveSelector (" + moveSelector + ") has neverEnding (" + moveSelector.isNeverEnding() @@ -75,8 +75,8 @@ private LocalSearchDecider buildDecider(HeuristicConfigPolicy with an ."); } - Integer moveThreadCount = configPolicy.getMoveThreadCount(); - EnvironmentMode environmentMode = configPolicy.getEnvironmentMode(); + var moveThreadCount = configPolicy.getMoveThreadCount(); + var environmentMode = configPolicy.getEnvironmentMode(); LocalSearchDecider decider; if (moveThreadCount == null) { decider = new LocalSearchDecider<>(configPolicy.getLogIndentation(), termination, moveSelector, acceptor, forager); @@ -148,7 +148,7 @@ protected LocalSearchForager buildForager(HeuristicConfigPolicy buildForager(HeuristicConfigPolicy create(foragerConfig_).buildForager(); } + @SuppressWarnings("rawtypes") protected MoveSelector buildMoveSelector(HeuristicConfigPolicy configPolicy) { MoveSelector moveSelector; - SelectionCacheType defaultCacheType = SelectionCacheType.JUST_IN_TIME; + var defaultCacheType = SelectionCacheType.JUST_IN_TIME; SelectionOrder defaultSelectionOrder; if (phaseConfig.getLocalSearchType() == LocalSearchType.VARIABLE_NEIGHBORHOOD_DESCENT) { defaultSelectionOrder = SelectionOrder.ORIGINAL; } else { defaultSelectionOrder = SelectionOrder.RANDOM; } - if (phaseConfig.getMoveSelectorConfig() == null) { + var moveSelectorConfig = phaseConfig.getMoveSelectorConfig(); + if (moveSelectorConfig == null) { moveSelector = new UnionMoveSelectorFactory(determineDefaultMoveSelectorConfig(configPolicy)) .buildMoveSelector(configPolicy, defaultCacheType, defaultSelectionOrder, true); } else { - AbstractMoveSelectorFactory moveSelectorFactory = - MoveSelectorFactory.create(phaseConfig.getMoveSelectorConfig()); + AbstractMoveSelectorFactory moveSelectorFactory = MoveSelectorFactory.create(moveSelectorConfig); if (configPolicy.getNearbyDistanceMeterClass() != null - && NearbyAutoConfigurationEnabled.class - .isAssignableFrom(phaseConfig.getMoveSelectorConfig().getClass()) - && !UnionMoveSelectorConfig.class.isAssignableFrom(phaseConfig.getMoveSelectorConfig().getClass())) { + && NearbyAutoConfigurationEnabled.class.isAssignableFrom(moveSelectorConfig.getClass()) + && !UnionMoveSelectorConfig.class.isAssignableFrom(moveSelectorConfig.getClass())) { // The move selector config is not a composite selector, but it accepts Nearby autoconfiguration. // We create a new UnionMoveSelectorConfig with the existing selector to enable Nearby autoconfiguration. - MoveSelectorConfig moveSelectorCopy = (MoveSelectorConfig) phaseConfig.getMoveSelectorConfig().copyConfig(); - UnionMoveSelectorConfig updatedConfig = new UnionMoveSelectorConfig() + var moveSelectorCopy = (MoveSelectorConfig) moveSelectorConfig.copyConfig(); + var updatedConfig = new UnionMoveSelectorConfig() .withMoveSelectors(moveSelectorCopy); moveSelectorFactory = MoveSelectorFactory.create(updatedConfig); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/LocalSearchForagerFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/LocalSearchForagerFactory.java index 480aca1233..3b9081433d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/LocalSearchForagerFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/LocalSearchForagerFactory.java @@ -20,7 +20,7 @@ public LocalSearchForagerFactory(LocalSearchForagerConfig foragerConfig) { public LocalSearchForager buildForager() { var pickEarlyType_ = Objects.requireNonNullElse(foragerConfig.getPickEarlyType(), LocalSearchPickEarlyType.NEVER); - int acceptedCountLimit_ = Objects.requireNonNullElse(foragerConfig.getAcceptedCountLimit(), Integer.MAX_VALUE); + var acceptedCountLimit_ = Objects.requireNonNullElse(foragerConfig.getAcceptedCountLimit(), Integer.MAX_VALUE); var finalistPodiumType_ = Objects.requireNonNullElse(foragerConfig.getFinalistPodiumType(), FinalistPodiumType.HIGHEST_SCORE); // Breaking ties randomly leads to better results statistically diff --git a/core/src/main/java/ai/timefold/solver/core/impl/phase/custom/DefaultCustomPhaseFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/phase/custom/DefaultCustomPhaseFactory.java index cc4e01eb24..a164532ee9 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/phase/custom/DefaultCustomPhaseFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/phase/custom/DefaultCustomPhaseFactory.java @@ -64,7 +64,9 @@ The customPhaseCommandClass (%s) cannot be null in the customPhase (%s). } private int getCustomPhaseCommandListSize() { - return (phaseConfig.getCustomPhaseCommandClassList() == null ? 0 : phaseConfig.getCustomPhaseCommandClassList().size()) - + (phaseConfig.getCustomPhaseCommandList() == null ? 0 : phaseConfig.getCustomPhaseCommandList().size()); + var customPhaseCommandClassList = phaseConfig.getCustomPhaseCommandClassList(); + var customPhaseCommandList = phaseConfig.getCustomPhaseCommandList(); + return (customPhaseCommandClassList == null ? 0 : customPhaseCommandClassList.size()) + + (customPhaseCommandList == null ? 0 : customPhaseCommandList.size()); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/easy/EasyScoreDirectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/easy/EasyScoreDirectorFactory.java index f08b423af5..0ad39fd873 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/easy/EasyScoreDirectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/easy/EasyScoreDirectorFactory.java @@ -23,13 +23,14 @@ public final class EasyScoreDirectorFactory> EasyScoreDirectorFactory buildScoreDirectorFactory(SolutionDescriptor solutionDescriptor, ScoreDirectorFactoryConfig config) { - if (!EasyScoreCalculator.class.isAssignableFrom(config.getEasyScoreCalculatorClass())) { + var easyScoreCalculatorClass = config.getEasyScoreCalculatorClass(); + if (easyScoreCalculatorClass == null || !EasyScoreCalculator.class.isAssignableFrom(easyScoreCalculatorClass)) { throw new IllegalArgumentException( "The easyScoreCalculatorClass (%s) does not implement %s." .formatted(config.getEasyScoreCalculatorClass(), EasyScoreCalculator.class.getSimpleName())); } - EasyScoreCalculator easyScoreCalculator = ConfigUtils.newInstance(config, - "easyScoreCalculatorClass", config.getEasyScoreCalculatorClass()); + EasyScoreCalculator easyScoreCalculator = + ConfigUtils.newInstance(config, "easyScoreCalculatorClass", easyScoreCalculatorClass); ConfigUtils.applyCustomProperties(easyScoreCalculator, "easyScoreCalculatorClass", config.getEasyScoreCalculatorCustomProperties(), "easyScoreCalculatorCustomProperties"); return new EasyScoreDirectorFactory<>(solutionDescriptor, easyScoreCalculator); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java index 08c97bdf69..dee49f3a09 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java @@ -24,25 +24,28 @@ public final class BavetConstraintStreamScoreDirectorFactory> BavetConstraintStreamScoreDirectorFactory buildScoreDirectorFactory(SolutionDescriptor solutionDescriptor, ScoreDirectorFactoryConfig config, EnvironmentMode environmentMode) { - if (!ConstraintProvider.class.isAssignableFrom(config.getConstraintProviderClass())) { + var providedConstraintProviderClass = config.getConstraintProviderClass(); + if (providedConstraintProviderClass == null + || !ConstraintProvider.class.isAssignableFrom(providedConstraintProviderClass)) { throw new IllegalArgumentException( "The constraintProviderClass (%s) does not implement %s." - .formatted(config.getConstraintProviderClass(), ConstraintProvider.class.getSimpleName())); + .formatted(providedConstraintProviderClass, ConstraintProvider.class.getSimpleName())); } - var constraintProviderClass = getConstraintProviderClass(config); + var constraintProviderClass = getConstraintProviderClass(config, providedConstraintProviderClass); var constraintProvider = ConfigUtils.newInstance(config, "constraintProviderClass", constraintProviderClass); ConfigUtils.applyCustomProperties(constraintProvider, "constraintProviderClass", config.getConstraintProviderCustomProperties(), "constraintProviderCustomProperties"); return new BavetConstraintStreamScoreDirectorFactory<>(solutionDescriptor, constraintProvider, environmentMode); } - private static Class getConstraintProviderClass(ScoreDirectorFactoryConfig config) { + private static Class getConstraintProviderClass(ScoreDirectorFactoryConfig config, + Class providedConstraintProviderClass) { if (Boolean.TRUE.equals(config.getConstraintStreamAutomaticNodeSharing())) { var enterpriseService = TimefoldSolverEnterpriseService.loadOrFail(TimefoldSolverEnterpriseService.Feature.AUTOMATIC_NODE_SHARING); return enterpriseService.buildLambdaSharedConstraintProvider(config.getConstraintProviderClass()); } else { - return config.getConstraintProviderClass(); + return providedConstraintProviderClass; } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java index db8bce7426..77f1e76d20 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverFactory.java @@ -192,24 +192,24 @@ private SolutionDescriptor buildSolutionDescriptor() { } public RandomFactory buildRandomFactory(EnvironmentMode environmentMode_) { - RandomFactory randomFactory; - if (solverConfig.getRandomFactoryClass() != null) { - if (solverConfig.getRandomType() != null || solverConfig.getRandomSeed() != null) { + var randomFactoryClass = solverConfig.getRandomFactoryClass(); + if (randomFactoryClass != null) { + var randomType = solverConfig.getRandomType(); + var randomSeed = solverConfig.getRandomSeed(); + if (randomType != null || randomSeed != null) { throw new IllegalArgumentException( - "The solverConfig with randomFactoryClass (" + solverConfig.getRandomFactoryClass() - + ") has a non-null randomType (" + solverConfig.getRandomType() - + ") or a non-null randomSeed (" + solverConfig.getRandomSeed() + ")."); + "The solverConfig with randomFactoryClass (%s) has a non-null randomType (%s) or a non-null randomSeed (%s)." + .formatted(randomFactoryClass, randomType, randomSeed)); } - randomFactory = ConfigUtils.newInstance(solverConfig, "randomFactoryClass", solverConfig.getRandomFactoryClass()); + return ConfigUtils.newInstance(solverConfig, "randomFactoryClass", randomFactoryClass); } else { - RandomType randomType_ = Objects.requireNonNullElse(solverConfig.getRandomType(), RandomType.JDK); - Long randomSeed_ = solverConfig.getRandomSeed(); + var randomType_ = Objects.requireNonNullElse(solverConfig.getRandomType(), RandomType.JDK); + var randomSeed_ = solverConfig.getRandomSeed(); if (solverConfig.getRandomSeed() == null && environmentMode_ != EnvironmentMode.NON_REPRODUCIBLE) { randomSeed_ = DEFAULT_RANDOM_SEED; } - randomFactory = new DefaultRandomFactory(randomType_, randomSeed_); + return new DefaultRandomFactory(randomType_, randomSeed_); } - return randomFactory; } public List> buildPhaseList(HeuristicConfigPolicy configPolicy, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverManager.java b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverManager.java index d8f0567ba1..d8b7bdaf4b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverManager.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolverManager.java @@ -42,18 +42,15 @@ public final class DefaultSolverManager implements Solver private final ExecutorService solverThreadPool; private final ConcurrentMap> problemIdToSolverJobMap; - public DefaultSolverManager(SolverFactory solverFactory, - SolverManagerConfig solverManagerConfig) { - defaultExceptionHandler = (problemId, throwable) -> LOGGER.error( - "Solving failed for problemId ({}).", problemId, throwable); + public DefaultSolverManager(SolverFactory solverFactory, SolverManagerConfig solverManagerConfig) { + this.defaultExceptionHandler = + (problemId, throwable) -> LOGGER.error("Solving failed for problemId ({}).", problemId, throwable); this.solverFactory = solverFactory; validateSolverFactory(); - int parallelSolverCount = solverManagerConfig.resolveParallelSolverCount(); - var threadFactory = Executors.defaultThreadFactory(); - if (solverManagerConfig.getThreadFactoryClass() != null) { - threadFactory = ConfigUtils.newInstance(solverManagerConfig, "threadFactoryClass", - solverManagerConfig.getThreadFactoryClass()); - } + var parallelSolverCount = solverManagerConfig.resolveParallelSolverCount(); + var threadFactoryClass = solverManagerConfig.getThreadFactoryClass(); + var threadFactory = threadFactoryClass == null ? Executors.defaultThreadFactory() + : ConfigUtils.newInstance(solverManagerConfig, "threadFactoryClass", threadFactoryClass); solverThreadPool = Executors.newFixedThreadPool(parallelSolverCount, threadFactory); problemIdToSolverJobMap = new ConcurrentHashMap<>(parallelSolverCount * 10); } diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java index 5e13521a08..e75c3b2a83 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonLikeType.java @@ -613,15 +613,15 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || !PythonLikeType.class.isAssignableFrom(o.getClass())) { + if (!(o instanceof PythonLikeType that)) { return false; } - PythonLikeType that = (PythonLikeType) o; - return JAVA_TYPE_INTERNAL_NAME.equals(that.JAVA_TYPE_INTERNAL_NAME); + return Objects.equals(JAVA_TYPE_INTERNAL_NAME, that.JAVA_TYPE_INTERNAL_NAME); } @Override public int hashCode() { - return Objects.hash(JAVA_TYPE_INTERNAL_NAME); + return JAVA_TYPE_INTERNAL_NAME.hashCode(); } + } diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java index eecf634f7c..8cb0c4c573 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/PythonString.java @@ -1504,10 +1504,10 @@ public String toString() { @Override public boolean equals(Object o) { - if (o instanceof String) { - return value.equals(o); - } else if (o instanceof PythonString) { - return ((PythonString) o).value.equals(value); + if (o instanceof String s) { + return s.equals(value); + } else if (o instanceof PythonString s) { + return s.value.equals(value); } else { return false; } diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java index 5377f0e23d..d0eca7631e 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeDict.java @@ -5,7 +5,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -387,8 +386,7 @@ public Iterator iterator() { @Override public boolean equals(Object o) { - if (o instanceof Map) { - Map other = (Map) o; + if (o instanceof Map other) { if (other.size() != this.size()) { return false; } @@ -399,7 +397,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(delegate); + return delegate.hashCode(); } @Override diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java index fee99d676d..d0ffe87d15 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeFrozenSet.java @@ -4,7 +4,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; -import java.util.Objects; import java.util.Set; import java.util.function.Predicate; @@ -329,8 +328,7 @@ public void clear() { @Override public boolean equals(Object o) { - if (o instanceof Set) { - Set other = (Set) o; + if (o instanceof Set other) { if (other.size() != this.size()) { return false; } @@ -341,7 +339,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(delegate); + return delegate.hashCode(); } @Override diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java index a5b4c321e9..9ea4ab0808 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeList.java @@ -581,8 +581,7 @@ public String toString() { @Override public boolean equals(Object o) { - if (o instanceof List) { - List other = (List) o; + if (o instanceof List other) { if (other.size() != delegate.size()) { return false; } @@ -599,7 +598,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(delegate); + return delegate.hashCode(); } public List getDelegate() { diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java index 6eb168b92c..eca5a85869 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeSet.java @@ -460,8 +460,7 @@ public void clear() { @Override public boolean equals(Object o) { - if (o instanceof Set) { - Set other = (Set) o; + if (o instanceof Set other) { if (other.size() != this.size()) { return false; } diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java index 23b5b17f23..6af38368aa 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/PythonLikeTuple.java @@ -392,8 +392,7 @@ public List subList(int i, int i1) { @Override public boolean equals(Object o) { - if (o instanceof List) { - List other = (List) o; + if (o instanceof List other) { if (other.size() != this.size()) { return false; } @@ -437,7 +436,7 @@ public int compareTo(PythonLikeTuple other) { @Override public int hashCode() { - return Objects.hash(delegate); + return delegate.hashCode(); } @Override diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java index d628f6ef85..f32ee97bc7 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/collections/view/DictItemView.java @@ -171,8 +171,7 @@ public PythonLikeSet symmetricDifference(DictItemView other) { @Override public boolean equals(Object o) { - if (o instanceof DictItemView) { - DictItemView other = (DictItemView) o; + if (o instanceof DictItemView other) { return entrySet.equals(other.entrySet); } return false; diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java index 3ed68b7085..12d63efcc5 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/JavaObjectWrapper.java @@ -304,11 +304,11 @@ public String toString() { } @Override - public boolean equals(Object other) { - if (other instanceof JavaObjectWrapper) { - return wrappedObject.equals(((JavaObjectWrapper) other).wrappedObject); + public boolean equals(Object o) { + if (o instanceof JavaObjectWrapper other) { + return wrappedObject.equals(other.wrappedObject); } - return wrappedObject.equals(other); + return wrappedObject.equals(o); } @Override diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/PythonObjectWrapper.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/PythonObjectWrapper.java index ca47d96d8d..2d1b922974 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/PythonObjectWrapper.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/types/wrappers/PythonObjectWrapper.java @@ -88,15 +88,13 @@ public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof PythonObjectWrapper)) { + if (!(o instanceof PythonObjectWrapper other)) { return false; } - PythonObjectWrapper other = (PythonObjectWrapper) o; Object maybeEquals = $getType().$getAttributeOrNull("__eq__"); - if (!(maybeEquals instanceof PythonLikeFunction)) { + if (!(maybeEquals instanceof PythonLikeFunction equals)) { return super.equals(o); } - PythonLikeFunction equals = (PythonLikeFunction) maybeEquals; PythonLikeObject result = equals.$call(List.of(this, other), Map.of(), null); if (result instanceof PythonBoolean) { return ((PythonBoolean) result).getBooleanValue(); @@ -107,10 +105,9 @@ public boolean equals(Object o) { @Override public int hashCode() { Object maybeHash = $getType().$getAttributeOrNull("__hash__"); - if (!(maybeHash instanceof PythonLikeFunction)) { + if (!(maybeHash instanceof PythonLikeFunction hash)) { return super.hashCode(); } - PythonLikeFunction hash = (PythonLikeFunction) maybeHash; PythonLikeObject result = hash.$call(List.of(this), Map.of(), null); if (result instanceof PythonInteger) { return ((PythonInteger) result).value.hashCode(); diff --git a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/util/ConcurrentWeakIdentityHashMap.java b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/util/ConcurrentWeakIdentityHashMap.java index f07ff65c1d..c9642f862c 100644 --- a/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/util/ConcurrentWeakIdentityHashMap.java +++ b/python/jpyinterpreter/src/main/java/ai/timefold/jpyinterpreter/util/ConcurrentWeakIdentityHashMap.java @@ -123,8 +123,8 @@ private static class Key extends WeakReference { } @Override - public boolean equals(Object obj) { - return this == obj || obj instanceof Key && ((Key) obj).get() == get(); + public boolean equals(Object o) { + return this == o || o instanceof Key other && other.get() == get(); } @Override