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}