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