I am wrapping Freemarker template loader in Spring MVC as described here to have default escaping in html pages.
So, I need to wrap content from java.io.Reader with my strings, not reading it, not converting it to string and not implementing myself. Is there class similar to WrappingReader or com.google.common.io.MultiReader (which is not public) in popular common libraries?
My implementation:
import com.google.common.io.CharStreams;
import com.google.common.io.InputSupplier;
...
private final TemplateLoader delegate;
@Autowired
public HtmlEscapingTemplateLoader(ResourceLoader resourceLoader)
{
delegate = new SpringTemplateLoader(resourceLoader, "/WEB-INF/templates/");
}
@Override
public Reader getReader(Object templateSource, String encoding) throws IOException
{
// collecting readers
Reader prologue = new StringReader("<#escape x as x?html>");
Reader originalReader = delegate.getReader(templateSource, encoding);
Reader epilogue = new StringReader("</#escape>");
// concatenating readers
return merge(prologue, originalReader, epilogue);
}
protected Reader merge(Reader prologue, Reader originalReader, Reader epilogue) throws IOException
{
return CharStreams.join(
Arrays.asList(new ReaderSupplier(prologue), new ReaderSupplier(originalReader), new ReaderSupplier(
epilogue))).getInput();
}
private static class ReaderSupplier
implements InputSupplier<Reader>
{
private final Reader reader;
public ReaderSupplier(Reader reader)
{
this.reader = reader;
}
@Override
public Reader getInput() throws IOException
{
return reader;
}
}
CharStreams i use is marked as @com.google.common.annotations.Beta. So, could it be rewritten in more solid way without usage of CharStreams?
Guava contributor here. …Okay.
@Betadoes not mean “not fully tested” or “not widely used.” The only thing it means is that we’re not quite sure we’re ready to freeze the API. That’s only a problem if you’re developing a library, or planning to upgrade versions of Guava later. (Also, honestly,CharStreamsis about as stable as@BetaAPIs get…)You should not have a
ReaderSupplierclass, which defeats the whole point of theInputSupplierinterface. For this particular case, instead of passing aroundReaders, you should be passing aroundInputSupplier<Reader>s. In this particular case,CharStreams.newReaderSupplier(String)returns anInputSupplierthat producesStringReaders, so that fits this use case perfectly.In any event, my complete implementation would be something like: