org.klojang.templates.Accessor Maven / Gradle / Ivy
Show all versions of klojang-templates Show documentation
package org.klojang.templates;
import java.util.function.IntFunction;
/**
* Accessors are used to extract values from objects. The {@link RenderSession} uses
* them to extract values from the objects passed to
* {@link RenderSession#insert(Object) insert()} and
* {@link RenderSession#populate(String, Object) populate()}. Object
* access is name-based and requires a mapping between template variables and bean
* properties or map keys. By default, Klojang Templates assumes an as-is
* mapping between the two, but you can use {@linkplain NameMapper name mappers} for
* more sophisticated mappings.
*
* @param the type of the source data object
* @author Ayco Holleman
* @see AccessorRegistry
*/
@FunctionalInterface
public interface Accessor {
/**
*
* The value that should be returned by accessors if a template variable
* cannot be mapped to a value in the source data object. {@code Accessor}
* implementations should not throw an exception and they should not return
* {@code null} in this case.
*
* Null vs. UNDEFINED
*
* There is a subtle difference in how a {@link RenderSession} treats {@code null}
* values versus how it treats {@code UNDEFINED}. {@code null} is a valid and
* legitimate value for a template variable. If the value of a bean property or map key
* is {@code null}, the corresponding template variable will be set to
* whatever {@code null} is stringified to —. The
* {@linkplain Stringifier#DEFAULT default stringifier} stringifies {@code null} to an
* empty string. If, on the other hand, the {@code RenderSession} receives
* {@code UNDEFINED} as the value for a template variable, it will just skip
* setting that variable. By itself this will make no difference when the template
* is rendered. An unset variable will be replaced with "nothing" —
* i.e. an empty string. However, it does make a difference
* if you want to set all unset variables to some default value after you have
* populated your template with model objects, hash maps, and/or anything else for
* which you have defined an accessor:
*
* {@code
* CompanyDao dao = new CompanyDao();
* Template template = Template.fromResource(getClass(), "/views/companies.html");
* RenderSession session = template.newRenderSession();
* session.populate("companies", dao.list());
* session.getAllUnsetVariables().forEach(var -> session.setPath(var, i -> "(unknown)");
* }
*
* You can make the {@code RenderSession} treat {@code null} just like
* {@code UNDEFINED}:
*
* {@code
* AccessorRegistry accessors = AccessorRegistry.build()
* .nullEqualsUndefined(true);
* .freeze();
* Template template = Template.fromResource(getClass(), "/views/companies.html");
* RenderSession session = template.newRenderSession(accessors);
* }
*
* @see AccessorRegistry.Builder#nullEqualsUndefined(boolean)
* @see RenderSession#setPath(String, IntFunction)
*/
Object UNDEFINED = new Object();
/**
* Returns the value identified by the specified name from the specified source
* data object. If the source data object is a {@code Map}, {@code name} would
* likely be a map key; if it is a JavaBean, {@code name} would likely be a bean
* property. However, it is up to individual {@code Accessor} implementations to
* determine the type of objects they provide access to, and how names are to be
* interpreted.
*
* @param data the data to be accessed
* @param name the name by which to retrieve the desired value from the data
* @return the value
*/
Object access(T data, String name);
}