New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MAJOR FEAT] Refactoring of the Context API #420

Closed
danielfernandez opened this Issue Oct 20, 2015 · 1 comment

Comments

Projects
None yet
1 participant
@danielfernandez
Member

danielfernandez commented Oct 20, 2015

The Thymeleaf Context API lives mostly in the org.thymeleaf.context package, and includes the classes needed for both specifying the variables and locale the user wants templates to be executed with (the template data, therefore), and also for the template engine to keep relevant information about the current state of template processing and exchange this among the diverse parts of the system.

The Context API in Thymeleaf 2.1 was suffering from an important degree of complexity which made it difficult for developers to understand the meaning of the different classes and interfaces. Also, variables were being copied accross several different Map instances during template execution, which provoked relevant inefficiencies at the context handling mechanisms.

This API has been completely refactored in Thymeleaf 3.0:

  • Context interfaces now follow an easy-to-understand hierarchy:
    • IContext implementations are the most basic ones, used as an argument to the process(...) methods in ITemplateEngine instances. They are meant as a mere way of specifying context variables and locale for template execution.
    • IExpressionContext extends IContext, and its implementations are meant to specify not only variables and locale, but also the information required to execute expressions (e.g. Thymeleaf Standard Expressions), so that parts of the expression mechanism like fragment signature parsing or the like can be executed separately, without the need to process an entire template.
    • ITemplateContext extends IExpressionContext, and its implementations are meant to contain everything that might be needed by processing artifacts during template execution (e.g. processors, pre-processors, etc.)
    • IEngineContext extends ITemplateContext, and is of mostly internal use. Its implementations add to those of ITemplateContext all the mechanisms internally required by the engine to manage template execution: from low-level access to local variable management to parsing hierarchy status.
  • Besides these, there is an additional interface called IWebContext that can be implemented by any of the implementations of the interfaces above to signal that they are context objects meant to be used in Servlet-API web environments, i.e. environments in which a request, a response, a servlet context and perhaps a session are available.

The IContext interface has two basic implementations that will cover most of the use cases: Context and WebContext (which also implements IWebContext). These will be the only ones most users will need to care about.

public interface IContext {
    public Locale getLocale();
    public boolean containsVariable(final String name);
    public Set<String> getVariableNames();
    public Object getVariable(final String name);
}
public interface IWebContext extends IContext {
    public HttpServletRequest getRequest();
    public HttpServletResponse getResponse();
    public HttpSession getSession();
    public ServletContext getServletContext();
}

The IExpressionContext interface offers two basic implementations too: ExpressionContext and WebExpressionContext, which can be used for executing the Thymeleaf expression engine from outside the template engine if needed, for example, to separately parse some expressions.

public interface IExpressionContext extends IContext {
    public IEngineConfiguration getConfiguration();
    public IExpressionObjects getExpressionObjects();
}

The ITemplateContext interface offers no direct implementations out of the box, as its true aim is to offer the subset of methods from its IEngineContext subinterface that are considered non-internal, and that therefore are safe to be used in processors and other user-developed artifacts. All out-of-the-box implementations of IEngineContext included in Thymeleaf 3.0 are also implementations of IEngineContext and are non-public due to their internal complexity.

public interface ITemplateContext extends IExpressionContext {

    public TemplateMode getTemplateMode();
    public TemplateData getTemplateData();
    public List<TemplateData> getTemplateStack();
    public List<IProcessableElementTag> getElementStack();

    public Map<String,Object> getTemplateResolutionAttributes();

    public IModelFactory getModelFactory();

    public String getMessage(
            final Class<?> origin, final String key, final Object[] messageParameters, 
            final boolean useAbsentMessageRepresentation);

    public String buildLink(final String base, final Map<String,Object> parameters);

    public IdentifierSequences getIdentifierSequences();

    public boolean hasSelectionTarget();
    public Object getSelectionTarget();

    public IInliner getInliner();

}

Let's have a defailed look at what the ITemplateContext interface seen above offers:

  • The template mode used by the template being processed. Note that the events being processed at a specific moment might not belong to the first-level template, but instead to an inserted fragment which might use a different template mode (see #325 (comment)).
  • The template data object containing the metadata corresponding to the template being processed. This includes the template mode (getTemplateMode() is actually equivalent to getTemplateData().getTemplateMode()), the template resource and the cache validity specification. Again, note that this template data might not correspond to the first-level template but instead the nested/inserted fragment whose events are currently being processed.
  • The stack of templates, a list containing all the template data objects that have been nested for reaching the current point of execution, starting from the first-level template being processed. This is a way in which processors can know the complete execution route that lead to the execution of a specific event (e.g. Template A inserted fragment B, which inserted fragment C).
  • The element stack is the hierarchy of elements (open or standalone) that had to be processed in order to reach the current point in execution. This allows any processor to know the hierarchy of containers inside of which it is executed, in order to extract information from it if needed.
  • The template resolution attributes, a map of configuration items that might have been specified when calling the template engine's process(...) methods and that further configures the template being used. This map of attributes will be passed to every execution of the ITemplateResolver performed during template processing, be it for the first-level template or for any of its arguments.
  • The model factory (IModelFactory) is the class needed to create new events to be included into models, such as IText, IComment, IOpenElementTag, ICloseElementTag, etc. also, the model factory provides some utility methods for managing attributes in tags.
  • A mechanism for obtaining externalized/internationalized messages, by means of the getMessage(...) method. This is a convenience, easier way of accessing the Message Resolution API than directly going to the engine configuration, retrieving the list of Message Resolvers, iterating it asking for resolution, etc.
  • A mechanism for creating links to be used in templates. Link creation is customizable by means of the ILinkBuilder interface, and this method allows the creation of links (i.e. URLs) from anywhere in any custom processor, making use of any link builders that might have been configured.
  • An identifier sequence generation mechanism, used in scenarios in which processors need to generate uniquely numbered id attribute values for its output markup, like e.g. for checkboxes in forms.
  • A mechanism for obtaining the current selection target, if any. The selection target is the object on which selection expressions (*{...}) should be executed, usually specified by means of the th:object attribute (e.g. in forms).
  • A mechanism for obtaining the inliner currently being used, if any. This will specify which of the existing IInliner implementations is currently being used. For more info on this, see #396.

Lastly, note that in the rare occasions in which users might need to define their own IEngineContext implementations, the template engine will perfectly allow that and use the user-defined objects for processing without performing any copies of data to Thymeleaf's own context implementations.

Lazy evaluation

As a plus, this new API offers a way to specify variables that are not meant to be already resolved when the context object is created and the template engine is executed, but instead only when they are really accessed from inside the template's expressions.

The org.thymeleaf.context.ILazyContextVariable interface takes care of this. For more info, see #101 (comment)

@danielfernandez danielfernandez self-assigned this Oct 20, 2015

@danielfernandez danielfernandez added this to the Thymeleaf 3.0 milestone Oct 20, 2015

@danielfernandez danielfernandez changed the title from Refactoring of the Context API to [MAJOR FEAT] Refactoring of the Context API Oct 20, 2015

@danielfernandez

This comment has been minimized.

Show comment
Hide comment
@danielfernandez

danielfernandez Oct 20, 2015

Member

Already in 3.0.0-SNAPSHOT

Member

danielfernandez commented Oct 20, 2015

Already in 3.0.0-SNAPSHOT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment