ThymeleafV3TemplateDetector.java

package fr.sii.ogham.template.thymeleaf.v3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.sii.ogham.core.exception.resource.ResourceResolutionException;
import fr.sii.ogham.core.exception.template.EngineDetectionException;
import fr.sii.ogham.core.resource.Resource;
import fr.sii.ogham.core.resource.path.ResourcePath;
import fr.sii.ogham.core.resource.resolver.ResourceResolver;
import fr.sii.ogham.core.template.context.Context;
import fr.sii.ogham.core.template.detector.TemplateEngineDetector;

/**
 * Detector that reads the content of the template. If the template contains the
 * Thymeleaf namespace (http://www.thymeleaf.org) then the detector returns
 * true. Otherwise it returns false.
 * 
 * @author Aurélien Baudet
 *
 */
public class ThymeleafV3TemplateDetector implements TemplateEngineDetector {
	private static final Logger LOG = LoggerFactory.getLogger(ThymeleafV3TemplateDetector.class);

	/**
	 * The pattern to search into the template
	 */
	private static final Pattern NAMESPACE_PATTERN = Pattern.compile("xmlns[^=]+=\\s*\"http://www.thymeleaf.org\"");

	/**
	 * The pattern to search into the template
	 */
	private static final Pattern VARIABLE_PATTERN = Pattern.compile("(\\[\\[\\$\\{[^}]+\\}\\]\\])|(\\[\\(\\$\\{[^}]+\\}\\)\\])");

	/**
	 * The template resolver used to find the template
	 */
	private final ResourceResolver resolver;

	public ThymeleafV3TemplateDetector(ResourceResolver resolver) {
		super();
		this.resolver = resolver;
	}

	@Override
	public boolean canParse(ResourcePath template, Context ctx) throws EngineDetectionException {
		LOG.debug("Checking if Thymeleaf can handle the template {}", template);
		Resource resolvedTemplate = getTemplate(template);
		if (resolvedTemplate == null) {
			return false;
		}
		try (BufferedReader br = new BufferedReader(new InputStreamReader(resolvedTemplate.getInputStream()))) {
			boolean isThymeleafTemplate = isThymeleafTemplate(br);
			if (isThymeleafTemplate) {
				LOG.debug("The template {} contains the namespace http://www.thymeleaf.org. Thymeleaf can be used", template);
			} else {
				LOG.debug("The template {} doesn't contain the namespace http://www.thymeleaf.org. Thymeleaf can't be used", template);
			}
			return isThymeleafTemplate || isEmptyTemplate(resolvedTemplate);
		} catch (IOException e) {
			throw new EngineDetectionException("Failed to detect because template can't be read by thymeleaf", e);
		}
	}

	private static boolean isThymeleafTemplate(BufferedReader br) throws IOException {
		String line;
		do {
			line = br.readLine();
			if (line != null && (containsThymeleafNamespace(line) || containsThymeleafVariables(line))) {
				return true;
			}
		} while (line != null);
		return false;
	}
	
	private static boolean isEmptyTemplate(Resource template) throws IOException {
		try (InputStream stream = template.getInputStream()) {
			return stream.read() == -1;
		}
	}

	private static boolean containsThymeleafNamespace(String line) {
		return NAMESPACE_PATTERN.matcher(line).find();
	}

	private static boolean containsThymeleafVariables(String line) {
		return VARIABLE_PATTERN.matcher(line).find();
	}

	private Resource getTemplate(ResourcePath templateName) {
		try {
			return resolver.getResource(templateName);
		} catch (ResourceResolutionException e) {
			LOG.trace("Thymeleaf detector can't be applied because {} couldn't be resolved", templateName, e);
			return null;
		}
	}

}