AbstractThymeleafBuilder.java
package fr.sii.ogham.template.thymeleaf.common.buider;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ITemplateResolver;
import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
import fr.sii.ogham.core.builder.configurer.Configurer;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.builder.context.DefaultBuildContext;
import fr.sii.ogham.core.builder.resolution.ClassPathResolutionBuilder;
import fr.sii.ogham.core.builder.resolution.FileResolutionBuilder;
import fr.sii.ogham.core.builder.resolution.ResourceResolutionBuilder;
import fr.sii.ogham.core.builder.resolution.ResourceResolutionBuilderHelper;
import fr.sii.ogham.core.builder.resolution.StringResolutionBuilder;
import fr.sii.ogham.core.builder.template.DetectorBuilder;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.core.resource.resolver.FirstSupportingResourceResolver;
import fr.sii.ogham.core.resource.resolver.ResourceResolver;
import fr.sii.ogham.core.template.detector.TemplateEngineDetector;
import fr.sii.ogham.core.template.parser.TemplateParser;
import fr.sii.ogham.template.thymeleaf.common.SimpleThymeleafContextConverter;
import fr.sii.ogham.template.thymeleaf.common.TemplateResolverOptions;
import fr.sii.ogham.template.thymeleaf.common.ThymeleafContextConverter;
import fr.sii.ogham.template.thymeleaf.common.ThymeleafParser;
import fr.sii.ogham.template.thymeleaf.common.adapter.FirstSupportingResolverAdapter;
import fr.sii.ogham.template.thymeleaf.common.adapter.TemplateResolverAdapter;
@SuppressWarnings("squid:S00119")
public abstract class AbstractThymeleafBuilder<MYSELF extends AbstractThymeleafBuilder<MYSELF, P, E>, P, E extends AbstractThymeleafEngineConfigBuilder<E, MYSELF>> extends AbstractParent<P>
implements DetectorBuilder<MYSELF>, ResourceResolutionBuilder<MYSELF>, Builder<TemplateParser> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractThymeleafBuilder.class);
protected final MYSELF myself;
protected final BuildContext buildContext;
protected TemplateEngineDetector detector;
protected ResourceResolutionBuilderHelper<MYSELF> resourceResolutionBuilderHelper;
protected TemplateEngine engine;
protected ThymeleafContextConverter contextConverter;
protected E engineBuilder;
protected final List<TemplateResolverAdapter> customAdapters;
protected final ConfigurationValueBuilderHelper<MYSELF, Boolean> enableCacheValueBuilder;
protected AbstractThymeleafBuilder(Class<?> selfType) {
this(selfType, null, new DefaultBuildContext());
}
@SuppressWarnings("unchecked")
protected AbstractThymeleafBuilder(Class<?> selfType, P parent, BuildContext buildContext) {
super(parent);
myself = (MYSELF) selfType.cast(this);
this.buildContext = buildContext;
customAdapters = new ArrayList<>();
enableCacheValueBuilder = buildContext.newConfigurationValueBuilder(myself, Boolean.class);
}
protected AbstractThymeleafBuilder(P parent, BuildContext buildContext) {
this(AbstractThymeleafBuilder.class, parent, buildContext);
}
/**
* Enable/disable cache for templates.
*
* <p>
* The value set using this method takes precedence over any property and
* default value configured using {@link #cache()}.
*
* <pre>
* .cache(false)
* .cache()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue(true)
* </pre>
*
* <pre>
* .cache(false)
* .cache()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue(true)
* </pre>
*
* In both cases, {@code cache(false)} is used.
*
* <p>
* If this method is called several times, only the last value is used.
*
* <p>
* If {@code null} value is set, it is like not setting a value at all. The
* property/default value configuration is applied.
*
* @param enable
* enable or disable cache
* @return this instance for fluent chaining
*/
public MYSELF cache(Boolean enable) {
enableCacheValueBuilder.setValue(enable);
return myself;
}
/**
* Enable/disable cache for templates.
*
* <p>
* This method is mainly used by {@link Configurer}s to register some
* property keys and/or a default value. The aim is to let developer be able
* to externalize its configuration (using system properties, configuration
* file or anything else). If the developer doesn't configure any value for
* the registered properties, the default value is used (if set).
*
* <pre>
* .cache()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue(true)
* </pre>
*
* <p>
* Non-null value set using {@link #cache(Boolean)} takes precedence over
* property values and default value.
*
* <pre>
* .cache(false)
* .cache()
* .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
* .defaultValue(true)
* </pre>
*
* The value {@code false} is used regardless of the value of the properties
* and default value.
*
* <p>
* See {@link ConfigurationValueBuilder} for more information.
*
*
* @return the builder to configure property keys/default value
*/
public ConfigurationValueBuilder<MYSELF, Boolean> cache() {
return enableCacheValueBuilder;
}
/**
* Fluent configurer for Thymeleaf engine configuration.
*
* @return the fluent builder for Thymeleaf engine
*/
public E engine() {
if (engineBuilder == null) {
engineBuilder = getThymeleafEngineConfigBuilder();
}
return engineBuilder;
}
/**
* Sets a Thymeleaf engine.
*
* This value preempts any other value defined by calling {@link #engine()}
* method.
*
* If this method is called several times, only the last provider is used.
*
* @param engine
* the Thymeleaf engine
* @return this instance for fluent chaining
*/
public MYSELF engine(TemplateEngine engine) {
this.engine = engine;
return myself;
}
/**
* Ogham provides a generic context concept for template parsing. Thymeleaf
* uses also a context concept. A context converter
* ({@link ThymeleafContextConverter}) is the way to transform an Ogham
* context into a Thymeleaf context.
*
* <p>
* Ogham provides and registers the default converter
*
* <p>
* If this method is called several times, only the last provider is used.
*
* @param converter
* the context converter
* @return this instance for fluent chaining
*/
public MYSELF contextConverter(ThymeleafContextConverter converter) {
this.contextConverter = converter;
return myself;
}
@Override
public MYSELF detector(TemplateEngineDetector detector) {
this.detector = detector;
return myself;
}
@Override
public ClassPathResolutionBuilder<MYSELF> classpath() {
initResolutionBuilder();
return resourceResolutionBuilderHelper.classpath();
}
@Override
public FileResolutionBuilder<MYSELF> file() {
initResolutionBuilder();
return resourceResolutionBuilderHelper.file();
}
@Override
public StringResolutionBuilder<MYSELF> string() {
initResolutionBuilder();
return resourceResolutionBuilderHelper.string();
}
@Override
public MYSELF resolver(ResourceResolver resolver) {
initResolutionBuilder();
return resourceResolutionBuilderHelper.resolver(resolver);
}
/**
* Ogham provides a generic resource resolution mechanism
* ({@link ResourceResolver}). Thymeleaf uses its own template resolution
* mechanism ({@link ITemplateResolver}). A resolver adapter
* ({@link TemplateResolverAdapter}) is the way to transform a
* {@link ResourceResolver} into a {@link ITemplateResolver}.
*
* <p>
* Ogham provides and registers default resolver adapters but you may need
* to use a custom {@link ResourceResolver}. So you also need to provide the
* corresponding {@link TemplateResolverAdapter}.
*
* @param adapter
* the resolver adapter
* @return this instance for fluent chaining
*/
public MYSELF resolverAdapter(TemplateResolverAdapter adapter) {
customAdapters.add(adapter);
return myself;
}
@Override
public TemplateParser build() {
LOG.info("Thymeleaf parser is registered");
return buildContext.register(new ThymeleafParser(buildEngine(), buildResolver(), buildContext()));
}
@Override
public TemplateEngineDetector buildDetector() {
return detector == null ? createTemplateDetector() : detector;
}
/**
* Builds the resolver used by Thymeleaf to resolve resources
*
* @return the resource resolver
*/
public FirstSupportingResourceResolver buildResolver() {
return buildContext.register(new FirstSupportingResourceResolver(buildResolvers()));
}
protected TemplateEngine buildEngine() {
TemplateEngine builtEngine;
if (engine != null) {
LOG.debug("Using custom Thymeleaf engine");
builtEngine = engine;
} else if (engineBuilder != null) {
LOG.debug("Using custom Thymeleaf engine built using engine()");
builtEngine = engineBuilder.build();
} else {
LOG.debug("Using default Thymeleaf engine");
builtEngine = buildContext.register(new TemplateEngine());
}
builtEngine.addTemplateResolver(buildTemplateResolver(builtEngine));
return builtEngine;
}
protected abstract TemplateEngineDetector createTemplateDetector();
protected abstract ITemplateResolver buildTemplateResolver(TemplateEngine builtEngine);
protected abstract E getThymeleafEngineConfigBuilder();
protected ThymeleafContextConverter buildContext() {
return contextConverter == null ? buildContext.register(new SimpleThymeleafContextConverter()) : contextConverter;
}
private List<ResourceResolver> buildResolvers() {
initResolutionBuilder();
return resourceResolutionBuilderHelper.buildResolvers();
}
protected abstract FirstSupportingResolverAdapter buildAdapters();
protected TemplateResolverOptions buildTemplateResolverOptions() {
TemplateResolverOptions options = buildContext.register(new TemplateResolverOptions());
options.setCacheable(enableCacheValueBuilder.getValue());
// TODO: handle other options
return options;
}
private void initResolutionBuilder() {
if (resourceResolutionBuilderHelper == null) {
resourceResolutionBuilderHelper = new ResourceResolutionBuilderHelper<>(myself, buildContext);
}
}
}