View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.csv;
18  
19  import static org.junit.jupiter.api.Assertions.*;
20  
21  import java.text.*;
22  import java.util.*;
23  
24  import org.apache.juneau.*;
25  import org.apache.juneau.annotation.*;
26  import org.apache.juneau.swap.*;
27  import org.junit.jupiter.api.*;
28  
29  class Csv_Test extends TestBase {
30  
31  	//====================================================================================================
32  	// testBasic
33  	//====================================================================================================
34  	@Test void a01_basic() throws Exception {
35  		var l = new LinkedList<>();
36  		l.add(new A("b1",1));
37  		l.add(new A("b2",2));
38  
39  		var s = CsvSerializer.DEFAULT;
40  		var r = s.serialize(l);
41  
42  		assertEquals("b,c\nb1,1\nb2,2\n", r);
43  	}
44  
45  	public static class A {
46  		public String b;
47  		public int c;
48  
49  		public A(String b, int c) {
50  			this.b = b;
51  			this.c = c;
52  		}
53  	}
54  
55  	//====================================================================================================
56  	// Test swaps on bean properties
57  	//====================================================================================================
58  	@Test void b01_swapOnBeanProperty() throws Exception {
59  		var l = new LinkedList<>();
60  		l.add(new B("user1", new Date(1000000)));
61  		l.add(new B("user2", new Date(2000000)));
62  
63  		var s = CsvSerializer.create().swaps(DateSwap.class).build();
64  		var r = s.serialize(l);
65  
66  		// Swaps should convert dates to yyyy-MM-dd format
67  		assertTrue(r.contains("1970-01-01") || r.contains("1969-12-31"), "Should have formatted dates but was: " + r);
68  		assertTrue(r.contains("user1"));
69  		assertTrue(r.contains("user2"));
70  	}
71  
72  	public static class B {
73  		public String name;
74  		public Date date;
75  
76  		public B(String name, Date date) {
77  			this.name = name;
78  			this.date = date;
79  		}
80  	}
81  
82  	public static class DateSwap extends StringSwap<Date> {
83  		private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
84  		@Override
85  		public String swap(BeanSession session, Date date) {
86  			return df.format(date);
87  		}
88  		@Override
89  		public Date unswap(BeanSession session, String str, ClassMeta<?> hint) throws java.text.ParseException {
90  			return df.parse(str);
91  		}
92  	}
93  
94  	//====================================================================================================
95  	// Test swaps on map values
96  	//====================================================================================================
97  	@Test void c01_swapOnMapValues() throws Exception {
98  		var l = new LinkedList<>();
99  		var m1 = new HashMap<String,Object>();
100 		m1.put("name", "user1");
101 		m1.put("date", new Date(1000000));
102 		var m2 = new HashMap<String,Object>();
103 		m2.put("name", "user2");
104 		m2.put("date", new Date(2000000));
105 		l.add(m1);
106 		l.add(m2);
107 
108 		var s = CsvSerializer.create().swaps(DateSwap.class).build();
109 		var r = s.serialize(l);
110 
111 		// Swaps should format dates
112 		assertTrue(r.contains("user1"));
113 		assertTrue(r.contains("user2"));
114 		assertTrue(r.contains("1970-01-01") || r.contains("1969-12-31"), "Should have formatted dates but was: " + r);
115 	}
116 
117 	//====================================================================================================
118 	// Test swaps on simple values
119 	//====================================================================================================
120 	@Test void d01_swapOnSimpleValues() throws Exception {
121 		var l = new LinkedList<>();
122 		l.add(new Date(1000000));
123 		l.add(new Date(2000000));
124 		l.add(new Date(3000000));
125 
126 		var s = CsvSerializer.create().swaps(DateSwap.class).build();
127 		var r = s.serialize(l);
128 
129 		// Should have value header and formatted dates
130 		assertTrue(r.startsWith("value\n"));
131 		assertTrue(r.contains("1970-01-01") || r.contains("1969-12-31"), "Should have formatted dates but was: " + r);
132 		// Should have 3 date lines + 1 header = 4 lines total
133 		assertEquals(4, r.split("\n").length);
134 	}
135 
136 	//====================================================================================================
137 	// Test swap with null values
138 	//====================================================================================================
139 	@Test void e01_swapWithNullValues() throws Exception {
140 		var l = new LinkedList<>();
141 		l.add(new B("user1", null));
142 		l.add(new B("user2", new Date(2000000)));
143 		l.add(new B("user3", null));
144 
145 		var s = CsvSerializer.create().swaps(DateSwap.class).build();
146 		var r = s.serialize(l);
147 
148 		// Should have users and null values
149 		assertTrue(r.contains("user1"));
150 		assertTrue(r.contains("user2"));
151 		assertTrue(r.contains("user3"));
152 		assertTrue(r.contains("null"));
153 		assertTrue(r.contains("1970-01-01") || r.contains("1969-12-31"), "Should have formatted date but was: " + r);
154 	}
155 
156 	//====================================================================================================
157 	// Test custom object swap
158 	//====================================================================================================
159 	@Test void f01_customObjectSwap() throws Exception {
160 		var l = new LinkedList<>();
161 		l.add(new C("John", new Address("123 Main St", "Seattle", "WA")));
162 		l.add(new C("Jane", new Address("456 Oak Ave", "Portland", "OR")));
163 
164 		var s = CsvSerializer.create().swaps(AddressSwap.class).build();
165 		var r = s.serialize(l);
166 
167 		// Should have names and pipe-delimited addresses
168 		assertTrue(r.contains("John"));
169 		assertTrue(r.contains("Jane"));
170 		assertTrue(r.contains("123 Main St|Seattle|WA"));
171 		assertTrue(r.contains("456 Oak Ave|Portland|OR"));
172 	}
173 
174 	public static class C {
175 		public String name;
176 		public Address address;
177 
178 		public C(String name, Address address) {
179 			this.name = name;
180 			this.address = address;
181 		}
182 	}
183 
184 	public static class Address {
185 		public String street, city, state;
186 
187 		public Address(String street, String city, String state) {
188 			this.street = street;
189 			this.city = city;
190 			this.state = state;
191 		}
192 	}
193 
194 	public static class AddressSwap extends StringSwap<Address> {
195 		@Override
196 		public String swap(BeanSession session, Address address) {
197 			if (address == null) return null;
198 			return address.street + "|" + address.city + "|" + address.state;
199 		}
200 		@Override
201 		public Address unswap(BeanSession session, String str, ClassMeta<?> hint) {
202 			if (str == null) return null;
203 			var parts = str.split("\\|");
204 			return new Address(parts[0], parts[1], parts[2]);
205 		}
206 	}
207 
208 	//====================================================================================================
209 	// Test @Swap annotation on field
210 	//====================================================================================================
211 	@Test void g01_swapAnnotationOnField() throws Exception {
212 		var l = new LinkedList<>();
213 		l.add(new D("user1", new Date(1000000)));
214 		l.add(new D("user2", new Date(2000000)));
215 
216 		var s = CsvSerializer.DEFAULT;
217 		var r = s.serialize(l);
218 
219 		// @Swap annotation on field should apply the swap
220 		assertTrue(r.contains("user1"));
221 		assertTrue(r.contains("user2"));
222 		assertTrue(r.contains("1970-01-01") || r.contains("1969-12-31"), "Should have formatted dates but was: " + r);
223 	}
224 
225 	public static class D {
226 		public String name;
227 
228 		@Swap(DateSwap.class)
229 		public Date timestamp;
230 
231 		public D(String name, Date timestamp) {
232 			this.name = name;
233 			this.timestamp = timestamp;
234 		}
235 	}
236 
237 	//====================================================================================================
238 	// Test enum swap
239 	//====================================================================================================
240 	@Test void h01_enumSwap() throws Exception {
241 		var l = new LinkedList<>();
242 		l.add(new E("Task1", Status.PENDING));
243 		l.add(new E("Task2", Status.COMPLETED));
244 		l.add(new E("Task3", Status.IN_PROGRESS));
245 
246 		var s = CsvSerializer.DEFAULT;
247 		var r = s.serialize(l);
248 
249 		// Enums should serialize as their string names
250 		assertTrue(r.contains("Task1"));
251 		assertTrue(r.contains("Task2"));
252 		assertTrue(r.contains("Task3"));
253 		assertTrue(r.contains("PENDING"));
254 		assertTrue(r.contains("COMPLETED"));
255 		assertTrue(r.contains("IN_PROGRESS"));
256 	}
257 
258 	public static class E {
259 		public String name;
260 		public Status status;
261 
262 		public E(String name, Status status) {
263 			this.name = name;
264 			this.status = status;
265 		}
266 	}
267 
268 	public enum Status {
269 		PENDING, IN_PROGRESS, COMPLETED
270 	}
271 }