VarGroup.java
package org.klojang.templates;
import org.klojang.check.Check;
import java.util.HashMap;
import static org.klojang.check.CommonChecks.notNull;
import static org.klojang.templates.x.Messages.ERR_NO_SUCH_VARGROUP;
/**
* Variable groups allow you to group template variables across one or more
* templates. This, in turn, allows you to format or escape their values in an
* identical manner — using a shared {@link Stringifier}. Within a template,
* variables can be assigned to a variable group by using the variable group's name
* as a prefix. For example: {@code ~%html:firstName%} or {@code ~%js:firstName%} or
* {@code ~%myDateFormat:birthDate%}. The first two examples specify the predefined
* {@link #HTML} and {@link #JS} variable groups, which provide HTML-escaping and
* ECMASScript-escaping, respectively. The predefined variable groups mostly rely on
* <a
* href="https://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/StringEscapeUtils.html">apache.commons.text</a>
* to do the actual escaping. The third example is a custom group that you can define
* using the {@link StringifierRegistry} class. Variable groups can also be assigned
* in a more
* <i>ad hoc</i> manner using the API. Many methods of the {@link RenderSession}
* class take a {@code VarGroup} argument.
*
* <p>Note that variable groups are assigned at the
* {@linkplain VariableOccurrence variable occurrence} level. For example, a template
* may contain multiple instances of a variable named {@code firstName}. For
* occurrences inside a {@code <script>} tag you might want to use the {@code js:}
* prefix, for the others the {@code html:} prefix. Therefore, stringifiers
* associated with a variable group take the highest precedence, even higher, perhaps
* counterintuitively, than stringifiers associated with a variable.
*
* @author Ayco Holleman
*/
public final class VarGroup {
private static final HashMap<String, VarGroup> VAR_GROUPS = new HashMap<>();
/**
* A predefined variable group corresponding to the {@code text:} prefix. Forces
* the variable instance to be stringified using the
* {@link Stringifier#DEFAULT default stringifier}.
*/
public static final VarGroup TEXT = withName("text");
/**
* A predefined variable group corresponding to the {@code html:} prefix. Variables
* with this prefix are HTML-escaped. Note that the fact alone that a variable
* appears inside an HTML element, as in {@code <td>~%age%</td>}, does not mean
* that the variable <i>has to</i> have the {@code html:} prefix. The {@code age}
* variable likely is an integer, which does not require any HTML escaping.
*/
public static final VarGroup HTML = withName("html");
/**
* A predefined variable group corresponding to the {@code js:} prefix. Variables
* with this prefix are ECMASScript-escaped. Especially for use in {@code <script>}
* tags.
*/
public static final VarGroup JS = withName("js");
/**
* A predefined variable group corresponding to the {@code attr:} prefix. Works
* just like the {@code html:} prefix except that single and double quote
* characters are also escaped. Especially for use in element attribute values.
*/
public static final VarGroup ATTR = withName("attr");
/**
* A predefined variable group corresponding to the {@code jsattr:} prefix.
* Variables with this prefix are first JavaScript-escaped and then HTML-escaped
* like the {@link #ATTR} variable group. Especially for use in HTML attributes
* that contain JavaScript, like {@code onclick} .
*/
public static final VarGroup JS_ATTR = withName("jsattr");
/**
* A predefined variable group corresponding to the {@code param:} prefix. To be
* used for template variables placed inside a URL as the value of a query
* parameter, like {@code http://example.com/?id=~%param:id%}. It could also be
* used in the more unlikely case that the variable functions as the <i>name</i> of
* the query parameter, because names and values are escaped identically in a URL.
* Note that it does not matter whether the URL as a whole is the value of a
* JavaScript variable or the contents of an HTML tag. Further escaping using
* either JavaScript-escaping rules or HTML-escaping rules would not change the
* value.
*/
public static final VarGroup PARAM = withName("param");
/**
* A predefined variable group corresponding to the {@code path:} prefix. To be
* used for template variables placed inside a URL as a path segment. For example:
* {@code http://example.com/~%path:city%/~%path:restaurant%}.
*/
public static final VarGroup PATH = withName("path");
/**
* <p>A predefined variable group corresponding to the {@code def:} prefix. This
* prefix will cause {@code null} values to be replaced with the placeholder value
* defined within the template. The example below will render as "Good morning,
* John" if {@code firstName} was {@code null}.
*
* <blockquote><pre>{@code
* Good morning, <!-- ~%def:firstName% -->John<!--%-->
* }</pre></blockquote>
*
* <p>See {@link Regex#REGEX_CMT_VARIABLE} for an explanation of the syntax and
* purpose of placeholder values.
*/
public static final VarGroup DEF = withName("def");
/**
* Returns the {@code VarGroup} with the specified name (which is also the prefix
* to be used inside a template). Throws an {@link IllegalArgumentException} if
* there is no {@code VarGroup} with the specified name.
*
* @param name the name or prefix
* @return the {@code VarGroup} instance corresponding to the specified name
*/
public static VarGroup forName(String name) {
VarGroup vg = Check.notNull(name).ok(VAR_GROUPS::get);
return Check.that(vg).is(notNull(), ERR_NO_SUCH_VARGROUP, name).ok();
}
static VarGroup withName(String name) {
return VAR_GROUPS.computeIfAbsent(name, VarGroup::new);
}
private final String name;
private VarGroup(String name) {
this.name = name;
}
/**
* Returns the name of this {@code VarGroup}, which is also the prefix to be used
* inside a template.
*
* @return the name of this {@code VarGroup}
*/
public String getName() {
return name;
}
/**
* Returns the name of this {@code VarGroup}, which is also the prefix to be used
* inside a template.
*
* @return the name of this {@code VarGroup}
*/
@Override
public String toString() {
return name;
}
}