/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2015 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 14. August 2004 by Joe Walnes
*/
package com.thoughtworks.acceptance;
import com.thoughtworks.acceptance.objects.StandardObject;
import com.thoughtworks.xstream.InitializationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class ImplicitCollectionTest extends AbstractAcceptanceTest {
public static class Farm extends StandardObject {
int size;
List animals = new ArrayList();
public Farm(int size) {
this.size = size;
}
public void add(Animal animal) {
animals.add(animal);
}
}
public static class Animal extends StandardObject implements Comparable {
String name;
public Animal(String name) {
this.name = name;
}
public int compareTo(Object o) {
return name.compareTo(((Animal)o).name);
}
}
protected void setUp() throws Exception {
super.setUp();
xstream.alias("zoo", Zoo.class);
xstream.alias("farm", Farm.class);
xstream.alias("animal", Animal.class);
xstream.alias("room", Room.class);
xstream.alias("house", House.class);
xstream.alias("person", Person.class);
xstream.alias("area", Area.class);
xstream.alias("country", Country.class);
xstream.ignoreUnknownElements();
}
public void testWithout() {
Farm farm = new Farm(100);
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
String expected = "" +
"<farm>\n" +
" <size>100</size>\n" +
" <animals>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" </animals>\n" +
"</farm>";
assertBothWays(farm, expected);
}
public void testWithList() {
Farm farm = new Farm(100);
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
String expected = "" +
"<farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
"</farm>";
xstream.addImplicitCollection(Farm.class, "animals");
assertBothWays(farm, expected);
}
public static class MegaFarm extends Farm {
String separator = "---";
List names;
public MegaFarm(int size) {
super(size);
}
}
public void testInheritsImplicitCollectionFromSuperclass() {
xstream.alias("MEGA-farm", MegaFarm.class);
Farm farm = new MegaFarm(100); // subclass
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
String expected = "" +
"<MEGA-farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <separator>---</separator>\n" +
"</MEGA-farm>";
xstream.addImplicitCollection(Farm.class, "animals");
assertBothWays(farm, expected);
}
public void testSupportsInheritedAndDirectDeclaredImplicitCollectionAtOnce() {
xstream.alias("MEGA-farm", MegaFarm.class);
MegaFarm farm = new MegaFarm(100); // subclass
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
farm.names = new ArrayList();
farm.names.add("McDonald");
farm.names.add("Ponte Rosa");
String expected = "" +
"<MEGA-farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <separator>---</separator>\n" +
" <name>McDonald</name>\n" +
" <name>Ponte Rosa</name>\n" +
"</MEGA-farm>";
xstream.addImplicitCollection(Farm.class, "animals");
xstream.addImplicitCollection(MegaFarm.class, "names", "name", String.class);
assertBothWays(farm, expected);
}
public void testInheritedAndDirectDeclaredImplicitCollectionAtOnceIsNotDeclarationSequenceDependent() {
xstream.alias("MEGA-farm", MegaFarm.class);
MegaFarm farm = new MegaFarm(100); // subclass
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
farm.names = new ArrayList();
farm.names.add("McDonald");
farm.names.add("Ponte Rosa");
String expected = "" +
"<MEGA-farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <separator>---</separator>\n" +
" <name>McDonald</name>\n" +
" <name>Ponte Rosa</name>\n" +
"</MEGA-farm>";
xstream.addImplicitCollection(MegaFarm.class, "names", "name", String.class);
xstream.addImplicitCollection(Farm.class, "animals");
assertBothWays(farm, expected);
}
public void testAllowsSubclassToOverrideImplicitCollectionInSuperclass() {
xstream.alias("MEGA-farm", MegaFarm.class);
Farm farm = new MegaFarm(100); // subclass
farm.add(new Animal("Cow"));
farm.add(new Animal("Sheep"));
String expected = "" +
"<MEGA-farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <separator>---</separator>\n" +
"</MEGA-farm>";
xstream.addImplicitCollection(MegaFarm.class, "animals");
assertBothWays(farm, expected);
}
public void testAllowDifferentImplicitCollectionDefinitionsInSubclass() {
xstream.alias("MEGA-farm", MegaFarm.class);
Farm farm = new Farm(10);
farm.add(new Animal("Cod"));
farm.add(new Animal("Salmon"));
MegaFarm megaFarm = new MegaFarm(100); // subclass
megaFarm.add(new Animal("Cow"));
megaFarm.add(new Animal("Sheep"));
megaFarm.names = new ArrayList();
megaFarm.names.add("McDonald");
megaFarm.names.add("Ponte Rosa");
List list = new ArrayList();
list.add(farm);
list.add(megaFarm);
String expected = "" +
"<list>\n" +
" <farm>\n" +
" <size>10</size>\n" +
" <fish>\n" +
" <name>Cod</name>\n" +
" </fish>\n" +
" <fish>\n" +
" <name>Salmon</name>\n" +
" </fish>\n" +
" </farm>\n" +
" <MEGA-farm>\n" +
" <size>100</size>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <separator>---</separator>\n" +
" <name>McDonald</name>\n" +
" <name>Ponte Rosa</name>\n" +
" </MEGA-farm>\n" +
"</list>";
xstream.addImplicitCollection(Farm.class, "animals", "fish", Animal.class);
xstream.addImplicitCollection(MegaFarm.class, "animals");
xstream.addImplicitCollection(MegaFarm.class, "names", "name", String.class);
assertBothWays(list, expected);
}
public static class House extends StandardObject {
private List rooms = new ArrayList();
private String separator = "---";
private List people = new ArrayList();
public void add(Room room) {
rooms.add(room);
}
public void add(Person person) {
people.add(person);
}
public List getPeople() {
return Collections.unmodifiableList(people);
}
public List getRooms() {
return Collections.unmodifiableList(rooms);
}
}
public static class Room extends StandardObject {
private String name;
public Room(String name) {
this.name = name;
}
}
public static class Person extends StandardObject {
private String name;
private LinkedList emailAddresses = new LinkedList();
public Person(String name) {
this.name = name;
}
public void addEmailAddress(String email) {
emailAddresses.add(email);
}
}
public void testDefaultCollectionBasedOnType() {
House house = new House();
house.add(new Room("kitchen"));
house.add(new Room("bathroom"));
Person joe = new Person("joe");
joe.addEmailAddress("joe@house.org");
joe.addEmailAddress("joe.farmer@house.org");
house.add(joe);
Person jaimie = new Person("jaimie");
jaimie.addEmailAddress("jaimie@house.org");
jaimie.addEmailAddress("jaimie.farmer@house.org");
jaimie.addEmailAddress("jaimie.ann.farmer@house.org");
house.add(jaimie);
String expected = ""
+ "<house>\n"
+ " <room>\n"
+ " <name>kitchen</name>\n"
+ " </room>\n"
+ " <room>\n"
+ " <name>bathroom</name>\n"
+ " </room>\n"
+ " <separator>---</separator>\n"
+ " <person>\n"
+ " <name>joe</name>\n"
+ " <email>joe@house.org</email>\n"
+ " <email>joe.farmer@house.org</email>\n"
+ " </person>\n"
+ " <person>\n"
+ " <name>jaimie</name>\n"
+ " <email>jaimie@house.org</email>\n"
+ " <email>jaimie.farmer@house.org</email>\n"
+ " <email>jaimie.ann.farmer@house.org</email>\n"
+ " </person>\n"
+ "</house>";
xstream.addImplicitCollection(House.class, "rooms", Room.class);
xstream.addImplicitCollection(House.class, "people", Person.class);
xstream.addImplicitCollection(Person.class, "emailAddresses", "email", String.class);
House serializedHouse = (House)assertBothWays(house, expected);
assertEquals(house.getPeople(), serializedHouse.getPeople());
assertEquals(house.getRooms(), serializedHouse.getRooms());
}
public void testWithEMPTY_LIST() {
House house = new House();
house.people = Collections.EMPTY_LIST;
house.rooms = Collections.EMPTY_LIST;
xstream.addImplicitCollection(House.class, "rooms", Room.class);
xstream.addImplicitCollection(House.class, "people", Person.class);
String expected = ""
+ "<house>\n"
+ " <separator>---</separator>\n"
+ "</house>";
assertEquals(expected, xstream.toXML(house));
}
public static class Zoo extends StandardObject {
private Set animals;
public Zoo() {
this(new HashSet());
}
public Zoo(Set set) {
animals = set;
}
public void add(Animal animal) {
animals.add(animal);
}
}
public void testWithSet() {
Zoo zoo = new Zoo();
zoo.add(new Animal("Lion"));
zoo.add(new Animal("Ape"));
String expected = "" +
"<zoo>\n" +
" <animal>\n" +
" <name>Lion</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Ape</name>\n" +
" </animal>\n" +
"</zoo>";
xstream.addImplicitCollection(Zoo.class, "animals");
assertBothWaysNormalized(zoo, expected, "zoo", "animal", "name");
}
public void testWithDifferentDefaultImplementation() {
String xml = "" +
"<zoo>\n" +
" <animal>\n" +
" <name>Lion</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Ape</name>\n" +
" </animal>\n" +
"</zoo>";
xstream.addImplicitCollection(Zoo.class, "animals");
xstream.addDefaultImplementation(TreeSet.class, Set.class);
Zoo zoo = (Zoo)xstream.fromXML(xml);
assertTrue("Collection was a " + zoo.animals.getClass().getName(), zoo.animals instanceof TreeSet);
}
public void testWithSortedSet() {
Zoo zoo = new Zoo(new TreeSet());
zoo.add(new Animal("Lion"));
zoo.add(new Animal("Ape"));
String expected = "" +
"<zoo>\n" +
" <animal>\n" +
" <name>Ape</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Lion</name>\n" +
" </animal>\n" +
"</zoo>";
xstream.addImplicitCollection(Zoo.class, "animals");
xstream.addDefaultImplementation(TreeSet.class, Set.class);
assertBothWays(zoo, expected);
}
public static class Aquarium extends StandardObject {
private String name;
private List fish = new ArrayList();
public Aquarium(String name) {
this.name = name;
}
public void addFish(String fish) {
this.fish.add(fish);
}
}
public void testWithExplicitItemNameMatchingTheNameOfTheFieldWithTheCollection() {
Aquarium aquarium = new Aquarium("hatchery");
aquarium.addFish("salmon");
aquarium.addFish("halibut");
aquarium.addFish("snapper");
String expected = "" +
"<aquarium>\n" +
" <name>hatchery</name>\n" +
" <fish>salmon</fish>\n" +
" <fish>halibut</fish>\n" +
" <fish>snapper</fish>\n" +
"</aquarium>";
xstream.alias("aquarium", Aquarium.class);
xstream.addImplicitCollection(Aquarium.class, "fish", "fish", String.class);
assertBothWays(aquarium, expected);
}
public void testWithImplicitNameMatchingTheNameOfTheFieldWithTheCollection() {
Aquarium aquarium = new Aquarium("hatchery");
aquarium.addFish("salmon");
aquarium.addFish("halibut");
aquarium.addFish("snapper");
String expected = "" +
"<aquarium>\n" +
" <name>hatchery</name>\n" +
" <fish>salmon</fish>\n" +
" <fish>halibut</fish>\n" +
" <fish>snapper</fish>\n" +
"</aquarium>";
xstream.alias("aquarium", Aquarium.class);
xstream.alias("fish", String.class);
xstream.addImplicitCollection(Aquarium.class, "fish");
assertBothWays(aquarium, expected);
}
public void testWithAliasedItemNameMatchingTheAliasedNameOfTheFieldWithTheCollection() {
Aquarium aquarium = new Aquarium("hatchery");
aquarium.addFish("salmon");
aquarium.addFish("halibut");
aquarium.addFish("snapper");
String expected = "" +
"<aquarium>\n" +
" <name>hatchery</name>\n" +
" <animal>salmon</animal>\n" +
" <animal>halibut</animal>\n" +
" <animal>snapper</animal>\n" +
"</aquarium>";
xstream.alias("aquarium", Aquarium.class);
xstream.aliasField("animal", Aquarium.class, "fish");
xstream.addImplicitCollection(Aquarium.class, "fish", "animal", String.class);
assertBothWays(aquarium, expected);
}
public void testCanBeDeclaredOnlyForMatchingType() {
try {
xstream.addImplicitCollection(Animal.class, "name");
fail("Thrown " + InitializationException.class.getName() + " expected");
} catch (final InitializationException e) {
assertTrue(e.getMessage().indexOf("declares no collection") >= 0);
}
}
public void testWithNullElement() {
Farm farm = new Farm(100);
farm.add(null);
farm.add(new Animal("Cow"));
String expected = "" +
"<farm>\n" +
" <size>100</size>\n" +
" <null/>\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
"</farm>";
xstream.addImplicitCollection(Farm.class, "animals");
assertBothWays(farm, expected);
}
public void testWithAliasAndNullElement() {
Farm farm = new Farm(100);
farm.add(null);
farm.add(new Animal("Cow"));
String expected = "" +
"<farm>\n" +
" <size>100</size>\n" +
" <null/>\n" +
" <beast>\n" +
" <name>Cow</name>\n" +
" </beast>\n" +
"</farm>";
xstream.addImplicitCollection(Farm.class, "animals", "beast", Animal.class);
assertBothWays(farm, expected);
}
public static class Area extends Farm {
List animals = new ArrayList();
public Area(int size) {
super(size);
}
}
public void testWithHiddenList() {
Area area = new Area(1000);
area.add(new Animal("Cow"));
area.add(new Animal("Sheep"));
area.animals.add(new Animal("Falcon"));
area.animals.add(new Animal("Sparrow"));
String expected = "" +
"<area>\n" +
" <size>1000</size>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Falcon</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sparrow</name>\n" +
" </animal>\n" +
"</area>";
xstream.addImplicitCollection(Farm.class, "animals");
xstream.addImplicitCollection(Area.class, "animals");
assertBothWays(area, expected);
}
public void testWithHiddenListAndDifferentAlias() {
Area area = new Area(1000);
area.add(new Animal("Cow"));
area.add(new Animal("Sheep"));
area.animals.add(new Animal("Falcon"));
area.animals.add(new Animal("Sparrow"));
String expected = "" +
"<area>\n" +
" <size>1000</size>\n" +
" <domesticated defined-in=\"farm\">\n" +
" <name>Cow</name>\n" +
" </domesticated>\n" +
" <domesticated defined-in=\"farm\">\n" +
" <name>Sheep</name>\n" +
" </domesticated>\n" +
" <wild>\n" +
" <name>Falcon</name>\n" +
" </wild>\n" +
" <wild>\n" +
" <name>Sparrow</name>\n" +
" </wild>\n" +
"</area>";
xstream.addImplicitCollection(Farm.class, "animals", "domesticated", Animal.class);
xstream.addImplicitCollection(Area.class, "animals", "wild", Animal.class);
assertBothWays(area, expected);
}
public void testDoesNotInheritFromHiddenListOfSuperclass() {
Area area = new Area(1000);
area.add(new Animal("Cow"));
area.add(new Animal("Sheep"));
area.animals.add(new Animal("Falcon"));
area.animals.add(new Animal("Sparrow"));
String expected = "" +
"<area>\n" +
" <size>1000</size>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <animals>\n" +
" <animal>\n" +
" <name>Falcon</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sparrow</name>\n" +
" </animal>\n" +
" </animals>\n" +
"</area>";
xstream.addImplicitCollection(Farm.class, "animals");
assertBothWays(area, expected);
}
public void testDoesNotPropagateToHiddenListOfSuperclass() {
Area area = new Area(1000);
area.add(new Animal("Cow"));
area.add(new Animal("Sheep"));
area.animals.add(new Animal("Falcon"));
area.animals.add(new Animal("Sparrow"));
String expected = "" +
"<area>\n" +
" <size>1000</size>\n" +
" <animals defined-in=\"farm\">\n" +
" <animal>\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" </animals>\n" +
" <animal>\n" +
" <name>Falcon</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Sparrow</name>\n" +
" </animal>\n" +
"</area>";
xstream.addImplicitCollection(Area.class, "animals");
assertBothWays(area, expected);
}
public static class County extends Area {
public County() {
super(10);
}
}
public static class Country extends County {
List animals = new ArrayList();
}
public void testWithDoubleHiddenList() {
Country country = new Country();
country.add(new Animal("Cow"));
country.add(new Animal("Sheep"));
((Area)country).animals.add(new Animal("Falcon"));
((Area)country).animals.add(new Animal("Sparrow"));
country.animals.add(new Animal("Wale"));
country.animals.add(new Animal("Dolphin"));
String expected = "" +
"<country>\n" +
" <size>10</size>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Cow</name>\n" +
" </animal>\n" +
" <animal defined-in=\"farm\">\n" +
" <name>Sheep</name>\n" +
" </animal>\n" +
" <animal defined-in=\"area\">\n" +
" <name>Falcon</name>\n" +
" </animal>\n" +
" <animal defined-in=\"area\">\n" +
" <name>Sparrow</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Wale</name>\n" +
" </animal>\n" +
" <animal>\n" +
" <name>Dolphin</name>\n" +
" </animal>\n" +
"</country>";
xstream.addImplicitCollection(Farm.class, "animals");
xstream.addImplicitCollection(Area.class, "animals");
xstream.addImplicitCollection(Country.class, "animals");
assertBothWays(country, expected);
}
}