Skip to content

Commit f731fbc

Browse files
authored
#27: Improved quoting and cleaned up handling of placeholders and parameters (#38)
* #27: Cleaned up code and documentation. Added more tests.
1 parent 8b2a039 commit f731fbc

18 files changed

+732
-218
lines changed

.gitattributes

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
pk_generated_parent.pom linguist-generated=true
1+
pk_generated_parent.pom linguist-generated=true
2+
versionsMavenPluginRules.xml linguist-generated=true
3+
dependencies.md linguist-generated=true
4+
doc/changes/changelog.md linguist-generated=true

README.md

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,37 +44,58 @@ Result:
4444

4545
The optional third parameter for `parameter(placeholder, value, description)` is used by the [error-code-crawler-maven-plugin](https://github.com/exasol/error-code-crawler-maven-plugin) to generate a parameter description.
4646

47-
The builder automatically quotes parameters of the following types:
48-
49-
* `String`
50-
* `Character`
51-
* `Path`
52-
* `File`
53-
* `URI`
54-
* `URL`
55-
56-
If you don't want that, use the `uq` switch in the correspondent placeholder. Switches are separated with a pipe symbol "|" from the parameter name.
47+
From version `0.3.0` on you can achieve the same result by specifying the parameter values directly in the `message()` method. This is a convenience variant that is a little more compact, but lacks the chance to describe the parameter.
5748

5849
```java
5950
ExaError.messageBuilder("E-TEST-2")
60-
.message("Unknown input: {{input|uq}}.")
61-
.parameter("input", "unknown", "The illegal user input.").toString();
51+
.message("Message with {{first-parameter}} and {{second-parameter}}.", "first value", "second value").toString();
6252
```
6353

6454
Result:
6555

66-
E-TEST-2: Unknown input: unknown.
56+
E-TEST-2: Message with 'q-value' and uq-value.
57+
58+
#### Automatic Quoting
59+
60+
When replacing placeholders in messages, `ExaError` quotes the values according to your choices. If you don't specify a quoting option in a placeholder, you get auto-quoting. In this mode values are quoted depending on their type.
61+
62+
| Type | Quoted with | Example | Since version |
63+
|----------------------|-----------------|----------------------------|--------------:|
64+
| `String` | single quotes | `'Hello world!'` | |
65+
| `Character` / `char` | single quotes | `'A'` | |
66+
| `Path` | single quotes | `'/etc/cron.d'` | 1.0.0 |
67+
| `File` | single quotes | `'~/.bashrc'` | 1.0.0 |
68+
| `URI` | single quotes | `'URN:ISBN:0-330-28700-1'` | 1.0.0 |
69+
| `URL` | single quotes | `'https://example.org'` | 1.0.0 |
70+
| null values | pointy brackets | `<null>` | |
71+
| everything else | not quoted | `42`, `3.1415`, `true` | |
72+
73+
#### Manual Quoting
6774

68-
From version `0.3.0` on you can achieve the same result by specifying the parameter values directly in the `message()` method. This is a convenience variant that is a little bit more compact, but lacks the chance to describe the parameter.
75+
If you need a different quoting style, you can add switches to the placeholder definition:
76+
77+
`u`
78+
: unquoted
79+
80+
`q`
81+
: forced single quotes
82+
83+
`d`
84+
: forced double quotes
85+
86+
If multiple conflicting switches are given, the one with the highest precedence (see list above) is taken.
87+
88+
Switches are separated with a pipe symbol `|` from the parameter name.
6989

7090
```java
7191
ExaError.messageBuilder("E-TEST-2")
72-
.message("Message with {{quotedParameter}} and {{unquotedParameter|uq}}.", "q-value", "uq-value").toString();
92+
.message("Unknown input: {{input|u}}.")
93+
.parameter("input", "unknown", "The illegal user input.").toString();
7394
```
7495

7596
Result:
7697

77-
E-TEST-2: Message with 'q-value' and uq-value.
98+
E-TEST-2: Unknown input: unknown.
7899

79100
### Mitigations
80101

doc/changes/changes_1.0.0.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
1-
# error-reporting-java 1.0.0, released 2022-08-30
1+
# error-reporting-java 1.0.0, released 2022-09-02
22

33
Code name: Quoting enhancements
44

55
In this release we added a guideline for deleting error codes and migrated the project to Project Keeper 2.
66

77
When you use Java types `Path`, `File`, `URL` or `URI` as parameter in a message, it now automatically gets quoted. Note that this can break existing unit tests in you code or client code that parses error messages.
88

9+
We also now support quoting with double quotes.
10+
911
Note that we removed the deprecated `unquotedParameter` methods.
1012

11-
For the reviewers: open TODOs for this release: #28, #27, #19
13+
Quoting is now exclusively controlled by the following single-character switches:
14+
15+
`u`
16+
: unquoted
17+
18+
`q`
19+
: forced single quotes
20+
21+
`d`
22+
: forced double quotes
23+
24+
none
25+
: automatic quoting depending on the type
26+
27+
If multiple conflicting switches are given, the one with the highest precedence (see list above) is taken.
28+
That means the previous `uq` switch still works because the `q` is ignored in this case.
29+
30+
Quoting now supports all collections, not only lists.
1231

1332
## Documentation
1433

src/main/java/com/exasol/errorreporting/ErrorMessageBuilder.java

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ public class ErrorMessageBuilder {
99
private final String errorCode;
1010
private final StringBuilder messageBuilder = new StringBuilder();
1111
private final List<String> mitigations = new ArrayList<>();
12-
private final Map<String, Object> parameterMapping = new HashMap<>();
13-
private final Map<String, Object> explicitlyUnquotedParameterMapping = new HashMap<>();
12+
private final ParameterDefinitionList parameterDefinitions = new ParameterDefinitionList();
1413

1514
/**
1615
* Create a new instance of {@link ErrorMessageBuilder}.
@@ -22,47 +21,17 @@ public class ErrorMessageBuilder {
2221
}
2322

2423
/**
25-
* Format a given message pattern with placeholders, filling them with the arguments passed in the specified form.
24+
* Define an error message.
2625
* <p>
27-
* Placeholders are defined in the message pattern by using double curly brackets {@code {{}}}. By default,
28-
* arguments are formatted with simple quotes unless specified other wise with the 'unquoted' format, defined by
29-
* {@code {{|uq}}}.
26+
* Error messages can optionally contain placeholders that can be replaced by arguments.
3027
* </p>
31-
* <p>
32-
* You should always define names in the placeholders. This name will be shown in case no argument is missing, by
33-
* {@code {{argumentName}}} or {@code {{argumentName|uq}}}.
34-
* </p>
35-
* <p>
36-
* Below you can find examples on how to use it.
37-
* </p>
38-
* <p>
39-
* Example for quoted arguments:
40-
* </p>
41-
* <p>
42-
* {@code ErrorMessageBuilder("ERROR_CODE").message("Message with {{namedQuotedArgument}}, {{}} and
43-
* {{missingQuotedArgument}}, "named", "unnamed")}
44-
* </p>
45-
* <p>
46-
* returns "ERROR_CODE: Message with 'named', 'unnamed' and UNKNOWN PLACEHOLDER('anotherQuotedArgument')".
47-
* </p>
48-
* <p>
49-
* Example for unquoted arguments:
50-
* </p>
51-
* <p>
52-
* {@code ErrorMessageBuilder("ERROR_CODE").message("Message with {{namedUnquotedArgument|uq}}, {{|uq}} and
53-
* {{missingUnquotedArgument|uq}}, "named", "unnamed")}
54-
* </p>
55-
* <p>
56-
* returns "ERROR_CODE: Message with named, unnamed and UNKNOWN PLACEHOLDER('anotherQuotedArgument')".
57-
* </p>
58-
*
5928
* @param message message that may contain placeholders
6029
* @param arguments arguments to fill the placeholders
6130
* @return self for fluent programming
6231
*/
6332
public ErrorMessageBuilder message(final String message, final Object... arguments) {
64-
this.messageBuilder.append(message);
65-
this.addParameters(message, arguments);
33+
messageBuilder.append(message);
34+
addParameters(message, arguments);
6635
return this;
6736
}
6837

@@ -84,12 +53,12 @@ private Object[] getPatternArguments(final Object[] arguments) {
8453
* You can use the parameter in message and mitigation using {@code {{parameter}}}.
8554
* </p>
8655
*
87-
* @param placeholder placeholder without parentheses
88-
* @param value value to insert
56+
* @param name parameter name
57+
* @param value value to insert
8958
* @return self for fluent programming
9059
*/
91-
public ErrorMessageBuilder parameter(final String placeholder, final Object value) {
92-
this.parameterMapping.put(placeholder, value);
60+
public ErrorMessageBuilder parameter(final String name, final Object value) {
61+
parameterDefinitions.add(ParameterDefinition.builder(name).value(value).build());
9362
return this;
9463
}
9564

@@ -98,14 +67,19 @@ public ErrorMessageBuilder parameter(final String placeholder, final Object valu
9867
* <p>
9968
* You can use the parameter in message and mitigation using {@code {{parameter}}}.
10069
* </p>
70+
* <p>
71+
* Note that the last parameter exists only as a means to add a description in the error catalog. It is not used
72+
* when displaying error messages to the application users.
73+
* </p>
10174
*
10275
* @param placeholder placeholder without parentheses
103-
* @param value value to insert
104-
* @param description description for the error catalog
76+
* @param value value to insert
77+
* @param ignoredDescription description for the error catalog
10578
* @return self for fluent programming
10679
*/
107-
public ErrorMessageBuilder parameter(final String placeholder, final Object value, final String description) {
108-
return this.parameter(placeholder, value);
80+
public ErrorMessageBuilder parameter(final String placeholder, final Object value,
81+
final String ignoredDescription) {
82+
return parameter(placeholder, value);
10983
}
11084

11185
/**
@@ -119,8 +93,8 @@ public ErrorMessageBuilder parameter(final String placeholder, final Object valu
11993
* @return self for fluent programming
12094
*/
12195
public ErrorMessageBuilder mitigation(final String mitigation, final Object... arguments) {
122-
this.mitigations.add(mitigation);
123-
this.addParameters(mitigation, arguments);
96+
mitigations.add(mitigation);
97+
addParameters(mitigation, arguments);
12498
return this;
12599
}
126100

@@ -161,7 +135,6 @@ public String toString() {
161135
}
162136

163137
private String replacePlaceholders(final String subject) {
164-
return PlaceholdersFiller.fillPlaceholders(subject, this.parameterMapping,
165-
this.explicitlyUnquotedParameterMapping);
138+
return PlaceholdersFiller.fillPlaceholders(subject, this.parameterDefinitions);
166139
}
167140
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package com.exasol.errorreporting;
2+
3+
/**
4+
* The class models an error code parameter.
5+
*/
6+
public class ParameterDefinition {
7+
/** Replacement Parameter in case a parameter is missing its name */
8+
public static final ParameterDefinition UNDEFINED_PARAMETER = ParameterDefinition.builder("undefined parameter")
9+
.build();
10+
private final String name;
11+
private final Object value;
12+
private final String description;
13+
14+
private ParameterDefinition(final Builder builder) {
15+
this.name = builder.name;
16+
this.value = builder.value;
17+
this.description = builder.description;
18+
}
19+
20+
/**
21+
* Create a builder for an error code {@link ParameterDefinition}.
22+
*
23+
* @param name name of the parameter
24+
* @return parameter builder
25+
*/
26+
public static ParameterDefinition.Builder builder(final String name) {
27+
return new Builder(name);
28+
}
29+
30+
/**
31+
* Get the parameter name.
32+
*
33+
* @return name of the parameter
34+
*/
35+
public String getName() {
36+
return this.name;
37+
}
38+
39+
/**
40+
* Get the parameter value.
41+
*
42+
* @return value assigned to the parameter
43+
*/
44+
public Object getValue() {
45+
return this.value;
46+
}
47+
48+
/**
49+
* Get the parameter description.
50+
*
51+
* @return description of the parameter
52+
*/
53+
public String getDescription() {
54+
return this.description;
55+
}
56+
57+
/**
58+
* Check if the parameter has a name.
59+
*
60+
* @return {@code true} if the name is set.
61+
*/
62+
public boolean hasName() {
63+
return this.name != null;
64+
}
65+
66+
/**
67+
* Check if the value is set.
68+
*
69+
* @return {@code true} if the value is set.
70+
*/
71+
public boolean hasValue() {
72+
return this.value != null;
73+
}
74+
75+
/**
76+
* Check if the description is set.
77+
*
78+
* @return {@code true} if the description is set
79+
*/
80+
public boolean hasDescription() {
81+
return this.description != null;
82+
}
83+
84+
@Override
85+
public String toString() {
86+
if(hasValue()) {
87+
return this.value.toString();
88+
}
89+
else {
90+
return "<null>";
91+
}
92+
}
93+
94+
/**
95+
* Builder for an error code {@link ParameterDefinition}.
96+
*/
97+
public static class Builder {
98+
private final String name;
99+
private Object value;
100+
private String description;
101+
102+
103+
private Builder(final String name) {
104+
this.name = name;
105+
}
106+
107+
/**
108+
* Set the parameter value.
109+
*
110+
* @param value value of the parameter
111+
* @return self for fluent programming
112+
*/
113+
public Builder value(final Object value) {
114+
this.value = value;
115+
return this;
116+
}
117+
118+
/**
119+
* Set the parameter description.
120+
*
121+
* @param description description of the parameter
122+
* @return self for fluent programming
123+
*/
124+
public Builder description(final String description) {
125+
this.description = description;
126+
return this;
127+
}
128+
129+
/**
130+
* Build the {@link ParameterDefinition}.
131+
*
132+
* @return new {@link ParameterDefinition}
133+
*/
134+
public ParameterDefinition build() {
135+
return new ParameterDefinition(this);
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)