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.html;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.net.*;
23  import java.util.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.annotation.*;
27  import org.apache.juneau.collections.*;
28  import org.apache.juneau.common.utils.*;
29  import org.junit.jupiter.api.*;
30  
31  class Common_Test extends TestBase {
32  
33  	//====================================================================================================
34  	// Trim nulls from beans
35  	//====================================================================================================
36  	@Test void a01_trimNullsFromBeans() throws Exception {
37  		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders();
38  		var p = HtmlParser.DEFAULT;
39  		var t1 = A.create();
40  
41  		s.keepNullProperties();
42  		var r = s.build().serialize(t1);
43  		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>s1</td><td><null/></td></tr><tr><td>s2</td><td>s2</td></tr></table>", r);
44  		var t2 = p.parse(r, A.class);
45  		assertEquals(json(t2), json(t1));
46  
47  		s = HtmlSerializer.create().sq().addKeyValueTableHeaders();
48  		r = s.build().serialize(t1);
49  		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table>", r);
50  		t2 = p.parse(r, A.class);
51  		assertEquals(json(t2), json(t1));
52  	}
53  
54  	public static class A {
55  		public String s1, s2;
56  
57  		public static A create() {
58  			var t = new A();
59  			t.s2 = "s2";
60  			return t;
61  		}
62  	}
63  
64  	//====================================================================================================
65  	// Trim empty maps
66  	//====================================================================================================
67  	@Test void a02_trimEmptyMaps() throws Exception {
68  		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders();
69  		var p = HtmlParser.DEFAULT;
70  		var t1 = B.create();
71  		var r = s.build().serialize(t1);
72  
73  		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><table><tr><th>key</th><th>value</th></tr></table></td></tr><tr><td>f2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>f2a</td><td><null/></td></tr><tr><td>f2b</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table></td></tr></table></td></tr></table>", r);
74  		var t2 = p.parse(r, B.class);
75  		assertEquals(json(t2), json(t1));
76  
77  		s.trimEmptyMaps();
78  		r = s.build().serialize(t1);
79  		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>f2a</td><td><null/></td></tr><tr><td>f2b</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>s2</td><td>s2</td></tr></table></td></tr></table></td></tr></table>", r);
80  		t2 = p.parse(r, B.class);
81  		assertNull(t2.f1);
82  	}
83  
84  	public static class B {
85  		public TreeMap<String,A> f1, f2;
86  
87  		public static B create() {
88  			var t = new B();
89  			t.f1 = new TreeMap<>();
90  			t.f2 = new TreeMap<>(){{put("f2a",null);put("f2b",A.create());}};
91  			return t;
92  		}
93  	}
94  
95  	//====================================================================================================
96  	// Trim empty lists
97  	//====================================================================================================
98  	@Test void a03_trimEmptyLists() throws Exception {
99  		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders();
100 		var p = HtmlParser.DEFAULT;
101 		var t1 = C.create();
102 		var r = s.build().serialize(t1);
103 
104 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><ul></ul></td></tr><tr><td>f2</td><td><table _type='array'><tr><th>s1</th><th>s2</th></tr><tr><null/></tr><tr><td><null/></td><td>s2</td></tr></table></td></tr></table>", r);
105 		var t2 = p.parse(r, C.class);
106 		assertEquals(json(t2), json(t1));
107 
108 		s.trimEmptyCollections();
109 		r = s.build().serialize(t1);
110 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f2</td><td><table _type='array'><tr><th>s1</th><th>s2</th></tr><tr><null/></tr><tr><td><null/></td><td>s2</td></tr></table></td></tr></table>", r);
111 		t2 = p.parse(r, C.class);
112 		assertNull(t2.f1);
113 	}
114 
115 	public static class C {
116 		public List<A> f1, f2;
117 
118 		public static C create() {
119 			var t = new C();
120 			t.f1 = alist();
121 			t.f2 = alist(null,A.create());
122 			return t;
123 		}
124 	}
125 
126 	//====================================================================================================
127 	// Trim empty arrays
128 	//====================================================================================================
129 	@Test void a04_trimEmptyArrays() throws Exception {
130 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders();
131 		var p = HtmlParser.DEFAULT;
132 		var t1 = D.create();
133 		var r = s.build().serialize(t1);
134 
135 		assertEquals(
136 			"<table>"
137 				+"<tr><th>key</th><th>value</th></tr>"
138 				+"<tr>"
139 					+"<td>f1</td>"
140 					+"<td><ul></ul></td>"
141 				+"</tr>"
142 				+"<tr>"
143 					+"<td>f2</td>"
144 					+"<td>"
145 						+"<table _type='array'>"
146 							+"<tr><th>s1</th><th>s2</th></tr>"
147 							+"<tr><null/></tr>"
148 							+"<tr><td><null/></td><td>s2</td></tr>"
149 						+"</table>"
150 					+"</td>"
151 				+"</tr>"
152 			+"</table>",
153 			r);
154 
155 		var t2 = p.parse(r, D.class);
156 		assertEquals(json(t2), json(t1));
157 
158 		s.trimEmptyCollections();
159 		r = s.build().serialize(t1);
160 		assertEquals(
161 			"<table>"
162 				+"<tr><th>key</th><th>value</th></tr>"
163 				+"<tr>"
164 					+"<td>f2</td>"
165 					+"<td>"
166 						+"<table _type='array'>"
167 							+"<tr><th>s1</th><th>s2</th></tr>"
168 							+"<tr><null/></tr>"
169 							+"<tr><td><null/></td><td>s2</td></tr>"
170 						+"</table>"
171 					+"</td>"
172 				+"</tr>"
173 			+"</table>",
174 			r);
175 		t2 = p.parse(r, D.class);
176 		assertNull(t2.f1);
177 	}
178 
179 	public static class D {
180 		public A[] f1, f2;
181 
182 		public static D create() {
183 			var t = new D();
184 			t.f1 = new A[]{};
185 			t.f2 = new A[]{null, A.create()};
186 			return t;
187 		}
188 	}
189 
190 	//====================================================================================================
191 	// @Beanp.bpi annotation.
192 	//====================================================================================================
193 	@Test void a05_beanPropertyProperties() throws Exception {
194 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().build();
195 		var t = new E1();
196 		var r = s.serialize(t);
197 
198 		assertEquals(
199 			"<table>"
200 				+"<tr>"
201 					+"<th>key</th>"
202 					+"<th>value</th>"
203 				+"</tr>"
204 				+"<tr>"
205 					+"<td>x1</td>"
206 					+"<td>"
207 						+"<table>"
208 							+"<tr><th>key</th><th>value</th></tr>"
209 							+"<tr><td>f1</td><td>1</td></tr>"
210 						+"</table>"
211 					+"</td>"
212 				+"</tr>"
213 				+"<tr>"
214 					+"<td>x2</td>"
215 					+"<td>"
216 						+"<table>"
217 							+"<tr><th>key</th><th>value</th></tr>"
218 							+"<tr><td>f1</td><td>3</td></tr>"
219 						+"</table>"
220 					+"</td>"
221 				+"</tr>"
222 				+"<tr>"
223 					+"<td>x3</td>"
224 					+"<td>"
225 						+"<table _type='array'>"
226 							+"<tr><th>f1</th></tr>"
227 							+"<tr><td>1</td></tr>"
228 						+"</table>"
229 					+"</td>"
230 				+"</tr>"
231 				+"<tr>"
232 					+"<td>x4</td>"
233 					+"<td>"
234 						+"<table _type='array'>"
235 							+"<tr><th>f1</th></tr>"
236 							+"<tr><td>1</td></tr>"
237 						+"</table>"
238 					+"</td>"
239 				+"</tr>"
240 				+"<tr>"
241 					+"<td>x5</td>"
242 					+"<td>"
243 						+"<table _type='array'>"
244 							+"<tr><th>f1</th></tr>"
245 							+"<tr><td><number>5</number></td></tr>"
246 						+"</table>"
247 					+"</td>"
248 				+"</tr>"
249 				+"<tr>"
250 					+"<td>x6</td>"
251 					+"<td>"
252 						+"<table _type='array'>"
253 							+"<tr><th>f1</th></tr>"
254 							+"<tr><td><number>7</number></td></tr>"
255 						+"</table>"
256 					+"</td>"
257 				+"</tr>"
258 			+"</table>",
259 		r);
260 		r = s.getSchemaSerializer().serialize(new E1());
261 		assertEquals(r.indexOf("f2"), -1);
262 	}
263 
264 	public static class E1 {
265 		@Beanp(properties="f1") public E2 x1 = new E2();
266 		@Beanp(properties="f1") public Map<String,Integer> x2 = map("f1",3,"f2",4);
267 		@Beanp(properties="f1") public E2[] x3 = {new E2()};
268 		@Beanp(properties="f1") public List<E2> x4 = alist(new E2());
269 		@Beanp(properties="f1") public JsonMap[] x5 = {JsonMap.of("f1",5,"f2",6)};
270 		@Beanp(properties="f1") public List<JsonMap> x6 = alist(JsonMap.of("f1",7,"f2",8));
271 	}
272 
273 	public static class E2 {
274 		public int f1 = 1;
275 		public int f2 = 2;
276 	}
277 
278 	//====================================================================================================
279 	// @Beanp.bpi annotation on list of beans.
280 	//====================================================================================================
281 	@Test void a06_beanPropertyPropertiesOnListOfBeans() throws Exception {
282 		var s = HtmlSerializer.DEFAULT_SQ;
283 		var l = new LinkedList<>();
284 		var t = new F();
285 		t.x1.add(new F());
286 		l.add(t);
287 		var html = s.serialize(l);
288 		assertEquals(
289 			"<table _type='array'>"
290 				+"<tr><th>x1</th><th>x2</th></tr>"
291 				+"<tr>"
292 					+"<td>"
293 						+"<table _type='array'>"
294 							+"<tr><th>x2</th></tr>"
295 							+"<tr><td>2</td></tr>"
296 						+"</table>"
297 					+"</td>"
298 					+"<td>2</td>"
299 				+"</tr>"
300 			+"</table>", html);
301 
302 	}
303 
304 	public static class F {
305 		@Beanp(properties="x2") public List<F> x1 = new LinkedList<>();
306 		public int x2 = 2;
307 	}
308 
309 	//====================================================================================================
310 	// Test that URLs and URIs are serialized and parsed correctly.
311 	//====================================================================================================
312 	@Test void a07_uRIAttr() throws Exception {
313 		var s = HtmlSerializer.DEFAULT_SQ;
314 		var p = HtmlParser.DEFAULT;
315 
316 		var t = new G();
317 		t.uri = new URI("http://uri");
318 		t.f1 = new URI("http://f1");
319 		t.f2 = url("http://f2");
320 
321 		var html = s.serialize(t);
322 		t = p.parse(html, G.class);
323 		assertEquals("http://uri", t.uri.toString());
324 		assertEquals("http://f1", t.f1.toString());
325 		assertEquals("http://f2", t.f2.toString());
326 	}
327 
328 	public static class G {
329 		public URI uri;
330 		public URI f1;
331 		public URL f2;
332 	}
333 
334 	//====================================================================================================
335 	// Recursion
336 	//====================================================================================================
337 	@Test void a08_recursion() throws Exception {
338 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().maxDepth(Integer.MAX_VALUE);
339 
340 		var r1 = new R1();
341 		var r2 = new R2();
342 		var r3 = new R3();
343 		r1.r2 = r2;
344 		r2.r3 = r3;
345 		r3.r1 = r1;
346 
347 		// No recursion detection
348 		assertThrowsWithMessage(Exception.class, "It's recommended you use the BeanTraverseContext.BEANTRAVERSE_detectRecursions setting to help locate the loop.", ()->s.build().serialize(r1));
349 		assertThrowsWithMessage(Exception.class, "It's recommended you use the BeanTraverseContext.BEANTRAVERSE_detectRecursions setting to help locate the loop.", ()->s.build().serialize(r1));
350 
351 		// Recursion detection, no ignore
352 		s.detectRecursions();
353 		assertThrowsWithMessage(Exception.class, Utils.list("[0] <noname>:org.apache.juneau.html.Common_Test$R1", "->[1] r2:org.apache.juneau.html.Common_Test$R2", "->[2] r3:org.apache.juneau.html.Common_Test$R3", "->[3] r1:org.apache.juneau.html.Common_Test$R1"), ()->s.build().serialize(r1));
354 
355 		s.ignoreRecursions();
356 		assertEquals(
357 			"<table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>foo</td></tr><tr><td>r2</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>bar</td></tr><tr><td>r3</td><td><table><tr><th>key</th><th>value</th></tr><tr><td>name</td><td>baz</td></tr></table></td></tr></table></td></tr></table>",
358 			s.build().serialize(r1)
359 		);
360 
361 		// Make sure this doesn't blow up.
362 		s.build().getSchemaSerializer().serialize(r1);
363 	}
364 
365 	public static class R1 {
366 		public String name = "foo";
367 		public R2 r2;
368 	}
369 	public static class R2 {
370 		public String name = "bar";
371 		public R3 r3;
372 	}
373 	public static class R3 {
374 		public String name = "baz";
375 		public R1 r1;
376 	}
377 
378 	//====================================================================================================
379 	// Basic bean
380 	//====================================================================================================
381 	@Test void a09_basicBean() throws Exception {
382 		var s = HtmlSerializer.create().sq().keepNullProperties().sortProperties().addKeyValueTableHeaders().build();
383 
384 		var a = new J();
385 		a.setF1("J");
386 		a.setF2(100);
387 		a.setF3(true);
388 		assertEquals(
389 			"<table>"
390 				+"<tr><th>key</th><th>value</th></tr>"
391 				+"<tr><td>f1</td><td>J</td></tr>"
392 				+"<tr><td>f2</td><td>100</td></tr>"
393 				+"<tr><td>f3</td><td>true</td></tr>"
394 			+"</table>",
395 			s.serialize(a));
396 	}
397 
398 	public static class J {
399 		private String f1;
400 		public String getF1() { return f1; }
401 		public void setF1(String v) { f1 = v; }
402 
403 		private int f2 = -1;
404 		public int getF2() { return f2; }
405 		public void setF2(int v) { f2 = v; }
406 
407 		private boolean f3;
408 		public boolean isF3() { return f3; }
409 		public void setF3(boolean v) { f3 = v; }
410 
411 		@Override /* Object */
412 		public String toString() {
413 			return ("J(f1: " + this.getF1() + ", f2: " + this.getF2() + ")");
414 		}
415 	}
416 }