diff --git a/README.md b/README.md index 2c6dae5..4c58153 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,13 @@ A library for handling exceptions with reasons. In Java programming, it is cumbersome to implement a separate exception class for each exception case. However, trying to handle multiple exception cases with a single exception class makes it difficult to distinguish between them. -The exception class `Exc` provided by this library solves this problem by accepting a `Record` object that represents the reason for the exception. +The exception class `Exc` provided by this library solves this problem by accepting an object that represents the reason for the exception. +Typically, the type of this reason object is `Record`. Since a `Record` object can have any fields, it can store information about the situation at the time the exception occurred. -The type of the `Record` object can be determined and cast using a switch statement, making it easy to write handling logic for each exception case. +The type of the reason can be determined and cast using a switch statement, making it easy to write handling logic for each exception case. Optionally, when an `Exc` object is instantiated, pre-registered exception handlers can receive notifications either synchronously or asynchronously. -However, to enable this feature, you must specify the system property `-Dgithub.sttk.errs.notify=true` at program startup. +However, to enable this feature, the system property `-Dgithub.sttk.errs.notify=true` must be specified at program startup. ## Install diff --git a/src/main/java/com/github/sttk/errs/Exc.java b/src/main/java/com/github/sttk/errs/Exc.java index 4698fc6..9eb5ee9 100644 --- a/src/main/java/com/github/sttk/errs/Exc.java +++ b/src/main/java/com/github/sttk/errs/Exc.java @@ -18,8 +18,9 @@ /** * Is the exception class with a reason. *

- * This class has a record field which indicates a reason for this exception. The class name of the reason record - * represents the type of reason, and the fields of the reason record hold the situation where the exception occurred. + * This class has a field which indicates a reason for this exception. Typically the type of this field is + * {@link Record}. In this case, the class name of this record represents the reason, and the fields of the record hold + * the situation where the exception occurred. *

* Optionally, this exception class can notify its instance creation to pre-registered exception handlers. This * notification feature can be enabled by specifying the system property {@code -Dgithub.sttk.errs.notify=true} when the @@ -44,18 +45,18 @@ public final class Exc extends Exception { private static final long serialVersionUID = 260427082865587554L; /** The reason for this exception. */ - private transient Record reason; + private transient Object reason; /** The stack trace for the location of occurrence. */ private StackTraceElement trace; /** - * Is the constructor which takes a {@link Record} object indicating the reason for this exception. + * Is the constructor which takes an object indicating the reason for this exception. * * @param reason * A reason for this exception. */ - public Exc(final Record reason) { + public Exc(final Object reason) { if (reason == null) { throw new IllegalArgumentException("reason is null"); } @@ -67,8 +68,8 @@ public Exc(final Record reason) { } /** - * Is the constructor which takes a {@link Record} object indicating the reason and {@link Throwable} object - * indicating the cause for this exception. + * Is the constructor which takes an object indicating the reason and {@link Throwable} object indicating the cause + * for this exception. * * @param reason * A reason for this exception. @@ -76,7 +77,7 @@ public Exc(final Record reason) { * A cause for this exception. */ @SuppressWarnings("this-escape") - public Exc(final Record reason, final Throwable cause) { + public Exc(final Object reason, final Throwable cause) { super(cause); if (reason == null) { @@ -94,7 +95,7 @@ public Exc(final Record reason, final Throwable cause) { * * @return The reason for this exception. */ - public Record getReason() { + public Object getReason() { return this.reason; } @@ -105,13 +106,7 @@ public Record getReason() { */ @Override public String getMessage() { - var rsn = this.reason.toString(); - var rname = this.reason.getClass().getSimpleName(); - rsn = rsn.substring(rname.length() + 1, rsn.length() - 1); - - var buf = new StringBuilder(this.reason.getClass().getName()); - buf.append(" { ").append(rsn).append(" }"); - return buf.toString(); + return reason.toString(); } /** @@ -123,7 +118,7 @@ public String getMessage() { @Override public String toString() { var buf = new StringBuilder(getClass().getName()); - buf.append(" { reason = ").append(getMessage()); + buf.append(" { reason = ").append(reason.getClass().getName()).append(" ").append(reason.toString()); buf.append(", file = ").append(this.trace.getFileName()); buf.append(", line = ").append(this.trace.getLineNumber()); if (getCause() != null) { @@ -166,8 +161,8 @@ public RuntimeException toRuntimeException() { /** * Writes a serial data of this exception to a stream. *

- * Since a {@link Record} object is not necessarily serializable, this method will throw a - * {@link NotSerializableException} if the {@code reason} field does not inherit {@link Serializable}. + * Since a reason object is not necessarily serializable, this method will throw a {@link NotSerializableException} + * if the {@code reason} field does not inherit {@link Serializable}. * * @param out * An {@link ObjectOutputStream} to which data is written. @@ -185,8 +180,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { /** * Reconstitutes the {@code Exc} instance from a stream and initialize the reason and cause properties when - * deserializing. If the reason by deserialization is null or invalid, this method throws - * {@link InvalidObjectException}. + * deserializing. If the reason by deserialization is null, this method throws {@link InvalidObjectException}. * * @param in * An {@link ObjectInputStream} from which data is read. @@ -198,7 +192,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { */ private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); - this.reason = Record.class.cast(in.readObject()); + this.reason = in.readObject(); if (this.reason == null) { throw new InvalidObjectException("reason is null or invalid."); diff --git a/src/test/java/com/github/sttk/errs/ExcTest.java b/src/test/java/com/github/sttk/errs/ExcTest.java index 609bb99..6718740 100644 --- a/src/test/java/com/github/sttk/errs/ExcTest.java +++ b/src/test/java/com/github/sttk/errs/ExcTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -31,7 +32,7 @@ record SerializableReason(String name, int index, int min, int max) implements S @Nested class TestConstructor { @Test - void with_reason() { + void with_Record_reason() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); var reason = IndexOutOfRange.class.cast(exc.getReason()); assertThat(reason.name()).isEqualTo("data"); @@ -43,6 +44,28 @@ void with_reason() { // exc.printStackTrace(); } + @Test + void with_enum_reason() { + enum Reasons { + FailToDoSomething, + } + + var exc = new Exc(Reasons.FailToDoSomething); + var reason = Reasons.class.cast(exc.getReason()); + assertThat(reason.name()).isEqualTo("FailToDoSomething"); + assertThat(exc.getCause()).isNull(); + + // exc.printStackTrace(); + } + + @Test + void with_String_reason() { + var exc = new Exc("FailToDoSomething"); + var reason = String.class.cast(exc.getReason()); + assertThat(reason).isEqualTo("FailToDoSomething"); + assertThat(exc.getCause()).isNull(); + } + @Test void with_reason_but_reason_is_null() { try { @@ -104,7 +127,7 @@ void identify_reason_with_instanceOf() { } @Test - void identify_reason_with_switch_expression() { + void identify_Record_reason_with_switch_expression() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); switch (exc.getReason()) { case IndexOutOfRange reason -> { @@ -116,6 +139,24 @@ void identify_reason_with_switch_expression() { default -> fail(); } } + + @Test + void identify_Enum_reason_with_switch_expression() { + enum Reasons { + FailToDoSomething, InvalidValue, + } + + var exc = new Exc(Reasons.FailToDoSomething); + + var s = switch (exc.getReason()) { + case Reasons enm -> switch (enm) { + case FailToDoSomething -> "fail to do something"; + case InvalidValue -> "invalid value"; + }; + default -> "unknown"; + }; + assertThat(s).isEqualTo("fail to do something"); + } } @Nested @@ -151,7 +192,7 @@ void getFile() { @Test void getLine() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); - assertThat(exc.getLine()).isEqualTo(153); + assertThat(exc.getLine()).isEqualTo(194); } } @@ -160,16 +201,14 @@ class TestGetMessage { @Test void with_cause() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); - assertThat(exc.getMessage()) - .isEqualTo("com.github.sttk.errs.ExcTest$IndexOutOfRange { name=data, index=4, min=0, max=3 }"); + assertThat(exc.getMessage()).isEqualTo("IndexOutOfRange[name=data, index=4, min=0, max=3]"); } @Test void with_no_cause() { var cause = new IndexOutOfBoundsException(4); var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3), cause); - assertThat(exc.getMessage()) - .isEqualTo("com.github.sttk.errs.ExcTest$IndexOutOfRange { name=data, index=4, min=0, max=3 }"); + assertThat(exc.getMessage()).isEqualTo("IndexOutOfRange[name=data, index=4, min=0, max=3]"); } } @@ -179,7 +218,7 @@ class TestToString { void with_reason() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); assertThat(exc.toString()).isEqualTo( - "com.github.sttk.errs.Exc { reason = com.github.sttk.errs.ExcTest$IndexOutOfRange { name=data, index=4, min=0, max=3 }, file = ExcTest.java, line = 180 }"); + "com.github.sttk.errs.Exc { reason = com.github.sttk.errs.ExcTest$IndexOutOfRange IndexOutOfRange[name=data, index=4, min=0, max=3], file = ExcTest.java, line = 219 }"); } @Test @@ -187,7 +226,7 @@ void with_reason_and_cause() { var cause = new IndexOutOfBoundsException(4); var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3), cause); assertThat(exc.toString()).isEqualTo( - "com.github.sttk.errs.Exc { reason = com.github.sttk.errs.ExcTest$IndexOutOfRange { name=data, index=4, min=0, max=3 }, file = ExcTest.java, line = 188, cause = java.lang.IndexOutOfBoundsException: Index out of range: 4 }"); + "com.github.sttk.errs.Exc { reason = com.github.sttk.errs.ExcTest$IndexOutOfRange IndexOutOfRange[name=data, index=4, min=0, max=3], file = ExcTest.java, line = 227, cause = java.lang.IndexOutOfBoundsException: Index out of range: 4 }"); } } @@ -197,8 +236,7 @@ class TestToRuntimeException { void getMessage() { var exc = new Exc(new IndexOutOfRange("data", 4, 0, 3)); var rtExc = exc.toRuntimeException(); - assertThat(rtExc.getMessage()) - .isEqualTo("com.github.sttk.errs.ExcTest$IndexOutOfRange { name=data, index=4, min=0, max=3 }"); + assertThat(rtExc.getMessage()).isEqualTo("IndexOutOfRange[name=data, index=4, min=0, max=3]"); } @Test