Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,6 @@ The class (%s) is annotated with @%s, but it is immutable.
.formatted(clz.getName(), PlanningId.class.getSimpleName(), PlanningId.class.getSimpleName()));
}
return true;
} else if (PlanningImmutable.class.isAssignableFrom(clz)) {
if (PlanningCloneable.class.isAssignableFrom(clz)) {
throw new IllegalStateException("""
The class (%s) implements %s, but it is %s.
Immutable objects can not be cloned."""
.formatted(clz.getName(), PlanningCloneable.class.getSimpleName(),
PlanningImmutable.class.getSimpleName()));
}
return true;
}
return IMMUTABLE_CLASSES.contains(clz);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,12 @@ private <C> C clone(C original, Map<Object, Object> originalToCloneMap, Queue<Un
}

var declaringClass = (Class<C>) original.getClass();
var clone = constructClone(original, declaringClassMetadata);
var clone = FieldAccessingSolutionCloner.<C> constructClone(declaringClassMetadata);
originalToCloneMap.put(original, clone);
copyFields(declaringClass, original, clone, unprocessedQueue, declaringClassMetadata);
return clone;
}

@SuppressWarnings("unchecked")
private static <C> C constructClone(C original, ClassMetadata classMetadata) {
if (original instanceof PlanningCloneable<?> planningCloneable) {
return (C) planningCloneable.createNewInstance();
} else {
return constructClone(classMetadata);
}
}

@SuppressWarnings("unchecked")
private static <C> C constructClone(ClassMetadata classMetadata) {
var constructor = classMetadata.getConstructor();
Expand Down Expand Up @@ -231,12 +222,8 @@ The cloneCollectionClass (%s) created for originalCollectionClass (%s) is not as
return cloneCollection;
}

@SuppressWarnings("unchecked")
public static <E> Collection<E> constructCloneCollection(Collection<E> originalCollection) {
// TODO Don't hardcode all standard collections
if (originalCollection instanceof PlanningCloneable<?> planningCloneable) {
return (Collection<E>) planningCloneable.createNewInstance();
}
if (originalCollection instanceof LinkedList) {
return new LinkedList<>();
}
Expand Down Expand Up @@ -301,11 +288,7 @@ The cloneMapClass (%s) created for originalMapClass (%s) is not assignable to th
return cloneMap;
}

@SuppressWarnings("unchecked")
public static <K, V> Map<K, V> constructCloneMap(Map<K, V> originalMap) {
if (originalMap instanceof PlanningCloneable<?> planningCloneable) {
return (Map<K, V>) planningCloneable.createNewInstance();
}
// Normally, a Map will never be selected for cloning, but extending implementations might anyway.
if (originalMap instanceof SortedMap<K, V> map) {
var setComparator = map.comparator();
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.util.stream.Stream;

import ai.timefold.solver.core.impl.domain.solution.cloner.DeepCloningUtils;
import ai.timefold.solver.core.impl.domain.solution.cloner.PlanningCloneable;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;

public final class GizmoCloningUtils {
Expand Down Expand Up @@ -51,7 +50,6 @@ public static Set<Class<?>> getDeepClonedClasses(SolutionDescriptor<?> solutionD
if (DeepCloningUtils.isFieldDeepCloned(solutionDescriptor, field, clazz)
&& !Collection.class.isAssignableFrom(field.getType())
&& !Map.class.isAssignableFrom(field.getType())
&& !PlanningCloneable.class.isAssignableFrom(field.getType())
&& !deepClonedClassSet.contains(field.getType())) {
classesToProcess.add(field.getType());
deepClonedClassSet.add(field.getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import ai.timefold.solver.core.impl.domain.common.accessor.gizmo.GizmoMemberDescriptor;
import ai.timefold.solver.core.impl.domain.solution.cloner.DeepCloningUtils;
import ai.timefold.solver.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner;
import ai.timefold.solver.core.impl.domain.solution.cloner.PlanningCloneable;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;

import io.quarkus.gizmo2.ClassOutput;
Expand Down Expand Up @@ -289,15 +288,8 @@ private static void createCloneSolutionRun(ClonerDescriptor clonerDescriptor,

var clone = isExactMatchWithCastBranch.localVar("newClone", solutionSubclass,
Const.ofNull(solutionSubclass));
if (PlanningCloneable.class.isAssignableFrom(solutionSubclass)) {
var newInstance = isExactMatchWithCastBranch.invokeInterface(
MethodDesc.of(PlanningCloneable.class, "createNewInstance", Object.class),
castedSolution);
isExactMatchWithCastBranch.set(clone, newInstance);
} else {
isExactMatchWithCastBranch.set(clone,
isExactMatchWithCastBranch.new_(ConstructorDesc.of(solutionSubclass)));
}
isExactMatchWithCastBranch.set(clone,
isExactMatchWithCastBranch.new_(ConstructorDesc.of(solutionSubclass)));
isExactMatchWithCastBranch.withMap(createdCloneMap).put(castedSolution, clone);

cloneShallowlyClonedFieldsOfObject(solutionSubclassDescriptor, clonerDescriptor,
Expand Down Expand Up @@ -644,9 +636,8 @@ private static void writeDeepCloneCollectionInstructions(ClonerDescriptor cloner
}

/**
* Write the following code
* <p>
*
* Writes the following code:
*
* <pre>
* if (constructedCollection instanceof deeplyClonedFieldClass temp) {
* cloneResultHolder = temp;
Expand Down Expand Up @@ -970,13 +961,7 @@ private void createDeepCloneHelperMethod(ClonerDescriptor clonerDescriptor,
newNoCloneBranch -> {
LocalVar cloneObj =
newNoCloneBranch.localVar("clonedObject", entityClass, Const.ofNull(entityClass));
if (PlanningCloneable.class.isAssignableFrom(entityClass)) {
newNoCloneBranch.set(cloneObj, newNoCloneBranch.invokeInterface(
MethodDesc.of(PlanningCloneable.class, "createNewInstance", Object.class),
toClone));
} else {
newNoCloneBranch.set(cloneObj, newNoCloneBranch.new_(entityClass));
}
newNoCloneBranch.set(cloneObj, newNoCloneBranch.new_(entityClass));
newNoCloneBranch.withMap(cloneMap).put(toClone, cloneObj);

// When deep cloning fields, they cannot be the first entity in the stack, since
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import ai.timefold.solver.core.testdomain.chained.shadow.TestdataShadowingChainedEntity;
import ai.timefold.solver.core.testdomain.chained.shadow.TestdataShadowingChainedObject;
import ai.timefold.solver.core.testdomain.chained.shadow.TestdataShadowingChainedSolution;
import ai.timefold.solver.core.testdomain.clone.cloneable.PlanningCloneableEntity;
import ai.timefold.solver.core.testdomain.clone.cloneable.PlanningCloneableSolution;
import ai.timefold.solver.core.testdomain.clone.deepcloning.AnnotatedTestdataVariousTypes;
import ai.timefold.solver.core.testdomain.clone.deepcloning.TestdataDeepCloningEntity;
import ai.timefold.solver.core.testdomain.clone.deepcloning.TestdataDeepCloningSolution;
Expand Down Expand Up @@ -1210,50 +1208,6 @@ void cloneExtendedShadowEntities() {
.isNotSameAs(original.shadowEntityList.get(0));
}

@Test
void clonePlanningCloneableItems() {
var solutionDescriptor = PlanningCloneableSolution.buildSolutionDescriptor();
var cloner = createSolutionCloner(solutionDescriptor);

var entityA = new PlanningCloneableEntity("A");
var entityB = new PlanningCloneableEntity("B");
var entityC = new PlanningCloneableEntity("C");

var original = new PlanningCloneableSolution(List.of(entityA, entityB, entityC));
var clone = cloner.cloneSolution(original);

assertThat(clone.entityList)
.hasSize(3)
.isNotSameAs(original.entityList)
.first()
.isNotNull();

assertThat(clone.entityList.get(0))
.isNotSameAs(original.entityList.get(0))
.hasFieldOrPropertyWithValue("code", "A");

assertThat(clone.entityList.get(1))
.isNotSameAs(original.entityList.get(1))
.hasFieldOrPropertyWithValue("code", "B");

assertThat(clone.entityList.get(2))
.isNotSameAs(original.entityList.get(2))
.hasFieldOrPropertyWithValue("code", "C");

assertThat(clone.codeToEntity)
.hasSize(3)
.isNotSameAs(original.codeToEntity);

assertThat(clone.codeToEntity.get("A"))
.isSameAs(clone.entityList.get(0));

assertThat(clone.codeToEntity.get("B"))
.isSameAs(clone.entityList.get(1));

assertThat(clone.codeToEntity.get("C"))
.isSameAs(clone.entityList.get(2));
}

private static class MaxStackFrameFinder {
int maxStackFrames = 0;

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading