private ValueSelector buildBaseValueSelector(
     EnvironmentMode environmentMode,
     PlanningVariableDescriptor variableDescriptor,
     SelectionCacheType minimumCacheType,
     boolean randomSelection) {
   if (variableDescriptor.getValueRangeDescriptor().isEntityDependent()) {
     FromEntityPropertyPlanningValueRangeDescriptor valueRangeDescriptor =
         (FromEntityPropertyPlanningValueRangeDescriptor)
             variableDescriptor.getValueRangeDescriptor();
     // TODO should we ignore the minimumCacheType so it can be cached on changeMoves too?
     return new FromEntityPropertyValueSelector(
         valueRangeDescriptor, minimumCacheType, randomSelection);
   } else {
     // FromSolutionPropertyValueSelector caches by design, so it uses the minimumCacheType
     if (variableDescriptor.isPlanningValuesCacheable()) {
       if (minimumCacheType.compareTo(SelectionCacheType.PHASE) < 0) {
         // TODO we probably want to default this to SelectionCacheType.JUST_IN_TIME
         minimumCacheType = SelectionCacheType.PHASE;
       }
     } else {
       if (minimumCacheType.compareTo(SelectionCacheType.STEP) < 0) {
         // TODO we probably want to default this to SelectionCacheType.JUST_IN_TIME
         minimumCacheType = SelectionCacheType.STEP;
       }
     }
     return new FromSolutionPropertyValueSelector(
         variableDescriptor, minimumCacheType, randomSelection);
   }
 }
 /**
  * @param configPolicy never null
  * @param minimumCacheType never null, If caching is used (different from {@link
  *     SelectionCacheType#JUST_IN_TIME}), then it should be at least this {@link
  *     SelectionCacheType} because an ancestor already uses such caching and less would be
  *     pointless.
  * @param inheritedSelectionOrder never null
  * @return never null
  */
 public PillarSelector buildPillarSelector(
     HeuristicConfigPolicy configPolicy,
     SelectionCacheType minimumCacheType,
     SelectionOrder inheritedSelectionOrder) {
   if (minimumCacheType.compareTo(SelectionCacheType.STEP) > 0) {
     throw new IllegalArgumentException(
         "The pillarSelectorConfig ("
             + this
             + ")'s minimumCacheType ("
             + minimumCacheType
             + ") must not be higher than "
             + SelectionCacheType.STEP
             + " because the pillars change every step.");
   }
   // EntitySelector uses SelectionOrder.ORIGINAL because a SameValuePillarSelector STEP caches the
   // values
   EntitySelectorConfig entitySelectorConfig_ =
       entitySelectorConfig == null ? new EntitySelectorConfig() : entitySelectorConfig;
   EntitySelector entitySelector =
       entitySelectorConfig_.buildEntitySelector(
           configPolicy, minimumCacheType, SelectionOrder.ORIGINAL);
   Collection<GenuineVariableDescriptor> variableDescriptors =
       entitySelector.getEntityDescriptor().getVariableDescriptors();
   return new SameValuePillarSelector(
       entitySelector, variableDescriptors, inheritedSelectionOrder.toRandomSelectionBoolean());
 }
 private ValueSelector applyCaching(
     SelectionCacheType resolvedCacheType,
     SelectionOrder resolvedSelectionOrder,
     ValueSelector valueSelector) {
   if (resolvedCacheType.isCached()
       && resolvedCacheType.compareTo(valueSelector.getCacheType()) > 0) {
     if (!(valueSelector instanceof EntityIndependentValueSelector)) {
       throw new IllegalArgumentException(
           "The valueSelectorConfig ("
               + this
               + ") with resolvedSelectionOrder ("
               + resolvedSelectionOrder
               + ") needs to be based on a EntityIndependentValueSelector."
               + " Check your @"
               + ValueRange.class.getSimpleName()
               + " annotations.");
     }
     valueSelector =
         new CachingValueSelector(
             (EntityIndependentValueSelector) valueSelector,
             resolvedCacheType,
             resolvedSelectionOrder.toRandomSelectionBoolean());
   }
   return valueSelector;
 }