io
In/Output stream extensions
- A MarkSupportedInputStream the wraps an underlying InputStream, guarenteeing that mark is supported
- Search and replace on InputStreams and OutputStreams
- OutputStream splitting, with notion of header, record and footer and option to split on maximum number of bytes and/or records
Examples
ExplodedJarInputStream (treat a directory as a zipped JAR)
try(JarInputStream input = ExplodedJarInputStream.jarInputStreamFrom(jarDir)) { Manifest manifest = input.getManifest(); // ... }
ReplaceAllInputStream
@Test public void replaceMutlipleOccurrences() throws IOException { byte[] in = bytes("Some sample text, sampled by some for sample's sake"); byte[] search = bytes("sample"); byte[] replace = bytes("example"); try(ReplaceAllInputStream input = new ReplaceAllInputStream(search, replace, wrapInput(in))) { String out = readAll(input); assertThat(out, is(equalTo("Some example text, exampled by some for example's sake"))); } }
ReplaceAllOutputStream
@Test public void replaceConsecutiveOccurrences() throws Exception { byte[] out = bytes("ample samplesamplesample samples"); byte[] search = bytes("sample"); byte[] replace = bytes("example"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(ReplaceAllOutputStream output = new ReplaceAllOutputStream(search, replace, baos)) { output.write(out); } assertThat(text(baos.toByteArray()), is(equalTo("ample exampleexampleexample examples"))); } // EARCAM_SNIPPET_BEGIN: ReplaceAllOutputStream @Test public void mutlipleOccurrencesWithShorterReplacementThanSearch() throws Exception { byte[] out = bytes("ample sampled samples sampled"); byte[] search = bytes(" sample"); byte[] replace = new byte[0]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try(ReplaceAllOutputStream output = new ReplaceAllOutputStream(search, replace, baos)) { output.write(out); } assertThat(text(baos.toByteArray()), is(equalTo("ampledsd"))); } @Test public void whenClosedThenClosesUnderlying() throws IOException { AtomicBoolean closed = new AtomicBoolean(false); OutputStream dummy = new OutputStream() { @Override public void write(int b) throws IOException {} @Override public void close() throws IOException { closed.set(true); } }; try(ReplaceAllOutputStream output = new ReplaceAllOutputStream(new byte[1], new byte[1], dummy)) {} assertThat(closed.get(), is(true)); }
}
SplittableOutputStream
Imports
import static io.earcam.utilitarian.io.SplittableOutputStream.splittable; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.stream; import static java.util.stream.Collectors.joining; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is;
Example
class CountingOutputStreamSupplier implements Supplier<OutputStream> { final List<ByteArrayOutputStream> supplied = new ArrayList<>(); @Override public OutputStream get() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); supplied.add(outputStream); return outputStream; } public List<ByteArrayOutputStream> supplied() { return supplied; } @Override public String toString() { return supplied.stream() .map(ByteArrayOutputStream::toByteArray) .map(b -> new String(b, UTF_8)) .collect(joining("\n")); } } @Test public void exampleSingleExactSplit() throws Exception { byte[] head = bytes("<html><head><head><body><ul>"); // length: 28 byte[] foot = bytes("</ul></body></html>"); // length: 19 // listItem length: [10,12] // total body length for 100 listItems: 1092 = 9 * 10 + 90 * 11 + 1 * 12 CountingOutputStreamSupplier supplier = new CountingOutputStreamSupplier(); // expect a single file: 28 + 1092 + 19 try(SplittableOutputStream output = splittable(supplier, head, foot).maxSize(28 + 1092 + 19).outputStream()) { writeOneToOneHundredAsListItems(output); } assertThat(supplier, wroteOneToOneHundredAsListItems()); assertThat(supplier, hasSuppliedUsedOutputStreams(1)); } private Matcher<CountingOutputStreamSupplier> hasSuppliedUsedOutputStreams(int expected) { return new TypeSafeMatcher<CountingOutputStreamSupplier>() { @Override public void describeTo(Description description) {} @Override protected boolean matchesSafely(CountingOutputStreamSupplier supplier) { List<ByteArrayOutputStream> supplied = supplier.supplied(); return supplied.size() == expected; } }; } private Matcher<CountingOutputStreamSupplier> wroteOneToOneHundredAsListItems() { return new TypeSafeMatcher<SplittableOutputStreamTest.CountingOutputStreamSupplier>() { @Override public void describeTo(Description description) {} @Override protected boolean matchesSafely(CountingOutputStreamSupplier supplier) { String allOutput = supplier.toString(); for(int i = 1; i <= 100; i++) { if(!allOutput.contains(listItem(i))) { return false; } } return true; } }; } private void writeOneToOneHundredAsListItems(SplittableOutputStream output) throws IOException { for(int i = 1; i <= 100; i++) { output.beginRecord(); output.write(bytes(listItem(i))); output.endRecord(); } } private String listItem(int i) { return "<li>" + i + "</li>"; } @Test public void exampleTwoExactSplits() throws Exception { byte[] head = bytes("<html><head><head><body><ul>"); // length: 28 byte[] foot = bytes("</ul></body></html>"); // length: 19 // listItem length: [10,12] // total body length for 100 listItems: 1092 = 9 * 10 + 90 * 11 + 1 * 12 CountingOutputStreamSupplier supplier = new CountingOutputStreamSupplier(); // expect a two files: 28 + 1092/2 + 19 try(SplittableOutputStream output = splittable(supplier, head, foot).maxSize(28 + 1092 / 2 + 12 + 19).outputStream()) { writeOneToOneHundredAsListItems(output); } assertThat(supplier, wroteOneToOneHundredAsListItems()); assertThat(supplier, hasSuppliedUsedOutputStreams(2)); } @Test public void exampleThreeUnevenSplits() throws Exception { byte[] head = bytes("<html><head><head><body><ul>"); // length: 28 byte[] foot = bytes("</ul></body></html>"); // length: 19 // listItem length: [10,12] // total body length for 100 listItems: 1092 = 9 * 10 + 90 * 11 + 1 * 12 CountingOutputStreamSupplier supplier = new CountingOutputStreamSupplier(); // expect a single file: 28 + 545 + 19 try(SplittableOutputStream output = splittable(supplier, head, foot).maxSize(28 + 545 + 19).outputStream()) { writeOneToOneHundredAsListItems(output); } assertThat(supplier, wroteOneToOneHundredAsListItems()); assertThat(supplier, hasSuppliedUsedOutputStreams(3)); }