mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Support o and x format specifiers in String.format
This commit is contained in:
parent
4ebaf476fe
commit
96b2d4e7d6
|
@ -21,14 +21,14 @@ public final class IntegerUtil {
|
||||||
private IntegerUtil() {
|
private IntegerUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toUnsignedLogRadixString(int value, int radixLog2, int size) {
|
public static String toUnsignedLogRadixString(int value, int radixLog2) {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
int radix = 1 << radixLog2;
|
int radix = 1 << radixLog2;
|
||||||
int mask = radix - 1;
|
int mask = radix - 1;
|
||||||
int sz = (size - Integer.numberOfLeadingZeros(value) + radixLog2 - 1) / radixLog2;
|
int sz = (Integer.SIZE - Integer.numberOfLeadingZeros(value) + radixLog2 - 1) / radixLog2;
|
||||||
char[] chars = new char[sz];
|
char[] chars = new char[sz];
|
||||||
|
|
||||||
int pos = (sz - 1) * radixLog2;
|
int pos = (sz - 1) * radixLog2;
|
||||||
|
@ -40,4 +40,24 @@ public final class IntegerUtil {
|
||||||
|
|
||||||
return new String(chars);
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String toUnsignedLogRadixString(long value, int radixLog2) {
|
||||||
|
if (value == 0) {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
int radix = 1 << radixLog2;
|
||||||
|
int mask = radix - 1;
|
||||||
|
int sz = (Long.SIZE - Long.numberOfLeadingZeros(value) + radixLog2 - 1) / radixLog2;
|
||||||
|
char[] chars = new char[sz];
|
||||||
|
|
||||||
|
long pos = (sz - 1) * radixLog2;
|
||||||
|
int target = 0;
|
||||||
|
while (pos >= 0) {
|
||||||
|
chars[target++] = TCharacter.forDigit((int) (value >>> pos) & mask, radix);
|
||||||
|
pos -= radixLog2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,15 +41,15 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toHexString(int i) {
|
public static String toHexString(int i) {
|
||||||
return toUnsignedLogRadixString(i, 4, SIZE);
|
return toUnsignedLogRadixString(i, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toOctalString(int i) {
|
public static String toOctalString(int i) {
|
||||||
return toUnsignedLogRadixString(i, 3, SIZE);
|
return toUnsignedLogRadixString(i, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toBinaryString(int i) {
|
public static String toBinaryString(int i) {
|
||||||
return toUnsignedLogRadixString(i, 1, SIZE);
|
return toUnsignedLogRadixString(i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toString(int i) {
|
public static String toString(int i) {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
||||||
|
|
||||||
public class TLong extends TNumber implements TComparable<TLong> {
|
public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
public static final long MIN_VALUE = -0x8000000000000000L;
|
public static final long MIN_VALUE = -0x8000000000000000L;
|
||||||
public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL;
|
public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL;
|
||||||
|
@ -183,26 +185,6 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
return toUnsignedLogRadixString(i, 1);
|
return toUnsignedLogRadixString(i, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toUnsignedLogRadixString(long value, int radixLog2) {
|
|
||||||
if (value == 0) {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
|
|
||||||
int radix = 1 << radixLog2;
|
|
||||||
int mask = radix - 1;
|
|
||||||
int sz = (SIZE - numberOfLeadingZeros(value) + radixLog2 - 1) / radixLog2;
|
|
||||||
char[] chars = new char[sz];
|
|
||||||
|
|
||||||
long pos = (sz - 1) * radixLog2;
|
|
||||||
int target = 0;
|
|
||||||
while (pos >= 0) {
|
|
||||||
chars[target++] = TCharacter.forDigit((int) (value >>> pos) & mask, radix);
|
|
||||||
pos -= radixLog2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new String(chars);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String toString(long value) {
|
public static String toString(long value) {
|
||||||
return new TStringBuilder().append(value).toString();
|
return new TStringBuilder().append(value).toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.util.FormatFlagsConversionMismatchException;
|
||||||
import java.util.IllegalFormatConversionException;
|
import java.util.IllegalFormatConversionException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.UnknownFormatConversionException;
|
import java.util.UnknownFormatConversionException;
|
||||||
|
import org.teavm.classlib.impl.IntegerUtil;
|
||||||
|
|
||||||
public final class TFormatter implements Closeable, Flushable {
|
public final class TFormatter implements Closeable, Flushable {
|
||||||
private Locale locale;
|
private Locale locale;
|
||||||
|
@ -149,6 +150,8 @@ public final class TFormatter implements Closeable, Flushable {
|
||||||
| TFormattableFlags.LEADING_SPACE | TFormattableFlags.ZERO_PADDED
|
| TFormattableFlags.LEADING_SPACE | TFormattableFlags.ZERO_PADDED
|
||||||
| TFormattableFlags.PARENTHESIZED_NEGATIVE | TFormattableFlags.SIGNED
|
| TFormattableFlags.PARENTHESIZED_NEGATIVE | TFormattableFlags.SIGNED
|
||||||
| TFormattableFlags.GROUPING_SEPARATOR;
|
| TFormattableFlags.GROUPING_SEPARATOR;
|
||||||
|
private static final int MASK_FOR_INT_RADIX_FORMAT = MASK_FOR_GENERAL_FORMAT
|
||||||
|
| TFormattableFlags.ZERO_PADDED | TFormattableFlags.PARENTHESIZED_NEGATIVE;
|
||||||
private TFormatter formatter;
|
private TFormatter formatter;
|
||||||
Appendable out;
|
Appendable out;
|
||||||
Locale locale;
|
Locale locale;
|
||||||
|
@ -223,6 +226,20 @@ public final class TFormatter implements Closeable, Flushable {
|
||||||
formatDecimalInt(specifier, true);
|
formatDecimalInt(specifier, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
formatRadixInt(specifier, 3, false);
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
formatRadixInt(specifier, 3, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
formatRadixInt(specifier, 4, false);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
formatRadixInt(specifier, 4, true);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new UnknownFormatConversionException(String.valueOf(specifier));
|
throw new UnknownFormatConversionException(String.valueOf(specifier));
|
||||||
}
|
}
|
||||||
|
@ -288,18 +305,7 @@ public final class TFormatter implements Closeable, Flushable {
|
||||||
|
|
||||||
private void formatDecimalInt(char specifier, boolean upperCase) throws IOException {
|
private void formatDecimalInt(char specifier, boolean upperCase) throws IOException {
|
||||||
verifyFlags(specifier, MASK_FOR_INT_DECIMAL_FORMAT);
|
verifyFlags(specifier, MASK_FOR_INT_DECIMAL_FORMAT);
|
||||||
if ((flags & TFormattableFlags.SIGNED) != 0 && (flags & TFormattableFlags.LEADING_SPACE) != 0) {
|
verifyIntFlags();
|
||||||
throw new TIllegalFormatFlagsException("+ ");
|
|
||||||
}
|
|
||||||
if ((flags & TFormattableFlags.ZERO_PADDED) != 0 && (flags & TFormattableFlags.LEFT_JUSTIFY) != 0) {
|
|
||||||
throw new TIllegalFormatFlagsException("0-");
|
|
||||||
}
|
|
||||||
if (precision >= 0) {
|
|
||||||
throw new TIllegalFormatPrecisionException(precision);
|
|
||||||
}
|
|
||||||
if ((flags & TFormattableFlags.LEFT_JUSTIFY) != 0 && width < 0) {
|
|
||||||
throw new TMissingFormatWidthException(format.substring(formatSpecifierStart, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
String str;
|
String str;
|
||||||
Object arg = args[argumentIndex];
|
Object arg = args[argumentIndex];
|
||||||
|
@ -371,6 +377,55 @@ public final class TFormatter implements Closeable, Flushable {
|
||||||
formatGivenString(upperCase, sb.toString());
|
formatGivenString(upperCase, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void formatRadixInt(char specifier, int radixLog2, boolean upperCase) throws IOException {
|
||||||
|
verifyFlags(specifier, MASK_FOR_INT_RADIX_FORMAT);
|
||||||
|
verifyIntFlags();
|
||||||
|
|
||||||
|
String str;
|
||||||
|
Object arg = args[argumentIndex];
|
||||||
|
if (arg instanceof Long) {
|
||||||
|
str = IntegerUtil.toUnsignedLogRadixString((Long) arg, radixLog2);
|
||||||
|
} else if (arg instanceof Integer) {
|
||||||
|
str = IntegerUtil.toUnsignedLogRadixString((Integer) arg, radixLog2);
|
||||||
|
} else if (arg instanceof Short) {
|
||||||
|
str = IntegerUtil.toUnsignedLogRadixString((Short) arg & 0xFFFF, radixLog2);
|
||||||
|
} else if (arg instanceof Byte) {
|
||||||
|
str = IntegerUtil.toUnsignedLogRadixString((Byte) arg & 0xFF, radixLog2);
|
||||||
|
} else {
|
||||||
|
throw new IllegalFormatConversionException(specifier, arg != null ? arg.getClass() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if ((flags & TFormattableFlags.ALTERNATE) != 0) {
|
||||||
|
String prefix = radixLog2 == 4 ? "0x" : "0";
|
||||||
|
str = prefix + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & TFormattableFlags.ZERO_PADDED) != 0) {
|
||||||
|
for (int i = str.length(); i < width; ++i) {
|
||||||
|
sb.append(Character.forDigit(0, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(str);
|
||||||
|
|
||||||
|
formatGivenString(upperCase, sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyIntFlags() {
|
||||||
|
if ((flags & TFormattableFlags.SIGNED) != 0 && (flags & TFormattableFlags.LEADING_SPACE) != 0) {
|
||||||
|
throw new TIllegalFormatFlagsException("+ ");
|
||||||
|
}
|
||||||
|
if ((flags & TFormattableFlags.ZERO_PADDED) != 0 && (flags & TFormattableFlags.LEFT_JUSTIFY) != 0) {
|
||||||
|
throw new TIllegalFormatFlagsException("0-");
|
||||||
|
}
|
||||||
|
if (precision >= 0) {
|
||||||
|
throw new TIllegalFormatPrecisionException(precision);
|
||||||
|
}
|
||||||
|
if ((flags & TFormattableFlags.LEFT_JUSTIFY) != 0 && width < 0) {
|
||||||
|
throw new TMissingFormatWidthException(format.substring(formatSpecifierStart, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void formatGivenString(boolean upperCase, String str) throws IOException {
|
private void formatGivenString(boolean upperCase, String str) throws IOException {
|
||||||
if (precision > 0) {
|
if (precision > 0) {
|
||||||
str = str.substring(0, precision);
|
str = str.substring(0, precision);
|
||||||
|
|
|
@ -190,4 +190,64 @@ public class FormatterTest {
|
||||||
assertEquals(2, e.getPrecision());
|
assertEquals(2, e.getPrecision());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatsOctalInteger() {
|
||||||
|
assertEquals("1 2 3 4", new Formatter().format("%o %o %o %o", (byte) 1, (short) 2, 3, 4L).toString());
|
||||||
|
|
||||||
|
assertEquals("00027", new Formatter().format("%05o", 23).toString());
|
||||||
|
assertEquals("0173", new Formatter().format("%#o", 123).toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%-01o", 23);
|
||||||
|
fail("Should have thrown exception 1");
|
||||||
|
} catch (IllegalFormatFlagsException e) {
|
||||||
|
assertTrue(e.getFlags().contains("-"));
|
||||||
|
assertTrue(e.getFlags().contains("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%-o", 23);
|
||||||
|
fail("Should have thrown exception 2");
|
||||||
|
} catch (MissingFormatWidthException e) {
|
||||||
|
assertTrue(e.getFormatSpecifier().contains("o"));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%1.2o", 23);
|
||||||
|
fail("Should have thrown exception 3");
|
||||||
|
} catch (IllegalFormatPrecisionException e) {
|
||||||
|
assertEquals(2, e.getPrecision());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatsHexInteger() {
|
||||||
|
assertEquals("1 2 3 4", new Formatter().format("%x %x %x %x", (byte) 1, (short) 2, 3, 4L).toString());
|
||||||
|
|
||||||
|
assertEquals("00017", new Formatter().format("%05x", 23).toString());
|
||||||
|
assertEquals("0x7b", new Formatter().format("%#x", 123).toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%-01x", 23);
|
||||||
|
fail("Should have thrown exception 1");
|
||||||
|
} catch (IllegalFormatFlagsException e) {
|
||||||
|
assertTrue(e.getFlags().contains("-"));
|
||||||
|
assertTrue(e.getFlags().contains("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%-x", 23);
|
||||||
|
fail("Should have thrown exception 2");
|
||||||
|
} catch (MissingFormatWidthException e) {
|
||||||
|
assertTrue(e.getFormatSpecifier().contains("x"));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Formatter().format("%1.2x", 23);
|
||||||
|
fail("Should have thrown exception 3");
|
||||||
|
} catch (IllegalFormatPrecisionException e) {
|
||||||
|
assertEquals(2, e.getPrecision());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user