public boolean isMoveDoable(ScoreDirector scoreDirector) {
   Object leftValue = variableDescriptor.getValue(leftEntity);
   Object rightEntity = inverseVariableSupply.getInverseSingleton(rightValue);
   if (ObjectUtils.equals(leftValue, rightValue)
       || ObjectUtils.equals(leftEntity, rightValue)
       || ObjectUtils.equals(rightEntity, leftValue)) {
     return false;
   }
   if (rightEntity == null) {
     Object leftAnchor = anchorVariableSupply.getAnchor(leftEntity);
     Object rightAnchor = determineRightAnchor();
     // TODO Currently unsupported because we fail to create a valid undoMove... even though doMove
     // supports it
     if (leftAnchor == rightAnchor) {
       return false;
     }
   }
   if (!variableDescriptor.isValueRangeEntityIndependent()) {
     ValueRangeDescriptor valueRangeDescriptor = variableDescriptor.getValueRangeDescriptor();
     Solution workingSolution = scoreDirector.getWorkingSolution();
     if (rightEntity != null) {
       ValueRange rightValueRange =
           valueRangeDescriptor.extractValueRange(workingSolution, rightEntity);
       if (!rightValueRange.contains(leftValue)) {
         return false;
       }
     }
     ValueRange leftValueRange =
         valueRangeDescriptor.extractValueRange(workingSolution, leftEntity);
     if (!leftValueRange.contains(rightValue)) {
       return false;
     }
   }
   return true;
 }
 // TODO remove this method and use the SingletonInverseVariableSupply directly
 public Object getTrailingEntity(
     GenuineVariableDescriptor chainedVariableDescriptor, Object planningValue) {
   SingletonInverseVariableSupply supply =
       variableListenerSupport.demand(
           new SingletonInverseVariableDemand(chainedVariableDescriptor));
   return supply.getInverseSingleton(planningValue);
 }
 protected Object findLastEntityInChainOrLeftEntity() {
   Object entity = rightValue;
   while (entity != leftEntity) {
     Object nextEntity = inverseVariableSupply.getInverseSingleton(entity);
     if (nextEntity == null) {
       return entity;
     }
     entity = nextEntity;
   }
   return leftEntity;
 }
 public String toString() {
   Object leftValue = variableDescriptor.getValue(leftEntity);
   Object rightEntity = inverseVariableSupply.getInverseSingleton(rightValue);
   return leftEntity
       + " {"
       + leftValue
       + "} <-tailChainSwap-> "
       + rightEntity
       + " {"
       + rightValue
       + "}";
 }
 @Override
 protected void doMoveOnGenuineVariables(ScoreDirector scoreDirector) {
   Object leftAnchor = anchorVariableSupply.getAnchor(leftEntity);
   Object rightAnchor = determineRightAnchor();
   Object leftValue = variableDescriptor.getValue(leftEntity);
   Object rightEntity = inverseVariableSupply.getInverseSingleton(rightValue); // Sometimes null
   if (leftAnchor != rightAnchor) {
     // Change the left entity
     scoreDirector.changeVariableFacade(variableDescriptor, leftEntity, rightValue);
     // Change the right entity
     if (rightEntity != null) {
       scoreDirector.changeVariableFacade(variableDescriptor, rightEntity, leftValue);
     }
   } else {
     Object lastEntityInChainOrLeftEntity = findLastEntityInChainOrLeftEntity();
     if (lastEntityInChainOrLeftEntity == leftEntity) {
       // Reverses loop on the side that doesn't include the anchor, because rightValue is earlier
       // than leftEntity
       Object leftNextEntity = inverseVariableSupply.getInverseSingleton(leftEntity);
       scoreDirector.changeVariableFacade(variableDescriptor, leftEntity, rightValue);
       reverseChain(scoreDirector, leftValue, leftEntity, rightEntity);
       if (leftNextEntity != null) {
         scoreDirector.changeVariableFacade(variableDescriptor, leftNextEntity, rightEntity);
       }
     } else {
       // Reverses loop on the side that does include the anchor, because rightValue is later than
       // leftEntity
       Object lastEntityInChain = lastEntityInChainOrLeftEntity;
       Object leftNextEntity = inverseVariableSupply.getInverseSingleton(leftEntity);
       Object entityAfterAnchor = inverseVariableSupply.getInverseSingleton(leftAnchor);
       // Change the head of the chain
       reverseChain(scoreDirector, leftValue, leftEntity, entityAfterAnchor);
       // Change leftEntity
       scoreDirector.changeVariableFacade(variableDescriptor, leftEntity, rightValue);
       // Change the tail of the chain
       reverseChain(scoreDirector, lastEntityInChain, leftAnchor, rightEntity);
       scoreDirector.changeVariableFacade(variableDescriptor, leftNextEntity, rightEntity);
     }
   }
 }
 public Move createUndoMove(ScoreDirector scoreDirector) {
   Object leftAnchor = anchorVariableSupply.getAnchor(leftEntity);
   Object rightAnchor = determineRightAnchor();
   Object leftValue = variableDescriptor.getValue(leftEntity);
   if (leftAnchor != rightAnchor) {
     return new TailChainSwapMove(
         variableDescriptor, inverseVariableSupply, anchorVariableSupply, leftEntity, leftValue);
   } else {
     Object rightEntity = inverseVariableSupply.getInverseSingleton(rightValue);
     if (rightEntity != null) {
       return new TailChainSwapMove(
           variableDescriptor,
           inverseVariableSupply,
           anchorVariableSupply,
           rightEntity,
           rightValue);
     } else {
       // TODO Currently unsupported because we fail to create a valid undoMove... even though
       // doMove supports it
       throw new IllegalStateException(
           "Impossible state, because isMoveDoable() should not return true.");
     }
   }
 }
 public Collection<? extends Object> getPlanningEntities() {
   Object rightEntity = inverseVariableSupply.getInverseSingleton(rightValue);
   return Arrays.asList(leftEntity, rightEntity);
 }