Skip to content

Commit 2021580

Browse files
authored
new: first implementation (#2)
1 parent cf7a716 commit 2021580

File tree

8 files changed

+622
-16
lines changed

8 files changed

+622
-16
lines changed

src/main/java/Sample.java

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/*
2+
* Exc class.
3+
* Copyright (C) 2025 Takayuki Sato. All Rights Reserved.
4+
*/
5+
package com.github.sttk.errs;
6+
7+
import java.io.ObjectInputStream;
8+
import java.io.ObjectOutputStream;
9+
import java.io.Serializable;
10+
import java.io.IOException;
11+
import java.io.NotSerializableException;
12+
import java.io.InvalidObjectException;
13+
14+
/**
15+
* Is the exception class with a reason.
16+
* <p>
17+
* This class has a record field which indicates a reason for this exception. The class name of the reason record
18+
* represents the type of reason, and the fields of the reason record hold the situation where the exception occurred.
19+
* <p>
20+
* Optionally, this exception class can notify its instance creation to pre-registered exception handlers.
21+
* <p>
22+
* The example code of creating and throwing an excepton is as follows:
23+
*
24+
* <pre>{@code
25+
* public record FailToDoSomething(String name, int value) {
26+
* }
27+
*
28+
* try {
29+
* throw new Exc(new FailToDoSomething("abc", 123));
30+
* } catch (Err e) {
31+
* System.out.println(e.getMessage()); // => "FailToDoSomething { name=abc, value=123 }"
32+
* }
33+
* }</pre>
34+
*/
35+
public final class Exc extends Exception {
36+
37+
/** The serial version UID. */
38+
private static final long serialVersionUID = 260427082865587554L;
39+
40+
/** The reason for this exception. */
41+
private transient Record reason;
42+
43+
/** The stack trace for the location of occurrence. */
44+
private StackTraceElement trace;
45+
46+
/**
47+
* Is the constructor which takes a {@link Record} object indicating the reason for this exception.
48+
*
49+
* @param reason
50+
* A reason for this exception.
51+
*/
52+
public Exc(final Record reason) {
53+
if (reason == null) {
54+
throw new IllegalArgumentException("reason is null");
55+
}
56+
this.reason = reason;
57+
58+
this.trace = getStackTrace()[0];
59+
}
60+
61+
/**
62+
* Is the constructor which takes a {@link Record} object indicating the reason and {@link Throwable} object
63+
* indicating the cause for this exception.
64+
*
65+
* @param reason
66+
* A reason for this exception.
67+
* @param cause
68+
* A cause for this exception.
69+
*/
70+
@SuppressWarnings("this-escape")
71+
public Exc(final Record reason, final Throwable cause) {
72+
super(cause);
73+
74+
if (reason == null) {
75+
throw new IllegalArgumentException("reason is null");
76+
}
77+
this.reason = reason;
78+
79+
this.trace = getStackTrace()[0];
80+
}
81+
82+
/**
83+
* Gets the reason for this exception. The type of the reason.
84+
*
85+
* @return The reason for this exception.
86+
*/
87+
public Record getReason() {
88+
return this.reason;
89+
}
90+
91+
/**
92+
* Returns the message of this exception, that is the reason.
93+
*
94+
* @return The message of this exception.
95+
*/
96+
@Override
97+
public String getMessage() {
98+
var rsn = this.reason.toString();
99+
var rname = this.reason.getClass().getSimpleName();
100+
rsn = rsn.substring(rname.length() + 1, rsn.length() - 1);
101+
102+
var buf = new StringBuilder(this.reason.getClass().getName());
103+
buf.append(" { ").append(rsn).append(" }");
104+
return buf.toString();
105+
}
106+
107+
/**
108+
* Returns the detail message of this exception, that contains the reason, source file name, line number, and the
109+
* cause if provided.
110+
*
111+
* @return The message of this exception.
112+
*/
113+
@Override
114+
public String toString() {
115+
var buf = new StringBuilder(getClass().getName());
116+
buf.append(" { reason = ").append(getMessage());
117+
buf.append(", file = ").append(this.trace.getFileName());
118+
buf.append(", line = ").append(this.trace.getLineNumber());
119+
if (getCause() != null) {
120+
buf.append(", cause = ").append(getCause().toString());
121+
}
122+
return buf.append(" }").toString();
123+
}
124+
125+
/**
126+
* Returns the name of the source file of this exception occurrance.
127+
* <p>
128+
* This method can return null if this information is unavailable.
129+
*
130+
* @return The name of the source file of this error occurrence.
131+
*/
132+
public String getFile() {
133+
return this.trace.getFileName();
134+
}
135+
136+
/**
137+
* Returns the line number of this exception occurrance in the source file.
138+
* <p>
139+
* This method can return a negative number if this information is unavailable.
140+
*
141+
* @return The line number of this exception occurrance in the source file.
142+
*/
143+
public int getLine() {
144+
return this.trace.getLineNumber();
145+
}
146+
147+
/**
148+
* Creates a {@link RuntimeException} object for methods that cannot throw a {@link Exc}.
149+
*
150+
* @return A {@link RuntimeException} object.
151+
*/
152+
public RuntimeException toRuntimeException() {
153+
return new RuntimeExc(this);
154+
}
155+
156+
/**
157+
* Writes a serial data of this exception to a stream.
158+
* <p>
159+
* Since a {@link Record} object is not necessarily serializable, this method will throw a
160+
* {@link NotSerializableException} if the {@code reason} field does not inherit {@link Serializable}.
161+
*
162+
* @param out
163+
* An {@link ObjectOutputStream} to which data is written.
164+
*
165+
* @throws IOException
166+
* if an I/O error occurs.
167+
*/
168+
private void writeObject(ObjectOutputStream out) throws IOException {
169+
if (!(this.reason instanceof Serializable)) {
170+
throw new NotSerializableException(this.reason.getClass().getName());
171+
}
172+
out.defaultWriteObject();
173+
out.writeObject(this.reason);
174+
}
175+
176+
/**
177+
* Reconstitutes the {@code Exc} instance from a stream and initialize the reason and cause properties when
178+
* deserializing. If the reason by deserialization is null or invalid, this method throws
179+
* {@link InvalidObjectException}.
180+
*
181+
* @param in
182+
* An {@link ObjectInputStream} from which data is read.
183+
*
184+
* @throws IOException
185+
* if an I/O error occurs.
186+
* @throws ClassNotFoundException
187+
* if a serialized class cannot be loaded.
188+
*/
189+
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
190+
in.defaultReadObject();
191+
this.reason = Record.class.cast(in.readObject());
192+
193+
if (this.reason == null) {
194+
throw new InvalidObjectException("reason is null or invalid.");
195+
}
196+
}
197+
}
198+
199+
final class RuntimeExc extends RuntimeException {
200+
private static final long serialVersionUID = 4664405757902479929L;
201+
202+
RuntimeExc(Exc exc) {
203+
super(exc);
204+
}
205+
206+
@Override
207+
public String getMessage() {
208+
return getCause().getMessage();
209+
}
210+
211+
@Override
212+
public String toString() {
213+
return getClass().getName() + ": " + getCause().toString();
214+
}
215+
216+
@Override
217+
public Throwable fillInStackTrace() {
218+
return null;
219+
}
220+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (C) 2024 Takayuki Sato. All Rights Reserved.
3+
*
4+
* This program is free software under MIT License.
5+
* See the file LICENSE in this distribution for more details.
6+
*/
7+
8+
/**
9+
* Provides classes for handling an exception with a reason.
10+
* <p>
11+
* This package contains the {@code Exc} class which has a record field indicates the reason for the exception.
12+
*
13+
* @version 0.1
14+
*/
15+
package com.github.sttk.errs;

src/main/java/module-info.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (C) 2025 Takayuki Sato. All Rights Reserved.
3+
*
4+
* This program is free software under MIT License.
5+
* See the file LICENSE in this distribution for more details.
6+
*/
7+
8+
/**
9+
* Contains a package which provides the APIs for handling an exception with a reason.
10+
*
11+
* @version 0.1
12+
*/
13+
module com.github.sttk.errs {
14+
exports com.github.sttk.errs;
15+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"types": [
3+
{
4+
"name": "com.github.sttk.errs.Exc"
5+
},
6+
{
7+
"name": "java.io.IOException"
8+
},
9+
{
10+
"name": "java.io.NotSerializableException"
11+
},
12+
{
13+
"name": "java.io.ObjectStreamException"
14+
},
15+
{
16+
"name": "java.lang.Exception"
17+
},
18+
{
19+
"name": "java.lang.RuntimeException"
20+
},
21+
{
22+
"name": "java.lang.StackTraceElement"
23+
},
24+
{
25+
"name": "java.lang.StackTraceElement[]"
26+
},
27+
{
28+
"name": "java.lang.String"
29+
},
30+
{
31+
"name": "java.lang.Throwable"
32+
},
33+
{
34+
"name": "java.util.Collections$EmptyList"
35+
}
36+
],
37+
"lambdaCapturingTypes":[
38+
],
39+
"proxies":[
40+
]
41+
}

src/test/java/SampleTest.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)