Skip to content

Commit 4024cff

Browse files
committed
Merge branch 'develop'
2 parents 74e7d5f + 69053ea commit 4024cff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2978
-115
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ updates:
44
target-branch: "develop"
55
directory: "/"
66
schedule:
7-
interval: daily
8-
time: "04:00"
7+
interval: "weekly"
8+
day: "saturday"
99
open-pull-requests-limit: 10
1010
- package-ecosystem: "github-actions"
1111
target-branch: "develop"

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535

3636
steps:
3737
- name: Checkout repository
38-
uses: actions/checkout@v3
38+
uses: actions/checkout@v4
3939

4040
# Initializes the CodeQL tools for scanning.
4141
- name: Initialize CodeQL

.github/workflows/java-ea-maven.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ jobs:
1111
strategy:
1212
fail-fast: false
1313
matrix:
14-
java: [ '-ea' ]
14+
java: [ 20 ]
1515
os: [ ubuntu-latest ]
1616

1717
name: JDK${{ matrix.java }} on ${{ matrix.os }}
1818
runs-on: ${{ matrix.os }}
1919

2020
steps:
2121
- name: Checkout source code
22-
uses: actions/checkout@v3
22+
uses: actions/checkout@v4
2323
with:
2424
submodules: true
2525
fetch-depth: 0

.github/workflows/java8-maven.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
steps:
1818
- name: Checkout source code
19-
uses: actions/checkout@v3
19+
uses: actions/checkout@v4
2020
with:
2121
submodules: true
2222
fetch-depth: 0
@@ -46,7 +46,7 @@ jobs:
4646

4747
steps:
4848
- name: Checkout source code
49-
uses: actions/checkout@v3
49+
uses: actions/checkout@v4
5050
with:
5151
submodules: true
5252
fetch-depth: 0
@@ -96,7 +96,7 @@ jobs:
9696

9797
steps:
9898
- name: Checkout source code
99-
uses: actions/checkout@v3
99+
uses: actions/checkout@v4
100100
with:
101101
submodules: true
102102
fetch-depth: 0

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
## [0.0.26] - 2023-09-20
11+
12+
### Added
13+
14+
- Add http module
15+
16+
### Fixed
17+
18+
- Fix AM/PM prefix compatibility in DateFormat
19+
1020
## [0.0.25] - 2023-07-18
1121

1222
### Fixed
@@ -230,7 +240,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
230240

231241
- Initial release
232242

233-
[Unreleased]: https://github.com/nbbrd/java-io-util/compare/v0.0.25...HEAD
243+
[Unreleased]: https://github.com/nbbrd/java-io-util/compare/v0.0.26...HEAD
244+
[0.0.26]: https://github.com/nbbrd/java-io-util/compare/v0.0.25...v0.0.26
234245
[0.0.25]: https://github.com/nbbrd/java-io-util/compare/v0.0.24...v0.0.25
235246
[0.0.24]: https://github.com/nbbrd/java-io-util/compare/v0.0.23...v0.0.24
236247
[0.0.23]: https://github.com/nbbrd/java-io-util/compare/v0.0.22...v0.0.23

java-io-base/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>com.github.nbbrd.java-io-util</groupId>
77
<artifactId>java-io-parent</artifactId>
8-
<version>0.0.25</version>
8+
<version>0.0.26</version>
99
</parent>
1010

1111
<artifactId>java-io-base</artifactId>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package internal.io.text;
2+
3+
import lombok.NonNull;
4+
import nbbrd.design.VisibleForTesting;
5+
import org.checkerframework.checker.nullness.qual.Nullable;
6+
7+
import java.text.DateFormat;
8+
import java.text.DateFormatSymbols;
9+
import java.text.ParsePosition;
10+
import java.text.SimpleDateFormat;
11+
import java.util.Date;
12+
13+
/**
14+
* Set of tools to overcome {@link DateFormat} pitfalls.
15+
*
16+
* @author Philippe Charles
17+
*/
18+
public final class DateFormats {
19+
20+
private DateFormats() {
21+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
22+
}
23+
24+
/**
25+
* Same as {@link DateFormat#parse(String)} but without throwing an exception.
26+
*
27+
* @param format the format used to parse
28+
* @param input the string to parse
29+
* @return null if parsing failed, a {@link Date} otherwise
30+
*/
31+
public static @Nullable Date parseOrNull(@NonNull DateFormat format, @NonNull CharSequence input) {
32+
String source = input.toString();
33+
ParsePosition pos = new ParsePosition(0);
34+
Date result = format.parse(source, pos);
35+
return pos.getIndex() == input.length() ? result : null;
36+
}
37+
38+
public static @NonNull CharSequence normalize(@NonNull DateFormat format, @NonNull CharSequence input) {
39+
return format instanceof SimpleDateFormat
40+
? normalizeSimpleDateFormat((SimpleDateFormat) format, input)
41+
: input;
42+
}
43+
44+
private static CharSequence normalizeSimpleDateFormat(SimpleDateFormat format, CharSequence input) {
45+
char amPmPrefix = getAmPmPrefix(format);
46+
return Character.isSpaceChar(amPmPrefix)
47+
? replaceAmPmPrefixSpaceChar(format, input, amPmPrefix)
48+
: input;
49+
}
50+
51+
private static CharSequence replaceAmPmPrefixSpaceChar(SimpleDateFormat format, CharSequence input, char amPmPrefix) {
52+
int amPmIndex = indexOfAmPm(format.getDateFormatSymbols(), input);
53+
if (amPmIndex <= 0 || !hasAmPmPrefix(input, amPmIndex)) {
54+
return input;
55+
}
56+
return new StringBuilder()
57+
.append(input, 0, amPmIndex - 1)
58+
.append(amPmPrefix)
59+
.append(input, amPmIndex, input.length())
60+
.toString();
61+
}
62+
63+
@VisibleForTesting
64+
static char getAmPmPrefix(SimpleDateFormat format) {
65+
String pattern = format.toPattern();
66+
for (int i = 0; i < pattern.length() - 1; i++) {
67+
char c = pattern.charAt(i);
68+
if (Character.isSpaceChar(c) && pattern.charAt(i + 1) == 'a') {
69+
return c;
70+
}
71+
}
72+
return '\0';
73+
}
74+
75+
private static final int NO_INDEX = -1;
76+
77+
private static int indexOfAmPm(DateFormatSymbols symbols, CharSequence input) {
78+
String inputAsString = input.toString();
79+
for (String amPmString : symbols.getAmPmStrings()) {
80+
int index = inputAsString.indexOf(amPmString);
81+
if (index != NO_INDEX) {
82+
return index;
83+
}
84+
}
85+
return NO_INDEX;
86+
}
87+
88+
private static boolean hasAmPmPrefix(CharSequence input, int amPmIndex) {
89+
return Character.isSpaceChar(input.charAt(amPmIndex - 1));
90+
}
91+
}

java-io-base/src/main/java/internal/io/text/InternalParser.java

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.nio.charset.Charset;
2525
import java.text.DateFormat;
2626
import java.text.NumberFormat;
27-
import java.text.ParsePosition;
2827
import java.time.format.DateTimeFormatter;
2928
import java.time.format.DateTimeParseException;
3029
import java.time.temporal.TemporalQuery;
@@ -43,25 +42,6 @@
4342
@lombok.experimental.UtilityClass
4443
public class InternalParser {
4544

46-
@SuppressWarnings("unchecked")
47-
public <T> T parseTemporalAccessor(DateTimeFormatter formatter, TemporalQuery<T>[] queries, CharSequence input) {
48-
if (input != null) {
49-
try {
50-
switch (queries.length) {
51-
case 0:
52-
throw new IllegalArgumentException("At least one query must be specified");
53-
case 1:
54-
return formatter.parse(input, queries[0]);
55-
default:
56-
return (T) formatter.parseBest(input, queries);
57-
}
58-
} catch (DateTimeParseException ex) {
59-
doNothing(ex);
60-
}
61-
}
62-
return null;
63-
}
64-
6545
public Boolean parseBoolean(CharSequence input) {
6646
if (input != null) {
6747
switch (input.toString()) {
@@ -174,18 +154,31 @@ public File parseFile(CharSequence input) {
174154
return input != null ? new File(input.toString()) : null;
175155
}
176156

177-
public Date parseDate(DateFormat dateFormat, CharSequence input) {
157+
@SuppressWarnings("unchecked")
158+
public <T> T parseTemporalAccessor(DateTimeFormatter formatter, TemporalQuery<T>[] queries, CharSequence input) {
178159
if (input != null) {
179-
String source = input.toString();
180-
ParsePosition pos = new ParsePosition(0);
181-
Date result = dateFormat.parse(source, pos);
182-
return pos.getIndex() == input.length() ? result : null;
160+
try {
161+
switch (queries.length) {
162+
case 0:
163+
throw new IllegalArgumentException("At least one query must be specified");
164+
case 1:
165+
return formatter.parse(input, queries[0]);
166+
default:
167+
return (T) formatter.parseBest(input, queries);
168+
}
169+
} catch (DateTimeParseException ex) {
170+
doNothing(ex);
171+
}
183172
}
184173
return null;
185174
}
186175

187-
public Number parseNumber(NumberFormat numberFormat, CharSequence input) {
188-
return input != null ? NumberFormats.parseAll(numberFormat, NumberFormats.simplify(numberFormat, input)) : null;
176+
public Date parseDate(DateFormat format, CharSequence input) {
177+
return input != null ? DateFormats.parseOrNull(format, DateFormats.normalize(format, input)) : null;
178+
}
179+
180+
public Number parseNumber(NumberFormat format, CharSequence input) {
181+
return input != null ? NumberFormats.parseOrNull(format, NumberFormats.normalize(format, input)) : null;
189182
}
190183

191184
public <T extends Enum<T>> T parseEnum(Class<T> enumClass, CharSequence input) {

java-io-base/src/main/java/internal/io/text/NumberFormats.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,52 @@
2020
import org.checkerframework.checker.nullness.qual.Nullable;
2121

2222
import java.text.DecimalFormat;
23-
import java.text.DecimalFormatSymbols;
2423
import java.text.NumberFormat;
2524
import java.text.ParsePosition;
2625

2726
/**
27+
* Set of tools to overcome {@link NumberFormat} pitfalls.
28+
*
2829
* @author Philippe Charles
2930
*/
30-
@lombok.experimental.UtilityClass
3131
final class NumberFormats {
3232

33-
@Nullable
34-
public Number parseAll(@NonNull NumberFormat numberFormat, @NonNull CharSequence input) {
33+
private NumberFormats() {
34+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
35+
}
36+
37+
/**
38+
* Same as {@link NumberFormat#parse(String)} but without throwing an exception.
39+
*
40+
* @param format the format used to parse
41+
* @param input the string to parse
42+
* @return null if parsing failed, a {@link Number} otherwise
43+
*/
44+
public static @Nullable Number parseOrNull(@NonNull NumberFormat format, @NonNull CharSequence input) {
3545
String source = input.toString();
3646
ParsePosition pos = new ParsePosition(0);
37-
Number result = numberFormat.parse(source, pos);
47+
Number result = format.parse(source, pos);
3848
return pos.getIndex() == input.length() ? result : null;
3949
}
4050

41-
@NonNull
42-
public CharSequence simplify(@NonNull NumberFormat numberFormat, @NonNull CharSequence input) {
43-
return NumberFormats.hasGroupingSpaceChar(numberFormat)
44-
? NumberFormats.removeGroupingSpaceChars(input)
51+
public static @NonNull CharSequence normalize(@NonNull NumberFormat format, @NonNull CharSequence input) {
52+
return format instanceof DecimalFormat
53+
? normalizeDecimalFormat((DecimalFormat) format, input)
4554
: input;
4655
}
4756

48-
private boolean hasGroupingSpaceChar(NumberFormat format) {
49-
return format instanceof DecimalFormat
50-
&& hasGroupingSpaceChar(((DecimalFormat) format).getDecimalFormatSymbols());
57+
private static CharSequence normalizeDecimalFormat(DecimalFormat format, CharSequence input) {
58+
char groupingSeparator = getGroupingSeparator(format);
59+
return Character.isSpaceChar(groupingSeparator)
60+
? removeGroupingSpaceChars(input)
61+
: input;
5162
}
5263

53-
private boolean hasGroupingSpaceChar(DecimalFormatSymbols symbols) {
54-
return Character.isSpaceChar(symbols.getGroupingSeparator());
64+
private static char getGroupingSeparator(DecimalFormat format) {
65+
return format.getDecimalFormatSymbols().getGroupingSeparator();
5566
}
5667

57-
private CharSequence removeGroupingSpaceChars(CharSequence input) {
68+
private static CharSequence removeGroupingSpaceChars(CharSequence input) {
5869
if (input.length() < 2) {
5970
return input;
6071
}
@@ -69,9 +80,9 @@ private CharSequence removeGroupingSpaceChars(CharSequence input) {
6980
return result.length() != input.length() ? result.toString() : input;
7081
}
7182

72-
private boolean isGroupingSpaceChar(CharSequence array, int index) {
73-
return Character.isSpaceChar(array.charAt(index))
74-
&& Character.isDigit(array.charAt(index - 1))
75-
&& Character.isDigit(array.charAt(index + 1));
83+
private static boolean isGroupingSpaceChar(CharSequence input, int index) {
84+
return Character.isSpaceChar(input.charAt(index))
85+
&& Character.isDigit(input.charAt(index - 1))
86+
&& Character.isDigit(input.charAt(index + 1));
7687
}
7788
}

0 commit comments

Comments
 (0)