/*
* Copyright (C) 2006 Joe Walnes.
* Copyright (C) 2006, 2007, 2008 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 20. February 2006 by Mauro Talevi
*/
package com.thoughtworks.acceptance;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import com.thoughtworks.xstream.testutil.TimeZoneChanger;
/**
* @author Paul Hammant
* @author Ian Cartwright
* @author Mauro Talevi
* @author Jörg Schaible
* @author Guilherme Silveira
*/
public class AttributeTest extends AbstractAcceptanceTest {
protected void setUp() throws Exception {
super.setUp();
TimeZoneChanger.change("GMT");
}
protected void tearDown() throws Exception {
TimeZoneChanger.reset();
super.tearDown();
}
public static class One implements HasID {
public ID id;
public Two two;
public void setID(ID id) {
this.id = id;
}
}
public static interface HasID {
void setID(ID id);
}
public static class Two {}
public static class Three {
public Date date;
}
public static class ID {
public ID(String value) {
this.value = value;
}
public String value;
}
private static class MyIDConverter extends AbstractSingleValueConverter {
public boolean canConvert(Class type) {
return type.equals(ID.class);
}
public String toString(Object obj) {
return obj == null ? null : ((ID) obj).value;
}
public Object fromString(String str) {
return new ID(str);
}
}
static class C
{
private Date dt;
private String str;
private int i;
C() {
// for JDK 1.3
}
C(Date dt, String st, int i)
{
this.dt = dt;
this.str = st;
this.i = i;
}
}
public void testAllowsAttributeWithCustomConverterAndFieldName() {
One one = new One();
one.two = new Two();
one.id = new ID("hullo");
xstream.alias("one", One.class);
xstream.useAttributeFor("id", ID.class);
xstream.registerConverter(new MyIDConverter());
String expected =
"<one id=\"hullo\">\n" +
" <two/>\n" +
"</one>";
assertBothWays(one, expected);
}
public void testDoesNotAllowAttributeWithCustomConverterAndDifferentFieldName() {
One one = new One();
one.two = new Two();
one.id = new ID("hullo");
xstream.alias("one", One.class);
xstream.useAttributeFor("foo", ID.class);
xstream.registerConverter(new MyIDConverter());
String expected =
"<one>\n" +
" <id>hullo</id>\n" +
" <two/>\n" +
"</one>";
assertBothWays(one, expected);
}
public void testAllowsAttributeWithKnownConverterAndFieldName() throws Exception {
Three three = new Three();
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
three.date = format.parse("19/02/2006");
xstream.alias("three", Three.class);
xstream.useAttributeFor("date", Date.class);
String expected =
"<three date=\"2006-02-19 00:00:00.0 GMT\"/>";
assertBothWays(three, expected);
}
public void testAllowsAttributeWithArbitraryFieldType() {
One one = new One();
one.two = new Two();
one.id = new ID("hullo");
xstream.alias("one", One.class);
xstream.useAttributeFor(ID.class);
xstream.registerConverter(new MyIDConverter());
String expected =
"<one id=\"hullo\">\n" +
" <two/>\n" +
"</one>";
assertBothWays(one, expected);
}
public void testDoesNotAllowAttributeWithNullAttribute() {
One one = new One();
one.two = new Two();
xstream.alias("one", One.class);
xstream.useAttributeFor(ID.class);
xstream.registerConverter(new MyIDConverter());
String expected =
"<one>\n" +
" <two/>\n" +
"</one>";
assertBothWays(one, expected);
}
public void testAllowsAttributeToBeAliased() {
One one = new One();
one.two = new Two();
one.id = new ID("hullo");
xstream.alias("one", One.class);
xstream.aliasAttribute("id-alias", "id");
xstream.useAttributeFor("id", ID.class);
xstream.registerConverter(new MyIDConverter());
String expected =
"<one id-alias=\"hullo\">\n" +
" <two/>\n" +
"</one>";
assertBothWays(one, expected);
}
public void testCanHandleNullValues() {
C c = new C(null, null, 0);
xstream.alias("C", C.class);
xstream.useAttributeFor(Date.class);
xstream.useAttributeFor(String.class);
String expected =
"<C>\n" +
" <i>0</i>\n" +
"</C>";
assertBothWays(c, expected);
}
public void testCanHandlePrimitiveValues() {
C c = new C(null, null, 0);
xstream.alias("C", C.class);
xstream.useAttributeFor(Date.class);
xstream.useAttributeFor(String.class);
xstream.useAttributeFor(int.class);
String expected ="<C i=\"0\"/>";
assertBothWays(c, expected);
}
static class Name {
private String name;
Name() {
// for JDK 1.3
}
Name(String name) {
this.name = name;
}
}
static class Camera {
private String name;
protected Name n;
Camera() {
// for JDK 1.3
}
Camera(String name) {
this.name = name;
}
}
public void testAllowsAnAttributeForASpecificField() {
xstream.alias("camera", Camera.class);
xstream.useAttributeFor(Camera.class, "name");
Camera camera = new Camera("Rebel 350");
camera.n = new Name("foo");
String expected = "" +
"<camera name=\"Rebel 350\">\n" +
" <n>\n" +
" <name>foo</name>\n" +
" </n>\n" +
"</camera>";
assertBothWays(camera, expected);
}
public void testAllowsAnAttributeForASpecificAliasedField() {
xstream.alias("camera", Camera.class);
xstream.aliasAttribute(Camera.class, "name", "model");
Camera camera = new Camera("Rebel 350");
camera.n = new Name("foo");
String expected = "" +
"<camera model=\"Rebel 350\">\n" +
" <n>\n" +
" <name>foo</name>\n" +
" </n>\n" +
"</camera>";
assertBothWays(camera, expected);
}
static class PersonalizedCamera extends Camera {
private String owner;
PersonalizedCamera() {
// for JDK 1.3
}
PersonalizedCamera(String name, String owner) {
super(name);
this.owner = owner;
}
}
public void testAllowsAnAttributeForASpecificFieldInASuperClass() {
xstream.alias("camera", PersonalizedCamera.class);
xstream.useAttributeFor(Camera.class, "name");
PersonalizedCamera camera = new PersonalizedCamera("Rebel 350", "Guilherme");
camera.n = new Name("foo");
String expected = "" +
"<camera name=\"Rebel 350\">\n" +
" <n>\n" +
" <name>foo</name>\n" +
" </n>\n" +
" <owner>Guilherme</owner>\n" +
"</camera>";
assertBothWays(camera, expected);
}
public void testAllowsAnAttributeForAFieldOfASpecialTypeAlsoInASuperClass() {
xstream.alias("camera", PersonalizedCamera.class);
xstream.useAttributeFor("name", String.class);
PersonalizedCamera camera = new PersonalizedCamera("Rebel 350", "Guilherme");
camera.n = new Name("foo");
String expected = "" +
"<camera name=\"Rebel 350\">\n" +
" <n name=\"foo\"/>\n" +
" <owner>Guilherme</owner>\n" +
"</camera>";
assertBothWays(camera, expected);
}
public static class TransientIdField {
transient String id;
String name;
public TransientIdField() {
// for JDK 1.3
}
public TransientIdField(String id, String name) {
this.id = id;
this.name = name;
}
public boolean equals(Object obj) {
return name.equals(((TransientIdField)obj).name);
}
}
public void testAttributeNamedLikeTransientFieldDoesNotAbortDeserializationOfFollowingFields() {
xstream.setMode(XStream.ID_REFERENCES);
xstream.alias("transient", TransientIdField.class);
TransientIdField field = new TransientIdField("foo", "test");
String xml = "" //
+ "<transient id=\"1\">\n" //
+ " <name>test</name>\n" //
+ "</transient>";
assertBothWays(field, xml);
}
static class Person {
String _name;
int _age;
Person() {} // JDK 1.3
Person(String name, int age) {
this._name = name;
this._age = age;
}
};
public void testAttributeMayHaveXmlUnfriendlyName() {
xstream.alias("person", Person.class);
xstream.useAttributeFor(Person.class, "_name");
xstream.useAttributeFor(Person.class, "_age");
Person person = new Person("joe", 25);
String xml = "<person __name=\"joe\" __age=\"25\"/>";
assertBothWays(person, xml);
}
}