8333396: Use StringBuilder internally for java.text.Format.* formatting

Reviewed-by: naoto, liach, jlu
This commit is contained in:
lingjun.cg 2024-07-22 02:01:08 +00:00
parent fd741a88e8
commit 4da9915875
16 changed files with 764 additions and 73 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -54,7 +54,7 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate {
}
public void formatted(Format.Field attr, Object value, int start, int end,
StringBuffer buffer) {
Format.StringBuf buffer) {
if (start != end) {
if (start < size) {
// Adjust attributes of existing runs
@ -93,7 +93,7 @@ class CharacterIteratorFieldDelegate implements Format.FieldDelegate {
}
public void formatted(int fieldID, Format.Field attr, Object value,
int start, int end, StringBuffer buffer) {
int start, int end, Format.StringBuf buffer) {
formatted(attr, value, start, end, buffer);
}

View File

@ -514,6 +514,12 @@ public class ChoiceFormat extends NumberFormat {
@Override
public StringBuffer format(long number, StringBuffer toAppendTo,
FieldPosition status) {
return format((double) number, StringBufFactory.of(toAppendTo), status).asStringBuffer();
}
@Override
StringBuf format(long number, StringBuf toAppendTo,
FieldPosition status) {
return format((double) number, toAppendTo, status);
}
@ -531,6 +537,12 @@ public class ChoiceFormat extends NumberFormat {
@Override
public StringBuffer format(double number, StringBuffer toAppendTo,
FieldPosition status) {
return format(number, StringBufFactory.of(toAppendTo), status).asStringBuffer();
}
@Override
StringBuf format(double number, StringBuf toAppendTo,
FieldPosition status) {
// find the number
int i;
for (i = 0; i < choiceLimits.length; ++i) {

View File

@ -544,6 +544,35 @@ public final class CompactNumberFormat extends NumberFormat {
throw new IllegalArgumentException("Cannot format null as a number");
}
if (number instanceof Long || number instanceof Integer
|| number instanceof Short || number instanceof Byte
|| number instanceof AtomicInteger
|| number instanceof AtomicLong
|| (number instanceof BigInteger
&& ((BigInteger) number).bitLength() < 64)) {
return format(((Number) number).longValue(), toAppendTo,
fieldPosition);
} else if (number instanceof BigDecimal) {
return format((BigDecimal) number, StringBufFactory.of(toAppendTo), fieldPosition).asStringBuffer();
} else if (number instanceof BigInteger) {
return format((BigInteger) number, StringBufFactory.of(toAppendTo), fieldPosition).asStringBuffer();
} else if (number instanceof Number) {
return format(((Number) number).doubleValue(), toAppendTo, fieldPosition);
} else {
throw new IllegalArgumentException("Cannot format "
+ number.getClass().getName() + " as a number");
}
}
@Override
StringBuf format(Object number,
StringBuf toAppendTo,
FieldPosition fieldPosition) {
if (number == null) {
throw new IllegalArgumentException("Cannot format null as a number");
}
if (number instanceof Long || number instanceof Integer
|| number instanceof Short || number instanceof Byte
|| number instanceof AtomicInteger
@ -591,12 +620,21 @@ public final class CompactNumberFormat extends NumberFormat {
public StringBuffer format(double number, StringBuffer result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer();
}
@Override
StringBuf format(double number, StringBuf result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, result, fieldPosition.getFieldDelegate());
}
private StringBuffer format(double number, StringBuffer result,
private StringBuf format(double number, StringBuf result,
FieldDelegate delegate) {
boolean nanOrInfinity = decimalFormat.handleNaN(number, result, delegate);
@ -681,12 +719,21 @@ public final class CompactNumberFormat extends NumberFormat {
public StringBuffer format(long number, StringBuffer result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer();
}
@Override
StringBuf format(long number, StringBuf result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, result, fieldPosition.getFieldDelegate());
}
private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) {
private StringBuf format(long number, StringBuf result, FieldDelegate delegate) {
boolean isNegative = (number < 0);
if (isNegative) {
number = -number;
@ -757,14 +804,14 @@ public final class CompactNumberFormat extends NumberFormat {
* of the prefix and the suffix fields can be
* obtained using {@link NumberFormat.Field#PREFIX}
* and {@link NumberFormat.Field#SUFFIX} respectively.
* @return the {@code StringBuffer} passed in as {@code result}
* @return the {@code StringBuf} passed in as {@code result}
* @throws ArithmeticException if rounding is needed with rounding
* mode being set to {@code RoundingMode.UNNECESSARY}
* @throws NullPointerException if any of the given parameter
* is {@code null}
* @see FieldPosition
*/
private StringBuffer format(BigDecimal number, StringBuffer result,
private StringBuf format(BigDecimal number, StringBuf result,
FieldPosition fieldPosition) {
Objects.requireNonNull(number);
@ -773,7 +820,7 @@ public final class CompactNumberFormat extends NumberFormat {
return format(number, result, fieldPosition.getFieldDelegate());
}
private StringBuffer format(BigDecimal number, StringBuffer result,
private StringBuf format(BigDecimal number, StringBuf result,
FieldDelegate delegate) {
boolean isNegative = number.signum() == -1;
@ -843,14 +890,14 @@ public final class CompactNumberFormat extends NumberFormat {
* prefix and the suffix fields can be obtained
* using {@link NumberFormat.Field#PREFIX} and
* {@link NumberFormat.Field#SUFFIX} respectively.
* @return the {@code StringBuffer} passed in as {@code result}
* @return the {@code StringBuf} passed in as {@code result}
* @throws ArithmeticException if rounding is needed with rounding
* mode being set to {@code RoundingMode.UNNECESSARY}
* @throws NullPointerException if any of the given parameter
* is {@code null}
* @see FieldPosition
*/
private StringBuffer format(BigInteger number, StringBuffer result,
private StringBuf format(BigInteger number, StringBuf result,
FieldPosition fieldPosition) {
Objects.requireNonNull(number);
@ -859,7 +906,7 @@ public final class CompactNumberFormat extends NumberFormat {
return format(number, result, fieldPosition.getFieldDelegate(), false);
}
private StringBuffer format(BigInteger number, StringBuffer result,
private StringBuf format(BigInteger number, StringBuf result,
FieldDelegate delegate, boolean formatLong) {
boolean isNegative = number.signum() == -1;
@ -936,7 +983,7 @@ public final class CompactNumberFormat extends NumberFormat {
* {@code NumberFormat.Field.SIGN} and
* {@code NumberFormat.Field.PREFIX} fields
*/
private void appendPrefix(StringBuffer result, String prefix,
private void appendPrefix(StringBuf result, String prefix,
FieldDelegate delegate) {
append(result, expandAffix(prefix), delegate,
getFieldPositions(prefix, NumberFormat.Field.PREFIX));
@ -952,7 +999,7 @@ public final class CompactNumberFormat extends NumberFormat {
* {@code NumberFormat.Field.SIGN} and
* {@code NumberFormat.Field.SUFFIX} fields
*/
private void appendSuffix(StringBuffer result, String suffix,
private void appendSuffix(StringBuf result, String suffix,
FieldDelegate delegate) {
append(result, expandAffix(suffix), delegate,
getFieldPositions(suffix, NumberFormat.Field.SUFFIX));
@ -968,7 +1015,7 @@ public final class CompactNumberFormat extends NumberFormat {
* @param positions a list of {@code FieldPosition} in the given
* string
*/
private void append(StringBuffer result, String string,
private void append(StringBuf result, String string,
FieldDelegate delegate, List<FieldPosition> positions) {
if (!string.isEmpty()) {
int start = result.length();
@ -1134,7 +1181,7 @@ public final class CompactNumberFormat extends NumberFormat {
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
CharacterIteratorFieldDelegate delegate
= new CharacterIteratorFieldDelegate();
StringBuffer sb = new StringBuffer();
StringBuf sb = StringBufFactory.of();
if (obj instanceof Double || obj instanceof Float) {
format(((Number) obj).doubleValue(), sb, delegate);

View File

@ -346,6 +346,19 @@ public abstract class DateFormat extends Format {
throw new IllegalArgumentException("Cannot format given Object as a Date");
}
@Override
final StringBuf format(Object obj, StringBuf toAppendTo,
FieldPosition fieldPosition) {
if (obj instanceof Date) {
return format((Date) obj, toAppendTo, fieldPosition);
} else if (obj instanceof Number) {
return format(new Date(((Number) obj).longValue()),
toAppendTo, fieldPosition);
} else {
throw new IllegalArgumentException("Cannot format given Object as a Date");
}
}
/**
* Formats a {@link Date} into a date-time string. The formatted
* string is appended to the given {@code StringBuffer}.
@ -371,6 +384,11 @@ public abstract class DateFormat extends Format {
public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition fieldPosition);
StringBuf format(Date date, StringBuf toAppendTo,
FieldPosition fieldPosition) {
throw new UnsupportedOperationException("Subclasses should override this method");
}
/**
* Formats a {@link Date} into a date-time string.
*
@ -379,9 +397,15 @@ public abstract class DateFormat extends Format {
*/
public final String format(Date date)
{
if ("java.text".equals(getClass().getPackageName())
&& "java.text".equals(numberFormat.getClass().getPackageName())) {
return format(date, StringBufFactory.of(),
DontCareFieldPosition.INSTANCE).toString();
} else {
return format(date, new StringBuffer(),
DontCareFieldPosition.INSTANCE).toString();
}
}
/**
* Parses text from the beginning of the given string to produce a date.

View File

@ -555,6 +555,28 @@ public class DecimalFormat extends NumberFormat {
(number instanceof BigInteger &&
((BigInteger)number).bitLength () < 64)) {
return format(((Number)number).longValue(), toAppendTo, pos);
} else if (number instanceof BigDecimal) {
return format((BigDecimal)number, StringBufFactory.of(toAppendTo), pos).asStringBuffer();
} else if (number instanceof BigInteger) {
return format((BigInteger)number, StringBufFactory.of(toAppendTo), pos).asStringBuffer();
} else if (number instanceof Number) {
return format(((Number)number).doubleValue(), toAppendTo, pos);
} else {
throw new IllegalArgumentException("Cannot format given Object as a Number");
}
}
@Override
final StringBuf format(Object number,
StringBuf toAppendTo,
FieldPosition pos) {
if (number instanceof Long || number instanceof Integer ||
number instanceof Short || number instanceof Byte ||
number instanceof AtomicInteger ||
number instanceof AtomicLong ||
(number instanceof BigInteger &&
((BigInteger) number).bitLength() < 64)) {
return format(((Number) number).longValue(), toAppendTo, pos);
} else if (number instanceof BigDecimal) {
return format((BigDecimal) number, toAppendTo, pos);
} else if (number instanceof BigInteger) {
@ -588,6 +610,12 @@ public class DecimalFormat extends NumberFormat {
@Override
public StringBuffer format(double number, StringBuffer result,
FieldPosition fieldPosition) {
return format(number, StringBufFactory.of(result), fieldPosition).asStringBuffer();
}
@Override
StringBuf format(double number, StringBuf result,
FieldPosition fieldPosition) {
// If fieldPosition is a DontCareFieldPosition instance we can
// try to go to fast-path code.
boolean tryFastPath = false;
@ -619,7 +647,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @return The formatted number string
*/
StringBuffer format(double number, StringBuffer result,
StringBuf format(double number, StringBuf result,
FieldDelegate delegate) {
boolean nanOrInfinity = handleNaN(number, result, delegate);
@ -666,7 +694,7 @@ public class DecimalFormat extends NumberFormat {
* @param delegate notified of locations of sub fields
* @return true, if number is a NaN; false otherwise
*/
boolean handleNaN(double number, StringBuffer result,
boolean handleNaN(double number, StringBuf result,
FieldDelegate delegate) {
if (Double.isNaN(number)
|| (Double.isInfinite(number) && multiplier == 0)) {
@ -691,7 +719,7 @@ public class DecimalFormat extends NumberFormat {
* @return true, if number is a {@code Double.NEGATIVE_INFINITY} or
* {@code Double.POSITIVE_INFINITY}; false otherwise
*/
boolean handleInfinity(double number, StringBuffer result,
boolean handleInfinity(double number, StringBuf result,
FieldDelegate delegate, boolean isNegative) {
if (Double.isInfinite(number)) {
if (isNegative) {
@ -720,7 +748,7 @@ public class DecimalFormat extends NumberFormat {
return false;
}
StringBuffer doubleSubformat(double number, StringBuffer result,
StringBuf doubleSubformat(double number, StringBuf result,
FieldDelegate delegate, boolean isNegative) {
synchronized (digitList) {
int maxIntDigits = super.getMaximumIntegerDigits();
@ -761,6 +789,14 @@ public class DecimalFormat extends NumberFormat {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, StringBufFactory.of(result), fieldPosition.getFieldDelegate()).asStringBuffer();
}
StringBuf format(long number, StringBuf result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
return format(number, result, fieldPosition.getFieldDelegate());
}
@ -774,7 +810,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.FieldPosition
*/
StringBuffer format(long number, StringBuffer result,
StringBuf format(long number, StringBuf result,
FieldDelegate delegate) {
boolean isNegative = (number < 0);
if (isNegative) {
@ -849,7 +885,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.FieldPosition
*/
private StringBuffer format(BigDecimal number, StringBuffer result,
private StringBuf format(BigDecimal number, StringBuf result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
@ -865,7 +901,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @return The formatted number string
*/
StringBuffer format(BigDecimal number, StringBuffer result,
StringBuf format(BigDecimal number, StringBuf result,
FieldDelegate delegate) {
if (multiplier != 1) {
number = number.multiply(getBigDecimalMultiplier());
@ -908,7 +944,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.FieldPosition
*/
private StringBuffer format(BigInteger number, StringBuffer result,
private StringBuf format(BigInteger number, StringBuf result,
FieldPosition fieldPosition) {
fieldPosition.setBeginIndex(0);
fieldPosition.setEndIndex(0);
@ -926,7 +962,7 @@ public class DecimalFormat extends NumberFormat {
* mode being set to RoundingMode.UNNECESSARY
* @see java.text.FieldPosition
*/
StringBuffer format(BigInteger number, StringBuffer result,
StringBuf format(BigInteger number, StringBuf result,
FieldDelegate delegate, boolean formatLong) {
if (multiplier != 1) {
number = number.multiply(getBigIntegerMultiplier());
@ -986,7 +1022,7 @@ public class DecimalFormat extends NumberFormat {
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
CharacterIteratorFieldDelegate delegate =
new CharacterIteratorFieldDelegate();
StringBuffer sb = new StringBuffer();
StringBuf sb = StringBufFactory.of();
if (obj instanceof Double || obj instanceof Float) {
format(((Number)obj).doubleValue(), sb, delegate);
@ -1779,7 +1815,7 @@ public class DecimalFormat extends NumberFormat {
* Complete the formatting of a finite number. On entry, the digitList must
* be filled in with the correct digits.
*/
private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
private StringBuf subformat(StringBuf result, FieldDelegate delegate,
boolean isNegative, boolean isInteger,
int maxIntDigits, int minIntDigits,
int maxFraDigits, int minFraDigits) {
@ -1821,7 +1857,7 @@ public class DecimalFormat extends NumberFormat {
* @param maxFraDigits maximum fraction digits
* @param minFraDigits minimum fraction digits
*/
void subformatNumber(StringBuffer result, FieldDelegate delegate,
void subformatNumber(StringBuf result, FieldDelegate delegate,
boolean isNegative, boolean isInteger,
int maxIntDigits, int minIntDigits,
int maxFraDigits, int minFraDigits) {
@ -2108,7 +2144,7 @@ public class DecimalFormat extends NumberFormat {
* <p>
* This is used by {@code subformat} to add the prefix/suffix.
*/
private void append(StringBuffer result, String string,
private void append(StringBuf result, String string,
FieldDelegate delegate,
FieldPosition[] positions,
Format.Field signAttribute) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -36,10 +36,10 @@ class DontCareFieldPosition extends FieldPosition {
private final Format.FieldDelegate noDelegate = new Format.FieldDelegate() {
public void formatted(Format.Field attr, Object value, int start,
int end, StringBuffer buffer) {
int end, Format.StringBuf buffer) {
}
public void formatted(int fieldID, Format.Field attr, Object value,
int start, int end, StringBuffer buffer) {
int start, int end, Format.StringBuf buffer) {
}
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -290,7 +290,7 @@ public class FieldPosition {
private boolean encounteredField;
public void formatted(Format.Field attr, Object value, int start,
int end, StringBuffer buffer) {
int end, Format.StringBuf buffer) {
if (!encounteredField && matchesField(attr)) {
setBeginIndex(start);
setEndIndex(end);
@ -299,7 +299,7 @@ public class FieldPosition {
}
public void formatted(int fieldID, Format.Field attr, Object value,
int start, int end, StringBuffer buffer) {
int start, int end, Format.StringBuf buffer) {
if (!encounteredField && matchesField(attr, fieldID)) {
setBeginIndex(start);
setEndIndex(end);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -148,7 +148,8 @@ public abstract class Format implements Serializable, Cloneable {
}
/**
* Formats an object to produce a string. This is equivalent to
* Formats an object to produce a string.
* This method returns a string that would be equal to the string returned by
* <blockquote>
* {@link #format(Object, StringBuffer, FieldPosition) format}<code>(obj,
* new StringBuffer(), new FieldPosition(0)).toString();</code>
@ -160,8 +161,12 @@ public abstract class Format implements Serializable, Cloneable {
* object
*/
public final String format (Object obj) {
if ("java.text".equals(getClass().getPackageName())) {
return format(obj, StringBufFactory.of(), new FieldPosition(0)).toString();
} else {
return format(obj, new StringBuffer(), new FieldPosition(0)).toString();
}
}
/**
* Formats an object and appends the resulting text to a given string
@ -185,6 +190,12 @@ public abstract class Format implements Serializable, Cloneable {
StringBuffer toAppendTo,
FieldPosition pos);
StringBuf format(Object obj,
StringBuf toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("Subclasses should override this method");
}
/**
* Formats an Object producing an {@code AttributedCharacterIterator}.
* You can use the returned {@code AttributedCharacterIterator}
@ -394,7 +405,7 @@ public abstract class Format implements Serializable, Cloneable {
* NOT modify it.
*/
public void formatted(Format.Field attr, Object value, int start,
int end, StringBuffer buffer);
int end, StringBuf buffer);
/**
* Notified when a particular region of the String is formatted.
@ -408,6 +419,38 @@ public abstract class Format implements Serializable, Cloneable {
* NOT modify it.
*/
public void formatted(int fieldID, Format.Field attr, Object value,
int start, int end, StringBuffer buffer);
int start, int end, StringBuf buffer);
}
/**
* StringBuf is the minimal common interface of {@code StringBuffer} and {@code StringBuilder}.
* It is used by the various {@code Format} implementations as the internal string buffer.
*/
sealed interface StringBuf
permits StringBufFactory.StringBufferImpl, StringBufFactory.StringBuilderImpl {
int length();
String substring(int start, int end);
String substring(int start);
StringBuf append(char c);
StringBuf append(String str);
StringBuf append(int i);
StringBuf append(char[] str, int offset, int len);
StringBuf append(CharSequence s, int start, int end);
StringBuf append(StringBuffer sb);
boolean isProxyStringBuilder();
StringBuffer asStringBuffer();
StringBuilder asStringBuilder();
}
}

View File

@ -356,7 +356,7 @@ public final class ListFormat extends Format {
public String format(List<String> input) {
Objects.requireNonNull(input);
return format(input, new StringBuffer(),
return format(input, StringBufFactory.of(),
DontCareFieldPosition.INSTANCE).toString();
}
@ -381,6 +381,18 @@ public final class ListFormat extends Format {
Objects.requireNonNull(obj);
Objects.requireNonNull(toAppendTo);
return format(obj, StringBufFactory.of(toAppendTo)).asStringBuffer();
}
@Override
StringBuf format(Object obj, StringBuf toAppendTo, FieldPosition pos) {
Objects.requireNonNull(obj);
Objects.requireNonNull(toAppendTo);
return format(obj, toAppendTo);
}
private StringBuf format(Object obj, StringBuf toAppendTo) {
if (obj instanceof Object[] objs) {
return generateMessageFormat(objs).format(objs, toAppendTo, DontCareFieldPosition.INSTANCE);
} else if (obj instanceof List<?> objs) {

View File

@ -1027,12 +1027,13 @@ public class MessageFormat extends Format {
public final StringBuffer format(Object[] arguments, StringBuffer result,
FieldPosition pos)
{
return subformat(arguments, result, pos, null);
return subformat(arguments, StringBufFactory.of(result), pos, null).asStringBuffer();
}
/**
* Creates a MessageFormat with the given pattern and uses it
* to format the given arguments. This is equivalent to
* to format the given arguments.
* This method returns a string that would be equal to the string returned by
* <blockquote>
* <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
* </blockquote>
@ -1076,6 +1077,12 @@ public class MessageFormat extends Format {
public final StringBuffer format(Object arguments, StringBuffer result,
FieldPosition pos)
{
return subformat((Object[]) arguments, StringBufFactory.of(result), pos, null).asStringBuffer();
}
@Override
final StringBuf format(Object arguments, StringBuf result,
FieldPosition pos) {
return subformat((Object[]) arguments, result, pos, null);
}
@ -1116,7 +1123,7 @@ public class MessageFormat extends Format {
*/
public AttributedCharacterIterator formatToCharacterIterator(Object arguments) {
Objects.requireNonNull(arguments, "arguments must not be null");
StringBuffer result = new StringBuffer();
StringBuf result = StringBufFactory.of();
ArrayList<AttributedCharacterIterator> iterators = new ArrayList<>();
subformat((Object[]) arguments, result, null, iterators);
@ -1472,7 +1479,7 @@ public class MessageFormat extends Format {
* {@code arguments} array is not of the type
* expected by the format element(s) that use it.
*/
private StringBuffer subformat(Object[] arguments, StringBuffer result,
private StringBuf subformat(Object[] arguments, StringBuf result,
FieldPosition fp, List<AttributedCharacterIterator> characterIterators) {
// note: this implementation assumes a fast substring & index.
// if this is not true, would be better to append chars one by one.
@ -1582,9 +1589,9 @@ public class MessageFormat extends Format {
/**
* Convenience method to append all the characters in
* {@code iterator} to the StringBuffer {@code result}.
* {@code iterator} to the StringBuf {@code result}.
*/
private void append(StringBuffer result, CharacterIterator iterator) {
private void append(StringBuf result, CharacterIterator iterator) {
if (iterator.first() != CharacterIterator.DONE) {
char aChar;

View File

@ -315,6 +315,23 @@ public abstract class NumberFormat extends Format {
}
}
@Override
StringBuf format(Object number,
StringBuf toAppendTo,
FieldPosition pos) {
if (number instanceof Long || number instanceof Integer ||
number instanceof Short || number instanceof Byte ||
number instanceof AtomicInteger || number instanceof AtomicLong ||
(number instanceof BigInteger &&
((BigInteger) number).bitLength() < 64)) {
return format(((Number) number).longValue(), toAppendTo, pos);
} else if (number instanceof Number) {
return format(((Number) number).doubleValue(), toAppendTo, pos);
} else {
throw new IllegalArgumentException("Cannot format given Object as a Number");
}
}
/**
* {@inheritDoc Format}
*
@ -347,9 +364,14 @@ public abstract class NumberFormat extends Format {
if (result != null)
return result;
if ("java.text".equals(getClass().getPackageName())) {
return format(number, StringBufFactory.of(),
DontCareFieldPosition.INSTANCE).toString();
} else {
return format(number, new StringBuffer(),
DontCareFieldPosition.INSTANCE).toString();
}
}
/*
* fastFormat() is supposed to be implemented in concrete subclasses only.
@ -367,9 +389,14 @@ public abstract class NumberFormat extends Format {
* @see java.text.Format#format
*/
public final String format(long number) {
if ("java.text".equals(getClass().getPackageName())) {
return format(number, StringBufFactory.of(),
DontCareFieldPosition.INSTANCE).toString();
} else {
return format(number, new StringBuffer(),
DontCareFieldPosition.INSTANCE).toString();
}
}
/**
* Specialization of format.
@ -394,6 +421,12 @@ public abstract class NumberFormat extends Format {
StringBuffer toAppendTo,
FieldPosition pos);
StringBuf format(double number,
StringBuf toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("Subclasses should override this method");
}
/**
* Specialization of format.
*
@ -417,6 +450,12 @@ public abstract class NumberFormat extends Format {
StringBuffer toAppendTo,
FieldPosition pos);
StringBuf format(long number,
StringBuf toAppendTo,
FieldPosition pos) {
throw new UnsupportedOperationException("Subclasses should override this method");
}
/**
* Parses text from the beginning of the given string to produce a {@code Number}.
* <p>

View File

@ -968,11 +968,18 @@ public class SimpleDateFormat extends DateFormat {
FieldPosition pos)
{
pos.beginIndex = pos.endIndex = 0;
return format(date, StringBufFactory.of(toAppendTo), pos.getFieldDelegate()).asStringBuffer();
}
@Override
final StringBuf format(Date date, StringBuf toAppendTo,
FieldPosition pos) {
pos.beginIndex = pos.endIndex = 0;
return format(date, toAppendTo, pos.getFieldDelegate());
}
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date, StringBuffer toAppendTo,
private StringBuf format(Date date, StringBuf toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
@ -1024,7 +1031,7 @@ public class SimpleDateFormat extends DateFormat {
*/
@Override
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
StringBuffer sb = new StringBuffer();
StringBuf sb = StringBufFactory.of();
CharacterIteratorFieldDelegate delegate = new
CharacterIteratorFieldDelegate();
@ -1130,7 +1137,7 @@ public class SimpleDateFormat extends DateFormat {
* Private member function that does the real date/time formatting.
*/
private void subFormat(int patternCharIndex, int count,
FieldDelegate delegate, StringBuffer buffer,
FieldDelegate delegate, StringBuf buffer,
boolean useDateFormatSymbols)
{
int maxIntCount = Integer.MAX_VALUE;
@ -1320,7 +1327,11 @@ public class SimpleDateFormat extends DateFormat {
}
int num = (value / 60) * 100 + (value % 60);
CalendarUtils.sprintf0d(buffer, num, width);
if (buffer.isProxyStringBuilder()) {
CalendarUtils.sprintf0d(buffer.asStringBuilder(), num, width);
} else {
CalendarUtils.sprintf0d(buffer.asStringBuffer(), num, width);
}
break;
case PATTERN_ISO_ZONE: // 'X'
@ -1340,7 +1351,11 @@ public class SimpleDateFormat extends DateFormat {
value = -value;
}
CalendarUtils.sprintf0d(buffer, value / 60, 2);
if (buffer.isProxyStringBuilder()) {
CalendarUtils.sprintf0d(buffer.asStringBuilder(), value / 60, 2);
} else {
CalendarUtils.sprintf0d(buffer.asStringBuffer(), value / 60, 2);
}
if (count == 1) {
break;
}
@ -1348,7 +1363,11 @@ public class SimpleDateFormat extends DateFormat {
if (count == 3) {
buffer.append(':');
}
CalendarUtils.sprintf0d(buffer, value % 60, 2);
if (buffer.isProxyStringBuilder()) {
CalendarUtils.sprintf0d(buffer.asStringBuilder(), value % 60, 2);
} else {
CalendarUtils.sprintf0d(buffer.asStringBuffer(), value % 60, 2);
}
break;
default:
@ -1382,7 +1401,7 @@ public class SimpleDateFormat extends DateFormat {
/**
* Formats a number with the specified minimum and maximum number of digits.
*/
private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuf buffer)
{
// Optimization for 1, 2 and 4 digit numbers. This should
// cover most cases of formatting date/time related items.
@ -1425,7 +1444,17 @@ public class SimpleDateFormat extends DateFormat {
numberFormat.setMinimumIntegerDigits(minDigits);
numberFormat.setMaximumIntegerDigits(maxDigits);
if (buffer.isProxyStringBuilder()) {
//User can set numberFormat with a user-defined NumberFormat which
//not override format(long, StringBuf, FieldPosition).
if ("java.text".equals(numberFormat.getClass().getPackageName())) {
numberFormat.format((long) value, buffer, DontCareFieldPosition.INSTANCE);
} else {
buffer.append(numberFormat.format((long) value, new StringBuffer(), DontCareFieldPosition.INSTANCE));
}
} else {
numberFormat.format((long) value, buffer.asStringBuffer(), DontCareFieldPosition.INSTANCE);
}
}
@ -2565,5 +2594,4 @@ public class SimpleDateFormat extends DateFormat {
originalNumberFormat = numberFormat;
}
}
}

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.text;
import java.text.Format.StringBuf;
/**
* {@code StringBufFactory} creates implementations of {@code Format.StringBuf},
* which is an interface with the minimum overlap required to support {@code StringBuffer}
* and {@code StringBuilder} in {@code Format}. This allows for {@code StringBuilder} to be used
* in place of {@code StringBuffer} to provide performance benefits for JDK internal
* {@code Format} subclasses.
*/
final class StringBufFactory {
private StringBufFactory() {
}
static StringBuf of(StringBuffer sb) {
return new StringBufferImpl(sb);
}
static StringBuf of(StringBuilder sb) {
return new StringBuilderImpl(sb);
}
static StringBuf of() {
return new StringBuilderImpl();
}
final static class StringBufferImpl implements StringBuf {
private final StringBuffer sb;
StringBufferImpl(StringBuffer sb) {
this.sb = sb;
}
@Override
public int length() {
return sb.length();
}
@Override
public String substring(int start, int end) {
return sb.substring(start, end);
}
@Override
public String substring(int start) {
return sb.substring(start);
}
@Override
public StringBuf append(char c) {
sb.append(c);
return this;
}
@Override
public StringBuf append(String str) {
sb.append(str);
return this;
}
@Override
public StringBuf append(int i) {
sb.append(i);
return this;
}
@Override
public StringBuf append(char[] str, int offset, int len) {
sb.append(str, offset, len);
return this;
}
@Override
public StringBuf append(CharSequence s, int start, int end) {
sb.append(s, start, end);
return this;
}
@Override
public StringBuf append(StringBuffer asb) {
sb.append(asb);
return this;
}
@Override
public boolean isProxyStringBuilder() {
return false;
}
@Override
public StringBuffer asStringBuffer() {
return sb;
}
@Override
public StringBuilder asStringBuilder() {
throw new AssertionError("Can't cast StringBuffer to StringBuilder");
}
@Override
public String toString() {
return sb.toString();
}
}
final static class StringBuilderImpl implements StringBuf {
private final StringBuilder sb;
StringBuilderImpl(StringBuilder sb) {
this.sb = sb;
}
StringBuilderImpl() {
this.sb = new StringBuilder();
}
@Override
public int length() {
return sb.length();
}
@Override
public String substring(int start, int end) {
return sb.substring(start, end);
}
@Override
public String substring(int start) {
return sb.substring(start);
}
@Override
public StringBuf append(char c) {
sb.append(c);
return this;
}
@Override
public StringBuf append(String str) {
sb.append(str);
return this;
}
@Override
public StringBuf append(int i) {
sb.append(i);
return this;
}
@Override
public StringBuf append(char[] str, int offset, int len) {
sb.append(str, offset, len);
return this;
}
@Override
public StringBuf append(CharSequence s, int start, int end) {
sb.append(s, start, end);
return this;
}
@Override
public StringBuf append(StringBuffer asb) {
sb.append(asb);
return this;
}
@Override
public boolean isProxyStringBuilder() {
return true;
}
@Override
public StringBuffer asStringBuffer() {
throw new AssertionError("Can't cast StringBuilder to StringBuffer");
}
@Override
public StringBuilder asStringBuilder() {
return sb;
}
@Override
public String toString() {
return sb.toString();
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.text;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
@State(Scope.Benchmark)
public class DateFormatterBench {
private Date date;
private Object objDate;
@Setup
public void setup() {
date = new Date();
objDate = new Date();
}
private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, Locale.ENGLISH);
@Benchmark
public String testFormatDate() {
return dateFormat.format(date);
}
@Benchmark
public String testFormatObject() {
return dateFormat.format(objDate);
}
public static void main(String... args) throws Exception {
Options opts = new OptionsBuilder().include(DateFormatterBench.class.getSimpleName()).shouldDoGC(true).build();
new Runner(opts).run();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.text;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.text.ListFormat;
import java.util.List;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
@State(Scope.Benchmark)
public class ListFormatterBench {
private List<String> data;
@Setup
public void setup() {
data = List.of("foo", "bar", "baz", "qux", "quux", "quuz");
}
private ListFormat listFormat = ListFormat.getInstance();
@Benchmark
public String testListFormat() {
return listFormat.format(data);
}
public static void main(String... args) throws Exception {
Options opts = new OptionsBuilder().include(ListFormatterBench.class.getSimpleName()).shouldDoGC(true).build();
new Runner(opts).run();
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.text;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(3)
@State(Scope.Benchmark)
public class MessageFormatterBench {
private Object[][] values;
@Setup
public void setup() {
values = new Object[][]{
new Object[]{Integer.valueOf(13), "MyDisk1"},
new Object[]{Float.valueOf(25.6f), "MyDisk2"},
new Object[]{Double.valueOf(123.89), "MyDisk3"},
new Object[]{Long.valueOf(1234567), "MyDisk4"},
};
}
private MessageFormat messageFormat = new MessageFormat("There is {0} GB of free space on the {1}.", Locale.ENGLISH);
@Benchmark
@OperationsPerInvocation(4)
public void testMessageFormat(final Blackhole bh) {
for (Object[] value : values) {
bh.consume(messageFormat.format(value));
}
}
public static void main(String... args) throws Exception {
Options opts = new OptionsBuilder().include(MessageFormatterBench.class.getSimpleName()).shouldDoGC(true).build();
new Runner(opts).run();
}
}