public static <Solution_> PartitionChangeMove<Solution_> createMove(
      InnerScoreDirector<Solution_> scoreDirector) {
    SolutionDescriptor<Solution_> solutionDescriptor = scoreDirector.getSolutionDescriptor();
    Solution_ workingSolution = scoreDirector.getWorkingSolution();

    int entityCount = solutionDescriptor.getEntityCount(workingSolution);
    Map<GenuineVariableDescriptor<Solution_>, List<Pair<Object, Object>>> changeMap =
        new LinkedHashMap<>(solutionDescriptor.getEntityDescriptors().size() * 3);
    for (EntityDescriptor<Solution_> entityDescriptor : solutionDescriptor.getEntityDescriptors()) {
      for (GenuineVariableDescriptor<Solution_> variableDescriptor :
          entityDescriptor.getDeclaredGenuineVariableDescriptors()) {
        changeMap.put(variableDescriptor, new ArrayList<>(entityCount));
      }
    }
    for (Iterator<Object> it = solutionDescriptor.extractAllEntitiesIterator(workingSolution);
        it.hasNext(); ) {
      Object entity = it.next();
      EntityDescriptor<Solution_> entityDescriptor =
          solutionDescriptor.findEntityDescriptorOrFail(entity.getClass());
      if (entityDescriptor.isMovable(scoreDirector, entity)) {
        for (GenuineVariableDescriptor<Solution_> variableDescriptor :
            entityDescriptor.getGenuineVariableDescriptors()) {
          Object value = variableDescriptor.getValue(entity);
          changeMap.get(variableDescriptor).add(Pair.of(entity, value));
        }
      }
    }
    return new PartitionChangeMove<>(changeMap);
  }
 public void linkShadowSources(DescriptorPolicy descriptorPolicy) {
   CustomShadowVariable shadowVariableAnnotation =
       variableMemberAccessor.getAnnotation(CustomShadowVariable.class);
   SolutionDescriptor solutionDescriptor = entityDescriptor.getSolutionDescriptor();
   CustomShadowVariable.Source[] sources = shadowVariableAnnotation.sources();
   sourceVariableDescriptorList = new ArrayList<VariableDescriptor>(sources.length);
   for (CustomShadowVariable.Source source : sources) {
     EntityDescriptor sourceEntityDescriptor;
     Class<?> sourceEntityClass = source.entityClass();
     if (sourceEntityClass.equals(CustomShadowVariable.Source.NullEntityClass.class)) {
       sourceEntityDescriptor = entityDescriptor;
     } else {
       sourceEntityDescriptor = solutionDescriptor.findEntityDescriptor(sourceEntityClass);
       if (sourceEntityDescriptor == null) {
         throw new IllegalArgumentException(
             "The entityClass ("
                 + entityDescriptor.getEntityClass()
                 + ") has a "
                 + CustomShadowVariable.class.getSimpleName()
                 + " annotated property ("
                 + variableMemberAccessor.getName()
                 + ") with a sourceEntityClass ("
                 + sourceEntityClass
                 + ") which is not a valid planning entity.");
       }
     }
     String sourceVariableName = source.variableName();
     VariableDescriptor sourceVariableDescriptor =
         sourceEntityDescriptor.getVariableDescriptor(sourceVariableName);
     if (sourceVariableDescriptor == null) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + CustomShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") with sourceVariableName ("
               + sourceVariableName
               + ") which is not a valid planning variable on entityClass ("
               + sourceEntityDescriptor.getEntityClass()
               + ").\n"
               + entityDescriptor.buildInvalidVariableNameExceptionMessage(sourceVariableName));
     }
     sourceVariableDescriptor.registerShadowVariableDescriptor(this);
     sourceVariableDescriptorList.add(sourceVariableDescriptor);
   }
 }
 public SwapMoveSelector(
     EntitySelector leftEntitySelector,
     EntitySelector rightEntitySelector,
     Collection<GenuineVariableDescriptor> variableDescriptors,
     boolean randomSelection) {
   this.leftEntitySelector = leftEntitySelector;
   this.rightEntitySelector = rightEntitySelector;
   this.variableDescriptors = variableDescriptors;
   this.randomSelection = randomSelection;
   EntityDescriptor leftEntityDescriptor = leftEntitySelector.getEntityDescriptor();
   EntityDescriptor rightEntityDescriptor = rightEntitySelector.getEntityDescriptor();
   if (!leftEntityDescriptor.getEntityClass().equals(rightEntityDescriptor.getEntityClass())) {
     throw new IllegalStateException(
         "The selector ("
             + this
             + ") has a leftEntitySelector's entityClass ("
             + leftEntityDescriptor.getEntityClass()
             + ") which is not equal to the rightEntitySelector's entityClass ("
             + rightEntityDescriptor.getEntityClass()
             + ").");
   }
   boolean anyChained = false;
   if (variableDescriptors.isEmpty()) {
     throw new IllegalStateException(
         "The selector ("
             + this
             + ")'s variableDescriptors ("
             + variableDescriptors
             + ") is empty.");
   }
   for (GenuineVariableDescriptor variableDescriptor : variableDescriptors) {
     if (!variableDescriptor
         .getEntityDescriptor()
         .getEntityClass()
         .isAssignableFrom(leftEntityDescriptor.getEntityClass())) {
       throw new IllegalStateException(
           "The selector ("
               + this
               + ") has a variableDescriptor with a entityClass ("
               + variableDescriptor.getEntityDescriptor().getEntityClass()
               + ") which is not equal or a superclass to the leftEntitySelector's entityClass ("
               + leftEntityDescriptor.getEntityClass()
               + ").");
     }
     if (variableDescriptor.isChained()) {
       anyChained = true;
     }
   }
   this.anyChained = anyChained;
   phaseLifecycleSupport.addEventListener(leftEntitySelector);
   if (leftEntitySelector != rightEntitySelector) {
     phaseLifecycleSupport.addEventListener(rightEntitySelector);
   }
 }
 public static EntityDescriptor mockEntityDescriptor(Class entityClass) {
   EntityDescriptor entityDescriptor = mock(EntityDescriptor.class);
   when(entityDescriptor.getEntityClass()).thenReturn(entityClass);
   return entityDescriptor;
 }
 public void linkShadowSources(DescriptorPolicy descriptorPolicy) {
   InverseRelationShadowVariable shadowVariableAnnotation =
       variableMemberAccessor.getAnnotation(InverseRelationShadowVariable.class);
   Class<?> variablePropertyType = getVariablePropertyType();
   Class<?> masterClass;
   if (Collection.class.isAssignableFrom(variablePropertyType)) {
     Type genericType = variableMemberAccessor.getGenericType();
     if (!(genericType instanceof ParameterizedType)) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + InverseRelationShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") with a property type ("
               + variablePropertyType
               + ") which is non parameterized collection.");
     }
     ParameterizedType parameterizedType = (ParameterizedType) genericType;
     Type[] typeArguments = parameterizedType.getActualTypeArguments();
     if (typeArguments.length != 1) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + InverseRelationShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") with a property type ("
               + variablePropertyType
               + ") which is parameterized collection with an unsupported number of type arguments ("
               + typeArguments.length
               + ").");
     }
     Type typeArgument = typeArguments[0];
     if (!(typeArgument instanceof Class)) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + InverseRelationShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") with a property type ("
               + variablePropertyType
               + ") which is parameterized collection with an unsupported type arguments ("
               + typeArgument
               + ").");
     }
     masterClass = ((Class) typeArgument);
     singleton = false;
   } else {
     masterClass = variablePropertyType;
     singleton = true;
   }
   EntityDescriptor sourceEntityDescriptor =
       getEntityDescriptor().getSolutionDescriptor().findEntityDescriptor(masterClass);
   if (sourceEntityDescriptor == null) {
     throw new IllegalArgumentException(
         "The entityClass ("
             + entityDescriptor.getEntityClass()
             + ") has a "
             + InverseRelationShadowVariable.class.getSimpleName()
             + " annotated property ("
             + variableMemberAccessor.getName()
             + ") with a masterClass ("
             + masterClass
             + ") which is not a valid planning entity.");
   }
   String sourceVariableName = shadowVariableAnnotation.sourceVariableName();
   sourceVariableDescriptor = sourceEntityDescriptor.getVariableDescriptor(sourceVariableName);
   if (sourceVariableDescriptor == null) {
     throw new IllegalArgumentException(
         "The entityClass ("
             + entityDescriptor.getEntityClass()
             + ") has a "
             + InverseRelationShadowVariable.class.getSimpleName()
             + " annotated property ("
             + variableMemberAccessor.getName()
             + ") with sourceVariableName ("
             + sourceVariableName
             + ") which is not a valid planning variable on entityClass ("
             + sourceEntityDescriptor.getEntityClass()
             + ").\n"
             + entityDescriptor.buildInvalidVariableNameExceptionMessage(sourceVariableName));
   }
   boolean chained =
       (sourceVariableDescriptor instanceof GenuineVariableDescriptor)
           && ((GenuineVariableDescriptor) sourceVariableDescriptor).isChained();
   if (singleton) {
     if (!chained) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + InverseRelationShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") which does not return a "
               + Collection.class.getSimpleName()
               + " with sourceVariableName ("
               + sourceVariableName
               + ") which is not chained. Only a chained variable supports a singleton inverse.");
     }
   } else {
     if (chained) {
       throw new IllegalArgumentException(
           "The entityClass ("
               + entityDescriptor.getEntityClass()
               + ") has a "
               + InverseRelationShadowVariable.class.getSimpleName()
               + " annotated property ("
               + variableMemberAccessor.getName()
               + ") which does returns a "
               + Collection.class.getSimpleName()
               + " with sourceVariableName ("
               + sourceVariableName
               + ") which is chained. A chained variable supports only a singleton inverse.");
     }
   }
   sourceVariableDescriptor.registerShadowVariableDescriptor(this);
 }