/*
* Copyright (C) 2007 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 23. November 2007 by Joerg Schaible
*/
package com.thoughtworks.acceptance.annotations;
import com.thoughtworks.acceptance.AbstractAcceptanceTest;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Tests annotations defining aliases for classes or fields.
*
* @author Chung-Onn Cheong
* @author Mauro Talevi
* @author Guilherme Silveira
* @author Jörg Schaible
*/
public class AliasTest extends AbstractAcceptanceTest {
@Override
protected XStream createXStream() {
XStream xstream = super.createXStream();
xstream.autodetectAnnotations(true);
return xstream;
}
public void testAnnotationForClassWithAnnotatedConverter() {
Map<String, Person> map = new HashMap<String, Person>();
map.put("first person", new Person("john doe"));
map.put("second person", new Person("jane doe"));
String xml = ""
+ "<map>\n"
+ " <entry>\n"
+ " <string>second person</string>\n"
+ " <person>jane doe</person>\n"
+ " </entry>\n"
+ " <entry>\n"
+ " <string>first person</string>\n"
+ " <person>john doe</person>\n"
+ " </entry>\n"
+ "</map>";
assertBothWaysNormalized(map, xml, "map", "entry", "string");
}
public void testAnnotationForFieldWithAliasCycle() {
Cycle cycle = new Cycle();
cycle.internal = cycle;
String xml = "" //
+ "<cycle>\n" //
+ " <oops reference=\"..\"/>\n" //
+ "</cycle>";
assertBothWays(cycle, xml);
}
@XStreamAlias("cycle")
public static class Cycle {
@XStreamAlias("oops")
private Cycle internal;
}
public void testAnnotationForField() {
List<String> nickNames = new ArrayList<String>();
nickNames.add("johnny");
nickNames.add("jack");
CustomPerson person = new CustomPerson("john", "doe", 25, nickNames);
String expectedXml = ""
+ "<person>\n"
+ " <first-name>john</first-name>\n"
+ " <last-name>doe</last-name>\n"
+ " <age-in-years>25</age-in-years>\n"
+ " <nick-names>\n"
+ " <string>johnny</string>\n"
+ " <string>jack</string>\n"
+ " </nick-names>\n"
+ "</person>";
assertBothWays(person, expectedXml);
}
@XStreamAlias("person")
public static class CustomPerson {
@XStreamAlias("first-name")
String firstName;
@XStreamAlias("last-name")
String lastName;
@XStreamAlias("age-in-years")
int ageInYears;
@XStreamAlias("nick-names")
List<String> nickNames;
public CustomPerson(
String firstName, String lastName, int ageInYears, List<String> nickNames) {
this.firstName = firstName;
this.lastName = lastName;
this.ageInYears = ageInYears;
this.nickNames = nickNames;
}
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof CustomPerson)) return false;
return toString().equals(obj.toString());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb
.append("firstName:")
.append(firstName)
.append(",lastName:")
.append(lastName)
.append(",ageInYears:")
.append(ageInYears)
.append(",nickNames:")
.append(nickNames);
return sb.toString();
}
}
@XStreamAlias("person")
@XStreamConverter(PersonConverter.class)
public static class Person {
String name;
AddressBookInfo addressBook;
public Person(String name) {
this.name = name;
addressBook = new AddressBook();
}
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof Person)) return false;
return addressBook.equals(((Person)obj).addressBook);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("name:").append(name).append("addresbook:").append(addressBook);
return sb.toString();
}
}
@XStreamAlias(value = "addressbook-info", impl = AddressBook.class)
public interface AddressBookInfo {
public List<AddressInfo> getAddresses();
public void setAddresses(List<AddressInfo> address);
}
@XStreamAlias("addressbookAlias")
public static class AddressBook implements AddressBookInfo {
// @XStreamContainedType
private List<AddressInfo> addresses;
public AddressBook() {
addresses = new ArrayList<AddressInfo>();
addresses.add(new Address("Home Address", 111));
addresses.add(new Address("Office Address", 222));
}
public List<AddressInfo> getAddresses() {
return addresses;
}
public void setAddresses(List<AddressInfo> addresses) {
this.addresses = addresses;
}
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof AddressBookInfo)) return false;
return addresses.containsAll(((AddressBookInfo)obj).getAddresses());
}
}
@XStreamAlias(value = "addressinfoAlias", impl = Address.class)
public interface AddressInfo {
public String addr();
public int zipcode();
}
@XStreamAlias(value = "addressAlias")
public static class Address implements AddressInfo {
private String addr;
private int zipcode;
public Address(String addr, int zipcode) {
this.addr = addr;
this.zipcode = zipcode;
}
public String addr() {
return addr;
}
public int zipcode() {
return zipcode;
}
}
public static class PersonConverter implements Converter {
public PersonConverter() {
}
public String toString(Object obj) {
return ((Person)obj).name;
}
public Object fromString(String str) {
return new Person(str);
}
public boolean canConvert(Class type) {
return type == Person.class;
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
writer.setValue(toString(source));
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
return fromString(reader.getValue());
}
}
static class Dash {
@XStreamAlias("camel-case")
int camelCase = 5;
}
public void testAnnotationForFieldWithAttributeDefinitionForFieldType() {
xstream.alias("dash", Dash.class);
xstream.useAttributeFor(int.class);
String xml = "<dash camel-case=\"5\"/>";
assertBothWays(new Dash(), xml);
}
public static abstract class Aged {
@XStreamAlias("age")
@XStreamAsAttribute
private Integer id;
Aged(Integer id) {
this.id = id;
}
}
@XStreamAlias("thing")
public static class AgedThing extends Aged {
@XStreamAsAttribute
private String name;
AgedThing(String name, Integer id) {
super(id);
this.name = name;
}
}
public void testAnnotationIsInheritedTogetherWithAsAttribute() {
String xml = "<thing age=\"99\" name=\"Name\"/>";
assertBothWays(new AgedThing("Name", 99), xml);
}
}