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

sirius.tagliatelle.Tagliatelle Maven / Gradle / Ivy

/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.tagliatelle;

import parsii.tokenizer.ParseError;
import parsii.tokenizer.Position;
import sirius.kernel.Sirius;
import sirius.kernel.cache.Cache;
import sirius.kernel.cache.CacheEntry;
import sirius.kernel.cache.CacheManager;
import sirius.kernel.commons.MultiMap;
import sirius.kernel.commons.Strings;
import sirius.kernel.commons.Tuple;
import sirius.kernel.commons.Value;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Parts;
import sirius.kernel.di.std.Register;
import sirius.kernel.health.Log;
import sirius.tagliatelle.compiler.CompilationContext;
import sirius.tagliatelle.compiler.CompileError;
import sirius.tagliatelle.compiler.CompileException;
import sirius.tagliatelle.compiler.Compiler;
import sirius.tagliatelle.rendering.GlobalRenderContext;
import sirius.web.resources.Resource;
import sirius.web.resources.Resources;
import sirius.web.templates.Templates;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Provides statically compiled and optimized templates to generate HTML, XML and text files.
 * 

* Helps to resolve and compile templates and also to generate a context used to render their output. */ @Register(classes = Tagliatelle.class) public class Tagliatelle { /** * Logs everything related to resolving, compiling and rendering tagliatelle templates. */ public static final Log LOG = Log.get("tagliatelle"); protected static final String PRAGMA_ALIAS = "alias"; /** * Contains all aliases collected via {@link ClassAliasProvider alias providers}. */ private Map> aliases; @Parts(ClassAliasProvider.class) private Collection aliasProviders; @Part private Resources resources; @Part private Templates templates; /** * Keeps compiled templates around to improve the speed of rendering. */ private Cache compiledTemplates = CacheManager.createLocalCache("tagliatelle-templates"); private List>> globalVariables; private MultiMap taglibTags; /** * Returns all taglibs and all tags within this taglib. * * @return a multimap containing all taglibs (prefix) and their tags */ public MultiMap getTagLibTags() { if (taglibTags == null) { MultiMap result = MultiMap.createOrdered(); Sirius.getClasspath() .find(Pattern.compile("(default/)?taglib/([a-z]+)/(.*).html.pasta")) .forEach(m -> result.put(m.group(2), m.group(3))); taglibTags = result; } return taglibTags; } /** * Returns a list of all tag lib prefixes and descriptions. * * @return a list of tuples containing the taglib prefix and a short description */ public List> getTagLibs() { return getTagLibTags().keySet() .stream() .map(name -> Tuple.create(name, Sirius.getSettings() .get("tagliatelle.taglib." + name) .asString(name))) .collect(Collectors.toList()); } /** * Determines if a taglib with the given prefix exists. * * @param prefix the prefix to check * @return true if a taglib with the given prefix exists, false otherwise */ public boolean isTaglib(String prefix) { return getTagLibTags().getUnderlyingMap().containsKey(prefix); } /** * Provides all known class aliases. * * @return an unmodifyable map of all known aliases for Java classes. */ public Map> getClassAliases() { if (aliases == null) { Map> aliasMap = new HashMap<>(); aliasProviders.forEach(p -> p.collectAliases(aliasMap::put)); aliases = aliasMap; } return Collections.unmodifiableMap(aliases); } /** * Creates a new list of global variables used to initialize a global render context. * * @return a new list of global variables */ public List createEnvironment() { return new ArrayList<>(templates.createGlobalContext().values()); } /** * Returns the names and types of the known global variables to assist the {@link Compiler}. * * @return the list of known global variables and their types */ public List>> getGlobalVariables() { if (globalVariables == null) { List>> globals = new ArrayList<>(); templates.createGlobalContext().forEach((key, value) -> globals.add(Tuple.create(key, value.getClass()))); globalVariables = Collections.unmodifiableList(globals); } return globalVariables; } /** * Creates a new {@link CompilationContext} for the given path and resource. * * @param path the path of the template being compiled * @param resource the actual resource which was used to determine the source code of the template * @param parent if the compilation was started while compiling another template, its context is given here. This * is mainly used to detect and abort cyclic dependencies at compile time. * @return a new compilation context for the given resource */ public CompilationContext createCompilationContext(@Nonnull String path, @Nullable Resource resource, @Nullable CompilationContext parent) { Template template = new Template(path, resource); return new CompilationContext(template, parent); } /** * Creates a new render context. * * @return a context used to render a template */ public GlobalRenderContext createRenderContext() { return new GlobalRenderContext(this); } /** * Determines if the type to is assignable from the given object. *

* In contrast to {@link Class#isAssignableFrom(Class)}, this also handles autoboxing appropriately. * * @param from the object to assign * @param to the type to assign to * @return true if the object is assignable, false otherwise */ public static boolean isAssignable(Object from, Class to) { if (from == null) { return !to.isPrimitive(); } return isAssignableTo(from.getClass(), to); } /** * Determines if the type to is assignable from the given type from. *

* In contrast to {@link Class#isAssignableFrom(Class)}, this also handles autoboxing appropriately. * * @param from the type to assign * @param to the type to assign to * @return true if the object is assignable, false otherwise */ public static boolean isAssignableTo(Class from, Class to) { if (to.isAssignableFrom(from)) { return true; } // Null if represented as void.class and can be assigned to any non-primitive type. if (from == void.class) { return !to.isPrimitive(); } if (from.isPrimitive()) { if (to.isPrimitive()) { return checkTypeConversion(from, to); } return checkAutoboxing(from, to); } else { return checkAutoboxing(to, from); } } private static boolean checkTypeConversion(Class from, Class to) { return from == long.class && to == int.class || from == int.class && to == long.class; } /** * Determines if the boxed type matches the primitive type. * * @param primitveType the primitive type to check, e.g. int.class * @param boxedType the boxed type to check, e.g. Integer.class * @return true if autoboxing would be successful for the given classes, false otherwise */ private static boolean checkAutoboxing(Class primitveType, Class boxedType) { if (primitveType == int.class || primitveType == long.class) { return boxedType.isAssignableFrom(Integer.class); } if (primitveType == boolean.class) { return boxedType.isAssignableFrom(Boolean.class); } return false; } /** * Resolves the given path using {@link Resources} and compiles it into a {@link Template}. * * @param path the path to resolve * @return the appropriate template or an empty template if no matching {@link Resource} was found. * @throws CompileException in case of one or more compilation errors in the template */ public Optional