package org.javersion.object; import static com.google.common.collect.ImmutableSet.copyOf; import static com.google.common.collect.ImmutableSet.of; import static java.util.Collections.singleton; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; import static org.javersion.core.Persistent.array; import static org.javersion.path.PropertyPath.parse; import static org.junit.Assert.assertThat; import java.util.*; import java.util.function.Function; import org.javersion.core.Persistent; import org.javersion.object.PolymorphismTest.Cat; import org.javersion.object.PolymorphismTest.Pet; import org.javersion.object.ReferencesTest.Node; import org.javersion.path.PropertyPath; import org.javersion.util.Check; import org.junit.Test; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; public class SetTest { @Versionable public static class NodeSet { private Set<Node> nodes = Sets.newLinkedHashSet(); } public static class NodeExt extends Node { Set<NodeExt> nodes = Sets.newLinkedHashSet(); SortedSet<String> sorted = new TreeSet<>(); } @Versionable public static class DoubleSet { Set<Double> doubles = new HashSet<>(); Set<Float> floats = new HashSet<>(); } @Versionable(alias = "MyComposite") @SetKey({ "first", "second", "third" }) static class MyComposite { final int first; final String second; @VersionProperty("third") final int another; @SuppressWarnings("unused") private MyComposite() { first = 0; second = null; another = 0; } public MyComposite(int first, String second, int another) { this.first = first; this.second = second; this.another = another; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MyComposite that = (MyComposite) o; if (first != that.first) return false; if (another != that.another) return false; return second != null ? second.equals(that.second) : that.second == null; } @Override public int hashCode() { int result = first; result = 31 * result + (second != null ? second.hashCode() : 0); result = 31 * result + another; return result; } } @Versionable static class MyCompositeContainerField { @SetKey({ "first", "second", "third" }) Set<MyComposite> set = new HashSet<>(); } @Versionable static class MyCompositeContainerType { Set<MyComposite> set = new HashSet<>(); } @Versionable static class MyBadType { @Id public int id; } @Versionable static class MyBadTypeContainer { @SetKey("id") public Set<MyBadType> set; } @Versionable static class ReadOnlyId { int a; int b; private ReadOnlyId() {} public ReadOnlyId(int a, int b) { this.a = a; this.b = b; } @Id public int getId() { return a + b; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof ReadOnlyId) { ReadOnlyId other = (ReadOnlyId) obj; return getId() == other.getId(); } return false; } @Override public int hashCode() { return getId(); } } @Versionable static class ReadOnlyIdContainer { Set<ReadOnlyId> set; } @Versionable static class PetContainer { @SetKey("name") Set<Pet> set; } @SetKey(by = SetWrapper.Key.class) static class SetWrapper { final Set<Integer> set; SetWrapper(Integer... set) { this(copyOf(set)); } @VersionCreator SetWrapper(Set<Integer> set) { this.set = Check.notNullOrEmpty(set, "set"); } @VersionValue Set<Integer> toSet() { return set; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SetWrapper that = (SetWrapper) o; return set.equals(that.set); } @Override public int hashCode() { return set.hashCode(); } static class Key implements Function<SetWrapper, Integer> { @Override public Integer apply(SetWrapper setWrapper) { return setWrapper.set.stream().reduce((a, b) -> a^b).get(); } } } public static TypeMappings typeMappings = TypeMappings.builder() .withClass(Node.class) .havingSubClasses(NodeExt.class) .asReferenceForPath("nodes") .build(); private final ObjectSerializer<NodeSet> nodeSetSerializer = new ObjectSerializer<>(NodeSet.class, typeMappings); private final ObjectSerializer<NodeExt> nodeExtSerializer = new ObjectSerializer<>(NodeExt.class, typeMappings); private final ObjectSerializer<DoubleSet> doubleSetSerializer = new ObjectSerializer<>(DoubleSet.class, typeMappings); private final MyComposite c1 = new MyComposite(1, "foo", 1); private final MyComposite c2 = new MyComposite(1, "bar", 1); private final MyComposite c3 = new MyComposite(2, "foo", 1); private final MyComposite c4 = new MyComposite(2, "foo", 2); @Test public void Write_And_Read_NodeSet() { NodeSet nodeSet = new NodeSet(); Node node1 = new Node(123); Node node2 = new Node(456); node1.left = node1; node1.right = node2; node2.left = node1; node2.right = node2; nodeSet.nodes.add(node1); nodeSet.nodes.add(node2); Map<PropertyPath, Object> map = nodeSetSerializer.toPropertyMap(nodeSet); nodeSet = nodeSetSerializer.fromPropertyMap(map); assertThat(nodeSet.nodes, hasSize(2)); Iterator<Node> iter = nodeSet.nodes.iterator(); node1 = iter.next(); node2 = iter.next(); if (node1.id != 123) { Node tmp = node2; node2 = node1; node1 = tmp; } assertThat(node1.id, equalTo(123)); assertThat(node2.id, equalTo(456)); assertThat(node1.left, sameInstance(node1)); assertThat(node1.right, sameInstance(node2)); assertThat(node2.left, sameInstance(node1)); assertThat(node2.right, sameInstance(node2)); } @Test public void NodeExt_Containing_Itself_In_a_Set() { NodeExt nodeExt = new NodeExt(); nodeExt.id = 789; nodeExt.nodes.add(nodeExt); nodeExt.sorted.add("omega"); nodeExt.sorted.add("alpha"); nodeExt.sorted.add("beta"); Map<PropertyPath, Object> map = nodeExtSerializer.toPropertyMap(nodeExt); nodeExt = nodeExtSerializer.fromPropertyMap(map); assertThat(nodeExt.id, equalTo(789)); assertThat(nodeExt.nodes, equalTo(singleton(nodeExt))); Iterator<String> iter = nodeExt.sorted.iterator(); assertThat(iter.next(), equalTo("alpha")); assertThat(iter.next(), equalTo("beta")); assertThat(iter.next(), equalTo("omega")); } @Test public void double_set() { Set<Double> doubles = of( Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.1 ); Set<Float> floats = of( Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, (float) 1.1 ); DoubleSet dset = new DoubleSet(); dset.doubles = doubles; dset.floats = floats; dset = doubleSetSerializer.fromPropertyMap(doubleSetSerializer.toPropertyMap(dset)); assertThat(dset.doubles, equalTo(doubles)); assertThat(dset.floats, equalTo(floats)); } @Test public void composite_id_field_annotation() { ObjectSerializer<MyCompositeContainerField> compositeSerializer = new ObjectSerializer<>(MyCompositeContainerField.class); MyCompositeContainerField container = new MyCompositeContainerField(); container.set = getContainerSet(); Map<PropertyPath, Object> properties = compositeSerializer.toPropertyMap(container); assertContainerProperties(properties); container = compositeSerializer.fromPropertyMap(properties); assertContainerSet(container.set); } @Test public void composite_id_type_annotation() { ObjectSerializer<MyCompositeContainerType> compositeSerializer = new ObjectSerializer<>(MyCompositeContainerType.class); MyCompositeContainerType container = new MyCompositeContainerType(); container.set = getContainerSet(); Map<PropertyPath, Object> properties = compositeSerializer.toPropertyMap(container); assertContainerProperties(properties); container = compositeSerializer.fromPropertyMap(properties); assertContainerSet(container.set); } @Test(expected = IllegalArgumentException.class) public void both_id_and_setKey_not_allowed() { new ObjectSerializer<>(MyBadTypeContainer.class); } @Test public void read_only_id() { ObjectSerializer<ReadOnlyIdContainer> serializer = new ObjectSerializer<ReadOnlyIdContainer>(ReadOnlyIdContainer.class); ReadOnlyId roi1 = new ReadOnlyId(1, 3); ReadOnlyId roi2 = new ReadOnlyId(2, 3); ReadOnlyIdContainer container = new ReadOnlyIdContainer(); container.set = of(roi1, roi2); Map<PropertyPath, Object> properties = serializer.toPropertyMap(container); assertThat(properties.keySet(), hasSize(8)); assertThat(properties.get(parse("set[4].a")), equalTo(1l)); assertThat(properties.get(parse("set[4].b")), equalTo(3l)); assertThat(properties.get(parse("set[5].a")), equalTo(2l)); assertThat(properties.get(parse("set[5].b")), equalTo(3l)); container = serializer.fromPropertyMap(properties); assertThat(container.set, equalTo(of(roi2, roi1))); } @Test public void set_of_pets() { ObjectSerializer<PetContainer> serializer = new ObjectSerializer<>(PetContainer.class); PetContainer container = new PetContainer(); container.set = of(new Cat("cat")); Map<PropertyPath, Object> properties = serializer.toPropertyMap(container); container = serializer.fromPropertyMap(properties); Pet pet = container.set.iterator().next(); assertThat(pet, instanceOf(Cat.class)); assertThat(pet.name, equalTo("cat")); } @Test public void functional_set_key() { final ImmutableSet<SetWrapper> input = of( new SetWrapper(1, 2), new SetWrapper(1, 3)); ObjectSerializer<Set<SetWrapper>> serializer = new ObjectSerializer<>(new TypeToken<Set<SetWrapper>>() {}); Map<PropertyPath, Object> properties = serializer.toPropertyMap(input); final Map<PropertyPath, Object> expected = ImmutableMap.<PropertyPath, Object>builder() .put(parse(""), array()) .put(parse("[3]"), array()) .put(parse("[3][1]"), 1l) .put(parse("[3][2]"), 2l) .put(parse("[2]"), array()) .put(parse("[2][1]"), 1l) .put(parse("[2][3]"), 3l) .build(); assertThat(properties, equalTo(expected)); Set<SetWrapper> output = serializer.fromPropertyMap(properties); assertThat(output, equalTo(input)); } private Set<MyComposite> getContainerSet() { return of(c1, c2, c3, c4); } private void assertContainerSet(Set<MyComposite> set) { assertThat(set, equalTo(of(c4, c3, c2, c1))); } private void assertContainerProperties(Map<PropertyPath, Object> properties) { assertThat(properties.keySet(), hasSize(18)); assertThat(properties.get(parse("set[1][\"foo\"][1].first")), equalTo(1l)); assertThat(properties.get(parse("set[1][\"bar\"][1].second")), equalTo("bar")); assertThat(properties.get(parse("set[2][\"foo\"][1]")), equalTo(Persistent.object("MyComposite"))); assertThat(properties.get(parse("set[2][\"foo\"][2].third")), equalTo(2l)); } }