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.html.annotation.HtmlFormat.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.util.*;
23  
24  import org.apache.juneau.*;
25  import org.apache.juneau.annotation.*;
26  import org.apache.juneau.collections.*;
27  import org.apache.juneau.html.annotation.*;
28  import org.apache.juneau.testutils.pojos.*;
29  import org.junit.jupiter.api.*;
30  
31  @SuppressWarnings("serial")
32  class Html_Test extends TestBase {
33  
34  	//-----------------------------------------------------------------------------------------------------------------
35  	// Verifies that lists of maps/beans are converted to tables correctly.
36  	//-----------------------------------------------------------------------------------------------------------------
37  	@Test void a01_testTables1() throws Exception {
38  		var s = HtmlSerializer.DEFAULT_SQ;
39  		var t = new Object[] {new A1(), new A1()};
40  		var html = s.serialize(t);
41  		assertEquals("<table _type='array'><tr><th>f1</th></tr><tr><td>f1</td></tr><tr><td>f1</td></tr></table>", html);
42  	}
43  
44  	public static class A1 {
45  		public String f1 = "f1";
46  	}
47  
48  	//-----------------------------------------------------------------------------------------------------------------
49  	// Test URI_ANCHOR_SET options
50  	//-----------------------------------------------------------------------------------------------------------------
51  	@Test void a02_testAnchorTextOptions() throws Exception {
52  		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().uriResolution(UriResolution.NONE);
53  		var t = new TestURI();
54  
55  		s.uriAnchorText(AnchorText.TO_STRING);
56  		var r = strip(s.build().serialize(t));
57  		var expected = """
58  
59  			[f0]=<a href='f0/x0'>f0/x0</a>
60  			[f1]=<a href='f1/x1'>f1/x1</a>
61  			[f2]=<a href='/f2/x2'>/f2/x2</a>
62  			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
63  			[f4]=<a href='f4/x4'>f4/x4</a>
64  			[f5]=<a href='/f5/x5'>/f5/x5</a>
65  			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
66  			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
67  			[f8]=<a href='f8/x8'>f8/x8</a>
68  			[f9]=<a href='f9/x9'>f9/x9</a>
69  			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>http://www.apache.org/fa/xa#MY_LABEL</a>
70  			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>
71  			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>
72  			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>
73  			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>""";
74  		assertEquals(expected, r);
75  
76  		s.uriAnchorText(AnchorText.URI);
77  		r = strip(s.build().serialize(t));
78  		expected = """
79  
80  			[f0]=<a href='f0/x0'>f0/x0</a>
81  			[f1]=<a href='f1/x1'>f1/x1</a>
82  			[f2]=<a href='/f2/x2'>/f2/x2</a>
83  			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
84  			[f4]=<a href='f4/x4'>f4/x4</a>
85  			[f5]=<a href='/f5/x5'>/f5/x5</a>
86  			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
87  			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
88  			[f8]=<a href='f8/x8'>f8/x8</a>
89  			[f9]=<a href='f9/x9'>f9/x9</a>
90  			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>http://www.apache.org/fa/xa#MY_LABEL</a>
91  			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>
92  			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>
93  			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>
94  			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>""";
95  		assertEquals(expected, r);
96  
97  		s.uriAnchorText(AnchorText.LAST_TOKEN);
98  		r = strip(s.build().serialize(t));
99  		expected = """
100 
101 			[f0]=<a href='f0/x0'>x0</a>
102 			[f1]=<a href='f1/x1'>x1</a>
103 			[f2]=<a href='/f2/x2'>x2</a>
104 			[f3]=<a href='http://www.apache.org/f3/x3'>x3</a>
105 			[f4]=<a href='f4/x4'>x4</a>
106 			[f5]=<a href='/f5/x5'>x5</a>
107 			[f6]=<a href='http://www.apache.org/f6/x6'>x6</a>
108 			[f7]=<a href='http://www.apache.org/f7/x7'>x7</a>
109 			[f8]=<a href='f8/x8'>x8</a>
110 			[f9]=<a href='f9/x9'>x9</a>
111 			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>xa</a>
112 			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>
113 			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>
114 			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>xd</a>
115 			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>xe</a>""";
116 		assertEquals(expected, r);
117 
118 		s.uriAnchorText(AnchorText.URI_ANCHOR);
119 		r = strip(s.build().serialize(t));
120 		expected = """
121 
122 			[f0]=<a href='f0/x0'>f0/x0</a>
123 			[f1]=<a href='f1/x1'>f1/x1</a>
124 			[f2]=<a href='/f2/x2'>/f2/x2</a>
125 			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
126 			[f4]=<a href='f4/x4'>f4/x4</a>
127 			[f5]=<a href='/f5/x5'>/f5/x5</a>
128 			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
129 			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
130 			[f8]=<a href='f8/x8'>f8/x8</a>
131 			[f9]=<a href='f9/x9'>f9/x9</a>
132 			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>
133 			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>MY_LABEL</a>
134 			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>MY_LABEL</a>
135 			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>
136 			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>""";
137 		assertEquals(expected, r);
138 
139 		s.labelParameter("label2");
140 		r = strip(s.build().serialize(t));
141 		expected = """
142 
143 			[f0]=<a href='f0/x0'>f0/x0</a>
144 			[f1]=<a href='f1/x1'>f1/x1</a>
145 			[f2]=<a href='/f2/x2'>/f2/x2</a>
146 			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
147 			[f4]=<a href='f4/x4'>f4/x4</a>
148 			[f5]=<a href='/f5/x5'>/f5/x5</a>
149 			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
150 			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
151 			[f8]=<a href='f8/x8'>f8/x8</a>
152 			[f9]=<a href='f9/x9'>f9/x9</a>
153 			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>
154 			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar</a>
155 			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL</a>
156 			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>MY_LABEL</a>
157 			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>MY_LABEL</a>""";
158 		assertEquals(expected, r);
159 
160 		s.disableDetectLinksInStrings();
161 		r = strip(s.build().serialize(t));
162 		expected = """
163 
164 			[f0]=<a href='f0/x0'>f0/x0</a>
165 			[f1]=<a href='f1/x1'>f1/x1</a>
166 			[f2]=<a href='/f2/x2'>/f2/x2</a>
167 			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
168 			[f4]=<a href='f4/x4'>f4/x4</a>
169 			[f5]=<a href='/f5/x5'>/f5/x5</a>
170 			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
171 			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
172 			[f8]=<a href='f8/x8'>f8/x8</a>
173 			[f9]=<a href='f9/x9'>f9/x9</a>
174 			[fa]=http://www.apache.org/fa/xa#MY_LABEL
175 			[fb]=http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar
176 			[fc]=http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL
177 			[fd]=http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar
178 			[fe]=http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL""";
179 		assertEquals(expected, r);
180 
181 		s.disableDetectLinksInStrings(false);
182 		s.disableDetectLabelParameters();
183 		r = strip(s.build().serialize(t));
184 		expected = """
185 
186 			[f0]=<a href='f0/x0'>f0/x0</a>
187 			[f1]=<a href='f1/x1'>f1/x1</a>
188 			[f2]=<a href='/f2/x2'>/f2/x2</a>
189 			[f3]=<a href='http://www.apache.org/f3/x3'>http://www.apache.org/f3/x3</a>
190 			[f4]=<a href='f4/x4'>f4/x4</a>
191 			[f5]=<a href='/f5/x5'>/f5/x5</a>
192 			[f6]=<a href='http://www.apache.org/f6/x6'>http://www.apache.org/f6/x6</a>
193 			[f7]=<a href='http://www.apache.org/f7/x7'>http://www.apache.org/f7/x7</a>
194 			[f8]=<a href='f8/x8'>f8/x8</a>
195 			[f9]=<a href='f9/x9'>f9/x9</a>
196 			[fa]=<a href='http://www.apache.org/fa/xa#MY_LABEL'>MY_LABEL</a>
197 			[fb]=<a href='http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar'>http://www.apache.org/fb/xb?label=MY_LABEL&amp;foo=bar</a>
198 			[fc]=<a href='http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL'>http://www.apache.org/fc/xc?foo=bar&amp;label=MY_LABEL</a>
199 			[fd]=<a href='http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar'>http://www.apache.org/fd/xd?label2=MY_LABEL&amp;foo=bar</a>
200 			[fe]=<a href='http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL'>http://www.apache.org/fe/xe?foo=bar&amp;label2=MY_LABEL</a>""";
201 		assertEquals(expected, r);
202 	}
203 
204 	private String strip(String html) {
205 		return html
206 			.replace("<table><tr><th>key</th><th>value</th></tr>", "")
207 			.replace("</table>", "")
208 			.replace("<tr><td>", "\n[")
209 			.replace("</td><td>", "]=")
210 			.replace("</td></tr>", "");
211 	}
212 
213 	//-----------------------------------------------------------------------------------------------------------------
214 	// Test @Html.asPlainText annotation on classes and fields
215 	//-----------------------------------------------------------------------------------------------------------------
216 	@Test void b01_testHtmlAnnotationAsPlainText() throws Exception {
217 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().build();
218 
219 		var o = new B1();
220 		var r = s.serialize(o);
221 		assertEquals("<test>", r);
222 
223 		var o2 = new B2();
224 		r = s.serialize(o2);
225 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><f1></td></tr></table>", r);
226 	}
227 
228 	@Html(format=PLAIN_TEXT)
229 	public static class B1 {
230 		public String f1 = "<f1>";
231 		@Override /* Object */
232 		public String toString() {
233 			return "<test>";
234 		}
235 	}
236 
237 	public static class B2 {
238 		@Html(format=PLAIN_TEXT)
239 		public String f1 = "<f1>";
240 	}
241 
242 	@Test void b02_testHtmlAnnotationAsPlainText_usingConfig() throws Exception {
243 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().applyAnnotations(B3Config.class).build();
244 
245 		var o = new B3();
246 		var r = s.serialize(o);
247 		assertEquals("<test>", r);
248 
249 		var o2 = new B4();
250 		r = s.serialize(o2);
251 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td><f1></td></tr></table>", r);
252 	}
253 
254 	@Html(on="B3", format=PLAIN_TEXT)
255 	@Html(on="B4.f1", format=PLAIN_TEXT)
256 	private static class B3Config {}
257 
258 	public static class B3 {
259 		public String f1 = "<f1>";
260 		@Override /* Object */
261 		public String toString() {
262 			return "<test>";
263 		}
264 	}
265 
266 	public static class B4 {
267 		public String f1 = "<f1>";
268 	}
269 
270 	//-----------------------------------------------------------------------------------------------------------------
271 	// Test @Html.asXml annotation on classes and fields
272 	//-----------------------------------------------------------------------------------------------------------------
273 	@Test void c01_testHtmlAnnotationAsXml() throws Exception {
274 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().build();
275 
276 		var o = new C1();
277 		var r = s.serialize(o);
278 		assertEquals("<object><f1>&lt;f1&gt;</f1></object>", r);
279 
280 		var o2 = new C2();
281 		r = s.serialize(o2);
282 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td>&lt;f1&gt;</td></tr></table>", r);
283 	}
284 
285 	@Html(format=XML)
286 	public static class C1 {
287 		public String f1 = "<f1>";
288 	}
289 
290 	public static class C2 {
291 		@Html(format=XML)
292 		public String f1 = "<f1>";
293 	}
294 
295 	@Test void c02_testHtmlAnnotationAsXml_usingConfig() throws Exception {
296 		var s = HtmlSerializer.create().sq().addKeyValueTableHeaders().applyAnnotations(C3Config.class).build();
297 
298 		var o = new C3();
299 		var r = s.serialize(o);
300 		assertEquals("<object><f1>&lt;f1&gt;</f1></object>", r);
301 
302 		var o2 = new C4();
303 		r = s.serialize(o2);
304 		assertEquals("<table><tr><th>key</th><th>value</th></tr><tr><td>f1</td><td>&lt;f1&gt;</td></tr></table>", r);
305 	}
306 
307 	@Html(on="C3,C4.f1",format=XML)
308 	private static class C3Config {}
309 
310 	public static class C3 {
311 		public String f1 = "<f1>";
312 	}
313 
314 	public static class C4 {
315 		public String f1 = "<f1>";
316 	}
317 
318 	//-----------------------------------------------------------------------------------------------------------------
319 	// Test @Html.noTableHeaders
320 	//-----------------------------------------------------------------------------------------------------------------
321 	@Test void d01_testNoTableHeaders() throws Exception {
322 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
323 
324 		var m = new MyMap();
325 		m.put("foo", "bar");
326 		var o = JsonList.of(m);
327 		var r = s.serialize(o);
328 		assertEquals("<ul><li><table><tr><td>foo</td><td>bar</td></tr></table></li></ul>", r);
329 	}
330 
331 	@Html(noTables=true, noTableHeaders=true)
332 	public static class MyMap extends LinkedHashMap<String,String> {}
333 
334 	@Test void d02_testNoTableHeaders_usingConfig() throws Exception {
335 		var s = HtmlSerializer.DEFAULT_SQ.copy().applyAnnotations(MyMap2Config.class).build();
336 
337 		var m = new MyMap2();
338 		m.put("foo", "bar");
339 		var o = JsonList.of(m);
340 		var r = s.serialize(o);
341 		assertEquals("<ul><li><table><tr><td>foo</td><td>bar</td></tr></table></li></ul>", r);
342 	}
343 
344 	@Html(on="org.apache.juneau.html.Html_Test$MyMap2", noTables=true, noTableHeaders=true)
345 	private static class MyMap2Config {}
346 
347 	public static class MyMap2 extends LinkedHashMap<String,String> {}
348 
349 	//-----------------------------------------------------------------------------------------------------------------
350 	// Test @Html.noTableHeaders on beans
351 	//-----------------------------------------------------------------------------------------------------------------
352 	@Test void d03_testNoTableHeadersOnBeans() throws Exception {
353 		HtmlSerializer s = HtmlSerializer.DEFAULT_SQ;
354 
355 		var b = new MyBean();
356 		var o = JsonList.of(b,b);
357 		var r = s.serialize(o);
358 		assertEquals("<table _type='array'><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>", r);
359 	}
360 
361 	@Html(noTableHeaders=true)
362 	public static class MyBean {
363 		public int a=1,b=2,c=3;
364 	}
365 
366 	@Test void d04_testNoTableHeadersOnBeans_usingConfig() throws Exception {
367 		var s = HtmlSerializer.DEFAULT_SQ.copy().applyAnnotations(MyBean2Config.class).build();
368 
369 		var b = new MyBean();
370 		var o = JsonList.of(b,b);
371 		var r = s.serialize(o);
372 		assertEquals("<table _type='array'><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>", r);
373 	}
374 
375 	@Html(on="MyBean2", noTableHeaders=true)
376 	private static class MyBean2Config {}
377 
378 	public static class MyBean2 {
379 		public int a=1,b=2,c=3;
380 	}
381 
382 	@Test void d05_testNoTableHeadersOnBeans_usingConcreteAnnotation() throws Exception {
383 		var s = HtmlSerializer.DEFAULT_SQ.copy().annotations(HtmlAnnotation.create("MyBean2").noTables(true).build()).build();
384 
385 		var b = new MyBean();
386 		var o = JsonList.of(b,b);
387 		var r = s.serialize(o);
388 		assertEquals("<table _type='array'><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>1</td><td>2</td><td>3</td></tr></table>", r);
389 	}
390 
391 	//-----------------------------------------------------------------------------------------------------------------
392 	// @Bean(bpi) on collections of beans
393 	//-----------------------------------------------------------------------------------------------------------------
394 
395 	@Bean(p="f3,f2,f1")
396 	public static class E {
397 		public Integer f1, f2, f3;
398 
399 		public E(Integer f1, Integer f2, Integer f3) {
400 			this.f1 = f1;
401 			this.f2 = f2;
402 			this.f3 = f3;
403 		}
404 	}
405 
406 	@Test void e01_collectionOfBeansWithBpi() {
407 		E[] ee = {
408 			new E(null, 2, 3),
409 			new E(4, 5, 6)
410 		};
411 		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td>3</td><td>2</td><td><null/></td></tr><tr><td>6</td><td>5</td><td>4</td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(ee));
412 		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td>3</td><td>2</td><td><null/></td></tr><tr><td>6</td><td>5</td><td>4</td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(Arrays.asList(ee)));
413 
414 		ee = new E[] {
415 			new E(null, null, null),
416 			new E(null, null, null)
417 		};
418 		assertEquals("<table _type='array'><tr><th>f3</th><th>f2</th><th>f1</th></tr><tr><td><null/></td><td><null/></td><td><null/></td></tr><tr><td><null/></td><td><null/></td><td><null/></td></tr></table>", HtmlSerializer.DEFAULT_SQ.toString(ee));
419 	}
420 }