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

com.mitchellbosecke.pebble.template.PebbleTemplateImpl Maven / Gradle / Ivy

/*******************************************************************************
 * This file is part of Pebble.
 * 

* Copyright (c) 2014 by Mitchell Bösecke *

* For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. ******************************************************************************/ package com.mitchellbosecke.pebble.template; import com.mitchellbosecke.pebble.PebbleEngine; import com.mitchellbosecke.pebble.error.PebbleException; import com.mitchellbosecke.pebble.extension.escaper.SafeString; import com.mitchellbosecke.pebble.node.ArgumentsNode; import com.mitchellbosecke.pebble.node.RootNode; import com.mitchellbosecke.pebble.utils.FutureWriter; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; /** * The actual implementation of a PebbleTemplate */ public class PebbleTemplateImpl implements PebbleTemplate { /** * A template has to store a reference to the main engine so that it can * compile other templates when using the "import" or "include" tags. *

* It will also retrieve some stateful information such as the default locale * when necessary. Luckily, the engine is immutable so this should be thread safe. */ private final PebbleEngine engine; /** * Blocks defined inside this template. */ private final Map blocks = new HashMap<>(); /** * Macros defined inside this template. */ private final Map macros = new HashMap<>(); /** * The root node of the AST to be rendered. */ private final RootNode rootNode; /** * Name of template. Used to help with debugging. */ private final String name; /** * Constructor * * @param engine The pebble engine used to construct this template * @param root The root not to evaluate * @param name The name of the template */ public PebbleTemplateImpl(PebbleEngine engine, RootNode root, String name) { this.engine = engine; this.rootNode = root; this.name = name; } public void evaluate(Writer writer) throws PebbleException, IOException { EvaluationContext context = initContext(null); evaluate(writer, context); } public void evaluate(Writer writer, Locale locale) throws PebbleException, IOException { EvaluationContext context = initContext(locale); evaluate(writer, context); } public void evaluate(Writer writer, Map map) throws PebbleException, IOException { EvaluationContext context = initContext(null); context.getScopeChain().pushScope(map); evaluate(writer, context); } public void evaluate(Writer writer, Map map, Locale locale) throws PebbleException, IOException { EvaluationContext context = initContext(locale); context.getScopeChain().pushScope(map); evaluate(writer, context); } /** * This is the authoritative evaluate method. It will evaluate the template * starting at the root node. * * @param writer The writer used to write the final output of the template * @param context The evaluation context * @throws PebbleException Thrown if any sort of template error occurs * @throws IOException Thrown from the writer object */ private void evaluate(Writer writer, EvaluationContext context) throws PebbleException, IOException { if (context.getExecutorService() != null) { writer = new FutureWriter(writer); } rootNode.render(this, writer, context); /* * If the current template has a parent then we know the current template * was only used to evaluate a very small subset of tags such as "set" and "import". * We now evaluate the parent template as to evaluate all of the actual content. * When evaluating the parent template, it will check the child template for overridden blocks. */ if (context.getHierarchy().getParent() != null) { PebbleTemplateImpl parent = context.getHierarchy().getParent(); context.getHierarchy().ascend(); parent.evaluate(writer, context); } writer.flush(); } /** * Initializes the evaluation context with settings from the engine. * * @param locale The desired locale * @return The evaluation context */ private EvaluationContext initContext(Locale locale) { locale = locale == null ? engine.getDefaultLocale() : locale; // globals Map globals = new HashMap<>(); globals.put("locale", locale); globals.put("template", this); ScopeChain scopeChain = new ScopeChain(globals); // global vars provided from extensions scopeChain.pushScope(engine.getExtensionRegistry().getGlobalVariables()); EvaluationContext context = new EvaluationContext(this, engine.isStrictVariables(), locale, engine.getExtensionRegistry(), engine.getTagCache(), engine.getExecutorService(), new ArrayList(), scopeChain, null); return context; } /** * Imports a template. * * @param context The evaluation context * @param name The template name * @throws PebbleException Thrown if an error occurs while rendering the imported * template */ public void importTemplate(EvaluationContext context, String name) throws PebbleException { context.getImportedTemplates().add((PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(name))); } /** * Includes a template with {@code name} into this template. * * @param writer the writer to which the output should be written to. * @param context the context within which the template is rendered in. * @param name the name of the template to include. * @param additionalVariables the map with additional variables provided with the include * tag to add within the include tag. * @throws PebbleException Any error occurring during the compilation of the template * @throws IOException Any error during the loading of the template */ public void includeTemplate(Writer writer, EvaluationContext context, String name, Map additionalVariables) throws PebbleException, IOException { PebbleTemplateImpl template = (PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(name)); EvaluationContext newContext = context.shallowCopyWithoutInheritanceChain(template); ScopeChain scopeChain = newContext.getScopeChain(); scopeChain.pushScope(); for (Entry entry : additionalVariables.entrySet()) { scopeChain.put((String) entry.getKey(), entry.getValue()); } template.evaluate(writer, newContext); scopeChain.popScope(); } /** * Checks if a macro exists * * @param macroName The name of the macro * @return Whether or not the macro exists */ public boolean hasMacro(String macroName) { return macros.containsKey(macroName); } /** * This method resolves the given relative path based on this template file * path. * * @param relativePath the path which should be resolved. * @return the resolved path. */ public String resolveRelativePath(String relativePath) { String resolved = this.engine.getLoader().resolveRelativePath(relativePath, this.name); if (resolved == null) { return relativePath; } else { return resolved; } } /** * Registers a block. * * @param block The block */ public void registerBlock(Block block) { blocks.put(block.getName(), block); } /** * Registers a macro * * @param macro The macro * @throws PebbleException Throws exception if macro already exists with the same name */ public void registerMacro(Macro macro) throws PebbleException { if (macros.containsKey(macro.getName())) { throw new PebbleException(null, "More than one macro can not share the same name: " + macro.getName()); } this.macros.put(macro.getName(), macro); } /** * A typical block declaration will use this method which evaluates the * block using the regular user-provided writer. * * @param blockName The name of the block * @param context The evaluation context * @param ignoreOverriden Whether or not to ignore overriden blocks * @param writer The writer * @throws PebbleException Thrown if an error occurs * @throws IOException Thrown from the writer object */ public void block(Writer writer, EvaluationContext context, String blockName, boolean ignoreOverriden) throws PebbleException, IOException { Hierarchy hierarchy = context.getHierarchy(); PebbleTemplateImpl childTemplate = hierarchy.getChild(); // check child if (!ignoreOverriden && childTemplate != null) { hierarchy.descend(); childTemplate.block(writer, context, blockName, false); hierarchy.ascend(); // check this template } else if (blocks.containsKey(blockName)) { Block block = blocks.get(blockName); block.evaluate(this, writer, context); // delegate to parent } else { if (hierarchy.getParent() != null) { PebbleTemplateImpl parent = hierarchy.getParent(); hierarchy.ascend(); parent.block(writer, context, blockName, true); hierarchy.descend(); } } } /** * Invokes a macro * * @param context The evaluation context * @param macroName The name of the macro * @param args The arguments * @param ignoreOverriden Whether or not to ignore macro definitions in child template * @return The results of the macro invocation * @throws PebbleException An exception that may have occurred */ public SafeString macro(EvaluationContext context, String macroName, ArgumentsNode args, boolean ignoreOverriden) throws PebbleException { SafeString result = null; boolean found = false; PebbleTemplateImpl childTemplate = context.getHierarchy().getChild(); // check child template first if (!ignoreOverriden && childTemplate != null) { found = true; context.getHierarchy().descend(); result = childTemplate.macro(context, macroName, args, false); context.getHierarchy().ascend(); // check current template } else if (hasMacro(macroName)) { found = true; Macro macro = macros.get(macroName); Map namedArguments = args.getArgumentMap(this, context, macro); result = new SafeString(macro.call(this, context, namedArguments)); } // check imported templates if (!found) { for (PebbleTemplateImpl template : context.getImportedTemplates()) { if (template.hasMacro(macroName)) { found = true; result = template.macro(context, macroName, args, false); } } } // delegate to parent template if (!found) { if (context.getHierarchy().getParent() != null) { PebbleTemplateImpl parent = context.getHierarchy().getParent(); context.getHierarchy().ascend(); result = parent.macro(context, macroName, args, true); context.getHierarchy().descend(); } else { throw new PebbleException(null, String.format("Function or Macro [%s] does not exist.", macroName)); } } return result; } public void setParent(EvaluationContext context, String parentName) throws PebbleException { context.getHierarchy() .pushAncestor((PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(parentName))); } /** * Returns the template name * * @return The name of the template */ public String getName() { return name; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy