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));
}