Skip to content

Commit ab77f30

Browse files
committed
#152: Make LocationFormatter::parse method thread-safe.
1 parent f56bca4 commit ab77f30

11 files changed

+75
-38
lines changed

jpx/src/main/java/io/jenetics/jpx/format/CompositeFormat.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void parse(
5858
final ParsePosition pos,
5959
final LocationBuilder builder
6060
) {
61-
for(var format : _formats ) {
61+
for(var format : _formats) {
6262
format.parse(in, pos, builder);
6363
}
6464
}

jpx/src/main/java/io/jenetics/jpx/format/ConstFormat.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private static String escape(final String value) {
9595
}
9696

9797
return quoted
98-
? "'" + out.toString() + "'"
98+
? "'" + out + "'"
9999
: out.toString();
100100
}
101101

jpx/src/main/java/io/jenetics/jpx/format/Elevation.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void setPrefixSign(final boolean b) {
4343
? ("+" + decimalPattern + ";" + "-" + decimalPattern)
4444
: decimalPattern;
4545

46-
_numberFormat = new DecimalFormat(pattern, SYMBOLS);
46+
setFormat(new DecimalFormat(pattern, SYMBOLS));
4747
}
4848

4949
@Override
@@ -57,14 +57,14 @@ public void parse(
5757
final ParsePosition pos,
5858
final LocationBuilder builder
5959
) {
60-
builder.setElevation(parseDouble(in, pos));
60+
builder.setElevation(parse(in, pos));
6161
}
6262

6363
@Override
6464
public Optional<String> format(final Location loc) {
6565
return loc.elevation()
6666
.map(l -> l.to(METER))
67-
.map(d -> _numberFormat.format(d));
67+
.map(this::format);
6868
}
6969

7070
@Override

jpx/src/main/java/io/jenetics/jpx/format/Field.java

+48-11
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,37 @@
1717

1818
import static java.lang.Math.abs;
1919
import static java.lang.Math.floor;
20+
import static java.util.Objects.requireNonNull;
2021

22+
import java.math.RoundingMode;
2123
import java.text.DecimalFormat;
2224
import java.text.DecimalFormatSymbols;
2325
import java.text.NumberFormat;
2426
import java.text.ParsePosition;
2527
import java.util.Locale;
2628
import java.util.Optional;
29+
import java.util.concurrent.atomic.AtomicReference;
2730

2831
/**
2932
* Represents one of the existing location fields: latitude, longitude and
3033
* elevation.
3134
*
3235
* @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
33-
* @version 2.2
36+
* @version !__version__!
3437
* @since 1.4
3538
*/
3639
abstract class Field implements Format {
3740

38-
final static DecimalFormatSymbols SYMBOLS =
41+
static final DecimalFormatSymbols SYMBOLS =
3942
DecimalFormatSymbols.getInstance(Locale.US);
4043

41-
protected NumberFormat _numberFormat;
42-
protected final String _pattern;
44+
final String _pattern;
4345

44-
Field(final String pattern){
45-
final var decimalPattern = toDecimalPattern(pattern);
46-
_numberFormat = new DecimalFormat(decimalPattern, SYMBOLS);
47-
_pattern = pattern;
46+
private final AtomicReference<NumberFormat> _format = new AtomicReference<>();
47+
48+
Field(final String pattern) {
49+
_pattern = requireNonNull(pattern);
50+
_format.set(new DecimalFormat(toDecimalPattern(pattern), SYMBOLS));
4851
}
4952

5053
/**
@@ -57,6 +60,18 @@ abstract class Field implements Format {
5760
void setPrefixSign(final boolean b) {
5861
}
5962

63+
void setFormat(final NumberFormat format) {
64+
_format.set(requireNonNull(format));
65+
}
66+
67+
void setRoundingMode(final RoundingMode mode) {
68+
_format.get().setRoundingMode(mode);
69+
}
70+
71+
int getMinimumFractionDigits() {
72+
return _format.get().getMinimumFractionDigits();
73+
}
74+
6075
static double toMinutes(final double degrees) {
6176
double dd = abs(degrees);
6277
return (dd - floor(dd)) * 60.0;
@@ -108,21 +123,43 @@ public String toPattern() {
108123
return _pattern;
109124
}
110125

111-
double parseDouble(final CharSequence in, final ParsePosition pos) {
126+
/**
127+
* Formatting the given double value with the field formatter.
128+
*
129+
* @param value the double value to format
130+
* @return the formatted double value
131+
*/
132+
String format(final double value) {
133+
return _format.get().format(value);
134+
}
135+
136+
/**
137+
* Parsers the given input string.
138+
*
139+
* @param in the input string to parse
140+
* @param pos the parse position
141+
* @return the parsed double value
142+
*/
143+
double parse(final CharSequence in, final ParsePosition pos) {
112144
int i = pos.getIndex();
113145
String s = in.toString();
114-
boolean strictWidth = 1 < _numberFormat.getMinimumIntegerDigits(); //better?
146+
boolean strictWidth = 1 < _format.get().getMinimumIntegerDigits(); //better?
115147
if (strictWidth) {
116148
int end = i + toPattern().length(); // toPattern() rather than pattern because LatitudeDegree.toPattern()
117149
s = in.subSequence(0, end).toString(); // don't eat more digits
118150
}
119151

120-
Number n = _numberFormat.parse(s, pos);
152+
final Number n;
153+
synchronized (_format) {
154+
n = _format.get().parse(s, pos);
155+
}
156+
121157
if (i == pos.getIndex()) {
122158
pos.setErrorIndex(i);
123159
throw new ParseException("Not found " + _pattern, in, i);
124160
}
125161

126162
return n.doubleValue();
127163
}
164+
128165
}

jpx/src/main/java/io/jenetics/jpx/format/LatitudeDegree.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ void setPrefixSign(boolean b) {
4747
_prefixSign = b;
4848
String decimalPattern = toDecimalPattern(_pattern);
4949
String p = b ? ("+" + decimalPattern + ";" + "-" + decimalPattern) : decimalPattern;
50-
_numberFormat = new DecimalFormat(p, SYMBOLS);
50+
setFormat(new DecimalFormat(p, SYMBOLS));
5151
}
5252

5353
boolean isPrefixSign() {
5454
return _prefixSign;
5555
}
5656

5757
void setTruncate(final boolean b) {
58-
_numberFormat.setRoundingMode(b ? DOWN : HALF_EVEN);
58+
setRoundingMode(b ? DOWN : HALF_EVEN);
5959
}
6060

6161
private boolean absolute = false;
@@ -75,7 +75,7 @@ public void parse(
7575
final ParsePosition pos,
7676
final LocationBuilder builder
7777
) {
78-
double d = parseDouble(in, pos);
78+
double d = parse(in, pos);
7979
builder.addLatitude(d);
8080
}
8181

@@ -84,7 +84,7 @@ public Optional<String> format(final Location loc) {
8484
return loc.latitude()
8585
.map(Latitude::toDegrees)
8686
.map(d -> absolute ? abs(d) : d)
87-
.map(d -> _numberFormat.format(d));
87+
.map(this::format);
8888
}
8989

9090
@Override

jpx/src/main/java/io/jenetics/jpx/format/LatitudeMinute.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ char type() {
4242
}
4343

4444
void setTruncate(final boolean b) {
45-
_numberFormat.setRoundingMode(b ? DOWN : HALF_EVEN);
45+
setRoundingMode(b ? DOWN : HALF_EVEN);
4646
}
4747

4848
@Override
@@ -51,7 +51,7 @@ public void parse(
5151
final ParsePosition pos,
5252
final LocationBuilder builder
5353
) {
54-
double d = parseDouble(in, pos);
54+
double d = parse(in, pos);
5555
builder.addLatitudeMinute(d);
5656
}
5757

@@ -60,7 +60,7 @@ public Optional<String> format(final Location loc) {
6060
return loc.latitude()
6161
.map(Latitude::toDegrees)
6262
.map(Field::toMinutes)
63-
.map(d -> _numberFormat.format(d));
63+
.map(this::format);
6464
}
6565

6666
}

jpx/src/main/java/io/jenetics/jpx/format/LatitudeSecond.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void parse(
4444
final ParsePosition pos,
4545
final LocationBuilder builder
4646
) {
47-
double d = parseDouble(in, pos);
47+
double d = parse(in, pos);
4848
builder.addLatitudeSecond(d);
4949
}
5050

@@ -53,7 +53,7 @@ public Optional<String> format(final Location loc) {
5353
return loc.latitude()
5454
.map(Latitude::toDegrees)
5555
.map(Field::toSeconds)
56-
.map(d -> _numberFormat.format(d));
56+
.map(this::format);
5757
}
5858

5959
}

jpx/src/main/java/io/jenetics/jpx/format/LocationFormatter.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -715,15 +715,15 @@ private void validate(){
715715

716716
// If D has fractional, no M or S
717717
if (D != null &&
718-
0 < D._numberFormat.getMinimumFractionDigits()
718+
0 < D.getMinimumFractionDigits()
719719
&& M != null)
720720
{
721721
throw iae("If 'D' has fraction, no 'M' or 'S' allowed.");
722722
}
723723

724724
// If M has fractional, no S
725725
if (M != null &&
726-
0 < M._numberFormat.getMinimumFractionDigits() &&
726+
0 < M.getMinimumFractionDigits() &&
727727
S != null)
728728
{
729729
throw iae("If 'M' has fraction, no 'S' allowed.");
@@ -745,15 +745,15 @@ private void validate(){
745745

746746
// If d has fractional, no m or s
747747
if (d != null &&
748-
0 < d._numberFormat.getMinimumFractionDigits() &&
748+
0 < d.getMinimumFractionDigits() &&
749749
m != null)
750750
{
751751
throw iae("If 'd' has fraction, no 'm' or 's' allowed.");
752752
}
753753

754754
// If m has fractional, no s.
755755
if (m != null &&
756-
0 < m._numberFormat.getMinimumFractionDigits() &&
756+
0 < m.getMinimumFractionDigits() &&
757757
s != null)
758758
{
759759
throw iae("If 'm' has fraction, no 's' allowed.");

jpx/src/main/java/io/jenetics/jpx/format/LongitudeDegree.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ void setPrefixSign(final boolean b) {
5151
? ("+" + decimalPattern + ";" + "-" + decimalPattern)
5252
: decimalPattern;
5353

54-
_numberFormat = new DecimalFormat(pattern, SYMBOLS);
54+
setFormat(new DecimalFormat(pattern, SYMBOLS));
5555
}
5656

5757
boolean isPrefixSign() {
5858
return prefixSign;
5959
}
6060

6161
void setTruncate(final boolean b) {
62-
_numberFormat.setRoundingMode(b ? DOWN : HALF_EVEN);
62+
setRoundingMode(b ? DOWN : HALF_EVEN);
6363
}
6464

6565
void setAbsolute(final boolean b) {
@@ -77,7 +77,7 @@ public void parse(
7777
final ParsePosition pos,
7878
final LocationBuilder builder
7979
) {
80-
double d = parseDouble(in, pos);
80+
double d = parse(in, pos);
8181
builder.addLongitude(d);
8282
}
8383

@@ -86,7 +86,7 @@ public Optional<String> format(final Location loc) {
8686
return loc.longitude()
8787
.map(Longitude::toDegrees)
8888
.map(d -> absolute ? abs(d) : d)
89-
.map(d -> _numberFormat.format(d));
89+
.map(this::format);
9090
}
9191

9292
@Override

jpx/src/main/java/io/jenetics/jpx/format/LongitudeMinute.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ char type() {
4242
}
4343

4444
void setTruncate(final boolean b) {
45-
_numberFormat.setRoundingMode(b ? DOWN : HALF_EVEN);
45+
setRoundingMode(b ? DOWN : HALF_EVEN);
4646
}
4747

4848
@Override
@@ -51,7 +51,7 @@ public void parse(
5151
final ParsePosition pos,
5252
final LocationBuilder builder
5353
) {
54-
double d = parseDouble(in, pos);
54+
double d = parse(in, pos);
5555
builder.addLongitudeMinute(d);
5656
}
5757

@@ -60,7 +60,7 @@ public Optional<String> format(final Location loc) {
6060
return loc.longitude()
6161
.map(Longitude::toDegrees)
6262
.map(Field::toMinutes)
63-
.map(d -> _numberFormat.format(d));
63+
.map(this::format);
6464
}
6565

6666
}

jpx/src/main/java/io/jenetics/jpx/format/LongitudeSecond.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ char type() {
4343
final ParsePosition pos,
4444
final LocationBuilder builder
4545
) {
46-
double d = parseDouble(in, pos);
46+
double d = parse(in, pos);
4747
builder.addLongitudeSecond(d);
4848
}
4949

@@ -52,7 +52,7 @@ public Optional<String> format(final Location loc) {
5252
return loc.longitude()
5353
.map(Longitude::toDegrees)
5454
.map(Field::toSeconds)
55-
.map(d -> _numberFormat.format(d));
55+
.map(this::format);
5656
}
5757

5858
}

0 commit comments

Comments
 (0)