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.a.rttests;
18  
19  import static java.util.Collections.*;
20  import static org.apache.juneau.TestUtils.*;
21  import static org.apache.juneau.commons.utils.CollectionUtils.*;
22  import static org.apache.juneau.junit.bct.BctAssertions.*;
23  
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.apache.juneau.annotation.*;
28  import org.apache.juneau.html.*;
29  import org.apache.juneau.json.*;
30  import org.apache.juneau.msgpack.*;
31  import org.apache.juneau.uon.*;
32  import org.apache.juneau.urlencoding.*;
33  import org.apache.juneau.xml.*;
34  import org.junit.jupiter.params.*;
35  import org.junit.jupiter.params.provider.*;
36  
37  /**
38   * Tests designed to serialize and parse objects to make sure we end up
39   * with the same objects for all serializers and parsers.
40   */
41  class RoundTripBeansWithBuilders_Test extends TestBase {
42  
43  	private static RoundTrip_Tester[] TESTERS = {
44  		tester(1, "Json - default")
45  			.serializer(JsonSerializer.create().keepNullProperties().addBeanTypes().addRootType())
46  			.parser(JsonParser.create())
47  			.build(),
48  		tester(2, "Json - lax")
49  			.serializer(JsonSerializer.create().json5().keepNullProperties().addBeanTypes().addRootType())
50  			.parser(JsonParser.create())
51  			.build(),
52  		tester(3, "Json - lax, readable")
53  			.serializer(JsonSerializer.create().json5().ws().keepNullProperties().addBeanTypes().addRootType())
54  			.parser(JsonParser.create())
55  			.build(),
56  		tester(4, "Xml - namespaces, validation, readable")
57  			.serializer(XmlSerializer.create().ns().sq().keepNullProperties().addNamespaceUrisToRoot().useWhitespace().addBeanTypes().addRootType())
58  			.parser(XmlParser.create())
59  			.validateXmlWhitespace()
60  			.validateXml()
61  			.build(),
62  		tester(5, "Xml - no namespaces, validation")
63  			.serializer(XmlSerializer.create().sq().keepNullProperties().addBeanTypes().addRootType())
64  			.parser(XmlParser.create())
65  			.validateXmlWhitespace()
66  			.build(),
67  		tester(6, "Html - default")
68  			.serializer(HtmlSerializer.create().keepNullProperties().addBeanTypes().addRootType())
69  			.parser(HtmlParser.create())
70  			.validateXmlWhitespace()
71  			.build(),
72  		tester(7, "Html - readable")
73  			.serializer(HtmlSerializer.create().sq().ws().keepNullProperties().addBeanTypes().addRootType())
74  			.parser(HtmlParser.create())
75  			.validateXmlWhitespace()
76  			.build(),
77  		tester(8, "Html - with key/value headers")
78  			.serializer(HtmlSerializer.create().addKeyValueTableHeaders().addBeanTypes().addRootType())
79  			.parser(HtmlParser.create())
80  			.validateXmlWhitespace()
81  			.build(),
82  		tester(9, "Uon - default")
83  			.serializer(UonSerializer.create().keepNullProperties().addBeanTypes().addRootType())
84  			.parser(UonParser.create())
85  			.build(),
86  		tester(10, "Uon - readable")
87  			.serializer(UonSerializer.create().ws().keepNullProperties().addBeanTypes().addRootType())
88  			.parser(UonParser.create())
89  			.build(),
90  		tester(11, "Uon - encoded")
91  			.serializer(UonSerializer.create().encoding().keepNullProperties().addBeanTypes().addRootType())
92  			.parser(UonParser.create().decoding())
93  			.build(),
94  		tester(12, "UrlEncoding - default")
95  			.serializer(UrlEncodingSerializer.create().keepNullProperties().addBeanTypes().addRootType())
96  			.parser(UrlEncodingParser.create())
97  			.build(),
98  		tester(13, "UrlEncoding - readable")
99  			.serializer(UrlEncodingSerializer.create().ws().keepNullProperties().addBeanTypes().addRootType())
100 			.parser(UrlEncodingParser.create())
101 			.build(),
102 		tester(14, "UrlEncoding - expanded params")
103 			.serializer(UrlEncodingSerializer.create().expandedParams().addBeanTypes().addRootType())
104 			.parser(UrlEncodingParser.create().expandedParams())
105 			.build(),
106 		tester(15, "MsgPack")
107 			.serializer(MsgPackSerializer.create().keepNullProperties().addBeanTypes().addRootType())
108 			.parser(MsgPackParser.create())
109 			.build(),
110 		tester(16, "Json schema")
111 			.serializer(JsonSchemaSerializer.create().keepNullProperties().addBeanTypes().addRootType())
112 			.returnOriginalObject()
113 			.build(),
114 	};
115 
116 	static RoundTrip_Tester[] testers() {
117 		return TESTERS;
118 	}
119 
120 	protected static RoundTrip_Tester.Builder tester(int index, String label) {
121 		return RoundTrip_Tester.create(index, label).annotatedClasses(AcConfig.class);
122 	}
123 
124 	//====================================================================================================
125 	// simple
126 	//====================================================================================================
127 
128 	@ParameterizedTest
129 	@MethodSource("testers")
130 	void a01_simple(RoundTrip_Tester t) throws Exception {
131 		var x = A.builder().f1(1).build();
132 		x = t.roundTrip(x, A.class);
133 		assertBean(x, "f1", "1");
134 	}
135 
136 	public static class A {
137 		private final int f1;
138 
139 		public A(Builder b) {
140 			f1 = b.f1;
141 		}
142 
143 		public static Builder builder() {
144 			return new Builder();
145 		}
146 
147 		@Bean(findFluentSetters=true)
148 		public static class Builder {
149 
150 			private int f1;
151 			public Builder f1(int v) { f1 = v; return this; }
152 
153 			public A build() {
154 				return new A(this);
155 			}
156 		}
157 
158 		public int getF1() { return f1; }
159 	}
160 
161 	@ParameterizedTest
162 	@MethodSource("testers")
163 	void a02_simple_usingConfig(RoundTrip_Tester t) throws Exception {
164 		var x = Ac.builder().f1(1).build();
165 		x = t.roundTrip(x, Ac.class);
166 		assertBean(x, "f1", "1");
167 	}
168 
169 	@Bean(on="Dummy1", findFluentSetters=true)
170 	@Bean(on="Builder", findFluentSetters=true)
171 	@Bean(on="Dummy2", findFluentSetters=true)
172 	private static class AcConfig {}
173 
174 	public static class Ac {
175 		private final int f1;
176 
177 		public Ac(Builder b) {
178 			f1 = b.f1;
179 		}
180 
181 		public static Builder builder() {
182 			return new Builder();
183 		}
184 
185 		public static class Builder {
186 
187 			private int f1;
188 			public Builder f1(int v) { f1 = v; return this; }
189 
190 			public Ac build() {
191 				return new Ac(this);
192 			}
193 		}
194 
195 		public int getF1() { return f1; }
196 	}
197 
198 	//====================================================================================================
199 	// Bean property builder, simple
200 	//====================================================================================================
201 
202 	@ParameterizedTest
203 	@MethodSource("testers")
204 	void a03_beanPropertyBuilder_simple(RoundTrip_Tester t) throws Exception {
205 		var x = A2.builder().f1(A.builder().f1(1).build()).build();
206 		x = t.roundTrip(x, A2.class);
207 		assertBean(x, "f1{f1}", "{1}");
208 	}
209 
210 	public static class A2 {
211 		private final A f1;
212 
213 		public A2(Builder b) {
214 			f1 = b.f1;
215 		}
216 
217 		public static Builder builder() {
218 			return new Builder();
219 		}
220 
221 		@Bean(findFluentSetters=true)
222 		public static class Builder {
223 
224 			private A f1;
225 			public Builder f1(A v) { f1 = v; return this; }
226 
227 			public A2 build() {
228 				return new A2(this);
229 			}
230 		}
231 
232 		public A getF1() { return f1; }
233 	}
234 
235 	//====================================================================================================
236 	// Bean property builder, collections
237 	//====================================================================================================
238 
239 	@ParameterizedTest
240 	@MethodSource("testers")
241 	void a04_beanPropertyBuilder_collections(RoundTrip_Tester t) throws Exception {
242 		// It's simply not possible to allow for expanded parameters with a builder-based approach
243 		// since the value on the builder can only be set once.
244 		if (t.label.contains("UrlEncoding - expanded params"))
245 			return;
246 		var x = A3.builder()
247 			.f1(a(A.builder().f1(1).build()))
248 			.f2(singletonList(A.builder().f1(2).build()))
249 			.f3(singletonList(singletonList(A.builder().f1(3).build())))
250 			.f4(singletonList(a(A.builder().f1(4).build())))
251 			.f5(singletonList(singletonList(a(A.builder().f1(5).build()))))
252 			.f6(singletonMap("foo", A.builder().f1(6).build()))
253 			.f7(singletonMap("foo", singletonMap("bar", A.builder().f1(7).build())))
254 			.f8(singletonMap("foo", a(A.builder().f1(8).build())))
255 			.f9(singletonMap("foo", singletonList(a(A.builder().f1(9).build()))))
256 			.build();
257 		x = t.roundTrip(x, A3.class);
258 		assertJson("{f1:[{f1:1}],f2:[{f1:2}],f3:[[{f1:3}]],f4:[[{f1:4}]],f5:[[[{f1:5}]]],f6:{foo:{f1:6}},f7:{foo:{bar:{f1:7}}},f8:{foo:[{f1:8}]},f9:{foo:[[{f1:9}]]}}", x);
259 	}
260 
261 	@Bean(sort=true)
262 	public static class A3 {
263 		private final A[] f1;
264 
265 		private final List<A> f2;
266 		private final List<List<A>> f3;
267 		private final List<A[]> f4;
268 		private final List<List<A[]>> f5;
269 
270 		private final Map<String,A> f6;
271 		private final Map<String,Map<String,A>> f7;
272 		private final Map<String,A[]> f8;
273 		private final Map<String,List<A[]>> f9;
274 
275 		public A3(Builder b) {
276 			f1 = b.f1;
277 			f2 = b.f2;
278 			f3 = b.f3;
279 			f4 = b.f4;
280 			f5 = b.f5;
281 			f6 = b.f6;
282 			f7 = b.f7;
283 			f8 = b.f8;
284 			f9 = b.f9;
285 		}
286 
287 		public static Builder builder() {
288 			return new Builder();
289 		}
290 
291 		@Bean(findFluentSetters=true)
292 		public static class Builder {
293 
294 			private A[] f1;
295 			public Builder f1(A[] v) { f1 = v; return this; }
296 
297 			private List<A> f2;
298 			public Builder f2(List<A> v) { f2 = v; return this; }
299 
300 			private List<List<A>> f3;
301 			public Builder f3(List<List<A>> v) { f3 = v; return this; }
302 
303 			private List<A[]> f4;
304 			public Builder f4(List<A[]> v) { f4 = v; return this; }
305 
306 			private List<List<A[]>> f5;
307 			public Builder f5(List<List<A[]>> v) { f5 = v; return this; }
308 
309 			private Map<String,A> f6;
310 			public Builder f6(Map<String,A> v) { f6 = v; return this; }
311 
312 			private Map<String,Map<String,A>> f7;
313 			public Builder f7(Map<String,Map<String,A>> v) { f7 = v; return this; }
314 
315 			private Map<String,A[]> f8;
316 			public Builder f8(Map<String,A[]> v) { f8 = v; return this; }
317 
318 			private Map<String,List<A[]>> f9;
319 			public Builder f9(Map<String,List<A[]>> v) { f9 = v; return this; }
320 
321 			public A3 build() {
322 				return new A3(this);
323 			}
324 		}
325 
326 		public A[] getF1() { return f1; }
327 		public List<A> getF2() { return f2; }
328 		public List<List<A>> getF3() { return f3; }
329 		public List<A[]> getF4() { return f4; }
330 		public List<List<A[]>> getF5() { return f5; }
331 		public Map<String,A> getF6() { return f6; }
332 		public Map<String,Map<String,A>> getF7() { return f7; }
333 		public Map<String,A[]> getF8() { return f8; }
334 		public Map<String,List<A[]>> getF9() { return f9; }
335 	}
336 }