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