Skip to content

Commit 4fc47fb

Browse files
phvegaguillerodriguez
authored andcommitted
DecimalFormat: Fix memory leak
DecimalFormat keeps a list of FieldPosition attributes for use by formatToCharacterIterator(). However, all format() methods add attributes to this list, even if they are not needed. Also, the list is only cleared by formatToCharacterIterator() (which might not be called at all). Fix this by only recording attribute information when required, and clearing the list immediately after use. Fixes #28 (BZ#30977)
1 parent f048aed commit 4fc47fb

File tree

1 file changed

+46
-18
lines changed

1 file changed

+46
-18
lines changed

java/text/DecimalFormat.java

+46-18
Original file line numberDiff line numberDiff line change
@@ -313,20 +313,30 @@ public int hashCode()
313313
* @return The String representation of this long.
314314
*/
315315
public final StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos)
316+
{
317+
return format(obj, sbuf, pos, false);
318+
}
319+
320+
private StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos,
321+
boolean addAttrs)
316322
{
317323
if (obj instanceof BigInteger)
318324
{
319325
BigDecimal decimal = new BigDecimal((BigInteger) obj);
320-
formatInternal(decimal, true, sbuf, pos);
326+
formatInternal(decimal, true, sbuf, pos, addAttrs);
321327
return sbuf;
322328
}
323329
else if (obj instanceof BigDecimal)
324330
{
325-
formatInternal((BigDecimal) obj, true, sbuf, pos);
331+
formatInternal((BigDecimal) obj, false, sbuf, pos, addAttrs);
326332
return sbuf;
327333
}
334+
else if (obj instanceof Number)
335+
{
336+
return format(((Number) obj).doubleValue(), sbuf, pos, addAttrs);
337+
}
328338

329-
return super.format(obj, sbuf, pos);
339+
throw new IllegalArgumentException("Cannot format given Object as a Number");
330340
}
331341

332342
/**
@@ -341,6 +351,12 @@ else if (obj instanceof BigDecimal)
341351
*/
342352
public StringBuffer format(double number, StringBuffer dest,
343353
FieldPosition fieldPos)
354+
{
355+
return format(number, dest, fieldPos, false);
356+
}
357+
358+
private StringBuffer format(double number, StringBuffer dest,
359+
FieldPosition fieldPos, boolean addAttrs)
344360
{
345361
// special cases for double: NaN and negative or positive infinity
346362
if (Double.isNaN(number))
@@ -384,7 +400,7 @@ else if (Double.isInfinite(number))
384400
{
385401
// get the number as a BigDecimal
386402
BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
387-
formatInternal(bigDecimal, false, dest, fieldPos);
403+
formatInternal(bigDecimal, false, dest, fieldPos, addAttrs);
388404
}
389405

390406
return dest;
@@ -401,9 +417,15 @@ else if (Double.isInfinite(number))
401417
*/
402418
public StringBuffer format(long number, StringBuffer dest,
403419
FieldPosition fieldPos)
420+
{
421+
return format(number, dest, fieldPos, false);
422+
}
423+
424+
private StringBuffer format(long number, StringBuffer dest,
425+
FieldPosition fieldPos, boolean addAttrs)
404426
{
405427
BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
406-
formatInternal(bigDecimal, true, dest, fieldPos);
428+
formatInternal(bigDecimal, true, dest, fieldPos, addAttrs);
407429
return dest;
408430
}
409431

@@ -430,9 +452,7 @@ public AttributedCharacterIterator formatToCharacterIterator(Object value)
430452
IllegalArgumentException("Cannot format given Object as a Number");
431453

432454
StringBuffer text = new StringBuffer();
433-
attributes.clear();
434-
super.format(value, text, new FieldPosition(0));
435-
455+
format(value, text, new FieldPosition(0), true);
436456
AttributedString as = new AttributedString(text.toString());
437457

438458
// add NumberFormat field attributes to the AttributedString
@@ -444,6 +464,7 @@ public AttributedCharacterIterator formatToCharacterIterator(Object value)
444464
as.addAttribute(attribute, attribute, pos.getBeginIndex(),
445465
pos.getEndIndex());
446466
}
467+
attributes.clear();
447468

448469
// return the CharacterIterator from AttributedString
449470
return as.getIterator();
@@ -1764,9 +1785,10 @@ else if (ch == patternSeparator)
17641785
* @param isLong A boolean that indicates if this BigDecimal is a real
17651786
* decimal or an integer.
17661787
* @param fieldPos Use to keep track of the formatting position.
1788+
* @param addAttrs If true, attribute information will be recorded.
17671789
*/
17681790
private void formatInternal(BigDecimal number, boolean isLong,
1769-
StringBuffer dest, FieldPosition fieldPos)
1791+
StringBuffer dest, FieldPosition fieldPos, boolean addAttrs)
17701792
{
17711793
// The specs says that fieldPos should not be null, and that we
17721794
// should throw a NPE, but it seems that in few classes that
@@ -1798,7 +1820,8 @@ private void formatInternal(BigDecimal number, boolean isLong,
17981820

17991821
_multiplier = negativePatternMultiplier;
18001822

1801-
addAttribute(Field.SIGN, attributeStart, dest.length());
1823+
if (addAttrs)
1824+
addAttribute(Field.SIGN, attributeStart, dest.length());
18021825
}
18031826
else
18041827
{
@@ -1930,7 +1953,8 @@ private void formatInternal(BigDecimal number, boolean isLong,
19301953
}
19311954

19321955
// add the INTEGER attribute
1933-
addAttribute(Field.INTEGER, attributeStart, dest.length());
1956+
if (addAttrs)
1957+
addAttribute(Field.INTEGER, attributeStart, dest.length());
19341958

19351959
// ...update field position, if needed, and return...
19361960
if ((fieldPos.getField() == INTEGER_FIELD ||
@@ -1940,7 +1964,7 @@ private void formatInternal(BigDecimal number, boolean isLong,
19401964
fieldPos.setEndIndex(endIndexInt);
19411965
}
19421966

1943-
handleFractionalPart(dest, fractPart, fieldPos, isLong);
1967+
handleFractionalPart(dest, fractPart, fieldPos, isLong, addAttrs);
19441968

19451969
// and the exponent
19461970
if (this.useExponentialNotation)
@@ -1949,15 +1973,17 @@ private void formatInternal(BigDecimal number, boolean isLong,
19491973

19501974
dest.append(symbols.getExponential());
19511975

1952-
addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
1976+
if (addAttrs)
1977+
addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length());
19531978
attributeStart = dest.length();
19541979

19551980
if (exponent < 0)
19561981
{
19571982
dest.append(symbols.getMinusSign());
19581983
exponent = -exponent;
19591984

1960-
addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
1985+
if (addAttrs)
1986+
addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length());
19611987
}
19621988

19631989
attributeStart = dest.length();
@@ -1971,7 +1997,8 @@ private void formatInternal(BigDecimal number, boolean isLong,
19711997
for (int i = 0; i < exponentLength; ++i)
19721998
dest.append(exponentString.charAt(i));
19731999

1974-
addAttribute(Field.EXPONENT, attributeStart, dest.length());
2000+
if (addAttrs)
2001+
addAttribute(Field.EXPONENT, attributeStart, dest.length());
19752002
}
19762003

19772004
// now include the suffixes...
@@ -1993,9 +2020,10 @@ private void formatInternal(BigDecimal number, boolean isLong,
19932020
* @param fractPart
19942021
* @param fieldPos
19952022
* @param isLong
2023+
* @param addAttrs
19962024
*/
19972025
private void handleFractionalPart(StringBuffer dest, String fractPart,
1998-
FieldPosition fieldPos, boolean isLong)
2026+
FieldPosition fieldPos, boolean isLong, boolean addAttrs)
19992027
{
20002028
int dotStart = 0;
20012029
int dotEnd = 0;
@@ -2071,10 +2099,10 @@ else if (!this.decimalSeparatorAlwaysShown)
20712099
}
20722100
}
20732101

2074-
if (addDecimal)
2102+
if (addAttrs && addDecimal)
20752103
addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd);
20762104

2077-
if (addFractional)
2105+
if (addAttrs && addFractional)
20782106
addAttribute(Field.FRACTION, fractStart, fractEnd);
20792107

20802108
if ((fieldPos.getField() == FRACTION_FIELD ||

0 commit comments

Comments
 (0)