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


Back to top

Version: 1.2.1. Last Published: 2018-11-25.

Earcam Maven Skin.