All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jknack.handlebars.Handlebars Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/**
 * Copyright (c) 2012-2015 Edgar Espina
 *
 * This file is part of Handlebars.java.
 *
 * Licensed 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 com.github.jknack.handlebars;

import static org.apache.commons.lang3.Validate.isTrue;
import static org.apache.commons.lang3.Validate.notEmpty;
import static org.apache.commons.lang3.Validate.notNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Array;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.slf4j.Logger;
import com.github.jknack.handlebars.cache.NullTemplateCache;
import com.github.jknack.handlebars.cache.TemplateCache;
import com.github.jknack.handlebars.helper.DefaultHelperRegistry;
import com.github.jknack.handlebars.helper.I18nHelper;
import com.github.jknack.handlebars.internal.Files;
import com.github.jknack.handlebars.internal.FormatterChain;
import com.github.jknack.handlebars.internal.HbsParserFactory;
import com.github.jknack.handlebars.internal.Throwing;
import com.github.jknack.handlebars.io.ClassPathTemplateLoader;
import com.github.jknack.handlebars.io.CompositeTemplateLoader;
import com.github.jknack.handlebars.io.StringTemplateSource;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.github.jknack.handlebars.io.TemplateSource;

/**
 *  

* Handlebars provides the power necessary to let you build semantic templates effectively with no * frustration. *

*

* Getting Started:

* *
 *  Handlebars handlebars = new Handlebars();
 *  Template template = handlebars.compileInline("Hello {{this}}!");
 *  System.out.println(template.apply("Handlebars.java"));
 *  
* *

Loading templates

Templates are loaded using the ```TemplateLoader``` class. * Handlebars.java provides three implementations of a ```TemplateLoader```: *
    *
  • ClassPathTemplateLoader (default)
  • *
  • FileTemplateLoader
  • *
  • SpringTemplateLoader (available at the handlebars-springmvc module)
  • *
* *

* This example load mytemplate.hbs from the root of the classpath: *

* *
 *  Handlebars handlebars = new Handlebars();
 *
 *  Template template = handlebars.compileInline(URI.create("mytemplate"));
 *
 *  System.out.println(template.apply("Handlebars.java"));
 *  
* *

* You can specify a different ```TemplateLoader``` by: *

* *
 *  TemplateLoader loader = ...;
 *  Handlebars handlebars = new Handlebars(loader);
 *  
* * @author edgar.espina * @since 0.1.0 * * @deprecated com.github.jknack.handlebars package is deprecated and marked for removal in subsequent releases which will involve removal of the handlebars dependency in AEM. */ @Deprecated(since = "2024-07-10") public class Handlebars implements HelperRegistry { /** * A {@link SafeString} tell {@link Handlebars} that the content should not be * escaped as HTML. * * @author edgar.espina * @since 0.1.0 * * @deprecated com.github.jknack.handlebars package is deprecated and marked for removal in subsequent releases which will involve removal of the handlebars dependency in AEM. */ @Deprecated(since = "2024-07-10") public static class SafeString implements CharSequence { /** * The content. */ public final CharSequence content; /** * Creates a new {@link SafeString}. * * @param content The string content. */ public SafeString(final CharSequence content) { this.content = content; } @Override public int length() { return content.length(); } @Override public char charAt(final int index) { return content.charAt(index); } @Override public CharSequence subSequence(final int start, final int end) { return content.subSequence(start, end); } @Override public String toString() { return content.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (content == null ? 0 : content.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (obj instanceof SafeString) { SafeString that = (SafeString) obj; return content.equals(that.content); } return false; } } /** * Utilities function like: {@link Utils#escapeExpression(CharSequence)} and * {@link Utils#isEmpty(Object)}. * * @author edgar.espina * @since 0.1.0 * * @deprecated com.github.jknack.handlebars package is deprecated and marked for removal in subsequent releases which will involve removal of the handlebars dependency in AEM. */ @Deprecated(since = "2024-07-10") public static class Utils { /** * Java 14. */ private static final int JAVA_14 = 14; /** * Current Java version: 8, 11, 15, etc. */ public static final int javaVersion = javaVersion(); /** * True when running on Java 14 or higher. */ public static final boolean javaVersion14 = javaVersion() >= JAVA_14; /** * Prefix for Java version: 1.8 (mostly). */ private static final String VERSION_PREFIX = "1."; /** * Evaluate the given object and return true is the object is considered * empty. Nulls, empty list or array and false values are considered empty. * * @param value The object value. * @return Return true is the object is considered empty. Nulls, empty list * or array and false values are considered empty. */ @SuppressWarnings("rawtypes") public static boolean isEmpty(final Object value) { if (value == null) { return true; } if (value instanceof CharSequence) { return ((CharSequence) value).length() == 0; } if (value instanceof Collection) { return ((Collection) value).size() == 0; } if (value instanceof Iterable) { return !((Iterable) value).iterator().hasNext(); } if (value instanceof Boolean) { return !((Boolean) value).booleanValue(); } if (value.getClass().isArray()) { return Array.getLength(value) == 0; } if (value instanceof Number) { return ((Number) value).doubleValue() == 0; } return false; } /** *

* Escapes the characters in a {@code String} using HTML entities. *

*

* For example: *

*

* "bread" {@literal &} "butter" *

* becomes: * *
         *  "bread" & "butter"
         * 
* * @param input the {@code String} to escape, may be null. * @return The escaped version of the input or the same input if it's a * SafeString. */ public static CharSequence escapeExpression(final CharSequence input) { return EscapingStrategy.DEF.escape(input); } static int javaVersion() { String version = System.getProperty("java.specification.version").trim(); return Integer.parseInt(version.replace(VERSION_PREFIX, "")); } /** * Throws any throwable 'sneakily' - you don't need to catch it, nor declare that you throw it * onwards. * The exception is still thrown - javac will just stop whining about it. *

* Example usage: *

public void run() {
         *     throw sneakyThrow(new IOException("You don't need to catch me!"));
         * }
*

* NB: The exception is not wrapped, ignored, swallowed, or redefined. The JVM actually does * not know or care about the concept of a 'checked exception'. All this method does is hide * the act of throwing a checked exception from the java compiler. *

* Note that this method has a return type of {@code RuntimeException}; it is advised you * always call this method as argument to the {@code throw} statement to avoid compiler * errors regarding no return statement and similar problems. This method won't of course * return an actual {@code RuntimeException} - it never returns, it always throws the provided * exception. * * @param x The throwable to throw without requiring you to catch its type. * @return A dummy RuntimeException; this method never returns normally, it always * throws an exception! */ public static RuntimeException propagate(final Throwable x) { if (x == null) { throw new NullPointerException("x"); } sneakyThrow0(x); return null; } /** * Make a checked exception un-checked and rethrow it. * * @param x Exception to throw. * @param Exception type. * @throws E Exception to throw. */ @SuppressWarnings("unchecked") private static void sneakyThrow0(final Throwable x) throws E { throw (E) x; } } /** * The default start delimiter. */ public static final String DELIM_START = "{{"; /** * The default end delimiter. */ public static final String DELIM_END = "}}"; /** * The logging system. */ private static final Logger logger = getLogger(Handlebars.class); /** * The template loader. Required. */ private TemplateLoader loader; /** * The template cache. Required. */ private TemplateCache cache = NullTemplateCache.INSTANCE; /** * If true, missing helper parameters will be resolve to their names. */ private boolean stringParams; /** * If true, unnecessary whitespace and new lines will be removed. */ private boolean prettyPrint; /** * The helper registry. */ private HelperRegistry registry = new DefaultHelperRegistry(); /** * If true, templates will be able to call him self directly or indirectly. Use with caution. * Default is: false. */ private boolean infiniteLoops; /** * If true, templates will be deleted once applied. Useful, in some advanced template inheritance * use cases. Default is: false. * At any time you can override the default setup with: * *

     * {{#block "footer" delete-after-merge=true}}
     * 
*/ private boolean deletePartialAfterMerge; /** * The escaping strategy. */ private EscapingStrategy escapingStrategy = EscapingStrategy.HTML_ENTITY; /** * The parser factory. Required. */ private ParserFactory parserFactory = new HbsParserFactory(); /** * The start delimiter. */ private String startDelimiter = DELIM_START; /** * The end delimiter. */ private String endDelimiter = DELIM_END; /** * Location of the handlebars.js file. */ private String handlebarsJsFile = "/handlebars-v4.7.7.js"; /** * List of formatters. */ private List formatters = new ArrayList<>(); /** * Default formatter. */ private Formatter.Chain formatter = Formatter.NOOP; /** * True, if we want to extend lookup to parent scope. */ private boolean parentScopeResolution = true; /** * If true partial blocks will be evaluated to allow side effects by defining inline * blocks within the partials blocks. * Attention: This feature slows down the performance severly if your templates use * deeply nested partial blocks. * Handlebars works *much* faster if this feature is set to false. * * Example of a feature that is usable when this is set to true: *
     *     {{#> myPartial}}{{#*inline 'myInline'}}Wow!!!{{/inline}}{{/myPartial}}
     * 
* With a myPartial.hbs template like this: *
     *     {{> myInline}}
     * 
* The text "Wow!!!" will actually be rendered. * * If this flag is set to false, you need to explicitly evaluate the partial block. * The template myPartial.hbs will have to look like this: *
     *     {{> @partial-block}}{{> myInline}}
     * 
* * Default is: true for compatibility reasons */ private boolean preEvaluatePartialBlocks = true; /** * Standard charset. */ private Charset charset = StandardCharsets.UTF_8; /** * Engine. */ private ScriptEngine engine; /** * Creates a new {@link Handlebars} with no cache. * * @param loader The template loader. Required. */ public Handlebars(final TemplateLoader loader) { with(loader); } /** * Creates a new {@link Handlebars} with a {@link ClassPathTemplateLoader} and no * cache. */ public Handlebars() { this(new ClassPathTemplateLoader()); } /** * Precompile a template to JavaScript. * * @param path Template path. * @return JavaScript. */ public String precompile(final String path) { return Throwing.get(() -> precompileInline(loader.sourceAt(path).content(charset))); } /** * Precompile a template to JavaScript. * * @param template Template. * @return JavaScript. */ public String precompileInline(final String template) { return Throwing.get(() -> { ScriptEngine engine = engine(); Object handlebars = engine.getContext().getAttribute("Handlebars"); Bindings bindings = engine.createBindings(); bindings.put("Handlebars", handlebars); bindings.put("template", template); return (String) engine.eval("Handlebars.precompile(template);", bindings); }); } /** * Compile the resource located at the given uri. * The implementation uses a cache for previously compiled Templates. By default, * if the resource has been compiled previously, and no changes have occurred * since in the resource, compilation will be skipped and the previously created * Template will be returned. You can set an alternate cache implementation * using {@link #with(TemplateCache cache) with}. * * @param location The resource's location. Required. * @return A compiled template. * @throws IOException If the resource cannot be loaded. */ public Template compile(final String location) throws IOException { return compile(location, startDelimiter, endDelimiter); } /** * Compile the resource located at the given uri. * The implementation uses a cache for previously compiled Templates. By default, * if the resource has been compiled previously, and no changes have occurred * since in the resource, compilation will be skipped and the previously created * Template will be returned. You can set an alternate cache implementation * using {@link #with(TemplateCache cache) with}. * * @param location The resource's location. Required. * @param startDelimiter The start delimiter. Required. * @param endDelimiter The end delimiter. Required. * @return A compiled template. * @throws IOException If the resource cannot be loaded. */ public Template compile(final String location, final String startDelimiter, final String endDelimiter) throws IOException { return compile(loader.sourceAt(location), startDelimiter, endDelimiter); } /** * Compile a handlebars template. * The implementation uses a cache for previously compiled Templates. By default, * if same input string has been compiled previously, compilation will be skipped * and the previously created Template will be returned. You can set an alternate * cache implementation using {@link #with(TemplateCache cache) with}. * * @param input The handlebars input. Required. * @return A compiled template. * @throws IOException If the resource cannot be loaded. */ public Template compileInline(final String input) throws IOException { return compileInline(input, startDelimiter, endDelimiter); } /** * Compile a handlebars template. * The implementation uses a cache for previously compiled Templates. By default, * if same input string has been compiled previously, compilation will be skipped * and the previously created Template will be returned. You can set an alternate * cache implementation using {@link #with(TemplateCache cache) with}. * * @param input The input text. Required. * @param startDelimiter The start delimiter. Required. * @param endDelimiter The end delimiter. Required. * @return A compiled template. * @throws IOException If the resource cannot be loaded. */ public Template compileInline(final String input, final String startDelimiter, final String endDelimiter) throws IOException { notNull(input, "The input is required."); String filename = "inline@" + Integer.toHexString(Math.abs(input.hashCode())); return compile(new StringTemplateSource(filename, input), startDelimiter, endDelimiter); } /** * Compile a handlebars template. * The implementation uses a cache for previously compiled Templates. By default, * if the resource has been compiled previously, and no changes have occurred * since in the resource, compilation will be skipped and the previously created * Template will be returned. You can set an alternate cache implementation * using {@link #with(TemplateCache cache) with}. * * @param source The template source. Required. * @return A handlebars template. * @throws IOException If the resource cannot be loaded. */ public Template compile(final TemplateSource source) throws IOException { return compile(source, startDelimiter, endDelimiter); } /** * Compile a handlebars template. * The implementation uses a cache for previously compiled Templates. By default, * if the resource has been compiled previously, and no changes have occurred * since in the resource, compilation will be skipped and the previously created * Template will be returned. You can set an alternate cache implementation * using {@link #with(TemplateCache cache) with}. * * @param source The template source. Required. * @param startDelimiter The start delimiter. Required. * @param endDelimiter The end delimiter. Required. * @return A handlebars template. * @throws IOException If the resource cannot be loaded. */ public Template compile(final TemplateSource source, final String startDelimiter, final String endDelimiter) throws IOException { notNull(source, "The template source is required."); notEmpty(startDelimiter, "The start delimiter is required."); notEmpty(endDelimiter, "The end delimiter is required."); Parser parser = parserFactory.create(this, startDelimiter, endDelimiter); Template template = cache.get(source, parser); return template; } /** * Find a helper by name. * * @param The helper runtime type. * @param name The helper's name. Required. * @return A helper or null if it's not found. */ @Override public Helper helper(final String name) { return registry.helper(name); } /** * Register a helper in the helper registry. * * @param The helper runtime type. * @param name The helper's name. Required. * @param helper The helper object. Required. * @return This handlebars. */ @Override public Handlebars registerHelper(final String name, final Helper helper) { registry.registerHelper(name, helper); return this; } /** * Register a missing helper in the helper registry. * * @param The helper runtime type. * @param helper The helper object. Required. * @return This handlebars. */ @Override public Handlebars registerHelperMissing(final Helper helper) { return registerHelper(HELPER_MISSING, helper); } /** *

* Register all the helper methods for the given helper source. *

*

* A helper method looks like: *

* *
     * public static? CharSequence methodName(context?, parameter*, options?) {
     * }
     * 
* * Where: *
    *
  • A method can/can't be static
  • *
  • The method's name became the helper's name
  • *
  • Context, parameters and options are all optional
  • *
  • If context and options are present they must be the first and last method arguments.
  • *
* * Instance and static methods will be registered as helpers. * * @param helperSource The helper source. Required. * @return This handlebars object. */ @Override public Handlebars registerHelpers(final Object helperSource) { registry.registerHelpers(helperSource); return this; } /** *

* Register all the helper methods for the given helper source. *

*

* A helper method looks like: *

* *
     * public static? CharSequence methodName(context?, parameter*, options?) {
     * }
     * 
* * Where: *
    *
  • A method can/can't be static
  • *
  • The method's name became the helper's name
  • *
  • Context, parameters and options are all optional
  • *
  • If context and options are present they must be the first and last method arguments.
  • *
* * Only static methods will be registered as helpers. *

* Enums are supported too *

* * @param helperSource The helper source. Enums are supported. Required. * @return This handlebars object. */ @Override public Handlebars registerHelpers(final Class helperSource) { registry.registerHelpers(helperSource); return this; } /** *

* Register helpers from a JavaScript source. *

*

* A JavaScript source file looks like: *

* *
     *  Handlebars.registerHelper('hey', function (context) {
     *    return 'Hi ' + context.name;
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, options) {
     *    return 'Hi ' + context.name + options.hash['x'];
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, p1, p2, options) {
     *    return 'Hi ' + context.name + p1 + p2 + options.hash['x'];
     *  });
     *  ...
     * 
* * To keep your helpers reusable between server and client avoid DOM manipulation. * * @param location A classpath location. Required. * @return This handlebars object. * @throws Exception If the JavaScript helpers can't be registered. */ @Override public Handlebars registerHelpers(final URI location) throws Exception { registry.registerHelpers(location); return this; } /** *

* Register helpers from a JavaScript source. *

*

* A JavaScript source file looks like: *

* *
     *  Handlebars.registerHelper('hey', function (context) {
     *    return 'Hi ' + context.name;
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, options) {
     *    return 'Hi ' + context.name + options.hash['x'];
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, p1, p2, options) {
     *    return 'Hi ' + context.name + p1 + p2 + options.hash['x'];
     *  });
     *  ...
     * 
* * To keep your helpers reusable between server and client avoid DOM manipulation. * * @param input A JavaScript file name. Required. * @return This handlebars object. * @throws Exception If the JavaScript helpers can't be registered. */ @Override public Handlebars registerHelpers(final File input) throws Exception { registry.registerHelpers(input); return this; } /** *

* Register helpers from a JavaScript source. *

*

* A JavaScript source file looks like: *

* *
     *  Handlebars.registerHelper('hey', function (context) {
     *    return 'Hi ' + context.name;
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, options) {
     *    return 'Hi ' + context.name + options.hash['x'];
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, p1, p2, options) {
     *    return 'Hi ' + context.name + p1 + p2 + options.hash['x'];
     *  });
     *  ...
     * 
* * To keep your helpers reusable between server and client avoid DOM manipulation. * * @param filename The file name (just for debugging purpose). Required. * @param source The JavaScript source. Required. * @return This handlebars object. * @throws Exception If the JavaScript helpers can't be registered. */ @Override public Handlebars registerHelpers(final String filename, final Reader source) throws Exception { registry.registerHelpers(filename, source); return this; } /** *

* Register helpers from a JavaScript source. *

*

* A JavaScript source file looks like: *

* *
     *  Handlebars.registerHelper('hey', function (context) {
     *    return 'Hi ' + context.name;
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, options) {
     *    return 'Hi ' + context.name + options.hash['x'];
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, p1, p2, options) {
     *    return 'Hi ' + context.name + p1 + p2 + options.hash['x'];
     *  });
     *  ...
     * 
* * To keep your helpers reusable between server and client avoid DOM manipulation. * * @param filename The file name (just for debugging purpose). Required. * @param source The JavaScript source. Required. * @return This handlebars object. * @throws Exception If the JavaScript helpers can't be registered. */ @Override public Handlebars registerHelpers(final String filename, final InputStream source) throws Exception { registry.registerHelpers(filename, source); return this; } /** *

* Register helpers from a JavaScript source. *

*

* A JavaScript source file looks like: *

* *
     *  Handlebars.registerHelper('hey', function (context) {
     *    return 'Hi ' + context.name;
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, options) {
     *    return 'Hi ' + context.name + options.hash['x'];
     *  });
     *  ...
     *  Handlebars.registerHelper('hey', function (context, p1, p2, options) {
     *    return 'Hi ' + context.name + p1 + p2 + options.hash['x'];
     *  });
     *  ...
     * 
* * To keep your helpers reusable between server and client avoid DOM manipulation. * * @param filename The file name (just for debugging purpose). Required. * @param source The JavaScript source. Required. * @return This handlebars object. * @throws IOException If the JavaScript helpers can't be registered. */ @Override public Handlebars registerHelpers(final String filename, final String source) throws IOException { registry.registerHelpers(filename, source); return this; } @Override public Set>> helpers() { return registry.helpers(); } /** * The resource locator. * * @return The resource locator. */ public TemplateLoader getLoader() { return loader; } /** * The template cache. * * @return The template cache. */ public TemplateCache getCache() { return cache; } /** * The escaping strategy. * * @return The escaping strategy. */ public EscapingStrategy getEscapingStrategy() { return escapingStrategy; } /** * If true, missing helper parameters will be resolve to their names. * * @return If true, missing helper parameters will be resolve to their names. */ public boolean stringParams() { return stringParams; } /** * If true, unnecessary spaces and new lines will be removed from output. Default is: false. * * @return If true, unnecessary spaces and new lines will be removed from output. Default is: * false. */ public boolean prettyPrint() { return prettyPrint; } /** * If true, unnecessary spaces and new lines will be removed from output. Default is: false. * * @param prettyPrint If true, unnecessary spaces and new lines will be removed from output. * Default is: false. */ public void setPrettyPrint(final boolean prettyPrint) { this.prettyPrint = prettyPrint; } /** * If true, unnecessary spaces and new lines will be removed from output. Default is: false. * * @param prettyPrint If true, unnecessary spaces and new lines will be removed from output. * Default is: false. * @return This handlebars object. */ public Handlebars prettyPrint(final boolean prettyPrint) { setPrettyPrint(prettyPrint); return this; } /** * If true, missing helper parameters will be resolve to their names. * * @param stringParams If true, missing helper parameters will be resolve to * their names. */ public void setStringParams(final boolean stringParams) { this.stringParams = stringParams; } /** * If true, missing helper parameters will be resolve to their names. * * @param stringParams If true, missing helper parameters will be resolve to * their names. * @return The handlebars object. */ public Handlebars stringParams(final boolean stringParams) { setStringParams(stringParams); return this; } /** * If true, templates will be able to call him self directly or indirectly. Use with caution. * Default is: false. * * @return If true, templates will be able to call him self directly or indirectly. Use with * caution. Default is: false. */ public boolean infiniteLoops() { return infiniteLoops; } /** * If true, templates will be able to call him self directly or indirectly. Use with caution. * Default is: false. * * @param infiniteLoops If true, templates will be able to call him self directly or * indirectly. */ public void setInfiniteLoops(final boolean infiniteLoops) { this.infiniteLoops = infiniteLoops; } /** * If true, templates will be able to call him self directly or indirectly. Use with caution. * Default is: false. * * @param infiniteLoops If true, templates will be able to call him self directly or * indirectly. * @return The handlebars object. */ public Handlebars infiniteLoops(final boolean infiniteLoops) { setInfiniteLoops(infiniteLoops); return this; } /** * If true, templates will be deleted once applied. Useful, in some advanced template inheritance * use cases. Used by {{#block}} helper. Default is: false. * At any time you can override the default setup with: * *
     * {{#block "footer" delete-after-merge=true}}
     * 
* * @return True for clearing up templates once they got applied. Used by * {{#block}} helper. */ public boolean deletePartialAfterMerge() { return deletePartialAfterMerge; } /** * If true, templates will be deleted once applied. Useful, in some advanced template inheritance * use cases. Used by {{#block}} helper. Default is: false. * At any time you can override the default setup with: * *
     * {{#block "footer" delete-after-merge=true}}
     * 
* * @param deletePartialAfterMerge True for clearing up templates once they got applied. Used by * {{#block}} helper. * * @return This handlebars object. */ public Handlebars deletePartialAfterMerge(final boolean deletePartialAfterMerge) { setDeletePartialAfterMerge(deletePartialAfterMerge); return this; } /** * If true, templates will be deleted once applied. Useful, in some advanced template inheritance * use cases. Used by {{#block}} helper. Default is: false. * At any time you can override the default setup with: * *
     * {{#block "footer" delete-after-merge=true}}
     * 
* * @param deletePartialAfterMerge True for clearing up templates once they got applied. Used by * {{#block}} helper. */ public void setDeletePartialAfterMerge(final boolean deletePartialAfterMerge) { this.deletePartialAfterMerge = deletePartialAfterMerge; } /** * Set the end delimiter. * * @param endDelimiter The end delimiter. Required. */ public void setEndDelimiter(final String endDelimiter) { this.endDelimiter = notEmpty(endDelimiter, "The endDelimiter is required."); } /** * Set the end delimiter. * * @param endDelimiter The end delimiter. Required. * @return This handlebars object. */ public Handlebars endDelimiter(final String endDelimiter) { setEndDelimiter(endDelimiter); return this; } /** * The End Delimiter. * * @return The End Delimiter. */ public String getEndDelimiter() { return this.endDelimiter; } /** * Set the start delimiter. * * @param startDelimiter The start delimiter. Required. */ public void setStartDelimiter(final String startDelimiter) { this.startDelimiter = notEmpty(startDelimiter, "The startDelimiter is required."); } /** * Set the start delimiter. * * @param startDelimiter The start delimiter. Required. * @return This handlebars object. */ public Handlebars startDelimiter(final String startDelimiter) { setStartDelimiter(startDelimiter); return this; } /** * The Start Delimiter. * * @return The Start Delimiter. */ public String getStartDelimiter() { return this.startDelimiter; } /** * Set one or more {@link TemplateLoader}. In the case of two or more {@link TemplateLoader}, a * {@link CompositeTemplateLoader} will be created. Default is: {@link ClassPathTemplateLoader}. * * @param loader The template loader. Required. * @return This handlebars object. * @see CompositeTemplateLoader */ public Handlebars with(final TemplateLoader... loader) { isTrue(loader.length > 0, "The template loader is required."); this.loader = loader.length == 1 ? loader[0] : new CompositeTemplateLoader(loader); return this; } /** * Set a new {@link ParserFactory}. * * @param parserFactory A parser factory. Required. * @return This handlebars object. */ public Handlebars with(final ParserFactory parserFactory) { this.parserFactory = notNull(parserFactory, "A parserFactory is required."); return this; } /** * Set a new {@link TemplateCache}. * * @param cache The template cache. Required. * @return This handlebars object. */ public Handlebars with(final TemplateCache cache) { this.cache = notNull(cache, "The template cache is required."); return this; } /** * Set the helper registry. This operation will override will remove any previously registered * helper. * * @param registry The helper registry. Required. * @return This handlebars object. */ public Handlebars with(final HelperRegistry registry) { this.registry = notNull(registry, "The registry is required."); return this; } /** * Set a new {@link EscapingStrategy}. * * @param escapingStrategy The escaping strategy. Required. * @return This handlebars object. */ public Handlebars with(final EscapingStrategy escapingStrategy) { this.escapingStrategy = notNull(escapingStrategy, "The escaping strategy is required."); return this; } /** * Set a new {@link EscapingStrategy}. * * @param chain The escaping strategy. Required. * @return This handlebars object. */ public Handlebars with(final EscapingStrategy... chain) { return with(newEscapeChain(chain)); } /** * @return A formatter chain. */ public Formatter.Chain getFormatter() { return formatter; } /** * Add a new variable formatter. * *
     *
     * Handlebars hbs = new Handlebars();
     *
     * hbs.with(new Formatter() {
     *   public Object format(Object value, Chain next) {
     *    if (value instanceof Date) {
     *      return ((Date) value).getTime();
     *    }
     *    return next.format(value);
     *   }
     * });
     *
     * 
* * @param formatter A formatter. * @return This handlebars object. */ public Handlebars with(final Formatter formatter) { notNull(formatter, "A formatter is required."); formatters.add(formatter); this.formatter = new FormatterChain(formatters); return this; } /** * Set the handlebars.js location used it to compile/precompile template to JavaScript. *

* Using handlebars.js 4.x: *

* *
     *   Handlebars handlebars = new Handlebars()
     *      .handlebarsJsFile("handlebars-v4.0.4.js");
     * 
*

* Using handlebars.js 1.x: *

* *
     *   Handlebars handlebars = new Handlebars()
     *      .handlebarsJsFile("handlebars-v1.3.0.js");
     * 
* * Default handlebars.js is handlebars-v4.0.4.js. * * @param location A classpath location of the handlebar.js file. * @return This instance of Handlebars. */ public Handlebars handlebarsJsFile(final String location) { this.handlebarsJsFile = notEmpty(location, "A handlebars.js location is required."); if (!this.handlebarsJsFile.startsWith("/")) { this.handlebarsJsFile = "/" + handlebarsJsFile; } URL resource = getClass().getResource(handlebarsJsFile); if (resource == null) { throw new IllegalArgumentException("File not found: " + handlebarsJsFile); } return this; } /** * @return Classpath location of the handlebars.js file. Default is: * handlebars-v4.0.4.js */ public String handlebarsJsFile() { return handlebarsJsFile; } /** * @return True, if we want to extend lookup to parent scope, like Mustache Spec. Or false, if * lookup is restricted to current scope, like handlebars.js. */ public boolean parentScopeResolution() { return parentScopeResolution; } /** * Given: *
     * {
     *   "value": "Brett",
     *   "child": {
     *      "bestQB" : "Favre"
     *    }
     * }
     * 
* * Handlebars.java will output: Hello Favre Brett while handlebars.js: * Hello Favre. * * Why? Handlebars.java is a 100% Mustache implementation while handlebars.js isn't. * * This option forces Handlebars.java mimics handlebars.js behavior: * *
     * Handlebars hbs = new Handlebars()
     *   .parentScopeResolution(true);
     * 
* * Outputs: Hello Favre. * * @param parentScopeResolution False, if we want to restrict lookup to current scope (like in * handlebars.js). Default is true */ public void setParentScopeResolution(final boolean parentScopeResolution) { this.parentScopeResolution = parentScopeResolution; } /** * Given: *
     * {
     *   "value": "Brett",
     *   "child": {
     *      "bestQB" : "Favre"
     *    }
     * }
     * 
* * Handlebars.java will output: Hello Favre Brett while handlebars.js: * Hello Favre. * * Why? Handlebars.java is a 100% Mustache implementation while handlebars.js isn't. * * This option forces Handlebars.java mimics handlebars.js behavior: * *
     * Handlebars hbs = new Handlebars()
     *   .parentScopeResolution(true);
     * 
* * Outputs: Hello Favre. * * @param parentScopeResolution False, if we want to restrict lookup to current scope (like in * handlebars.js). Default is true * @return This handlebars. */ public Handlebars parentScopeResolution(final boolean parentScopeResolution) { setParentScopeResolution(parentScopeResolution); return this; } /** * If true, partial blocks will implicitly be evaluated before the partials will actually * be executed. If false, you need to explicitly evaluate and render partial blocks with *
{@code
     *     {{> @partial-block}}
     * }
* Attention: If this is set to true, Handlebars works *much* slower! while rendering * partial blocks. Default is: true for compatibility reasons. * * @return If true partial blocks will be evaluated before the partial will be rendered * to allow inline block side effects. * If false, you will have to evaluate and render partial blocks explitly (this * option is *much* faster). */ public boolean preEvaluatePartialBlocks() { return preEvaluatePartialBlocks; } /** * If true, partial blocks will implicitly be evaluated before the partials will actually * be executed. If false, you need to explicitly evaluate and render partial blocks with *
{@code
     *     {{> @partial-block}}
     * }
* Attention: If this is set to true, Handlebars works *much* slower! while rendering * partial blocks. Default is: true for compatibility reasons. * * @param preEvaluatePartialBlocks If true partial blocks will be evaluated before the * partial will be rendered to allow inline block side * effects. * If false, you will have to evaluate and render partial * blocks explitly (this option is *much* faster). */ public void setPreEvaluatePartialBlocks(final boolean preEvaluatePartialBlocks) { this.preEvaluatePartialBlocks = preEvaluatePartialBlocks; } /** * If true, partial blocks will implicitly be evaluated before the partials will actually * be executed. If false, you need to explicitly evaluate and render partial blocks with * *
{@code
     *     {{> @partial-block}}
     * }
* Attention: If this is set to true, Handlebars works *much* slower! while rendering * partial blocks. Default is: true for compatibility reasons. * * @param preEvaluatePartialBlocks If true partial blocks will be evaluated before the * partial will be rendered to allow inline block side * effects. * If false, you will have to evaluate and render partial * blocks explitly (this option is *much* faster). * @return The Handlebars object */ public Handlebars preEvaluatePartialBlocks(final boolean preEvaluatePartialBlocks) { setPreEvaluatePartialBlocks(preEvaluatePartialBlocks); return this; } /** * Return a parser factory. * * @return A parser factory. */ public ParserFactory getParserFactory() { return parserFactory; } /** * Log the given message and format the message within the args. * * @param message The log's message. * @param args The optional args. * @see String#format(String, Object...) */ public static void log(final String message, final Object... args) { logger.info(String.format(message, args)); } /** * Log the given message and format the message within the args. * * @param message The log's message. * @see String#format(String, Object...) */ public static void log(final String message) { logger.info(message); } /** * Log the given message as warn and format the message within the args. * * @param message The log's message. * @param args The optional args. * @see String#format(String, Object...) */ public static void warn(final String message, final Object... args) { if (logger.isWarnEnabled()) { logger.warn(String.format(message, args)); } } /** * Log the given message as warn and format the message within the args. * * @param message The log's message. * @see String#format(String, Object...) */ public static void warn(final String message) { logger.warn(message); } /** * Log the given message as debug and format the message within the args. * * @param message The log's message. * @param args The optional args. * @see String#format(String, Object...) */ public static void debug(final String message, final Object... args) { if (logger.isDebugEnabled()) { logger.debug(String.format(message, args)); } } /** * Log the given message as debug and format the message within the args. * * @param message The log's message. * @see String#format(String, Object...) */ public static void debug(final String message) { logger.debug(message); } /** * Log the given message as error and format the message within the args. * * @param message The log's message. * @param args The optional args. * @see String#format(String, Object...) */ public static void error(final String message, final Object... args) { logger.error(String.format(message, args)); } /** * Log the given message as error and format the message within the args. * * @param message The log's message. * @see String#format(String, Object...) */ public static void error(final String message) { logger.error(message); } @Override public Decorator decorator(final String name) { return registry.decorator(name); } @Override public Handlebars registerDecorator(final String name, final Decorator decorator) { registry.registerDecorator(name, decorator); return this; } @Override public Handlebars setCharset(final Charset charset) { this.charset = notNull(charset, "Charset required."); registry.setCharset(charset); loader.setCharset(charset); I18nHelper.i18n.setCharset(charset); I18nHelper.i18nJs.setCharset(charset); return this; } /** * @return Charset. */ public Charset getCharset() { return charset; } /** * Chain escape strategies. * * @param chain Escape to chain. * @return Composite escape strategy. */ private static EscapingStrategy newEscapeChain(final EscapingStrategy[] chain) { return value -> { CharSequence result = value; for (EscapingStrategy escape : chain) { result = escape.escape(result); } return result; }; } /** * @return Nashorn engine. */ private ScriptEngine engine() { synchronized (this) { if (this.engine == null) { this.engine = new ScriptEngineManager().getEngineByName("nashorn"); Throwing.run(() -> engine.eval(Files.read(this.handlebarsJsFile, charset))); } return this.engine; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy