001: /*
002: * Copyright (C) 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 20. February 2006 by Mauro Talevi
011: */
012: package com.thoughtworks.acceptance;
013:
014: import java.text.DateFormat;
015: import java.text.SimpleDateFormat;
016: import java.util.Date;
017:
018: import com.thoughtworks.xstream.XStream;
019: import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
020: import com.thoughtworks.xstream.testutil.TimeZoneChanger;
021:
022: /**
023: * @author Paul Hammant
024: * @author Ian Cartwright
025: * @author Mauro Talevi
026: * @author Jörg Schaible
027: * @author Guilherme Silveira
028: */
029: public class AttributeTest extends AbstractAcceptanceTest {
030:
031: protected void setUp() throws Exception {
032: super .setUp();
033: TimeZoneChanger.change("GMT");
034: }
035:
036: protected void tearDown() throws Exception {
037: TimeZoneChanger.reset();
038: super .tearDown();
039: }
040:
041: public static class One implements HasID {
042: public ID id;
043: public Two two;
044:
045: public void setID(ID id) {
046: this .id = id;
047: }
048: }
049:
050: public static interface HasID {
051: void setID(ID id);
052: }
053:
054: public static class Two {
055: }
056:
057: public static class Three {
058: public Date date;
059: }
060:
061: public static class ID {
062: public ID(String value) {
063: this .value = value;
064: }
065:
066: public String value;
067: }
068:
069: private static class MyIDConverter extends
070: AbstractSingleValueConverter {
071: public boolean canConvert(Class type) {
072: return type.equals(ID.class);
073: }
074:
075: public String toString(Object obj) {
076: return obj == null ? null : ((ID) obj).value;
077: }
078:
079: public Object fromString(String str) {
080: return new ID(str);
081: }
082: }
083:
084: static class C {
085: private Date dt;
086: private String str;
087: private int i;
088:
089: C() {
090: // for JDK 1.3
091: }
092:
093: C(Date dt, String st, int i) {
094: this .dt = dt;
095: this .str = st;
096: this .i = i;
097: }
098: }
099:
100: public void testAllowsAttributeWithCustomConverterAndFieldName() {
101: One one = new One();
102: one.two = new Two();
103: one.id = new ID("hullo");
104:
105: xstream.alias("one", One.class);
106: xstream.useAttributeFor("id", ID.class);
107: xstream.registerConverter(new MyIDConverter());
108:
109: String expected = "<one id=\"hullo\">\n" + " <two/>\n"
110: + "</one>";
111: assertBothWays(one, expected);
112: }
113:
114: public void testDoesNotAllowAttributeWithCustomConverterAndDifferentFieldName() {
115: One one = new One();
116: one.two = new Two();
117: one.id = new ID("hullo");
118:
119: xstream.alias("one", One.class);
120: xstream.useAttributeFor("foo", ID.class);
121: xstream.registerConverter(new MyIDConverter());
122:
123: String expected = "<one>\n" + " <id>hullo</id>\n"
124: + " <two/>\n" + "</one>";
125: assertBothWays(one, expected);
126: }
127:
128: public void testAllowsAttributeWithKnownConverterAndFieldName()
129: throws Exception {
130: Three three = new Three();
131: DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
132: three.date = format.parse("19/02/2006");
133:
134: xstream.alias("three", Three.class);
135: xstream.useAttributeFor("date", Date.class);
136:
137: String expected = "<three date=\"2006-02-19 00:00:00.0 GMT\"/>";
138: assertBothWays(three, expected);
139: }
140:
141: public void testAllowsAttributeWithArbitraryFieldType() {
142: One one = new One();
143: one.two = new Two();
144: one.id = new ID("hullo");
145:
146: xstream.alias("one", One.class);
147: xstream.useAttributeFor(ID.class);
148: xstream.registerConverter(new MyIDConverter());
149:
150: String expected = "<one id=\"hullo\">\n" + " <two/>\n"
151: + "</one>";
152: assertBothWays(one, expected);
153: }
154:
155: public void testDoesNotAllowAttributeWithNullAttribute() {
156: One one = new One();
157: one.two = new Two();
158:
159: xstream.alias("one", One.class);
160: xstream.useAttributeFor(ID.class);
161: xstream.registerConverter(new MyIDConverter());
162:
163: String expected = "<one>\n" + " <two/>\n" + "</one>";
164: assertBothWays(one, expected);
165: }
166:
167: public void testAllowsAttributeToBeAliased() {
168: One one = new One();
169: one.two = new Two();
170: one.id = new ID("hullo");
171:
172: xstream.alias("one", One.class);
173: xstream.aliasAttribute("id-alias", "id");
174: xstream.useAttributeFor("id", ID.class);
175: xstream.registerConverter(new MyIDConverter());
176:
177: String expected = "<one id-alias=\"hullo\">\n" + " <two/>\n"
178: + "</one>";
179: assertBothWays(one, expected);
180: }
181:
182: public void testCanHandleNullValues() {
183: C c = new C(null, null, 0);
184: xstream.alias("C", C.class);
185: xstream.useAttributeFor(Date.class);
186: xstream.useAttributeFor(String.class);
187: String expected = "<C>\n" + " <i>0</i>\n" + "</C>";
188: assertBothWays(c, expected);
189: }
190:
191: public void testCanHandlePrimitiveValues() {
192: C c = new C(null, null, 0);
193: xstream.alias("C", C.class);
194: xstream.useAttributeFor(Date.class);
195: xstream.useAttributeFor(String.class);
196: xstream.useAttributeFor(int.class);
197: String expected = "<C i=\"0\"/>";
198: assertBothWays(c, expected);
199: }
200:
201: static class Name {
202: private String name;
203:
204: Name() {
205: // for JDK 1.3
206: }
207:
208: Name(String name) {
209: this .name = name;
210: }
211: }
212:
213: static class Camera {
214: private String name;
215: protected Name n;
216:
217: Camera() {
218: // for JDK 1.3
219: }
220:
221: Camera(String name) {
222: this .name = name;
223: }
224: }
225:
226: public void testAllowsAnAttributeForASpecificField() {
227: xstream.alias("camera", Camera.class);
228: xstream.useAttributeFor(Camera.class, "name");
229: Camera camera = new Camera("Rebel 350");
230: camera.n = new Name("foo");
231: String expected = "" + "<camera name=\"Rebel 350\">\n"
232: + " <n>\n" + " <name>foo</name>\n" + " </n>\n"
233: + "</camera>";
234: assertBothWays(camera, expected);
235: }
236:
237: public void testAllowsAnAttributeForASpecificAliasedField() {
238: xstream.alias("camera", Camera.class);
239: xstream.aliasAttribute(Camera.class, "name", "model");
240: Camera camera = new Camera("Rebel 350");
241: camera.n = new Name("foo");
242: String expected = "" + "<camera model=\"Rebel 350\">\n"
243: + " <n>\n" + " <name>foo</name>\n" + " </n>\n"
244: + "</camera>";
245: assertBothWays(camera, expected);
246: }
247:
248: static class PersonalizedCamera extends Camera {
249: private String owner;
250:
251: PersonalizedCamera() {
252: // for JDK 1.3
253: }
254:
255: PersonalizedCamera(String name, String owner) {
256: super (name);
257: this .owner = owner;
258: }
259: }
260:
261: public void testAllowsAnAttributeForASpecificFieldInASuperClass() {
262: xstream.alias("camera", PersonalizedCamera.class);
263: xstream.useAttributeFor(Camera.class, "name");
264: PersonalizedCamera camera = new PersonalizedCamera("Rebel 350",
265: "Guilherme");
266: camera.n = new Name("foo");
267: String expected = "" + "<camera name=\"Rebel 350\">\n"
268: + " <n>\n" + " <name>foo</name>\n" + " </n>\n"
269: + " <owner>Guilherme</owner>\n" + "</camera>";
270: assertBothWays(camera, expected);
271: }
272:
273: public void testAllowsAnAttributeForAFieldOfASpecialTypeAlsoInASuperClass() {
274: xstream.alias("camera", PersonalizedCamera.class);
275: xstream.useAttributeFor("name", String.class);
276: PersonalizedCamera camera = new PersonalizedCamera("Rebel 350",
277: "Guilherme");
278: camera.n = new Name("foo");
279: String expected = "" + "<camera name=\"Rebel 350\">\n"
280: + " <n name=\"foo\"/>\n"
281: + " <owner>Guilherme</owner>\n" + "</camera>";
282: assertBothWays(camera, expected);
283: }
284:
285: public static class TransientIdField {
286: transient String id;
287: String name;
288:
289: public TransientIdField() {
290: // for JDK 1.3
291: }
292:
293: public TransientIdField(String id, String name) {
294: this .id = id;
295: this .name = name;
296: }
297:
298: public boolean equals(Object obj) {
299: return name.equals(((TransientIdField) obj).name);
300: }
301: }
302:
303: public void testAttributeNamedLikeTransientFieldDoesNotAbortDeserializationOfFollowingFields() {
304: xstream.setMode(XStream.ID_REFERENCES);
305: xstream.alias("transient", TransientIdField.class);
306:
307: TransientIdField field = new TransientIdField("foo", "test");
308: String xml = "" //
309: + "<transient id=\"1\">\n" //
310: + " <name>test</name>\n" //
311: + "</transient>";
312:
313: assertBothWays(field, xml);
314: }
315:
316: static class Person {
317: String _name;
318: transient int _age;
319:
320: Person(String name, int age) {
321: this ._name = name;
322: this ._age = age;
323: }
324: };
325:
326: // FIXME: reader.getAttribute(name) does not escape the given name anymore
327: public void XXXtestAttributeMayHaveXmlUnfriendlyName() {
328: xstream.alias("person", Person.class);
329: xstream.useAttributeFor(Person.class, "_name");
330: xstream.useAttributeFor(Person.class, "_age");
331: Person person = new Person("joe", 25);
332: String xml = "<person __name=\"joe\" __age=\"25\"/>";
333: assertBothWays(person, xml);
334: }
335: }
|