001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static org.apache.juneau.html.HtmlDocSerializer.*; 016import static org.apache.juneau.internal.StringUtils.*; 017 018import java.util.*; 019import java.util.regex.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.html.*; 023import org.apache.juneau.html.annotation.*; 024import org.apache.juneau.rest.annotation.*; 025import org.apache.juneau.utils.*; 026 027/** 028 * Programmatic interface for setting properties used by the HtmlDoc serializer. 029 * 030 * <div class='warn'> 031 * <b>Deprecated</b> - Use {@link HtmlDocConfig} 032 * </div> 033 * 034 * <p> 035 * Basically just a convenience wrapper around the servlet or method level properties for setting properties defined 036 * by the {@link HtmlDocSerializer} class. 037 * 038 * <p> 039 * This class is instantiated through the following methods: 040 * <ul> 041 * <li class='jm'>{@link RestResponse#getHtmlDocBuilder()} - Set values programmatically during a REST request. 042 * </ul> 043 * 044 * <ul class='seealso'> 045 * <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation} 046 * </ul> 047 */ 048@Deprecated 049public class HtmlDocBuilder { 050 051 private final PropertyStoreBuilder builder; 052 053 /** 054 * Constructor. 055 * 056 * @param builder The builder object. 057 */ 058 public HtmlDocBuilder(PropertyStoreBuilder builder) { 059 this.builder = builder; 060 } 061 062 /** 063 * Processes the contents of an {@link HtmlDoc} tag. 064 * 065 * @param hd The annotation to process. 066 */ 067 public void process(HtmlDoc hd) { 068 if (hd.header().length > 0) 069 header((Object[])hd.header()); 070 if (hd.nav().length > 0) 071 nav((Object[])hd.nav()); 072 if (hd.aside().length > 0) 073 aside((Object[])hd.aside()); 074 if (hd.footer().length > 0) 075 footer((Object[])hd.footer()); 076 if (hd.style().length > 0) 077 style((Object[])hd.style()); 078 if (hd.script().length > 0) 079 script((Object[])hd.script()); 080 if (hd.navlinks().length > 0) 081 navlinks((Object[])hd.navlinks()); 082 if (hd.head().length > 0) 083 head((Object[])hd.head()); 084 if (hd.stylesheet().length > 0) 085 stylesheet((Object[])hd.stylesheet()); 086 if (! hd.noResultsMessage().isEmpty()) 087 noResultsMessage(hd.noResultsMessage()); 088 if (! hd.nowrap().isEmpty()) 089 nowrap(Boolean.valueOf(hd.nowrap())); 090 if (hd.template() != HtmlDocTemplate.class) 091 template(hd.template()); 092 } 093 094 /** 095 * Sets the HTML header section contents. 096 * 097 * <p> 098 * The page header normally contains the title and description, but this value can be used to override the contents 099 * to be whatever you want. 100 * 101 * <ul class='notes'> 102 * <li> 103 * The format of this value is HTML. 104 * <li> 105 * When a value is specified, the {@link #navlinks(Object...)} value will be ignored. 106 * <li> 107 * Supports {@doc DefaultRestSvlVariables} 108 * (e.g. <js>"$L{my.localized.variable}"</js>). 109 * <li> 110 * A value of <js>"INHERIT"</js> means copy the values from the parent. 111 * <li> 112 * A value of <js>"NONE"</js> can be used to force no value. 113 * <li> 114 * This is the programmatic equivalent to the {@link HtmlDoc#header() @HtmlDoc(header)} annotation. 115 * </ul> 116 * 117 * @param value 118 * The HTML header section contents. 119 * Object will be converted to a string using {@link Object#toString()}. 120 * <p> 121 * <div class='info'> 122 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 123 * waste string concatenation cycles on non-HTML views. 124 * </div> 125 * @return This object (for method chaining). 126 */ 127 public HtmlDocBuilder header(Object...value) { 128 return set(HTMLDOC_header, resolveList(value, getStringArray(HTMLDOC_header))); 129 } 130 131 /** 132 * Sets the links in the HTML nav section. 133 * 134 * <p> 135 * The page links are positioned immediately under the title and text. 136 * 137 * <ul class='notes'> 138 * <li> 139 * The format of this value is a lax-JSON map of key/value pairs where the keys are the link text and the values are 140 * relative (to the servlet) or absolute URLs. 141 * <li> 142 * Supports {@doc DefaultRestSvlVariables} 143 * (e.g. <js>"$L{my.localized.variable}"</js>). 144 * <li> 145 * Supports {@doc juneau-marshall.URIs} (e.g. <js>"servlet:/..."</js>, <js>"request:/..."</js>). 146 * <li> 147 * A value of <js>"INHERIT"</js> means copy the values from the parent. 148 * <li> 149 * A value of <js>"NONE"</js> can be used to force no value. 150 * <li> 151 * This is the programmatic equivalent to the {@link HtmlDoc#navlinks() @HtmlDoc(navlinks)} annotation. 152 * </ul> 153 * 154 * @param value 155 * The HTML nav section links links. 156 * <p> 157 * <div class='info'> 158 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 159 * waste string concatenation cycles on non-HTML views. 160 * </div> 161 * @return This object (for method chaining). 162 */ 163 public HtmlDocBuilder navlinks(Object...value) { 164 return set(HTMLDOC_navlinks, resolveLinks(value, getStringArray(HTMLDOC_navlinks))); 165 } 166 167 /** 168 * Sets the HTML nav section contents. 169 * 170 * <p> 171 * The nav section of the page contains the links. 172 * 173 * <ul class='notes'> 174 * <li> 175 * The format of this value is HTML. 176 * <li> 177 * Supports {@doc DefaultRestSvlVariables} 178 * (e.g. <js>"$L{my.localized.variable}"</js>). 179 * <li> 180 * When a value is specified, the {@link #navlinks(Object[])} value will be ignored. 181 * <li> 182 * A value of <js>"INHERIT"</js> means copy the values from the parent. 183 * <li> 184 * A value of <js>"NONE"</js> can be used to force no value. 185 * <li> 186 * This is the programmatic equivalent to the {@link HtmlDoc#nav() @HtmlDoc(nav)} annotation. 187 * </ul> 188 * 189 * @param value 190 * The HTML nav section contents. 191 * Object will be converted to a string using {@link Object#toString()}. 192 * <p> 193 * <div class='info'> 194 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 195 * waste string concatenation cycles on non-HTML views. 196 * </div> 197 * @return This object (for method chaining). 198 */ 199 public HtmlDocBuilder nav(Object...value) { 200 return set(HTMLDOC_nav, resolveList(value, getStringArray(HTMLDOC_nav))); 201 } 202 203 /** 204 * Sets the HTML aside section contents. 205 * 206 * <p> 207 * The aside section typically floats on the right side of the page. 208 * 209 * <ul class='notes'> 210 * <li> 211 * The format of this value is HTML. 212 * <li> 213 * Supports {@doc DefaultRestSvlVariables} 214 * (e.g. <js>"$L{my.localized.variable}"</js>). 215 * <li> 216 * A value of <js>"INHERIT"</js> means copy the values from the parent. 217 * <li> 218 * A value of <js>"NONE"</js> can be used to force no value. 219 * <li> 220 * This is the programmatic equivalent to the {@link HtmlDoc#aside() @HtmlDoc(aside)} annotation. 221 * </ul> 222 * 223 * @param value 224 * The HTML aside section contents. 225 * Object will be converted to a string using {@link Object#toString()}. 226 * <p> 227 * <div class='info'> 228 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to waste 229 * string concatenation cycles on non-HTML views. 230 * </div> 231 * @return This object (for method chaining). 232 */ 233 public HtmlDocBuilder aside(Object...value) { 234 return set(HTMLDOC_aside, resolveList(value, getStringArray(HTMLDOC_aside))); 235 } 236 237 /** 238 * Sets the HTML footer section contents. 239 * 240 * <p> 241 * The footer section typically floats on the bottom of the page. 242 * 243 * <ul class='notes'> 244 * <li> 245 * The format of this value is HTML. 246 * <li> 247 * Supports {@doc DefaultRestSvlVariables} 248 * (e.g. <js>"$L{my.localized.variable}"</js>). 249 * <li> 250 * A value of <js>"INHERIT"</js> means copy the values from the parent. 251 * <li> 252 * A value of <js>"NONE"</js> can be used to force no value. 253 * <li> 254 * This is the programmatic equivalent to the {@link HtmlDoc#footer() @HtmlDoc(footer)} annotation. 255 * </ul> 256 * 257 * @param value 258 * The HTML footer section contents. 259 * Object will be converted to a string using {@link Object#toString()}. 260 * <p> 261 * <div class='info'> 262 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 263 * waste string concatenation cycles on non-HTML views. 264 * </div> 265 * @return This object (for method chaining). 266 */ 267 public HtmlDocBuilder footer(Object...value) { 268 return set(HTMLDOC_footer, resolveList(value, getStringArray(HTMLDOC_footer))); 269 } 270 271 /** 272 * Sets the HTML CSS style section contents. 273 * 274 * <ul class='notes'> 275 * <li> 276 * The format of this value is CSS. 277 * <li> 278 * Supports {@doc DefaultRestSvlVariables} 279 * (e.g. <js>"$L{my.localized.variable}"</js>). 280 * <li> 281 * A value of <js>"INHERIT"</js> means copy the values from the parent. 282 * <li> 283 * A value of <js>"NONE"</js> can be used to force no value. 284 * <li> 285 * This is the programmatic equivalent to the {@link HtmlDoc#style() @HtmlDoc(style)} annotation. 286 * </ul> 287 * 288 * @param value 289 * The HTML CSS style section contents. 290 * Object will be converted to a string using {@link Object#toString()}. 291 * <p> 292 * <div class='info'> 293 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 294 * waste string concatenation cycles on non-HTML views. 295 * </div> 296 * @return This object (for method chaining). 297 */ 298 public HtmlDocBuilder style(Object...value) { 299 return set(HTMLDOC_style, resolveList(value, getStringArray(HTMLDOC_style))); 300 } 301 302 /** 303 * Sets the CSS URL in the HTML CSS style section. 304 * 305 * <p> 306 * Specifies the URL to the stylesheet to add as a link in the style tag in the header. 307 * 308 * <ul class='notes'> 309 * <li> 310 * The format of this value is a comma-delimited list of URLs. 311 * <li> 312 * Supports {@doc DefaultRestSvlVariables} 313 * (e.g. <js>"$L{my.localized.variable}"</js>). 314 * <li> 315 * This is the programmatic equivalent to the {@link HtmlDoc#stylesheet() @HtmlDoc(stylesheet)} annotation. 316 * </ul> 317 * 318 * @param value 319 * The CSS URL in the HTML CSS style section. 320 * Object will be converted to a string using {@link Object#toString()}. 321 * <p> 322 * <div class='info'> 323 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 324 * waste string concatenation cycles on non-HTML views. 325 * </div> 326 * @return This object (for method chaining). 327 */ 328 public HtmlDocBuilder stylesheet(Object...value) { 329 return set(HTMLDOC_stylesheet, resolveSet(value, getStringArray(HTMLDOC_nav))); 330 } 331 332 /** 333 * Sets the HTML script section contents. 334 * 335 * <ul class='notes'> 336 * <li> 337 * The format of this value is Javascript. 338 * <li> 339 * Supports {@doc DefaultRestSvlVariables} 340 * (e.g. <js>"$L{my.localized.variable}"</js>). 341 * <li> 342 * A value of <js>"INHERIT"</js> means copy the values from the parent. 343 * <li> 344 * A value of <js>"NONE"</js> can be used to force no value. 345 * <li> 346 * This is the programmatic equivalent to the {@link HtmlDoc#script() @HtmlDoc(script)} annotation. 347 * </ul> 348 * 349 * @param value 350 * The HTML script section contents. 351 * Object will be converted to a string using {@link Object#toString()}. 352 * <p> 353 * <div class='info'> 354 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 355 * waste string concatenation cycles on non-HTML views. 356 * </div> 357 * @return This object (for method chaining). 358 */ 359 public HtmlDocBuilder script(Object...value) { 360 return set(HTMLDOC_script, resolveList(value, getStringArray(HTMLDOC_script))); 361 } 362 363 /** 364 * Sets the HTML head section contents. 365 * 366 * <ul class='notes'> 367 * <li> 368 * The format of this value is HTML. 369 * <li> 370 * Supports {@doc DefaultRestSvlVariables} 371 * (e.g. <js>"$L{my.localized.variable}"</js>). 372 * <li> 373 * A value of <js>"INHERIT"</js> means copy the values from the parent. 374 * <li> 375 * A value of <js>"NONE"</js> can be used to force no value. 376 * <li> 377 * This is the programmatic equivalent to the {@link HtmlDoc#head() @HtmlDoc(head)} annotation. 378 * </ul> 379 * 380 * @param value 381 * The HTML head section contents. 382 * <p> 383 * <div class='info'> 384 * <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to 385 * waste string concatenation cycles on non-HTML views. 386 * </div> 387 * @return This object (for method chaining). 388 */ 389 public HtmlDocBuilder head(Object...value) { 390 return set(HTMLDOC_head, resolveList(value, getStringArray(HTMLDOC_head))); 391 } 392 393 /** 394 * Shorthand method for forcing the rendered HTML content to be no-wrap. 395 * 396 * <ul class='notes'> 397 * <li> 398 * Supports {@doc DefaultRestSvlVariables} 399 * (e.g. <js>"$L{my.localized.variable}"</js>). 400 * <li> 401 * This is the programmatic equivalent to the {@link HtmlDoc#nowrap() @HtmlDoc(nowrap)} annotation. 402 * </ul> 403 * 404 * @param value The new nowrap setting. 405 * @return This object (for method chaining). 406 */ 407 public HtmlDocBuilder nowrap(boolean value) { 408 return set(HTMLDOC_nowrap, value); 409 } 410 411 /** 412 * Specifies the text to display when serializing an empty array or collection. 413 * 414 * <ul class='notes'> 415 * <li> 416 * Supports {@doc DefaultRestSvlVariables} 417 * (e.g. <js>"$L{my.localized.variable}"</js>). 418 * <li> 419 * This is the programmatic equivalent to the {@link HtmlDoc#noResultsMessage() @HtmlDoc(noResultsMessage)} annotation. 420 * </ul> 421 * 422 * @param value The text to display when serializing an empty array or collection. 423 * @return This object (for method chaining). 424 */ 425 public HtmlDocBuilder noResultsMessage(Object value) { 426 return set(HTMLDOC_noResultsMessage, value); 427 } 428 429 /** 430 * Specifies the template class to use for rendering the HTML page. 431 * 432 * <p> 433 * By default, uses {@link BasicHtmlDocTemplate} to render the contents, although you can provide your own custom 434 * renderer or subclasses from the basic class to have full control over how the page is rendered. 435 * 436 * <ul class='notes'> 437 * <li> 438 * Supports {@doc DefaultRestSvlVariables} 439 * (e.g. <js>"$L{my.localized.variable}"</js>). 440 * <li> 441 * This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc(template)} annotation. 442 * </ul> 443 * 444 * @param value The HTML page template to use to render the HTML page. 445 * @return This object (for method chaining). 446 */ 447 public HtmlDocBuilder template(Class<? extends HtmlDocTemplate> value) { 448 return set(HTMLDOC_template, value); 449 } 450 451 /** 452 * Specifies the template class to use for rendering the HTML page. 453 * 454 * <p> 455 * By default, uses {@link BasicHtmlDocTemplate} to render the contents, although you can provide your own custom 456 * renderer or subclasses from the basic class to have full control over how the page is rendered. 457 * 458 * <ul class='notes'> 459 * <li> 460 * Supports {@doc DefaultRestSvlVariables} 461 * (e.g. <js>"$L{my.localized.variable}"</js>). 462 * <li> 463 * This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc(template)} annotation. 464 * </ul> 465 * 466 * @param value The HTML page template to use to render the HTML page. 467 * @return This object (for method chaining). 468 */ 469 public HtmlDocBuilder template(HtmlDocTemplate value) { 470 return set(HTMLDOC_template, value); 471 } 472 473 private static final Pattern INDEXED_LINK_PATTERN = Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)"); 474 475 private static String[] resolveLinks(Object[] value, String[] prev) { 476 List<String> list = new ArrayList<>(); 477 for (Object v : value) { 478 String s = stringify(v); 479 if ("INHERIT".equals(s)) { 480 list.addAll(Arrays.asList(prev)); 481 } else if (s.indexOf('[') != -1 && INDEXED_LINK_PATTERN.matcher(s).matches()) { 482 Matcher lm = INDEXED_LINK_PATTERN.matcher(s); 483 lm.matches(); 484 String key = lm.group(1); 485 int index = Math.min(list.size(), Integer.parseInt(lm.group(2))); 486 String remainder = lm.group(3); 487 list.add(index, key.isEmpty() ? remainder : key + ":" + remainder); 488 } else { 489 list.add(s); 490 } 491 } 492 return list.toArray(new String[list.size()]); 493 } 494 495 private static String[] resolveSet(Object[] value, String[] prev) { 496 Set<String> set = new HashSet<>(); 497 for (Object v : value) { 498 String s = stringify(v); 499 if ("INHERIT".equals(s)) { 500 if (prev != null) 501 set.addAll(Arrays.asList(prev)); 502 } else if ("NONE".equals(s)) { 503 return new String[0]; 504 } else { 505 set.add(s); 506 } 507 } 508 return set.toArray(new String[set.size()]); 509 } 510 511 private static String[] resolveList(Object[] value, String[] prev) { 512 Set<String> set = new LinkedHashSet<>(); 513 for (Object v : value) { 514 String s = stringify(v); 515 if ("INHERIT".equals(s)) { 516 if (prev != null) 517 set.addAll(Arrays.asList(prev)); 518 } else if ("NONE".equals(s)) { 519 return new String[0]; 520 } else { 521 set.add(s); 522 } 523 } 524 return set.toArray(new String[set.size()]); 525 } 526 527 private HtmlDocBuilder set(String key, Object value) { 528 builder.set(key, value); 529 return this; 530 } 531 532 private String[] getStringArray(String name) { 533 return builder.peek(String[].class, name); 534 } 535}