private void processValueRangeAnnotation() {
   Method propertyGetter = variablePropertyAccessor.getReadMethod();
   ValueRange valueRangeAnnotation = propertyGetter.getAnnotation(ValueRange.class);
   ValueRanges valueRangesAnnotation = propertyGetter.getAnnotation(ValueRanges.class);
   if (valueRangeAnnotation != null) {
     if (valueRangesAnnotation != null) {
       throw new IllegalArgumentException(
           "The planningEntityClass ("
               + planningEntityDescriptor.getPlanningEntityClass()
               + ") has a PlanningVariable annotated property ("
               + variablePropertyAccessor.getName()
               + ") that has a @ValueRange and @ValueRanges annotation: fold them into 1 @ValueRanges.");
     }
     valueRangeDescriptor = buildValueRangeDescriptor(valueRangeAnnotation);
   } else {
     if (valueRangesAnnotation == null) {
       throw new IllegalArgumentException(
           "The planningEntityClass ("
               + planningEntityDescriptor.getPlanningEntityClass()
               + ") has a PlanningVariable annotated property ("
               + variablePropertyAccessor.getName()
               + ") that has no @ValueRange or @ValueRanges annotation.");
     }
     List<PlanningValueRangeDescriptor> valueRangeDescriptorList =
         new ArrayList<PlanningValueRangeDescriptor>(valueRangesAnnotation.value().length);
     for (ValueRange partialValueRangeAnnotation : valueRangesAnnotation.value()) {
       valueRangeDescriptorList.add(buildValueRangeDescriptor(partialValueRangeAnnotation));
     }
     valueRangeDescriptor =
         new CompositePlanningValueRangeDescriptor(this, valueRangeDescriptorList);
   }
 }
 private void processChained(PlanningVariable planningVariableAnnotation) {
   chained = planningVariableAnnotation.chained();
   if (chained
       && !variablePropertyAccessor
           .getPropertyType()
           .isAssignableFrom(planningEntityDescriptor.getPlanningEntityClass())) {
     throw new IllegalArgumentException(
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") has a PlanningVariable annotated property ("
             + variablePropertyAccessor.getName()
             + ") with chained ("
             + chained
             + ") and propertyType ("
             + variablePropertyAccessor.getPropertyType()
             + ") which is not a superclass/interface of or the same as the planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ").");
   }
   if (chained && nullable) {
     throw new IllegalArgumentException(
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") has a PlanningVariable annotated property ("
             + variablePropertyAccessor.getName()
             + ") with chained ("
             + chained
             + "), which is not compatible with nullable ("
             + nullable
             + ").");
   }
 }
 private void processNullable(PlanningVariable planningVariableAnnotation) {
   nullable = planningVariableAnnotation.nullable();
   if (nullable && variablePropertyAccessor.getPropertyType().isPrimitive()) {
     throw new IllegalArgumentException(
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") has a PlanningVariable annotated property ("
             + variablePropertyAccessor.getName()
             + ") with nullable ("
             + nullable
             + "), which is not compatible with the primitive propertyType ("
             + variablePropertyAccessor.getPropertyType()
             + ").");
   }
   Class<? extends SelectionFilter> reinitializeVariableEntityFilterClass =
       planningVariableAnnotation.reinitializeVariableEntityFilter();
   if (reinitializeVariableEntityFilterClass
       == PlanningVariable.NullReinitializeVariableEntityFilter.class) {
     reinitializeVariableEntityFilterClass = null;
   }
   if (reinitializeVariableEntityFilterClass != null) {
     reinitializeVariableEntityFilter =
         ConfigUtils.newInstance(
             this, "reinitializeVariableEntityFilterClass", reinitializeVariableEntityFilterClass);
   } else {
     reinitializeVariableEntityFilter = new NullValueReinitializeVariableEntityFilter(this);
   }
 }
 private void processStrength(PlanningVariable planningVariableAnnotation) {
   Class<? extends Comparator> strengthComparatorClass =
       planningVariableAnnotation.strengthComparatorClass();
   if (strengthComparatorClass == PlanningVariable.NullStrengthComparator.class) {
     strengthComparatorClass = null;
   }
   Class<? extends SelectionSorterWeightFactory> strengthWeightFactoryClass =
       planningVariableAnnotation.strengthWeightFactoryClass();
   if (strengthWeightFactoryClass == PlanningVariable.NullStrengthWeightFactory.class) {
     strengthWeightFactoryClass = null;
   }
   if (strengthComparatorClass != null && strengthWeightFactoryClass != null) {
     throw new IllegalStateException(
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") property ("
             + variablePropertyAccessor.getName()
             + ") cannot have a strengthComparatorClass ("
             + strengthComparatorClass.getName()
             + ") and a strengthWeightFactoryClass ("
             + strengthWeightFactoryClass.getName()
             + ") at the same time.");
   }
   if (strengthComparatorClass != null) {
     Comparator<Object> strengthComparator =
         ConfigUtils.newInstance(this, "strengthComparatorClass", strengthComparatorClass);
     valueSorter.setStrengthComparator(strengthComparator);
   }
   if (strengthWeightFactoryClass != null) {
     SelectionSorterWeightFactory strengthWeightFactory =
         ConfigUtils.newInstance(this, "strengthWeightFactoryClass", strengthWeightFactoryClass);
     valueSorter.setStrengthWeightFactory(strengthWeightFactory);
   }
 }
 @Override
 public String toString() {
   return getClass().getSimpleName()
       + "("
       + variablePropertyAccessor.getName()
       + " of "
       + planningEntityDescriptor.getPlanningEntityClass().getName()
       + ")";
 }
 private void processPlanningEntityProperty(ValueRange valueRangeAnnotation) {
   String planningEntityProperty = valueRangeAnnotation.planningEntityProperty();
   PlanningEntityDescriptor planningEntityDescriptor =
       variableDescriptor.getPlanningEntityDescriptor();
   rangePropertyAccessor =
       new ReflectionPropertyAccessor(
           planningEntityDescriptor.getPropertyDescriptor(planningEntityProperty));
   if (rangePropertyAccessor == null) {
     String exceptionMessage =
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") has a PlanningVariable annotated property ("
             + variableDescriptor.getVariableName()
             + ") that refers to a planningEntityProperty ("
             + planningEntityProperty
             + ") that does not exist.";
     if (planningEntityProperty.length() >= 2
         && Character.isUpperCase(planningEntityProperty.charAt(1))) {
       String correctedPlanningEntityProperty =
           planningEntityProperty.substring(0, 1).toUpperCase()
               + planningEntityProperty.substring(1);
       exceptionMessage +=
           " But it probably needs to be correctedPlanningEntityProperty ("
               + correctedPlanningEntityProperty
               + ") instead because the JavaBeans spec states"
               + " the first letter should be a upper case if the second is upper case.";
     }
     throw new IllegalArgumentException(exceptionMessage);
   }
   if (!Collection.class.isAssignableFrom(rangePropertyAccessor.getPropertyType())) {
     throw new IllegalArgumentException(
         "The planningEntityClass ("
             + planningEntityDescriptor.getPlanningEntityClass()
             + ") has a PlanningVariable annotated property ("
             + variableDescriptor.getVariableName()
             + ") that refers to a planningEntityProperty ("
             + planningEntityProperty
             + ") that does not return a Collection.");
   }
 }
  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;
  }