blob: aca6f629631bcd6bd35bc91611c1db8d9dd09377 [file] [log] [blame]
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest;
import static org.apache.juneau.html.HtmlDocSerializer.*;
import static org.apache.juneau.internal.StringUtils.*;
import java.util.*;
import java.util.regex.*;
import org.apache.juneau.*;
import org.apache.juneau.html.*;
import org.apache.juneau.html.annotation.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.utils.*;
/**
* Programmatic interface for setting properties used by the HtmlDoc serializer.
*
* <p>
* Basically just a convenience wrapper around the servlet or method level properties for setting properties defined
* by the {@link HtmlDocSerializer} class.
*
* <p>
* This class is instantiated through the following methods:
* <ul>
* <li class='jm'>{@link RestResponse#getHtmlDocBuilder()} - Set values programmatically during a REST request.
* </ul>
*
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-server.HtmlDocAnnotation}
* </ul>
*
* @deprecated Use {@link HtmlDocConfig}
*/
@Deprecated
public class HtmlDocBuilder {
private final PropertyStoreBuilder builder;
/**
* Constructor.
*
* @param builder The builder object.
*/
public HtmlDocBuilder(PropertyStoreBuilder builder) {
this.builder = builder;
}
/**
* Processes the contents of an {@link HtmlDoc} tag.
*
* @param hd The annotation to process.
*/
public void process(HtmlDoc hd) {
if (hd.header().length > 0)
header((Object[])hd.header());
if (hd.nav().length > 0)
nav((Object[])hd.nav());
if (hd.aside().length > 0)
aside((Object[])hd.aside());
if (hd.footer().length > 0)
footer((Object[])hd.footer());
if (hd.style().length > 0)
style((Object[])hd.style());
if (hd.script().length > 0)
script((Object[])hd.script());
if (hd.navlinks().length > 0)
navlinks((Object[])hd.navlinks());
if (hd.head().length > 0)
head((Object[])hd.head());
if (hd.stylesheet().length > 0)
stylesheet((Object[])hd.stylesheet());
if (! hd.noResultsMessage().isEmpty())
noResultsMessage(hd.noResultsMessage());
if (! hd.nowrap().isEmpty())
nowrap(Boolean.valueOf(hd.nowrap()));
if (hd.template() != HtmlDocTemplate.class)
template(hd.template());
}
/**
* Sets the HTML header section contents.
*
* <p>
* The page header normally contains the title and description, but this value can be used to override the contents
* to be whatever you want.
*
* <ul class='notes'>
* <li>
* The format of this value is HTML.
* <li>
* When a value is specified, the {@link #navlinks(Object...)} value will be ignored.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#header() @HtmlDoc(header)} annotation.
* </ul>
*
* @param value
* The HTML header section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder header(Object...value) {
return set(HTMLDOC_header, resolveList(value, getStringArray(HTMLDOC_header)));
}
/**
* Sets the links in the HTML nav section.
*
* <p>
* The page links are positioned immediately under the title and text.
*
* <ul class='notes'>
* <li>
* 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
* relative (to the servlet) or absolute URLs.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* Supports {@doc juneau-marshall.URIs} (e.g. <js>"servlet:/..."</js>, <js>"request:/..."</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#navlinks() @HtmlDoc(navlinks)} annotation.
* </ul>
*
* @param value
* The HTML nav section links links.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder navlinks(Object...value) {
return set(HTMLDOC_navlinks, resolveLinks(value, getStringArray(HTMLDOC_navlinks)));
}
/**
* Sets the HTML nav section contents.
*
* <p>
* The nav section of the page contains the links.
*
* <ul class='notes'>
* <li>
* The format of this value is HTML.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* When a value is specified, the {@link #navlinks(Object[])} value will be ignored.
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#nav() @HtmlDoc(nav)} annotation.
* </ul>
*
* @param value
* The HTML nav section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder nav(Object...value) {
return set(HTMLDOC_nav, resolveList(value, getStringArray(HTMLDOC_nav)));
}
/**
* Sets the HTML aside section contents.
*
* <p>
* The aside section typically floats on the right side of the page.
*
* <ul class='notes'>
* <li>
* The format of this value is HTML.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#aside() @HtmlDoc(aside)} annotation.
* </ul>
*
* @param value
* The HTML aside section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to waste
* string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder aside(Object...value) {
return set(HTMLDOC_aside, resolveList(value, getStringArray(HTMLDOC_aside)));
}
/**
* Sets the HTML footer section contents.
*
* <p>
* The footer section typically floats on the bottom of the page.
*
* <ul class='notes'>
* <li>
* The format of this value is HTML.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#footer() @HtmlDoc(footer)} annotation.
* </ul>
*
* @param value
* The HTML footer section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder footer(Object...value) {
return set(HTMLDOC_footer, resolveList(value, getStringArray(HTMLDOC_footer)));
}
/**
* Sets the HTML CSS style section contents.
*
* <ul class='notes'>
* <li>
* The format of this value is CSS.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#style() @HtmlDoc(style)} annotation.
* </ul>
*
* @param value
* The HTML CSS style section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder style(Object...value) {
return set(HTMLDOC_style, resolveList(value, getStringArray(HTMLDOC_style)));
}
/**
* Sets the CSS URL in the HTML CSS style section.
*
* <p>
* Specifies the URL to the stylesheet to add as a link in the style tag in the header.
*
* <ul class='notes'>
* <li>
* The format of this value is a comma-delimited list of URLs.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#stylesheet() @HtmlDoc(stylesheet)} annotation.
* </ul>
*
* @param value
* The CSS URL in the HTML CSS style section.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder stylesheet(Object...value) {
return set(HTMLDOC_stylesheet, resolveSet(value, getStringArray(HTMLDOC_nav)));
}
/**
* Sets the HTML script section contents.
*
* <ul class='notes'>
* <li>
* The format of this value is Javascript.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#script() @HtmlDoc(script)} annotation.
* </ul>
*
* @param value
* The HTML script section contents.
* Object will be converted to a string using {@link Object#toString()}.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder script(Object...value) {
return set(HTMLDOC_script, resolveList(value, getStringArray(HTMLDOC_script)));
}
/**
* Sets the HTML head section contents.
*
* <ul class='notes'>
* <li>
* The format of this value is HTML.
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* A value of <js>"INHERIT"</js> means copy the values from the parent.
* <li>
* A value of <js>"NONE"</js> can be used to force no value.
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#head() @HtmlDoc(head)} annotation.
* </ul>
*
* @param value
* The HTML head section contents.
* <p>
* <div class='info'>
* <b>Tip:</b> Use {@link StringMessage} to generate value with delayed serialization so as not to
* waste string concatenation cycles on non-HTML views.
* </div>
* @return This object (for method chaining).
*/
public HtmlDocBuilder head(Object...value) {
return set(HTMLDOC_head, resolveList(value, getStringArray(HTMLDOC_head)));
}
/**
* Shorthand method for forcing the rendered HTML content to be no-wrap.
*
* <ul class='notes'>
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#nowrap() @HtmlDoc(nowrap)} annotation.
* </ul>
*
* @param value The new nowrap setting.
* @return This object (for method chaining).
*/
public HtmlDocBuilder nowrap(boolean value) {
return set(HTMLDOC_nowrap, value);
}
/**
* Specifies the text to display when serializing an empty array or collection.
*
* <ul class='notes'>
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#noResultsMessage() @HtmlDoc(noResultsMessage)} annotation.
* </ul>
*
* @param value The text to display when serializing an empty array or collection.
* @return This object (for method chaining).
*/
public HtmlDocBuilder noResultsMessage(Object value) {
return set(HTMLDOC_noResultsMessage, value);
}
/**
* Specifies the template class to use for rendering the HTML page.
*
* <p>
* By default, uses {@link BasicHtmlDocTemplate} to render the contents, although you can provide your own custom
* renderer or subclasses from the basic class to have full control over how the page is rendered.
*
* <ul class='notes'>
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc(template)} annotation.
* </ul>
*
* @param value The HTML page template to use to render the HTML page.
* @return This object (for method chaining).
*/
public HtmlDocBuilder template(Class<? extends HtmlDocTemplate> value) {
return set(HTMLDOC_template, value);
}
/**
* Specifies the template class to use for rendering the HTML page.
*
* <p>
* By default, uses {@link BasicHtmlDocTemplate} to render the contents, although you can provide your own custom
* renderer or subclasses from the basic class to have full control over how the page is rendered.
*
* <ul class='notes'>
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
* <li>
* This is the programmatic equivalent to the {@link HtmlDoc#template() @HtmlDoc(template)} annotation.
* </ul>
*
* @param value The HTML page template to use to render the HTML page.
* @return This object (for method chaining).
*/
public HtmlDocBuilder template(HtmlDocTemplate value) {
return set(HTMLDOC_template, value);
}
private static final Pattern INDEXED_LINK_PATTERN = Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)");
private static String[] resolveLinks(Object[] value, String[] prev) {
List<String> list = new ArrayList<>();
for (Object v : value) {
String s = stringify(v);
if ("INHERIT".equals(s)) {
list.addAll(Arrays.asList(prev));
} else if (s.indexOf('[') != -1 && INDEXED_LINK_PATTERN.matcher(s).matches()) {
Matcher lm = INDEXED_LINK_PATTERN.matcher(s);
lm.matches();
String key = lm.group(1);
int index = Math.min(list.size(), Integer.parseInt(lm.group(2)));
String remainder = lm.group(3);
list.add(index, key.isEmpty() ? remainder : key + ":" + remainder);
} else {
list.add(s);
}
}
return list.toArray(new String[list.size()]);
}
private static String[] resolveSet(Object[] value, String[] prev) {
Set<String> set = new HashSet<>();
for (Object v : value) {
String s = stringify(v);
if ("INHERIT".equals(s)) {
if (prev != null)
set.addAll(Arrays.asList(prev));
} else if ("NONE".equals(s)) {
return new String[0];
} else {
set.add(s);
}
}
return set.toArray(new String[set.size()]);
}
private static String[] resolveList(Object[] value, String[] prev) {
Set<String> set = new LinkedHashSet<>();
for (Object v : value) {
String s = stringify(v);
if ("INHERIT".equals(s)) {
if (prev != null)
set.addAll(Arrays.asList(prev));
} else if ("NONE".equals(s)) {
return new String[0];
} else {
set.add(s);
}
}
return set.toArray(new String[set.size()]);
}
private HtmlDocBuilder set(String key, Object value) {
builder.set(key, value);
return this;
}
private String[] getStringArray(String name) {
return builder.peek(String[].class, name);
}
}