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 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 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);
   }
 }
 private void processPropertyAnnotations() {
   PlanningVariable planningVariableAnnotation =
       variablePropertyAccessor.getReadMethod().getAnnotation(PlanningVariable.class);
   valueSorter = new PlanningValueSorter();
   processNullable(planningVariableAnnotation);
   processStrength(planningVariableAnnotation);
   processChained(planningVariableAnnotation);
   processValueRangeAnnotation();
 }
 @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 void setValue(Object entity, Object value) {
   variablePropertyAccessor.executeSetter(entity, value);
 }
 public Object getValue(Object entity) {
   return variablePropertyAccessor.executeGetter(entity);
 }
 public Class<?> getVariablePropertyType() {
   return variablePropertyAccessor.getPropertyType();
 }
 public String getVariableName() {
   return variablePropertyAccessor.getName();
 }
 public Collection<Object> extractValues(Object entity) {
   return (Collection<Object>) rangePropertyAccessor.executeGetter(entity);
 }