Skip to content

Commit dbc209a

Browse files
Add convenience ctor overrides to AbstractProblem
Not sure if it will ever be useful, but there aren't many plans for releases after 1.4.0. Might as well include it right now. At first, I was afraid that having so many constructors might bloat the intended usage. However, the field order is always kept the same and due to typing differences (String, URI, Map, int), changes of accidental usage mistakes seem unlikely.
1 parent 6ea1f6e commit dbc209a

3 files changed

Lines changed: 251 additions & 26 deletions

File tree

src/main/java/io/github/problem4j/core/AbstractProblem.java

Lines changed: 226 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,228 @@ public abstract class AbstractProblem implements Problem, Serializable {
5959
private final @Nullable URI instance;
6060
private final Map<String, Object> extensions;
6161

62+
/**
63+
* Constructs a new {@link AbstractProblem} instance with the given details.
64+
*
65+
* @param status the HTTP status code applicable to this problem
66+
*/
67+
public AbstractProblem(int status) {
68+
this(
69+
Problem.BLANK_TYPE,
70+
ProblemStatus.findValue(status).map(ProblemStatus::getTitle).orElse(Problem.UNKNOWN_TITLE),
71+
status,
72+
null,
73+
null,
74+
Collections.emptyMap());
75+
}
76+
77+
/**
78+
* Constructs a new {@link AbstractProblem} instance with the given details.
79+
*
80+
* @param status the HTTP status code applicable to this problem
81+
*/
82+
public AbstractProblem(ProblemStatus status) {
83+
this(
84+
Problem.BLANK_TYPE,
85+
status.getTitle(),
86+
status.getStatus(),
87+
null,
88+
null,
89+
Collections.emptyMap());
90+
}
91+
92+
/**
93+
* Constructs a new {@link AbstractProblem} instance with the given details.
94+
*
95+
* @param title a short, human-readable summary of the problem
96+
* @param status the HTTP status code applicable to this problem
97+
*/
98+
public AbstractProblem(String title, int status) {
99+
this(Problem.BLANK_TYPE, title, status, null, null, Collections.emptyMap());
100+
}
101+
102+
/**
103+
* Constructs a new {@link AbstractProblem} instance with the given details.
104+
*
105+
* @param title a short, human-readable summary of the problem
106+
* @param status the HTTP status code applicable to this problem
107+
* @param detail a human-readable explanation specific to this occurrence of the problem
108+
*/
109+
public AbstractProblem(String title, int status, @Nullable String detail) {
110+
this(Problem.BLANK_TYPE, title, status, detail, null, Collections.emptyMap());
111+
}
112+
113+
/**
114+
* Constructs a new {@link AbstractProblem} instance with the given details.
115+
*
116+
* @param title a short, human-readable summary of the problem
117+
* @param status the HTTP status code applicable to this problem
118+
* @param instance a URI reference that identifies the specific occurrence of the problem
119+
*/
120+
public AbstractProblem(String title, int status, @Nullable URI instance) {
121+
this(Problem.BLANK_TYPE, title, status, null, instance, Collections.emptyMap());
122+
}
123+
124+
/**
125+
* Constructs a new {@link AbstractProblem} instance with the given details.
126+
*
127+
* @param title a short, human-readable summary of the problem
128+
* @param status the HTTP status code applicable to this problem
129+
* @param extensions a map of additional, application-specific properties to include in the
130+
* problem; a defensive copy is made, so changes to the original map do not affect this
131+
* instance
132+
*/
133+
public AbstractProblem(String title, int status, @Nullable Map<String, Object> extensions) {
134+
this(Problem.BLANK_TYPE, title, status, null, null, extensions);
135+
}
136+
137+
/**
138+
* Constructs a new {@link AbstractProblem} instance with the given details.
139+
*
140+
* @param title a short, human-readable summary of the problem
141+
* @param status the HTTP status code applicable to this problem
142+
* @param instance a URI reference that identifies the specific occurrence of the problem
143+
* @param extensions a map of additional, application-specific properties to include in the
144+
* problem; a defensive copy is made, so changes to the original map do not affect this
145+
* instance
146+
*/
147+
public AbstractProblem(
148+
String title, int status, @Nullable URI instance, @Nullable Map<String, Object> extensions) {
149+
this(Problem.BLANK_TYPE, title, status, null, instance, extensions);
150+
}
151+
152+
/**
153+
* Constructs a new {@link AbstractProblem} instance with the given details.
154+
*
155+
* @param title a short, human-readable summary of the problem
156+
* @param status the HTTP status code applicable to this problem
157+
* @param detail a human-readable explanation specific to this occurrence of the problem
158+
* @param instance a URI reference that identifies the specific occurrence of the problem
159+
*/
160+
public AbstractProblem(
161+
String title, int status, @Nullable String detail, @Nullable URI instance) {
162+
this(Problem.BLANK_TYPE, title, status, detail, instance, Collections.emptyMap());
163+
}
164+
165+
/**
166+
* Constructs a new {@link AbstractProblem} instance with the given details.
167+
*
168+
* @param title a short, human-readable summary of the problem
169+
* @param status the HTTP status code applicable to this problem
170+
* @param detail a human-readable explanation specific to this occurrence of the problem
171+
* @param extensions a map of additional, application-specific properties to include in the
172+
* problem; a defensive copy is made, so changes to the original map do not affect this
173+
* instance
174+
*/
175+
public AbstractProblem(
176+
String title, int status, @Nullable String detail, @Nullable Map<String, Object> extensions) {
177+
this(Problem.BLANK_TYPE, title, status, detail, null, extensions);
178+
}
179+
180+
/**
181+
* Constructs a new {@link AbstractProblem} instance with the given details.
182+
*
183+
* @param type the URI that identifies the type of the problem; must not be {@code null}
184+
* @param title a short, human-readable summary of the problem
185+
* @param status the HTTP status code applicable to this problem
186+
*/
187+
public AbstractProblem(URI type, String title, int status) {
188+
this(type, title, status, null, null, Collections.emptyMap());
189+
}
190+
191+
/**
192+
* Constructs a new {@link AbstractProblem} instance with the given details.
193+
*
194+
* @param type the URI that identifies the type of the problem; must not be {@code null}
195+
* @param title a short, human-readable summary of the problem
196+
* @param status the HTTP status code applicable to this problem
197+
* @param detail a human-readable explanation specific to this occurrence of the problem
198+
*/
199+
public AbstractProblem(URI type, String title, int status, @Nullable String detail) {
200+
this(type, title, status, detail, null, Collections.emptyMap());
201+
}
202+
203+
/**
204+
* Constructs a new {@link AbstractProblem} instance with the given details.
205+
*
206+
* @param type the URI that identifies the type of the problem; must not be {@code null}
207+
* @param title a short, human-readable summary of the problem
208+
* @param status the HTTP status code applicable to this problem
209+
* @param instance a URI reference that identifies the specific occurrence of the problem
210+
*/
211+
public AbstractProblem(URI type, String title, int status, @Nullable URI instance) {
212+
this(type, title, status, null, instance, Collections.emptyMap());
213+
}
214+
215+
/**
216+
* Constructs a new {@link AbstractProblem} instance with the given details.
217+
*
218+
* @param type the URI that identifies the type of the problem; must not be {@code null}
219+
* @param title a short, human-readable summary of the problem
220+
* @param status the HTTP status code applicable to this problem
221+
* @param extensions a map of additional, application-specific properties to include in the
222+
* problem; a defensive copy is made, so changes to the original map do not affect this
223+
* instance
224+
*/
225+
public AbstractProblem(
226+
URI type, String title, int status, @Nullable Map<String, Object> extensions) {
227+
this(type, title, status, null, null, extensions);
228+
}
229+
230+
/**
231+
* Constructs a new {@link AbstractProblem} instance with the given details.
232+
*
233+
* @param type the URI that identifies the type of the problem; must not be {@code null}
234+
* @param title a short, human-readable summary of the problem
235+
* @param status the HTTP status code applicable to this problem
236+
* @param instance a URI reference that identifies the specific occurrence of the problem
237+
* @param extensions a map of additional, application-specific properties to include in the
238+
* problem; a defensive copy is made, so changes to the original map do not affect this
239+
* instance
240+
*/
241+
public AbstractProblem(
242+
URI type,
243+
String title,
244+
int status,
245+
@Nullable URI instance,
246+
@Nullable Map<String, Object> extensions) {
247+
this(type, title, status, null, instance, extensions);
248+
}
249+
250+
/**
251+
* Constructs a new {@link AbstractProblem} instance with the given details.
252+
*
253+
* @param type the URI that identifies the type of the problem; must not be {@code null}
254+
* @param title a short, human-readable summary of the problem
255+
* @param status the HTTP status code applicable to this problem
256+
* @param detail a human-readable explanation specific to this occurrence of the problem
257+
* @param instance a URI reference that identifies the specific occurrence of the problem
258+
*/
259+
public AbstractProblem(
260+
URI type, String title, int status, @Nullable String detail, @Nullable URI instance) {
261+
this(type, title, status, detail, instance, Collections.emptyMap());
262+
}
263+
264+
/**
265+
* Constructs a new {@link AbstractProblem} instance with the given details.
266+
*
267+
* @param type the URI that identifies the type of the problem; must not be {@code null}
268+
* @param title a short, human-readable summary of the problem
269+
* @param status the HTTP status code applicable to this problem
270+
* @param detail a human-readable explanation specific to this occurrence of the problem
271+
* @param extensions a map of additional, application-specific properties to include in the
272+
* problem; a defensive copy is made, so changes to the original map do not affect this
273+
* instance
274+
*/
275+
public AbstractProblem(
276+
URI type,
277+
String title,
278+
int status,
279+
@Nullable String detail,
280+
@Nullable Map<String, Object> extensions) {
281+
this(type, title, status, detail, null, extensions);
282+
}
283+
62284
/**
63285
* Constructs a new {@link AbstractProblem} instance with the given details.
64286
*
@@ -72,18 +294,18 @@ public abstract class AbstractProblem implements Problem, Serializable {
72294
* instance
73295
*/
74296
public AbstractProblem(
75-
@Nullable URI type,
297+
URI type,
76298
String title,
77299
int status,
78300
@Nullable String detail,
79301
@Nullable URI instance,
80-
Map<String, Object> extensions) {
81-
this.type = type != null ? type : Problem.BLANK_TYPE;
302+
@Nullable Map<String, Object> extensions) {
303+
this.type = type;
82304
this.title = title;
83305
this.status = status;
84306
this.detail = detail;
85307
this.instance = instance;
86-
this.extensions = new HashMap<>(extensions);
308+
this.extensions = extensions != null ? new HashMap<>(extensions) : new HashMap<>();
87309
}
88310

89311
/**

src/main/java/io/github/problem4j/core/ProblemImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,17 @@ final class ProblemImpl extends AbstractProblem {
2828

2929
private static final long serialVersionUID = 1L;
3030

31+
public ProblemImpl(String title, int status, @Nullable Map<String, Object> extensions) {
32+
super(title, status, extensions);
33+
}
34+
35+
public ProblemImpl(
36+
String title, int status, @Nullable String detail, @Nullable Map<String, Object> extensions) {
37+
super(title, status, detail, extensions);
38+
}
39+
3140
ProblemImpl(
32-
@Nullable URI type,
41+
URI type,
3342
String title,
3443
int status,
3544
@Nullable String detail,

src/test/java/io/github/problem4j/core/ProblemImplTest.java

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void givenAllFieldsPopulated_whenToString_thenContainsAllFields() {
6767
@Test
6868
void givenNullExtensionsAndNullableFields_whenToString_thenOmitsNulls() {
6969
Map<String, Object> extensions = new HashMap<>();
70-
Problem problem = new ProblemImpl(null, ProblemStatus.OK_TITLE, 200, null, null, extensions);
70+
Problem problem = new ProblemImpl(ProblemStatus.OK_TITLE, 200, extensions);
7171

7272
String result = problem.toString();
7373

@@ -80,8 +80,7 @@ void givenOnlyNumberExtensions_whenToString_thenContainsNumbers() {
8080
extensions.put("ext1", 123);
8181
extensions.put("ext2", 456.78);
8282

83-
Problem problem =
84-
new ProblemImpl(null, ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, null, null, extensions);
83+
Problem problem = new ProblemImpl(ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, extensions);
8584

8685
String result = problem.toString();
8786

@@ -95,8 +94,7 @@ void givenOnlyBooleanExtensions_whenToString_thenContainsBooleans() {
9594
extensions.put("flag1", true);
9695
extensions.put("flag2", false);
9796

98-
Problem problem =
99-
new ProblemImpl(null, ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, null, null, extensions);
97+
Problem problem = new ProblemImpl(ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, extensions);
10098

10199
String result = problem.toString();
102100

@@ -109,8 +107,7 @@ void givenNonPrimitiveExtension_whenToString_thenUsesItsToString() {
109107
Map<String, Object> extensions = new HashMap<>();
110108
extensions.put("obj", new DummyObject("biz\tbar"));
111109

112-
Problem problem =
113-
new ProblemImpl(null, ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, null, null, extensions);
110+
Problem problem = new ProblemImpl(ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, extensions);
114111

115112
String result = problem.toString();
116113

@@ -122,8 +119,7 @@ void givenStringWithSpecialCharacters_whenToString_thenProperlyHandlesIt() {
122119
Map<String, Object> extensions = new HashMap<>();
123120
extensions.put("ext", "a\"b\\c\nd");
124121

125-
Problem problem =
126-
new ProblemImpl(null, ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, null, null, extensions);
122+
Problem problem = new ProblemImpl(ProblemStatus.INTERNAL_SERVER_ERROR_TITLE, 0, extensions);
127123

128124
String result = problem.toString();
129125

@@ -132,8 +128,8 @@ void givenStringWithSpecialCharacters_whenToString_thenProperlyHandlesIt() {
132128

133129
@Test
134130
void givenTwoEqualProblems_shouldBeEqual() {
135-
Problem problem1 = new ProblemImpl(null, "title", 404, "detail", null, mapOf("key", "value"));
136-
Problem problem2 = new ProblemImpl(null, "title", 404, "detail", null, mapOf("key", "value"));
131+
Problem problem1 = new ProblemImpl("title", 404, "detail", mapOf("key", "value"));
132+
Problem problem2 = new ProblemImpl("title", 404, "detail", mapOf("key", "value"));
137133

138134
assertThat(problem1).isEqualTo(problem2);
139135
}
@@ -143,43 +139,41 @@ void givenSameProblems_shouldBeEqual() {
143139
Object problem;
144140
Object other;
145141

146-
problem = other = new ProblemImpl(null, "title", 404, "detail", null, mapOf("key", "value"));
142+
problem = other = new ProblemImpl( "title", 404, "detail", mapOf("key", "value"));
147143

148144
assertThat(problem).isEqualTo(other);
149145
}
150146

151147
@Test
152148
void givenTwoDifferentProblems_shouldNotBeEqual() {
153-
Problem problem1 =
154-
new ProblemImpl(null, "title1", 404, "detail1", null, mapOf("key", "value1"));
155-
Problem problem2 =
156-
new ProblemImpl(null, "title2", 500, "detail2", null, mapOf("key", "value2"));
149+
Problem problem1 = new ProblemImpl("title1", 404, "detail1", mapOf("key", "value1"));
150+
Problem problem2 = new ProblemImpl("title2", 500, "detail2", mapOf("key", "value2"));
157151

158152
assertThat(problem1).isNotEqualTo(problem2);
159153
}
160154

161155
@Test
162156
void givenProblemAndDifferentObject_shouldNotBeEqual() {
163-
Object problem = new ProblemImpl(null, "title1", 404, "detail1", null, mapOf("key", "value"));
157+
Object problem = new ProblemImpl( "title1", 404, "detail1", mapOf("key", "value"));
164158
Object differentObject = "always wanted to be a problem";
165159

166160
assertThat(problem).isNotEqualTo(differentObject);
167161
}
168162

169163
@Test
170164
void givenTwoEqualProblems_shouldHaveSameHashCode() {
171-
Problem problem1 = new ProblemImpl(null, "title", 404, "detail", null, mapOf("key", "value"));
172-
Problem problem2 = new ProblemImpl(null, "title", 404, "detail", null, mapOf("key", "value"));
165+
Problem problem1 = new ProblemImpl( "title", 404, "detail", mapOf("key", "value"));
166+
Problem problem2 = new ProblemImpl( "title", 404, "detail", mapOf("key", "value"));
173167

174168
assertThat(problem1.hashCode()).isEqualTo(problem2.hashCode());
175169
}
176170

177171
@Test
178172
void givenTwoDifferentProblems_shouldHaveDifferentHashCodes() {
179173
Problem problem1 =
180-
new ProblemImpl(null, "title1", 404, "detail1", null, mapOf("key", "value1"));
174+
new ProblemImpl( "title1", 404, "detail1", mapOf("key", "value1"));
181175
Problem problem2 =
182-
new ProblemImpl(null, "title2", 500, "detail2", null, mapOf("key", "value2"));
176+
new ProblemImpl( "title2", 500, "detail2", mapOf("key", "value2"));
183177

184178
assertThat(problem1.hashCode()).isNotEqualTo(problem2.hashCode());
185179
}

0 commit comments

Comments
 (0)