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}