public ValueSelector buildValueSelector(
      EnvironmentMode environmentMode,
      SolutionDescriptor solutionDescriptor,
      SelectionOrder inheritedResolvedSelectionOrder,
      PlanningEntityDescriptor entityDescriptor) {
    PlanningVariableDescriptor variableDescriptor;
    if (planningVariableName != null) {
      variableDescriptor = entityDescriptor.getPlanningVariableDescriptor(planningVariableName);
      if (variableDescriptor == null) {
        throw new IllegalArgumentException(
            "The variableSelectorConfig ("
                + this
                + ") has a planningVariableName ("
                + planningVariableName
                + ") for planningEntityClass ("
                + entityDescriptor.getPlanningEntityClass()
                + ") that is not annotated as a planningVariable.\n"
                + "Check your planningEntity implementation's annotated methods.");
      }
    } else {
      Collection<PlanningVariableDescriptor> planningVariableDescriptors =
          entityDescriptor.getPlanningVariableDescriptors();
      if (planningVariableDescriptors.size() != 1) {
        throw new IllegalArgumentException(
            "The variableSelectorConfig ("
                + this
                + ") has no configured planningVariableName ("
                + planningVariableName
                + ") for planningEntityClass ("
                + entityDescriptor.getPlanningEntityClass()
                + ") and because there are multiple in the planningVariableNameSet ("
                + entityDescriptor.getPlanningVariableNameSet()
                + "), it can not be deducted automatically.");
      }
      variableDescriptor = planningVariableDescriptors.iterator().next();
    }
    SelectionOrder resolvedSelectionOrder =
        SelectionOrder.resolveSelectionOrder(selectionOrder, inheritedResolvedSelectionOrder);
    boolean randomSelection =
        resolvedSelectionOrder == SelectionOrder.RANDOM
            && valueProbabilityWeightFactoryClass == null;
    // TODO we probably want to default this to SelectionCacheType.JUST_IN_TIME
    SelectionCacheType resolvedCacheType = cacheType == null ? SelectionCacheType.PHASE : cacheType;
    ValueSelector valueSelector =
        new FromSolutionPropertyValueSelector(
            variableDescriptor, randomSelection, resolvedCacheType);

    // TODO filterclass

    if (valueProbabilityWeightFactoryClass != null) {
      if (resolvedSelectionOrder != SelectionOrder.RANDOM) {
        throw new IllegalArgumentException(
            "The variableSelectorConfig ("
                + this
                + ") with valueProbabilityWeightFactoryClass ("
                + valueProbabilityWeightFactoryClass
                + ") has a non-random resolvedSelectionOrder ("
                + resolvedSelectionOrder
                + ").");
      }
      SelectionProbabilityWeightFactory valueProbabilityWeightFactory;
      try {
        valueProbabilityWeightFactory = valueProbabilityWeightFactoryClass.newInstance();
      } catch (InstantiationException e) {
        throw new IllegalArgumentException(
            "valueProbabilityWeightFactoryClass ("
                + valueProbabilityWeightFactoryClass.getName()
                + ") does not have a public no-arg constructor",
            e);
      } catch (IllegalAccessException e) {
        throw new IllegalArgumentException(
            "valueProbabilityWeightFactoryClass ("
                + valueProbabilityWeightFactoryClass.getName()
                + ") does not have a public no-arg constructor",
            e);
      }
      ProbabilityValueSelector probabilityValueSelector =
          new ProbabilityValueSelector(
              valueSelector, resolvedCacheType, valueProbabilityWeightFactory);
      valueSelector = probabilityValueSelector;
    }
    return valueSelector;
  }