Uploaded image for project: 'OptaPlanner'
  1. OptaPlanner
  2. PLANNER-316

FieldAccessingSolutionCloner does not clone a superclass field deeply if the planner annotations are on the subclass

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • 6.3.0.Beta1
    • None
    • None
    • None
    • Hide

      Execute testcase:

      package org.optaplanner.core.impl.domain.solution.cloner;
      
      import junit.framework.TestCase;
      import org.optaplanner.core.api.domain.entity.PlanningEntity;
      import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
      import org.optaplanner.core.api.domain.solution.Solution;
      import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
      import org.optaplanner.core.api.domain.variable.PlanningVariable;
      import org.optaplanner.core.api.domain.variable.PlanningVariableGraphType;
      import org.optaplanner.core.api.score.buildin.simple.SimpleScore;
      import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
      
      import java.util.ArrayList;
      import java.util.Collection;
      import java.util.List;
      
      /**
       * Description: <br>
       * <p>
       * Date: 07.04.15<br>
       * </p>
       */
      public class FieldAccessingSolutionClonerTest extends TestCase {
          FieldAccessingSolutionCloner cloner;
      
          public static abstract class AbstractTestSolution implements Solution<SimpleScore> {
              List<TestVehicle> vehicles;
              private SimpleScore score;
              List<TestWorkload> workloads;
      
              @Override
              public SimpleScore getScore() {
                  return score;
              }
      
              @Override
              public void setScore(SimpleScore score) {
                  this.score = score;
              }
      
              @Override
              public Collection<?> getProblemFacts() {
                  return vehicles;
              }
      
              @PlanningEntityCollectionProperty
              @ValueRangeProvider(id = "workloadsRange")
              public List<TestWorkload> getWorkloads() {
                  return workloads;
              }
      
              @ValueRangeProvider(id = "vehiclesRange")
              public List<TestVehicle> getVehicles() {
                  return vehicles;
              }
      
          }
      
          public static class TestSolution extends AbstractTestSolution {
          }
      
          public static abstract class AbstractTestWorkload implements TestStandstill {
              String id;
              private TestStandstill previousStandstill;
      
              @PlanningVariable(graphType = PlanningVariableGraphType.CHAINED,
                  valueRangeProviderRefs = {"vehiclesRange", "workloadsRange"})
              public TestStandstill getPreviousStandstill() {
                  return previousStandstill;
              }
      
              public void setPreviousStandstill(
                  TestStandstill previousStandstill) {
                  this.previousStandstill = previousStandstill;
              }
      
              public String toString() {
                  return getClass().getSimpleName() + "{id=" + id + "}";
              }
          }
      
          @PlanningEntity
          public static class TestWorkload extends AbstractTestWorkload {
      
          }
      
          public static class TestVehicle implements TestStandstill {
              String id;
      
              public String toString() {
                  return getClass().getSimpleName() + "{id=" + id + "}";
              }
          }
      
          public interface TestStandstill {
      
          }
      
          public void testCloneSolutionWithInheritedVariables() {
              SolutionDescriptor descriptor = new SolutionDescriptor(TestSolution.class);
              cloner = new FieldAccessingSolutionCloner(descriptor);
      
              TestSolution solution = createTestSolution();
      
              TestSolution clone = (TestSolution) cloner.cloneSolution(solution);
              assertTrue(clone != null);
              assertTrue(clone != solution); // a clone has been created that is not the same instance as the original
              assertTrue(clone.workloads != solution.workloads); // the collection of workloads must be a clone...
              assertTrue(clone.workloads.size() == solution.workloads.size()); // ... of same size
              assertTrue(clone.workloads.get(0) != solution.workloads.get(0)); // ... containing clones (deep cloned)
              assertTrue(
                  clone.workloads.get(0).getPreviousStandstill() == clone.workloads.get(1)); // ... and correct cross-refs
              assertTrue(clone.workloads.get(1).getPreviousStandstill() == clone.vehicles.get(0));
          }
      
          private TestSolution createTestSolution() {
              TestSolution s = new TestSolution();
              s.vehicles = new ArrayList<>();
              s.vehicles.add(new TestVehicle());
              s.workloads = new ArrayList<>();
              s.workloads.add(new TestWorkload());
              s.workloads.add(new TestWorkload());
              s.workloads.get(0).setPreviousStandstill(s.workloads.get(1));
              s.workloads.get(1).setPreviousStandstill(s.vehicles.get(0));
      
              s.workloads.get(0).id = "W1";
              s.workloads.get(1).id = "W2";
              s.vehicles.get(0).id = "V1";
              return s;
          }
      }
      
      
      Show
      Execute testcase: package org.optaplanner.core.impl.domain.solution.cloner; import junit.framework.TestCase; import org.optaplanner.core.api.domain.entity.PlanningEntity; import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty; import org.optaplanner.core.api.domain.solution.Solution; import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider; import org.optaplanner.core.api.domain.variable.PlanningVariable; import org.optaplanner.core.api.domain.variable.PlanningVariableGraphType; import org.optaplanner.core.api.score.buildin.simple.SimpleScore; import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Description: <br> * <p> * Date: 07.04.15<br> * </p> */ public class FieldAccessingSolutionClonerTest extends TestCase { FieldAccessingSolutionCloner cloner; public static abstract class AbstractTestSolution implements Solution<SimpleScore> { List<TestVehicle> vehicles; private SimpleScore score; List<TestWorkload> workloads; @Override public SimpleScore getScore() { return score; } @Override public void setScore(SimpleScore score) { this .score = score; } @Override public Collection<?> getProblemFacts() { return vehicles; } @PlanningEntityCollectionProperty @ValueRangeProvider(id = "workloadsRange" ) public List<TestWorkload> getWorkloads() { return workloads; } @ValueRangeProvider(id = "vehiclesRange" ) public List<TestVehicle> getVehicles() { return vehicles; } } public static class TestSolution extends AbstractTestSolution { } public static abstract class AbstractTestWorkload implements TestStandstill { String id; private TestStandstill previousStandstill; @PlanningVariable(graphType = PlanningVariableGraphType.CHAINED, valueRangeProviderRefs = { "vehiclesRange" , "workloadsRange" }) public TestStandstill getPreviousStandstill() { return previousStandstill; } public void setPreviousStandstill( TestStandstill previousStandstill) { this .previousStandstill = previousStandstill; } public String toString() { return getClass().getSimpleName() + "{id=" + id + "}" ; } } @PlanningEntity public static class TestWorkload extends AbstractTestWorkload { } public static class TestVehicle implements TestStandstill { String id; public String toString() { return getClass().getSimpleName() + "{id=" + id + "}" ; } } public interface TestStandstill { } public void testCloneSolutionWithInheritedVariables() { SolutionDescriptor descriptor = new SolutionDescriptor(TestSolution.class); cloner = new FieldAccessingSolutionCloner(descriptor); TestSolution solution = createTestSolution(); TestSolution clone = (TestSolution) cloner.cloneSolution(solution); assertTrue(clone != null ); assertTrue(clone != solution); // a clone has been created that is not the same instance as the original assertTrue(clone.workloads != solution.workloads); // the collection of workloads must be a clone... assertTrue(clone.workloads.size() == solution.workloads.size()); // ... of same size assertTrue(clone.workloads.get(0) != solution.workloads.get(0)); // ... containing clones (deep cloned) assertTrue( clone.workloads.get(0).getPreviousStandstill() == clone.workloads.get(1)); // ... and correct cross-refs assertTrue(clone.workloads.get(1).getPreviousStandstill() == clone.vehicles.get(0)); } private TestSolution createTestSolution() { TestSolution s = new TestSolution(); s.vehicles = new ArrayList<>(); s.vehicles.add( new TestVehicle()); s.workloads = new ArrayList<>(); s.workloads.add( new TestWorkload()); s.workloads.add( new TestWorkload()); s.workloads.get(0).setPreviousStandstill(s.workloads.get(1)); s.workloads.get(1).setPreviousStandstill(s.vehicles.get(0)); s.workloads.get(0).id = "W1" ; s.workloads.get(1).id = "W2" ; s.vehicles.get(0).id = "V1" ; return s; } }
    • Hide

      Implement PlanningClonable yourself in the Solution class

      Show
      Implement PlanningClonable yourself in the Solution class

      Issue PLANNER-193 is fixed, but the test case shows, that the variable containing planningEntities is not deeply cloned when it is declared in a superclass of the solution.

      see reproducer

              gdesmet@redhat.com Geoffrey De Smet (Inactive)
              romanstumm Roman Stumm (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated:
                Resolved: