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

liqp.Template Maven / Gradle / Ivy

package liqp;

import com.fasterxml.jackson.databind.ObjectMapper;
import liqp.filters.Filter;
import liqp.nodes.LNode;
import liqp.parser.LiquidLexer;
import liqp.parser.LiquidParser;
import liqp.nodes.LiquidWalker;
import liqp.tags.Tag;
import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * The main class of this library. Use one of its static
 * parse(...) to get a hold of a reference.
 * 

* Also see: https://github.com/Shopify/liquid */ public class Template { /** * The root of the AST denoting the Liquid input source. */ private final CommonTree root; /** * This instance's tags. */ private final Map tags; /** * This instance's filters. */ private final Map filters; /** * Creates a new Template instance from a given input. * @param input * the file holding the Liquid source. * @param tags * the tags this instance will make use of. * @param filters * the filters this instance will make use of. */ private Template(String input, Map tags, Map filters) { this.tags = tags; this.filters = filters; LiquidLexer lexer = new LiquidLexer(new ANTLRStringStream(input)); LiquidParser parser = new LiquidParser(new CommonTokenStream(lexer)); try { root = (CommonTree) parser.parse().getTree(); } catch (RecognitionException e) { throw new RuntimeException("could not parse input: " + input, e); } } /** * Creates a new Template instance from a given file. * * @param file * the file holding the Liquid source. */ private Template(File file, Map tags, Map filters) throws IOException { this.tags = tags; this.filters = filters; try { LiquidLexer lexer = new LiquidLexer(new ANTLRFileStream(file.getAbsolutePath())); LiquidParser parser = new LiquidParser(new CommonTokenStream(lexer)); root = (CommonTree) parser.parse().getTree(); } catch (RecognitionException e) { throw new RuntimeException("could not parse input from " + file, e); } } /** * Returns the root of the AST of the parsed input. * * @return the root of the AST of the parsed input. */ public CommonTree getAST() { return root; } /** * Returns a new Template instance from a given input string. * * @param input * the input string holding the Liquid source. * * @return a new Template instance from a given input string. */ public static Template parse(String input) { return new Template(input, Tag.getTags(), Filter.getFilters()); } /** * Returns a new Template instance from a given input file. * * @param file * the input file holding the Liquid source. * * @return a new Template instance from a given input file. */ public static Template parse(File file) throws IOException { return new Template(file, Tag.getTags(), Filter.getFilters()); } public Template with(Tag tag) { this.tags.put(tag.name, tag); return this; } public Template with(Filter filter) { this.filters.put(filter.name, filter); return this; } /** * Renders the template. * * @param jsonMap * a JSON-map denoting the (possibly nested) * variables that can be used in this Template. * * @return a string denoting the rendered template. */ @SuppressWarnings("unchecked") public String render(String jsonMap) { Map map; try { map = new ObjectMapper().readValue(jsonMap, HashMap.class); } catch (Exception e) { throw new RuntimeException("invalid json map: '" + jsonMap + "'", e); } return render(map); } /** * Renders the template. * * @param context * an array denoting key-value pairs where the * uneven numbers (even indexes) should be Strings. * If the length of this array is uneven, the last * key (without the value) gets `null` attached to * it. Note that a call to this method with a single * String as parameter, will be handled by * `render(String jsonMap)` instead. * * @return a string denoting the rendered template. */ public String render(Object... context) { Map map = new HashMap(); for (int i = 0; i < context.length - 1; i++) { Object key = context[i]; if (key.getClass() != String.class) { throw new RuntimeException("illegal key: " + String.valueOf(key) + " (" + key.getClass().getName() + "). Must be a String."); } Object value = context[i + 1]; map.put((String) key, value); } return render(map); } /** * Renders the template. * * @param context * a Map denoting the (possibly nested) * variables that can be used in this * Template. * * @return a string denoting the rendered template. */ public String render(Map context) { LiquidWalker walker = new LiquidWalker(new CommonTreeNodeStream(root), this.tags, this.filters); try { LNode node = walker.walk(); Object rendered = node.render(context); return rendered == null ? "" : String.valueOf(rendered); } catch (Exception e) { throw new RuntimeException(e); } } /** * Returns a string representation of the AST of the parsed * input source. * * @return a string representation of the AST of the parsed * input source. */ public String toStringAST() { StringBuilder builder = new StringBuilder(); walk(root, builder); return builder.toString(); } /** * Walks a (sub) tree of the root of the input source and builds * a string representation of the structure of the AST. *

* Note that line breaks and multiple white space characters are * trimmed to a single white space character. * * @param tree * the (sub) tree. * @param builder * the StringBuilder to fill. */ @SuppressWarnings("unchecked") private void walk(CommonTree tree, StringBuilder builder) { List firstStack = new ArrayList(); firstStack.add(tree); List> childListStack = new ArrayList>(); childListStack.add(firstStack); while (!childListStack.isEmpty()) { List childStack = childListStack.get(childListStack.size() - 1); if (childStack.isEmpty()) { childListStack.remove(childListStack.size() - 1); } else { tree = childStack.remove(0); String indent = ""; for (int i = 0; i < childListStack.size() - 1; i++) { indent += (childListStack.get(i).size() > 0) ? "| " : " "; } String tokenName = LiquidParser.tokenNames[tree.getType()]; String tokenText = tree.getText().replaceAll("\\s+", " ").trim(); builder.append(indent) .append(childStack.isEmpty() ? "'- " : "|- ") .append(tokenName) .append(!tokenName.equals(tokenText) ? "='" + tokenText + "'" : "") .append("\n"); if (tree.getChildCount() > 0) { childListStack.add(new ArrayList((List) tree.getChildren())); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy