refcodes-io: Paving the road for complex and low level I/O across boundaries

README

The REFCODES.ORG codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.

What is this repository for?

This artifact defines basic types handling communication between processes (across system boundaries), to be harnessed by complex and low level I/O, such as InputStream decorators adding replay, filter, clipboard, compression, replace or timeout functionality as well as OutputStream decorators which add clipboard, line break or compression functionality.

How do I get set up?

To get up and running, include the following dependency (without the three dots “…”) in your pom.xml:

1
2
3
4
5
6
7
8
9
<dependencies>
	...
	<dependency>
		<artifactId>refcodes-io</artifactId>
		<groupId>org.refcodes</groupId>
		<version>4.0.0</version>
	</dependency>
	...
</dependencies>

The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.

Snippets of interest

Below find some code snippets which demonstrate the various aspects of using the refcodes-io artifact (and , if applicable, its offsprings). See also the example source codes of this artifact for further information on the usage of this artifact.

Replaying input streams

The ReplayInputStream decorates any InputStream with replay functionality for streams not supporting the mark and reset operations:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
InputStream theInptuStream = ...;
byte[] theBytes = new byte[16];
byte[] theReset = new byte[16];
try (ReplayInputStream theReplayInputStream = new ReplayInputStream( theInptuStream )) {
	theReplayInputStream.mark( 128 );
	theReplayInputStream.read( theBytes );
	...
	theReplayInputStream.reset();
	theReplayInputStream.read( theReset );
	...
}
...

In the above example, the replay size is set to 128 bytes (line 6) and the number of bytes we want to read twice is set to 16 (lines 3 and 4). We read the first 16 bytes (line 7) before resetting the stream (line 9) and reading the same bytes from the same stream again (line 10).

Filtering input streams

The FilterInputStream decorates any InputStream with filter functionality for removing unwanted data from the original stream:

1
2
3
4
5
6
7
8
9
10
...
InputStream theInptuStream = ...;
try (FilterInputStream theFilterInputStream = new FilterInputStream( theInputStream, '\n', '\t', '\r', ' ' )) {
	int e;
	while ( (e = theFilterInputStream.read()) != -1 ) {
		...
	}
	...
}
...

In the above example, we decorate our InputStream with a filter removing the white spaces \n, \t, \r as well as the space character (line 3). The data read from the InputStream (line 5) does not contain these white spaces anymore so that the filtered data can be processed (line 6) as required.

Find and replace for input streams

The ReplaceInputStream is similar to the FilterInputStream in that it manipulates any InputStream though instead of filtering data from the original stream data from the original stream is substituted with the according replacements:

1
2
3
4
5
6
7
8
9
10
...
InputStream theInptuStream = ...;
try (ReplaceInputStream theReplaceInputStream = new ReplaceInputStream( theInptuStream, "world", "universe" )) {
	int e;
	while ( (e = theReplaceInputStream.read()) != -1 ) {
		...
	}
	...
}
...

In the above example, we decorate our InputStream to replace the word “world” with the word “universe” (line 3). The data read from the InputStream (line 5) does now contain the words “universe” where the original stream has delivered the words “world” so that the modified (“find & replace”) data can be processed (line 6) as required. Instead of providing the words “world” and “universe”, you can use raw byte arrays. To apply multiple find & replace operations, you may nest multiple ReplaceInputStream instances.

Timeouted input streams

The TimeoutInputStream adds timeout functionality to the read operations of the original InputStream. The usage is straight forward:

1
2
3
4
5
6
7
8
9
10
...
InputStream theInptuStream = ...;
try (TimeoutInputStream theTimeoutInputStream = new TimeoutInputStream( theInptuStream, 500 )) {
	int e;
	while ( (e = theTimeoutInputStream.read()) != -1 ) {
		...
	}
	...
}
...

In the above example, we decorate our InputStream with a timeout of 500 milliseconds (line 3). In case the read operation exceeds the 500 milliseconds (line 5), an IOException is thrown (actually the more specific TimeoutIOException is being thrown).

The TimeoutInputStream works despite the decorated InputStream returning realistic values when calling the available method. If it can be guaranteed that the available method of the original InputStream returns realistic values, then you may use the AvailableInputStream: The benefit of the AvailableInputStream over the TimeoutInputStream is that the AvailableInputStream does not(!) use any additional threads for under the hood asynchronous operation!

Copy & paste to and from the clipboard

Copy

The ClipboardOutputStream provides an OutputStream for directly writing to the system’s clipboard. Whenever data is written to an OutputStream, the data may also be copied into the system clipboard by using the ClipboardOutputStream:

1
2
3
4
5
...
try (OutputStream theClipboardOutputStream = new ClipboardOutputStream()) {
	theClipboardOutputStream.write( "Hello world!" );
}
...

In the above example, after preparing to write to the clipboard (line 2), we copy the text “Hello world!” into the system’s clipboard (line 3).

Paste

The ClipboardInputStream provides the system’s clipboard as InputStream. Whenever data is read from an InputStream, the data may also be pasted from the system’s clipboard when using the ClipboardInputStream:

1
2
3
4
5
6
7
8
...
try (InputStream theClipboardInputStream = new ClipboardInputStream()) {
	int e;
	while ( (e = theClipboardInputStream.read()) != -1 ) {
		...
	}
}
...

In the above example, after preparing to read from the system clipboard (line 2), we paste from the system’s clipboard (line 4) for further processing (line 5).

Examples

See the example source codes of this artifact for further information on the usage of this artifact.

See also

Contribution guidelines

  • Report issues
  • Finding bugs
  • Helping fixing bugs
  • Making code and documentation better
  • Enhance the code

Who do I talk to?

Licensing Philosophy

This project follows a dual-licensing model designed to balance openness, pragmatism and fair attribution.

You may choose between the LGPL v3.0 or later and the Apache License v2.0 when using this software.

The intention behind this model is simple:

  • Enable use in both open-source and proprietary projects
  • Keep the codebase approachable and reusable
  • Ensure that improvements to the library itself remain available to the community
  • Preserve clear attribution to the original author and the ecosystem

Under the LGPL v3.0+, you are free to use this library in any application. If you modify the library itself, those modifications must be made available under the same license and must retain proper attribution.

Alternatively, the Apache License v2.0 allows broad use, modification and distribution, including commercial usage, provided that copyright notices and the accompanying NOTICE file are preserved.

This dual-licensing approach intentionally avoids artificial barriers while discouraging closed, uncredited forks of the core library. Contributions, improvements and refinements are encouraged to flow back into the project, benefiting both the community and downstream users.

For licensing questions, alternative licensing arrangements or commercial inquiries, please contact the copyright holder.