简体   繁体   中英

How to merge java.io.Reader's or wrap Reader's content?

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.

@Beta does 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, CharStreams is about as stable as @Beta APIs get...)

You should not have a ReaderSupplier class, which defeats the whole point of the InputSupplier interface. For this particular case, instead of passing around Reader s, you should be passing around InputSupplier<Reader> s. In this particular case, CharStreams.newReaderSupplier(String) returns an InputSupplier that produces StringReader s, so that fits this use case perfectly.

In any event, my complete implementation would be something like:

static final String prologue = "<#escape x as x?html>";
static final String epilogue = "</#escape>";

// ideally you shouldn't be passing around Readers at all
// the point of InputSupplier, etc. is that you should never get direct access
// to the Reader directly, so you don't have to track whether it's closed or not
public InputSupplier<Reader> getReaderSupplier(
    final Object templateSource, final String encoding) {
  return CharStreams.join(
    CharStreams.newReaderSupplier(prologue),
    new InputSupplier<Reader>() {
      public Reader getInput() {
        return delegate.getReader(templateSource, encoding);
      }
    },
    CharStreams.newReaderSupplier(epilogue));
 }

I am only answering the question: How to merge java.io.Reader's.

Using plain Java I was able to come up with the solution of using SequenceInputStream on InputStreams , which seems to be the better solution for merging two InputStreams . See this stackoverflow question for merging InputStreams. Idea is simple: convert readers into InputStreams , merge those and save the InputStream into a Reader .

Reader reader1 = new StringReader("String1");
Reader reader2 = new StringReader("String2");

ReaderInputStream readInputStream1 = new ReaderInputStream(reader1, StandardCharsets.UTF_8)
ReaderInputStream readInputStream2 = new ReaderInputStream(reader2, StandardCharsets.UTF_8)

Reader merged = new InputStreamReader(new SequenceInputStream(readInputStream1,readInputStream2), StandardCharsets.UTF_8);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM