Skip to content
Permalink
Browse files
GEOMETRY-138: switching to unchecked exceptions in IO modules
  • Loading branch information
darkma773r committed Jul 31, 2021
1 parent f3959b2 commit eddfd0823e36ac29044ee5eac371da62fda0957b
Showing 70 changed files with 1,592 additions and 1,127 deletions.
@@ -16,7 +16,6 @@
*/
package org.apache.commons.geometry.io.core;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -39,8 +38,24 @@
* All IO operations are delegated to registered format-specific {@link BoundaryReadHandler read handlers}
* and {@link BoundaryWriteHandler write handlers}.
*
* <p>Instances of this class are thread-safe as long as the registered handler instances are
* thread-safe.</p>
* <p><strong>Exceptions</strong>
* <p>Despite having functionality related to I/O operations, this class has been designed to <em>not</em>
* throw checked exceptions, in particular {@link java.io.IOException IOException}. The primary reasons for
* this choice are
* <ul>
* <li>convenience,</li>
* <li>compatibility with functional programming, and </li>
* <li>the fact that modern Java practice is moving away from checked exceptions in general (as exemplified
* by the JDK's {@link java.io.UncheckedIOException UncheckedIOException}).</li>
* </ul>
* As a result, any {@link java.io.IOException IOException} thrown internally by this or related classes
* is wrapped with {@link java.io.UncheckedIOException UncheckedIOException}. Other common runtime exceptions
* include {@link IllegalArgumentException}, which typically indicates mathematically invalid data, and
* {@link IllegalStateException}, which typically indicates format or parsing errors. See the method-level
* documentation for more details.
*
* <p><strong>Implementation note:</strong> Instances of this class are thread-safe as long as the
* registered handler instances are thread-safe.</p>
* @param <H> Geometric boundary type
* @param <B> Boundary source type
* @param <R> Read handler type
@@ -193,12 +208,12 @@ public W getWriteHandlerForFileExtension(final String fileExt) {
* file extension of the input {@link GeometryInput#getFileName() file name}
* @param precision precision context used for floating point comparisons
* @return object containing all boundaries from the input
* @throws IllegalArgumentException if no {@link BoundaryReadHandler read handler}
* can be found for the input format
* @throws IOException if an IO error occurs
* @throws IllegalArgumentException if mathematically invalid data is encountered or no
* {@link BoundaryReadHandler read handler} can be found for the input format
* @throws IllegalStateException if a data format error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public B read(final GeometryInput in, final GeometryFormat fmt, final Precision.DoubleEquivalence precision)
throws IOException {
public B read(final GeometryInput in, final GeometryFormat fmt, final Precision.DoubleEquivalence precision) {
return requireReadHandler(in, fmt).read(in, precision);
}

@@ -210,20 +225,24 @@ public B read(final GeometryInput in, final GeometryFormat fmt, final Precision.
* // access stream content
* }
* </pre>
* <p>An {@link IOException} is thrown immediately by this method if stream creation fails. Any IO errors
* occurring during stream iteration are wrapped with {@link java.io.UncheckedIOException}. Other runtime
* exceptions may be thrown during stream iteration if mathematically invalid boundaries are encountered.</p>
* <p>The following exceptions may be thrown during stream iteration:
* <ul>
* <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
* <li>{@link IllegalStateException} if a data format error occurs</li>
* <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
* </ul>
* @param in input to read boundaries from
* @param fmt format of the input; if null, the format is determined implicitly from the
* file extension of the input {@link GeometryInput#getFileName() file name}
* @param precision precision context used for floating point comparisons
* @return stream providing access to all boundaries from the input
* @throws IllegalArgumentException if no {@link BoundaryReadHandler read handler}
* can be found for the input format
* @throws IOException if an IO error occurs
* @throws IllegalArgumentException if no {@link BoundaryReadHandler read handler} can be found for
* the input format
* @throws IllegalStateException if a data format error occurs during stream creation
* @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
*/
public Stream<H> boundaries(final GeometryInput in, final GeometryFormat fmt,
final Precision.DoubleEquivalence precision) throws IOException {
final Precision.DoubleEquivalence precision) {
return requireReadHandler(in, fmt).boundaries(in, precision);
}

@@ -234,9 +253,9 @@ public Stream<H> boundaries(final GeometryInput in, final GeometryFormat fmt,
* file extension of the output {@link GeometryOutput#getFileName()}
* @throws IllegalArgumentException if no {@link BoundaryWriteHandler write handler} can be found
* for the output format
* @throws IOException if an IO error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public void write(final B src, final GeometryOutput out, final GeometryFormat fmt) throws IOException {
public void write(final B src, final GeometryOutput out, final GeometryFormat fmt) {
requireWriteHandler(out, fmt).write(src, out);
}

@@ -16,7 +16,6 @@
*/
package org.apache.commons.geometry.io.core;

import java.io.IOException;
import java.util.stream.Stream;

import org.apache.commons.geometry.core.partitioning.BoundarySource;
@@ -44,14 +43,15 @@
GeometryFormat getFormat();

/** Return an object containing all boundaries read from {@code input} using the handler's
* supported data format. Implementations may throw runtime exceptions if mathematically
* invalid boundaries are encountered.
* @param input input to read form
* supported data format.
* @param input input to read from
* @param precision precision context used for floating point comparisons
* @return object containing all boundaries read from {@code input}
* @throws IOException if an IO error occurs
* @throws IllegalArgumentException if mathematically invalid data is encountered
* @throws IllegalStateException if a data format error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
B read(GeometryInput input, Precision.DoubleEquivalence precision) throws IOException;
B read(GeometryInput input, Precision.DoubleEquivalence precision);

/** Return a {@link Stream} that can be used to access all boundary information from the given input,
* which is expected to contain data in the format supported by this handler. Unlike the
@@ -70,13 +70,17 @@
* }
* </pre>
*
* <p>An {@link IOException} is thrown immediately by this method if stream creation fails. Any IO errors
* occurring during stream iteration are wrapped with {@link java.io.UncheckedIOException}. Other runtime
* exceptions may be thrown during stream iteration if mathematically invalid boundaries are encountered.</p>
* <p>The following exceptions may be thrown during stream iteration:
* <ul>
* <li>{@link IllegalArgumentException} if mathematically invalid data is encountered</li>
* <li>{@link IllegalStateException} if a data format error occurs</li>
* <li>{@link java.io.UncheckedIOException UncheckedIOException} if an I/O error occurs</li>
* </ul>
* @param in input to read from
* @param precision precision context used for floating point comparisons
* @return stream providing access to the boundary information from the given input
* @throws IOException if an I/O error occurs during stream creation
* @throws IllegalStateException if a data format error occurs during stream creation
* @throws java.io.UncheckedIOException if an I/O error occurs during stream creation
*/
Stream<H> boundaries(GeometryInput in, Precision.DoubleEquivalence precision) throws IOException;
Stream<H> boundaries(GeometryInput in, Precision.DoubleEquivalence precision);
}
@@ -16,8 +16,6 @@
*/
package org.apache.commons.geometry.io.core;

import java.io.IOException;

import org.apache.commons.geometry.core.partitioning.BoundarySource;
import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
import org.apache.commons.geometry.io.core.output.GeometryOutput;
@@ -45,7 +43,7 @@
* the instance.
* @param src boundary source
* @param out output to write to
* @throws IOException if an IO error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
void write(B src, GeometryOutput out) throws IOException;
void write(B src, GeometryOutput out);
}
@@ -17,7 +17,6 @@
package org.apache.commons.geometry.io.core.input;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
@@ -62,8 +61,8 @@ public Path getFile() {
* <p>The returned input stream is buffered.</p>
*/
@Override
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(Files.newInputStream(file));
public InputStream getInputStream() {
return GeometryIOUtils.getUnchecked(() -> new BufferedInputStream(Files.newInputStream(file)));
}

/** {@inheritDoc} */
@@ -16,7 +16,6 @@
*/
package org.apache.commons.geometry.io.core.input;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

@@ -38,7 +37,7 @@ public interface GeometryInput {

/** Get the input stream for reading from the input.
* @return input stream for reading from the input
* @throws IOException if an IO error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
InputStream getInputStream() throws IOException;
InputStream getInputStream();
}
@@ -17,7 +17,6 @@
package org.apache.commons.geometry.io.core.input;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
@@ -61,8 +60,8 @@ public URL getUrl() {
* <p>The returned input stream is buffered.</p>
*/
@Override
public InputStream getInputStream() throws IOException {
return new BufferedInputStream(url.openStream());
public InputStream getInputStream() {
return GeometryIOUtils.getUnchecked(() -> new BufferedInputStream(url.openStream()));
}

/** {@inheritDoc} */
@@ -22,7 +22,7 @@

/** Class used to buffer characters read from an underlying {@link Reader}.
* Characters can be consumed from the buffer, examined without being consumed,
* and pushed back onto the buffer. The internal buffer is resized as needed.
* and pushed back onto the buffer. The internal bufer is resized as needed.
*/
public class CharReadBuffer {

@@ -101,9 +101,9 @@ public CharReadBuffer(final Reader reader, final int initialCapacity, final int

/** Return true if more characters are available from the read buffer.
* @return true if more characters are available from the read buffer
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public boolean hasMoreCharacters() throws IOException {
public boolean hasMoreCharacters() {
return makeAvailable(1) > 0;
}

@@ -112,9 +112,9 @@ public boolean hasMoreCharacters() throws IOException {
* is returned.
* @param n number of characters requested to be available
* @return number of characters available for immediate use in the buffer
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public int makeAvailable(final int n) throws IOException {
public int makeAvailable(final int n) {
final int diff = n - count;
if (diff > 0) {
readChars(diff);
@@ -125,10 +125,10 @@ public int makeAvailable(final int n) throws IOException {
/** Remove and return the next character in the buffer.
* @return the next character in the buffer or {@value #EOF}
* if the end of the content has been reached
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
* @see #peek()
*/
public int read() throws IOException {
public int read() {
final int result = peek();
charsRemoved(1);

@@ -141,10 +141,10 @@ public int read() throws IOException {
* @param len requested length of the string
* @return a string from the read buffer or null if no more characters are available
* @throws IllegalArgumentException if {@code len} is less than 0
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
* @see #peekString(int)
*/
public String readString(final int len) throws IOException {
public String readString(final int len) {
final String result = peekString(len);
if (result != null) {
charsRemoved(result.length());
@@ -156,10 +156,10 @@ public String readString(final int len) throws IOException {
/** Return the next character in the buffer without removing it.
* @return the next character in the buffer or {@value #EOF}
* if the end of the content has been reached
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
* @see #read()
*/
public int peek() throws IOException {
public int peek() {
if (makeAvailable(1) < 1) {
return EOF;
}
@@ -172,10 +172,10 @@ public int peek() throws IOException {
* @param len requested length of the string
* @return a string from the read buffer or null if no more characters are available
* @throws IllegalArgumentException if {@code len} is less than 0
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
* @see #readString(int)
*/
public String peekString(final int len) throws IOException {
public String peekString(final int len) {
if (len < 0) {
throw new IllegalArgumentException("Requested string length cannot be negative; was " + len);
} else if (len == 0) {
@@ -206,9 +206,9 @@ public String peekString(final int len) throws IOException {
* @param index index of the character to receive relative to the buffer start
* @return the character at the given index of {@code -1} if the character is
* past the end of the stream content
* @throws IOException if an I/O exception occurs
* @throws java.io.UncheckedIOException if an I/O exception occurs
*/
public int charAt(final int index) throws IOException {
public int charAt(final int index) {
if (index < 0) {
throw new IllegalArgumentException("Character index cannot be negative; was " + index);
}
@@ -224,10 +224,10 @@ public int charAt(final int index) throws IOException {
* and then from the underlying reader using {@link Reader#skip(long)} if needed.
* @param n number of character to skip
* @return the number of characters skipped
* @throws IOException if an I/O error occurs
* @throws IllegalArgumentException if {@code n} is negative
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
public int skip(final int n) throws IOException {
public int skip(final int n) {
if (n < 0) {
throw new IllegalArgumentException("Character skip count cannot be negative; was " + n);
}
@@ -239,7 +239,11 @@ public int skip(final int n) throws IOException {
// skip from the reader if required
final int remaining = n - skipped;
if (remaining > 0) {
skipped += reader.skip(remaining);
try {
skipped += reader.skip(remaining);
} catch (IOException exc) {
throw GeometryIOUtils.createUnchecked(exc);
}
}

return skipped;
@@ -281,29 +285,33 @@ private void pushCharInternal(final char ch) {
* the internal buffer.
* @param n minimum number of characters requested to be placed
* in the buffer
* @throws IOException if an I/O error occurs
* @throws java.io.UncheckedIOException if an I/O error occurs
*/
private void readChars(final int n) throws IOException {
private void readChars(final int n) {
if (!reachedEof) {
int remaining = Math.max(n, minRead);

ensureCapacity(count + remaining);

int tail;
int len;
int read;
while (remaining > 0) {
tail = (head + count) % buffer.length;
len = Math.min(buffer.length - tail, remaining);

read = reader.read(buffer, tail, len);
if (read == EOF) {
reachedEof = true;
break;
try {
int tail;
int len;
int read;
while (remaining > 0) {
tail = (head + count) % buffer.length;
len = Math.min(buffer.length - tail, remaining);

read = reader.read(buffer, tail, len);
if (read == EOF) {
reachedEof = true;
break;
}

charsAppended(read);
remaining -= read;
}

charsAppended(read);
remaining -= read;
} catch (IOException exc) {
throw GeometryIOUtils.createUnchecked(exc);
}
}
}

0 comments on commit eddfd08

Please sign in to comment.