Distigme

A little learning is a dangerous thing.


1 Comment

Hacking Thymeleaf to minimize white space

Thymeleaf has an open feature request to suppress superfluous white space (whitespace?) in its output.

Getting this right is good for efficiency but rife with pitfalls, which is why the safest thing is to just leave all the white space alone. For sure, you don’t want to mess with the white space inside a <pre> or <code>, or any element that might potentially be styled white-space: pre, which is nearly all of them. Even if you remove only text nodes that are only white space, you can run into things like this:

<p>I am <u>very</u> <a href="#2">interested</a>.</p>

Caveats aside, let’s see how to configure Thymeleaf now to strip whitespace-only nodes. This approach will use a custom template writer, as suggested here.

In Spring, start with the templateEngine:

<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
	<property name="templateResolver" ref="templateResolver" />
	<property name="templateModeHandlers">
		<bean class="org.thymeleaf.templatemode.TemplateModeHandler">
			<constructor-arg index="0" value="HTML5" />
			<constructor-arg index="1" 
				value="#{T(org.thymeleaf.templatemode.StandardTemplateModeHandlers).HTML5.templateParser}" />
			<constructor-arg index="2">
				<bean class="org.example.WhiteSpaceNormalizedTemplateWriter" />
			</constructor-arg>
		</bean>
	</property>
</bean>

This is replacing the default list of template mode handlers (HTML5, XHTML, etc.) with a single custom one, which we are calling “HTML5” (or you could pick a distinctive name). A template mode handler really just ties together a parser and a writer, so there is no need to write a new class; instead, we construct an instance right here, passing in the name, the original parser for HTML5, and our custom template writer.

So, that’s how to wire the custom template writer into Thymeleaf via Spring. The class itself can look like this:

public final class WhiteSpaceNormalizedTemplateWriter extends AbstractGeneralTemplateWriter {
	
	@Override protected boolean shouldWriteXmlDeclaration() { return false; }
	@Override protected boolean useXhtmlTagMinimizationRules() { return true; }

	@Override
	protected void writeText(final Arguments arguments, final Writer writer, final Text text)
			throws IOException {
		final char[] textChars = text.unsafeGetContentCharArray();
		if ( StringUtils.hasText(CharBuffer.wrap(textChars)) ) {
			writer.write(textChars);
		}
	}
	
}
Advertisements