Skip to content

Commit fc49cad

Browse files
committed
Make LOWERCASE the default normalization
1 parent 2b94fef commit fc49cad

File tree

6 files changed

+86
-68
lines changed

6 files changed

+86
-68
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
Use `NormalizationOptionsBuilder#stripQuotes()` instead.
99

1010

11+
- Removed `Email#normalized(boolean)` method which allowed for a normalized email with stripped quotes.
12+
Use `myEmail.normalized(NormalizationOptions.builder().stripQuotes().build())` instead.
13+
14+
1115
- `FailureReason` was switched from an enum to a class in order to support custom failure reasons, so you cannot
1216
use it in a `switch` statement.
1317

src/main/java/com/sanctionco/jmail/Email.java

+5-38
Original file line numberDiff line numberDiff line change
@@ -255,36 +255,6 @@ public String normalized() {
255255
return normalized(NormalizationOptions.builder().build());
256256
}
257257

258-
/**
259-
* <p>Return a "normalized" version of this email address. The actual result of normalization
260-
* depends on the configured normalization options (see {@link NormalizationOptions}),
261-
* but in general this method returns a version of the email address that is the same as
262-
* the original email address, except that all comments and optional parts
263-
* (identifiers, source routing) are removed. For example, the address
264-
* {@code "test@(comment)example.com"} will return {@code "[email protected]"}.</p>
265-
*
266-
* <p>This method uses the default set of {@link NormalizationOptions}. This default set
267-
* of options can be adjusted using system properties. See {@link NormalizationOptions}
268-
* for more details on which properties to set to adjust the defaults.</p>
269-
*
270-
* <p>Alternatively, one can use the {@link Email#normalized(NormalizationOptions)} method
271-
* and pass in a custom set of options to adjust the behavior.</p>
272-
*
273-
* @param stripQuotes set to true if you want to remove all unnecessary quotes within the
274-
* local-part of the email address
275-
* @return the normalized version of this email address
276-
*/
277-
@Deprecated
278-
public String normalized(boolean stripQuotes) {
279-
NormalizationOptionsBuilder optionsBuilder = NormalizationOptions.builder();
280-
281-
if (stripQuotes) {
282-
optionsBuilder.stripQuotes();
283-
}
284-
285-
return normalized(optionsBuilder.build());
286-
}
287-
288258
/**
289259
* <p>Return a "normalized" version of this email address. The actual result of normalization
290260
* depends on the configured normalization options, but in general this method returns
@@ -311,18 +281,15 @@ public String normalized(NormalizationOptions options) {
311281
}
312282
}
313283

284+
CaseOption caseOption = options.getCaseOption();
285+
314286
localPart = options.shouldRemoveDots()
315-
? localPart.replace(".", "")
316-
: localPart;
287+
? caseOption.adjustLocalPart(localPart.replace(".", ""))
288+
: caseOption.adjustLocalPart(localPart);
317289

318290
String domain = isIpAddress
319291
? "[" + this.domainWithoutComments + "]"
320-
: this.domainWithoutComments;
321-
322-
// Adjust casing
323-
CaseOption caseOption = options.getCaseOption();
324-
localPart = caseOption.adjustLocalPart(localPart);
325-
domain = caseOption.adjustDomain(domain);
292+
: caseOption.adjustDomain(this.domainWithoutComments);
326293

327294
if (options.shouldPerformUnicodeNormalization()) {
328295
localPart = Normalizer.normalize(localPart, options.getUnicodeNormalizationForm());

src/main/java/com/sanctionco/jmail/normalization/NormalizationOptionsBuilder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* {@link com.sanctionco.jmail.Email#normalized(NormalizationOptions)} to adjust its behavior.
99
*/
1010
public class NormalizationOptionsBuilder {
11-
CaseOption caseOption = CaseOption.NO_CHANGE;
11+
CaseOption caseOption = CaseOption.LOWERCASE;
1212
boolean stripQuotes = false;
1313
boolean removeDots = false;
1414
boolean removeSubAddress = false;
@@ -35,7 +35,7 @@ public NormalizationOptionsBuilder stripQuotes() {
3535
* <p>Adjust the case of the email address. See {@link CaseOption} to see all available casing
3636
* adjustments you can choose from.</p>
3737
*
38-
* <p>The default normalization behavior does <strong>not</strong> adjust the case.</p>
38+
* <p>The default normalization behavior uses {@link CaseOption#LOWERCASE}.</p>
3939
*
4040
* @param caseOption the {@link CaseOption} that should be used when adjusting the case
4141
* @return this

src/test/java/com/sanctionco/jmail/EmailTest.java

+66-27
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import static org.junit.jupiter.api.Assumptions.assumeTrue;
2020

2121
class EmailTest {
22+
private static final NormalizationOptions MINIMAL_NORMALIZATION = NormalizationOptions
23+
.builder()
24+
.adjustCase(CaseOption.NO_CHANGE)
25+
.build();
2226

2327
@Test
2428
void ensureEqualsContract() {
@@ -59,68 +63,73 @@ void ensureNormalizedIsCorrectForLongComment() {
5963
void ensureNormalizedStripsQuotes(String address, String expected) {
6064
assertThat(Email.of(address))
6165
.isPresent().get()
62-
.returns(expected, email -> email.normalized(
63-
NormalizationOptions.builder().stripQuotes().build()));
66+
.returns(expected, email -> email.normalized(NormalizationOptions.builder()
67+
.adjustCase(CaseOption.NO_CHANGE)
68+
.stripQuotes()
69+
.build()));
6470

6571
// Check that nothing happens when stripQuotes is false
6672
assertThat(Email.of(address))
6773
.isPresent().get()
68-
.returns(address, email -> email.normalized(
69-
NormalizationOptions.builder().build()));
74+
.returns(address, email -> email.normalized(MINIMAL_NORMALIZATION));
7075
}
7176

7277
@ParameterizedTest(name = "{0}")
7378
@MethodSource("provideValidForLowerCase")
7479
void ensureNormalizedConvertsToLowerCase(String address, String expected) {
80+
// Lowercase conversion is default
7581
assertThat(Email.of(address))
7682
.isPresent().get()
7783
.returns(expected, email -> email.normalized(
78-
NormalizationOptions.builder().adjustCase(CaseOption.LOWERCASE).build()));
84+
NormalizationOptions.builder().build()));
7985

8086
// Check that nothing happens when adjustCase is NO_CHANGE
8187
assertThat(Email.of(address))
8288
.isPresent().get()
83-
.returns(address, email -> email.normalized(
84-
NormalizationOptions.builder().build()));
89+
.returns(address, email -> email.normalized(MINIMAL_NORMALIZATION));
8590
}
8691

8792
@ParameterizedTest(name = "{0}")
8893
@MethodSource("provideValidForDots")
8994
void ensureNormalizedRemovesDots(String address, String expected) {
9095
assertThat(Email.of(address))
9196
.isPresent().get()
92-
.returns(expected, email -> email.normalized(
93-
NormalizationOptions.builder().removeDots().build()));
97+
.returns(expected, email -> email.normalized(NormalizationOptions.builder()
98+
.adjustCase(CaseOption.NO_CHANGE)
99+
.removeDots()
100+
.build()));
94101

95102
// Check that nothing happens when removeDots is false
96103
assertThat(Email.of(address))
97104
.isPresent().get()
98-
.returns(address, email -> email.normalized(
99-
NormalizationOptions.builder().build()));
105+
.returns(address, email -> email.normalized(MINIMAL_NORMALIZATION));
100106
}
101107

102108
@ParameterizedTest(name = "{0}")
103109
@MethodSource("provideValidForSubAddress")
104110
void ensureNormalizedRemovesSubAddresses(String address, String expected) {
105111
assertThat(Email.of(address))
106112
.isPresent().get()
107-
.returns(expected, email -> email.normalized(
108-
NormalizationOptions.builder().removeSubAddress().build()));
113+
.returns(expected, email -> email.normalized(NormalizationOptions.builder()
114+
.adjustCase(CaseOption.NO_CHANGE)
115+
.removeSubAddress()
116+
.build()));
109117

110118
// Check that nothing happens when removeSubAddress is false
111119
assertThat(Email.of(address))
112120
.isPresent().get()
113-
.returns(address, email -> email.normalized(
114-
NormalizationOptions.builder().build()));
121+
.returns(address, email -> email.normalized(MINIMAL_NORMALIZATION));
115122
}
116123

117124
@ParameterizedTest(name = "{0}")
118125
@MethodSource("provideValidForSubAddressSeparator")
119126
void ensureNormalizedRemovesSubAddressesWithCustomSeparator(String address, String expected) {
120127
assertThat(Email.of(address))
121128
.isPresent().get()
122-
.returns(expected, email -> email.normalized(
123-
NormalizationOptions.builder().removeSubAddress("%%").build()));
129+
.returns(expected, email -> email.normalized(NormalizationOptions.builder()
130+
.adjustCase(CaseOption.NO_CHANGE)
131+
.removeSubAddress("%%")
132+
.build()));
124133
}
125134

126135
@Test
@@ -129,8 +138,11 @@ void ensureNormalizedPerformsUnicodeNormalization() {
129138

130139
assertThat(Email.of(address))
131140
.isPresent().get()
132-
.returns("Äffintest1⁄[email protected]", email -> email.normalized(
133-
NormalizationOptions.builder().performUnicodeNormalization().build()));
141+
.returns("Äffintest1⁄[email protected]", email -> email.normalized(NormalizationOptions
142+
.builder()
143+
.adjustCase(CaseOption.NO_CHANGE)
144+
.performUnicodeNormalization()
145+
.build()));
134146
}
135147

136148
@Test
@@ -139,10 +151,11 @@ void ensureNormalizedPerformsUnicodeNormalizationWithCustomForm() {
139151

140152
assertThat(Email.of(address))
141153
.isPresent().get()
142-
.returns("Äffintest½@gmail.com", email -> email.normalized(
143-
NormalizationOptions.builder()
144-
.performUnicodeNormalization(Normalizer.Form.NFC)
145-
.build()));
154+
.returns("Äffintest½@gmail.com", email -> email.normalized(NormalizationOptions
155+
.builder()
156+
.adjustCase(CaseOption.NO_CHANGE)
157+
.performUnicodeNormalization(Normalizer.Form.NFC)
158+
.build()));
146159
}
147160

148161
@ParameterizedTest(name = "{0}")
@@ -161,8 +174,32 @@ void ensureNormalizedStripsQuotesForAllValidAddresses(String address) {
161174

162175
assertThat(Email.of(quoted))
163176
.isPresent().get()
164-
.returns(validated.normalized(), email -> email.normalized(
165-
NormalizationOptions.builder().stripQuotes().build()));
177+
.returns(
178+
validated.normalized(MINIMAL_NORMALIZATION),
179+
email -> email.normalized(NormalizationOptions.builder()
180+
.adjustCase(CaseOption.NO_CHANGE)
181+
.stripQuotes()
182+
.build()));
183+
}
184+
185+
@ParameterizedTest(name = "{0}")
186+
@ValueSource(strings = {
187+
"aaa@[123.123.123.123]", "first.last@[IPv6:a1::11.22.33.44]",
188+
"FIRST.LAST@[IPv6:a1::b2:11.22.33.44]", "aAa@[123.123.123.123]",
189+
"hElLo23@[1.2.3.4]"
190+
})
191+
void ensureNormalizedDoesNotAdjustCaseOfIPDomains(String address) {
192+
Email validated = Email.of(address).get();
193+
194+
// The test only works for IP domains
195+
assumeTrue(validated.isIpAddress());
196+
197+
String normalized = validated.normalized();
198+
199+
assertThat(normalized.substring(normalized.indexOf('@') + 1))
200+
.isEqualTo("[" + validated.domain() + "]");
201+
assertThat(normalized.substring(0, normalized.indexOf('@')))
202+
.isEqualTo(validated.localPart().toLowerCase());
166203
}
167204

168205
@ParameterizedTest(name = "{0}")
@@ -173,8 +210,10 @@ void ensureNormalizedStripsQuotesForAllValidAddresses(String address) {
173210
void ensureNormalizedDoesNotStripQuotesIfInvalid(String address) {
174211
assertThat(Email.of(address))
175212
.isPresent().get()
176-
.returns(address, email -> email.normalized(
177-
NormalizationOptions.builder().stripQuotes().build()));
213+
.returns(address, email -> email.normalized(NormalizationOptions.builder()
214+
.adjustCase(CaseOption.NO_CHANGE)
215+
.stripQuotes()
216+
.build()));
178217
}
179218

180219
static Stream<Arguments> provideValidForStripQuotes() {

src/test/java/com/sanctionco/jmail/FailureReasonTest.java

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.util.stream.Stream;
44

5+
import nl.jqno.equalsverifier.EqualsVerifier;
6+
7+
import org.junit.jupiter.api.Test;
58
import org.junit.jupiter.params.ParameterizedTest;
69
import org.junit.jupiter.params.provider.Arguments;
710
import org.junit.jupiter.params.provider.MethodSource;
@@ -60,4 +63,9 @@ void ensureFailureReasonsMatch(String email, FailureReason failureReason) {
6063
assertThat(result.isFailure()).isTrue();
6164
assertThat(result.getFailureReason()).isEqualTo(failureReason);
6265
}
66+
67+
@Test
68+
void ensureEqualsContract() {
69+
EqualsVerifier.forClass(FailureReason.class).verify();
70+
}
6371
}

src/test/java/com/sanctionco/jmail/normalization/NormalizationOptionsBuilderTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class NormalizationOptionsBuilderTest {
1212
void ensureDefaults() {
1313
assertThat(NormalizationOptions.builder().build())
1414
.returns(false, NormalizationOptions::shouldStripQuotes)
15-
.returns(CaseOption.NO_CHANGE, NormalizationOptions::getCaseOption)
15+
.returns(CaseOption.LOWERCASE, NormalizationOptions::getCaseOption)
1616
.returns(false, NormalizationOptions::shouldRemoveDots)
1717
.returns(false, NormalizationOptions::shouldRemoveSubAddress)
1818
.returns("+", NormalizationOptions::getSubAddressSeparator)

0 commit comments

Comments
 (0)