Merge branch 'decimal-format'

This commit is contained in:
Alexey Andreev 2015-06-17 19:15:18 +04:00
commit c38c07becc
22 changed files with 2386 additions and 87 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014 Alexey Andreev.
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,62 +15,59 @@
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev
*/
public interface CLDRDecimalData extends Resource {
int getMaximumFractionDigits();
public class CLDRDecimalData {
int groupingSeparator;
int decimalSeparator;
int listSeparator;
int perMille;
int percent;
String NaN;
String infinity;
int minusSign;
int monetaryDecimalSeparator;
String exponentSeparator;
void setMaximumFractionDigits(int value);
public int getGroupingSeparator() {
return groupingSeparator;
}
int getMaximumIntegerDigits();
public int getDecimalSeparator() {
return decimalSeparator;
}
void setMaximumIntegerDigits(int value);
public int getListSeparator() {
return listSeparator;
}
int getMinimumFractionDigits();
public int getPerMille() {
return perMille;
}
void setMinimumFractionDigits(int value);
public int getPercent() {
return percent;
}
int getMinimumIntegerDigits();
public String getNaN() {
return NaN;
}
void setMinimumIntegerDigits(int value);
public String getInfinity() {
return infinity;
}
int getGroupingSeparator();
public int getMinusSign() {
return minusSign;
}
void setGroupingSeparator(int value);
public int getMonetaryDecimalSeparator() {
return monetaryDecimalSeparator;
}
int getDecimalSeparator();
void setDecimalSeparator(int value);
int getPerMill();
void setPerMill(int value);
int getPercent();
void setPercent(int value);
String getNaN();
void setNaN(String nan);
String getInfinity();
void setInfinity(String infinity);
int getMinusSign();
void setMinusSign(int value);
int getMonetaryDecimalSeparator();
void setMonetaryDecimalSeparator(int value);
String getExponentSeparator();
void setExponentSeparator(String value);
public String getExponentSeparator() {
return exponentSeparator;
}
}

View File

@ -200,14 +200,15 @@ public class CLDRHelper {
return res.getValue();
}
public static CLDRDecimalData resolveDecimalData(String language, String country) {
ResourceMap<CLDRDecimalData> map = getDecimalDataMap();
public static DecimalData resolveDecimalData(String language, String country) {
ResourceMap<DecimalData> map = getDecimalDataMap();
String localeCode = getCode(language, country);
return map.has(localeCode) ? map.get(localeCode) : map.has(language) ? map.get(language) :
map.get("root");
}
private static native ResourceMap<CLDRDecimalData> getDecimalDataMap();
@MetadataProvider(DecimalMetadataGenerator.class)
private static native ResourceMap<DecimalData> getDecimalDataMap();
public static CurrencyLocalization resolveCurrency(String language, String country, String currency) {
String localeCode = getCode(language, country);

View File

@ -38,6 +38,7 @@ public class CLDRLocale {
CLDRDateFormats timeFormats;
CLDRDateFormats dateTimeFormats;
CLDRTimeZone[] timeZones;
CLDRDecimalData decimalData = new CLDRDecimalData();
public Map<String, String> getLanguages() {
return Collections.unmodifiableMap(languages);
@ -90,4 +91,8 @@ public class CLDRLocale {
public CLDRTimeZone[] getTimeZones() {
return timeZones.clone();
}
public CLDRDecimalData getDecimalData() {
return decimalData;
}
}

View File

@ -127,9 +127,12 @@ public class CLDRReader {
readDateTimeFormats(localeName, localeInfo, root);
break;
}
case "currencies.json": {
case "currencies.json":
readCurrencies(localeName, localeInfo, input);
}
break;
case "numbers.json":
readNumbers(localeName, localeInfo, input);
break;
}
}
} catch (IOException e) {
@ -207,6 +210,23 @@ public class CLDRReader {
}
}
private void readNumbers(String localeCode, CLDRLocale locale, InputStream input) {
JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input));
JsonObject numbersJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("numbers").getAsJsonObject();
String numbering = numbersJson.get("defaultNumberingSystem").getAsString();
JsonObject symbolsJson = numbersJson.get("symbols-numberSystem-" + numbering).getAsJsonObject();
locale.decimalData.decimalSeparator = symbolsJson.get("decimal").getAsString().charAt(0);
locale.decimalData.groupingSeparator = symbolsJson.get("group").getAsString().charAt(0);
locale.decimalData.listSeparator = symbolsJson.get("list").getAsString().charAt(0);
locale.decimalData.percent = symbolsJson.get("percentSign").getAsString().charAt(0);
locale.decimalData.minusSign = symbolsJson.get("minusSign").getAsString().charAt(0);
locale.decimalData.exponentSeparator = symbolsJson.get("exponential").getAsString();
locale.decimalData.perMille = symbolsJson.get("perMille").getAsString().charAt(0);
locale.decimalData.infinity = symbolsJson.get("infinity").getAsString();
locale.decimalData.NaN = symbolsJson.get("nan").getAsString();
}
private void readEras(String localeCode, CLDRLocale locale, JsonObject root) {
JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject()
.get("dates").getAsJsonObject().get("calendars").getAsJsonObject()

View File

@ -0,0 +1,60 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl.unicode;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev
*/
public interface DecimalData extends Resource {
int getGroupingSeparator();
void setGroupingSeparator(int value);
int getDecimalSeparator();
void setDecimalSeparator(int value);
int getListSeparator();
void setListSeparator(int value);
int getPerMille();
void setPerMille(int value);
int getPercent();
void setPercent(int value);
String getNaN();
void setNaN(String nan);
String getInfinity();
void setInfinity(String infinity);
int getMinusSign();
void setMinusSign(int value);
String getExponentSeparator();
void setExponentSeparator(String value);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl.unicode;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataGeneratorContext;
import org.teavm.platform.metadata.Resource;
import org.teavm.platform.metadata.ResourceMap;
/**
*
* @author Alexey Andreev
*/
public class DecimalMetadataGenerator implements MetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
CLDRReader reader = context.getService(CLDRReader.class);
ResourceMap<DecimalData> map = context.createResourceMap();
for (Map.Entry<String, CLDRLocale> entry : reader.getKnownLocales().entrySet()) {
CLDRDecimalData data = entry.getValue().getDecimalData();
DecimalData dataRes = context.createResource(DecimalData.class);
dataRes.setDecimalSeparator(data.getDecimalSeparator());
dataRes.setExponentSeparator(data.getExponentSeparator());
dataRes.setGroupingSeparator(data.getGroupingSeparator());
dataRes.setInfinity(data.getInfinity());
dataRes.setListSeparator(data.getListSeparator());
dataRes.setMinusSign(data.getMinusSign());
dataRes.setNaN(data.getNaN());
dataRes.setPercent(data.getPercent());
dataRes.setPerMille(data.getPerMille());
map.put(entry.getKey(), dataRes);
}
return map;
}
}

View File

@ -169,7 +169,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
return mantissa * decimalExponent(exp);
}
private static double decimalExponent(int n) {
public static double decimalExponent(int n) {
double d;
if (n < 0) {
d = 0.1;

View File

@ -0,0 +1,304 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.text;
import java.util.ArrayList;
import java.util.List;
import org.teavm.classlib.java.text.TDecimalFormat.FormatField;
/**
*
* @author Alexey Andreev
*/
class TDecimalFormatParser {
private FormatField[] positivePrefix;
private FormatField[] positiveSuffix;
private FormatField[] negativePrefix;
private FormatField[] negativeSuffix;
private int groupSize;
private int minimumIntLength;
private int intLength;
private int minimumFracLength;
private int fracLength;
private int exponentLength;
private boolean decimalSeparatorRequired;
private String string;
private int index;
private int multiplier;
public void parse(String string) {
groupSize = 0;
minimumFracLength = 0;
fracLength = 0;
exponentLength = 0;
decimalSeparatorRequired = false;
multiplier = 1;
this.string = string;
index = 0;
positivePrefix = parseText(false, false);
if (index == string.length()) {
throw new IllegalArgumentException("Positive number pattern not found in " + string);
}
parseNumber(true);
negativePrefix = null;
negativeSuffix = null;
if (index < string.length() && string.charAt(index) != ';') {
positiveSuffix = parseText(true, false);
}
if (index < string.length()) {
if (string.charAt(index++) != ';') {
throw new IllegalArgumentException("Expected ';' at " + index + " in " + string);
}
negativePrefix = parseText(false, true);
parseNumber(false);
negativeSuffix = parseText(true, true);
}
}
public void apply(TDecimalFormat format) {
format.positivePrefix = positivePrefix;
format.positiveSuffix = positiveSuffix;
if (negativePrefix != null) {
format.negativePrefix = negativePrefix;
} else {
format.negativePrefix = new FormatField[positivePrefix.length + 1];
System.arraycopy(positivePrefix, 0, format.negativePrefix, 1, positivePrefix.length);
format.negativePrefix[0] = new TDecimalFormat.MinusField();
}
format.negativeSuffix = negativeSuffix != null ? negativeSuffix : positiveSuffix;
format.setGroupingSize(groupSize);
format.setGroupingUsed(groupSize > 0);
format.setMinimumIntegerDigits(!decimalSeparatorRequired ? minimumIntLength :
Math.max(1, minimumIntLength));
format.setMaximumIntegerDigits(intLength);
format.setMinimumFractionDigits(minimumFracLength);
format.setMaximumFractionDigits(fracLength);
format.setDecimalSeparatorAlwaysShown(decimalSeparatorRequired);
format.exponentDigits = exponentLength;
format.setMultiplier(multiplier);
}
FormatField[] parseText(boolean suffix, boolean end) {
List<FormatField> fields = new ArrayList<>();
StringBuilder sb = new StringBuilder();
loop: while (index < string.length()) {
char c = string.charAt(index);
switch (c) {
case '#':
case '0':
if (suffix) {
throw new IllegalArgumentException("Prefix contains special character at " + index + " in " +
string);
}
break loop;
case ';':
if (end) {
throw new IllegalArgumentException("Prefix contains special character at " + index + " in " +
string);
}
break loop;
case '.':
case 'E':
throw new IllegalArgumentException("Prefix contains special character at " + index + " in " +
string);
case '\'': {
++index;
int next = string.indexOf('\'', index);
if (next < 0) {
throw new IllegalArgumentException("Quote opened at " + index + " was not closed in " +
string);
}
if (next == index) {
sb.append('\'');
} else {
sb.append(string.substring(index, next));
}
index = next + 1;
break;
}
// Currency symbol ¤
case '\u00A4':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.CurrencyField());
++index;
break;
case '%':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.PercentField());
++index;
multiplier = 100;
break;
// Per mill symbol
case '\u2030':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.PerMillField());
++index;
multiplier = 1000;
break;
case '-':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.MinusField());
++index;
break;
default:
sb.append(c);
++index;
break;
}
}
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
}
return fields.toArray(new FormatField[fields.size()]);
}
private void parseNumber(boolean apply) {
parseIntegerPart(apply);
if (index < string.length() && string.charAt(index) == '.') {
++index;
parseFractionalPart(apply);
}
if (index < string.length() && string.charAt(index) == 'E') {
++index;
parseExponent(apply);
}
}
private void parseIntegerPart(boolean apply) {
int start = index;
int lastGroup = index;
boolean optionalDigits = true;
int length = 0;
int minimumLength = 0;
loop: while (index < string.length()) {
switch (string.charAt(index)) {
case '#':
if (!optionalDigits) {
throw new IllegalArgumentException("Unexpected '#' at non-optional digit part at " + index +
" in " + string);
}
++length;
break;
case ',':
if (lastGroup == index) {
throw new IllegalArgumentException("Two group separators at " + index + " in " + string);
}
if (apply) {
groupSize = index - lastGroup;
}
lastGroup = index + 1;
break;
case '0':
optionalDigits = false;
++length;
++minimumLength;
break;
default:
break loop;
}
++index;
}
if (length == 0) {
throw new IllegalArgumentException("Pattern does not specify integer digits at " + index +
" in " + string);
}
if (lastGroup == index) {
throw new IllegalArgumentException("Group separator at the end of number at " + index + " in " + string);
}
if (apply && lastGroup > start) {
groupSize = index - lastGroup;
}
if (apply) {
intLength = length;
minimumIntLength = minimumLength;
}
}
private void parseFractionalPart(boolean apply) {
boolean optionalDigits = false;
int length = 0;
int minimumLength = 0;
loop: while (index < string.length()) {
switch (string.charAt(index)) {
case '#':
++length;
optionalDigits = true;
break;
case ',':
throw new IllegalArgumentException("Group separator found at fractional part at " + index +
" in " + string);
case '0':
if (optionalDigits) {
throw new IllegalArgumentException("Unexpected '0' at optional digit part at " + index +
" in " + string);
}
++length;
++minimumLength;
break;
case '.':
throw new IllegalArgumentException("Unexpected second decimal separator at " + index +
" in " + string);
default:
break loop;
}
++index;
}
if (apply) {
fracLength = length;
minimumFracLength = minimumLength;
decimalSeparatorRequired = length == 0;
}
}
private void parseExponent(boolean apply) {
int length = 0;
loop: while (index < string.length()) {
switch (string.charAt(index)) {
case '#':
case ',':
case '.':
case 'E':
throw new IllegalArgumentException("Unexpected char at exponent at " + index +
" in " + string);
case '0':
++length;
break;
default:
break loop;
}
++index;
}
if (length == 0) {
throw new IllegalArgumentException("Pattern does not specify exponent digits at " + index +
" in " + string);
}
if (apply) {
exponentLength = length;
}
}
}

View File

@ -15,7 +15,7 @@
*/
package org.teavm.classlib.java.text;
import org.teavm.classlib.impl.unicode.CLDRDecimalData;
import org.teavm.classlib.impl.unicode.DecimalData;
import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.java.util.TLocale;
@ -23,7 +23,7 @@ import org.teavm.classlib.java.util.TLocale;
*
* @author Alexey Andreev
*/
public class TDecimalFormatSymbols {
public class TDecimalFormatSymbols implements Cloneable {
private TLocale locale;
private char zeroDigit;
private char groupingSeparator;
@ -48,18 +48,18 @@ public class TDecimalFormatSymbols {
}
private void initData() {
CLDRDecimalData data = CLDRHelper.resolveDecimalData(locale.getLanguage(), locale.getCountry());
DecimalData data = CLDRHelper.resolveDecimalData(locale.getLanguage(), locale.getCountry());
zeroDigit = '0';
groupingSeparator = (char)data.getGroupingSeparator();
decimalSeparator = (char)data.getDecimalSeparator();
perMill = (char)data.getPerMill();
perMill = (char)data.getPerMille();
percent = (char)data.getPercent();
digit = '#';
patternSeparator = ';';
NaN = data.getNaN();
infinity = data.getInfinity();
minusSign = (char)data.getMinusSign();
monetaryDecimalSeparator = (char)data.getMonetaryDecimalSeparator();
monetaryDecimalSeparator = (char)data.getDecimalSeparator();
exponentSeparator = data.getExponentSeparator();
}

View File

@ -15,7 +15,10 @@
*/
package org.teavm.classlib.java.text;
import java.util.Objects;
import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.java.math.TRoundingMode;
import org.teavm.classlib.java.util.TCurrency;
import org.teavm.classlib.java.util.TLocale;
/**
@ -25,9 +28,11 @@ import org.teavm.classlib.java.util.TLocale;
public abstract class TNumberFormat extends TFormat {
public static final int INTEGER_FIELD = 0;
public static final int FRACTION_FIELD = 1;
private boolean groupingUsed = true, parseIntegerOnly = false;
private boolean groupingUsed = true, parseIntegerOnly;
private int maximumIntegerDigits = 40, minimumIntegerDigits = 1,
maximumFractionDigits = 3, minimumFractionDigits = 0;
private TRoundingMode roundingMode = TRoundingMode.HALF_EVEN;
TCurrency currency = TCurrency.getInstance(TLocale.getDefault());
public TNumberFormat() {
}
@ -37,6 +42,14 @@ public abstract class TNumberFormat extends TFormat {
return super.clone();
}
public TCurrency getCurrency() {
return currency;
}
public void setCurrency(TCurrency currency) {
this.currency = currency;
}
@Override
public boolean equals(Object object) {
if (object == this) {
@ -51,7 +64,9 @@ public abstract class TNumberFormat extends TFormat {
&& maximumFractionDigits == obj.maximumFractionDigits
&& maximumIntegerDigits == obj.maximumIntegerDigits
&& minimumFractionDigits == obj.minimumFractionDigits
&& minimumIntegerDigits == obj.minimumIntegerDigits;
&& minimumIntegerDigits == obj.minimumIntegerDigits
&& roundingMode == obj.roundingMode
&& currency == obj.currency;
}
public final String format(double value) {
@ -140,7 +155,8 @@ public abstract class TNumberFormat extends TFormat {
public int hashCode() {
return (groupingUsed ? 1231 : 1237) + (parseIntegerOnly ? 1231 : 1237)
+ maximumFractionDigits + maximumIntegerDigits
+ minimumFractionDigits + minimumIntegerDigits;
+ minimumFractionDigits + minimumIntegerDigits +
roundingMode.hashCode() + Objects.hashCode(currency);
}
public boolean isGroupingUsed() {
@ -211,6 +227,14 @@ public abstract class TNumberFormat extends TFormat {
parseIntegerOnly = value;
}
public TRoundingMode getRoundingMode() {
return roundingMode;
}
public void setRoundingMode(TRoundingMode roundingMode) {
this.roundingMode = roundingMode;
}
public static class Field extends TFormat.Field {
public static final Field SIGN = new Field("sign");
public static final Field INTEGER = new Field("integer");

View File

@ -20,7 +20,7 @@ package org.teavm.classlib.java.util;
* @author shannah
*/
public class TObservable {
TList<TObserver> observers = new TArrayList<TObserver>();
TList<TObserver> observers = new TArrayList<>();
boolean changed = false;
@ -34,7 +34,7 @@ public class TObservable {
/**
* Adds the specified observer to the list of observers. If it is already
* registered, it is not added a second time.
*
*
* @param observer
* the Observer to add.
*/
@ -58,7 +58,7 @@ public class TObservable {
/**
* Returns the number of observers registered to this {@code Observable}.
*
*
* @return the number of observers.
*/
public int countObservers() {
@ -68,7 +68,7 @@ public class TObservable {
/**
* Removes the specified observer from the list of observers. Passing null
* won't do anything.
*
*
* @param observer
* the observer to remove.
*/
@ -85,7 +85,7 @@ public class TObservable {
/**
* Returns the changed flag for this {@code Observable}.
*
*
* @return {@code true} when the changed flag for this {@code Observable} is
* set, {@code false} otherwise.
*/
@ -108,11 +108,10 @@ public class TObservable {
* If {@code hasChanged()} returns {@code true}, calls the {@code update()}
* method for every Observer in the list of observers using the specified
* argument. Afterwards calls {@code clearChanged()}.
*
*
* @param data
* the argument passed to {@code update()}.
*/
@SuppressWarnings("unchecked")
public void notifyObservers(Object data) {
int size = 0;
TObserver[] arrays = null;

View File

@ -65,6 +65,10 @@ public final class TeaVMTestRunner {
.withDescription("qualified class names of transformers")
.withLongOpt("transformers")
.create("T"));
options.addOption(OptionBuilder
.withDescription("Incremental build")
.withLongOpt("incremental")
.create('i'));
if (args.length == 0) {
printUsage(options);
@ -97,6 +101,10 @@ public final class TeaVMTestRunner {
tool.getTransformers().add(instantiateTransformer(transformerType));
}
}
if (commandLine.hasOption('i')) {
tool.setIncremental(true);
}
args = commandLine.getArgs();
if (args.length == 0) {
System.err.println("You did not specify any test classes");

View File

@ -0,0 +1,30 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Alexey Andreev
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NoCache {
}

View File

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.teavm.cache.NoCache;
import org.teavm.common.Graph;
import org.teavm.common.GraphIndexer;
import org.teavm.common.Loop;
@ -245,7 +246,7 @@ public class Decompiler {
}
public RegularMethodNode decompileRegular(MethodHolder method) {
if (regularMethodCache == null) {
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
return decompileRegularCacheMiss(method);
}
RegularMethodNode node = regularMethodCache.get(method.getReference());
@ -283,7 +284,7 @@ public class Decompiler {
}
public AsyncMethodNode decompileAsync(MethodHolder method) {
if (regularMethodCache == null) {
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
return decompileAsyncCacheMiss(method);
}
AsyncMethodNode node = regularMethodCache.getAsync(method.getReference());

View File

@ -17,6 +17,7 @@ package org.teavm.model;
import java.util.HashMap;
import java.util.Map;
import org.teavm.model.util.ProgramUtils;
/**
*
@ -27,11 +28,12 @@ public class InMemoryProgramCache implements ProgramCache {
@Override
public Program get(MethodReference method) {
return cache.get(method);
Program program = cache.get(method);
return program != null ? ProgramUtils.copy(program) : null;
}
@Override
public void store(MethodReference method, Program program) {
cache.put(method, program);
cache.put(method, ProgramUtils.copy(program));
}
}

View File

@ -236,7 +236,7 @@ public class TeaVMTool {
symbolTable.update();
fileTable.update();
} catch (IOException e) {
log.info("Cache was not read");
log.info("Cache is missing");
}
vmBuilder.setClassLoader(classLoader).setClassSource(cachedClassSource);
} else {

View File

@ -17,6 +17,7 @@ package org.teavm.vm;
import java.io.*;
import java.util.*;
import org.teavm.cache.NoCache;
import org.teavm.codegen.*;
import org.teavm.common.ServiceRepository;
import org.teavm.debugging.information.DebugInformationEmitter;
@ -616,7 +617,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
if (method.getProgram() == null) {
return;
}
Program optimizedProgram = incremental && programCache != null ?
boolean noCache = method.getAnnotations().get(NoCache.class.getName()) != null;
Program optimizedProgram = incremental && !noCache && programCache != null ?
programCache.get(method.getReference()) : null;
if (optimizedProgram == null) {
optimizedProgram = ProgramUtils.copy(method.getProgram());

View File

@ -744,6 +744,9 @@ function Long_rem(a, b) {
return Long_divRem(a, b)[1];
}
function Long_divRem(a, b) {
if (b.lo == 0 && b.hi == 0) {
throw new Error("Division by zero");
}
var positive = Long_isNegative(a) === Long_isNegative(b);
if (Long_isNegative(a)) {
a = Long_neg(a);

View File

@ -15,6 +15,7 @@
*/
package org.teavm.platform.plugin;
import org.teavm.cache.NoCache;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.model.*;
@ -49,10 +50,14 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
method.getReference(), ClassScopedMetadataProvider.class.getName(),
PlatformClass.class.getName());
}
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
ClassScopedMetadataProviderNativeGenerator.class.getName())));
method.getAnnotations().add(genAnnot);
AnnotationHolder noCacheAnnot = new AnnotationHolder(NoCache.class.getName());
method.getAnnotations().add(noCacheAnnot);
}
}
}
@ -95,6 +100,9 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
fork.setElse(pe.createBlock());
pe.setField(field.getReference(), field.getType(), pe.invoke(createMethod.getReference()));
pe.jump(resourceFound);
AnnotationHolder noCacheAnnot = new AnnotationHolder(NoCache.class.getName());
method.getAnnotations().add(noCacheAnnot);
}
private boolean validate(MethodHolder method, Diagnostics diagnostics) {

View File

@ -0,0 +1,127 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.text;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
import org.junit.Test;
/**
*
* @author Alexey Andreev
*/
public class DecimalFormatParseTest {
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
@Test
public void parsesNumber() throws ParseException {
DecimalFormat format = createFormat("#,#00.#");
assertEquals(2L, format.parse("2"));
assertEquals(23L, format.parse("23"));
assertEquals(23L, format.parse("23.0"));
assertEquals(2300L, format.parse("2,3,0,0"));
assertEquals(23.1, format.parse("23.1"));
}
@Test
public void parsesBigNumber() throws ParseException {
DecimalFormat format = createFormat("#,#00.#");
format.setParseBigDecimal(true);
assertEquals(BigDecimal.valueOf(2), format.parse("2"));
assertEquals(BigDecimal.valueOf(23), format.parse("23"));
assertEquals(BigDecimal.valueOf(230, 1), format.parse("23.0"));
assertEquals(BigDecimal.valueOf(2300), format.parse("2,3,0,0"));
assertEquals(BigDecimal.valueOf(231, 1), format.parse("23.1"));
}
@Test
public void parsesLargeValue() throws ParseException {
DecimalFormat format = createFormat("#,#00.#");
assertEquals(9223372036854775807L, format.parse("9223372036854775807"));
assertEquals(99E18, format.parse("99000000000000000000"));
assertEquals(3.333333333333333E20, format.parse("333333333333333333456").doubleValue(), 1000000);
assertEquals(10E20, format.parse("999999999999999999999").doubleValue(), 1000000);
}
@Test
public void parsesExponential() throws ParseException {
DecimalFormat format = createFormat("0.#E0");
assertEquals(23L, format.parse("2.3E1"));
assertEquals(23L, format.parse("2300E-2"));
assertEquals(0.23, format.parse("2300E-4").doubleValue(), 0.0001);
assertEquals(99E18, format.parse("99E18"));
}
@Test
public void parsesBigExponential() throws ParseException {
DecimalFormat format = createFormat("0.#E0");
format.setParseBigDecimal(true);
assertEquals(BigDecimal.valueOf(23), format.parse("2.3E1"));
assertEquals(BigDecimal.valueOf(2300, 2), format.parse("2300E-2"));
assertEquals(BigDecimal.valueOf(2300, 4), format.parse("2300E-4"));
assertEquals(BigDecimal.valueOf(99, -18), format.parse("99E18"));
}
@Test
public void parsesPrefixSuffix() throws ParseException {
DecimalFormat format = createFormat("[0.#E0]");
assertEquals(23L, format.parse("[23]"));
assertEquals(-23L, format.parse("-[23]"));
try {
format.parse("23");
fail("Exception expected as there aren't neither prefix nor suffix");
} catch (ParseException e) {
assertEquals(0, e.getErrorOffset());
}
try {
format.parse("[23");
fail("Exception expected as there is no suffix");
} catch (ParseException e) {
assertEquals(3, e.getErrorOffset());
}
}
@Test
public void parsesPercent() throws ParseException {
DecimalFormat format = createFormat("0.#E0%");
assertEquals(0.23, format.parse("23%").doubleValue(), 0.001);
assertEquals(23L, format.parse("2300%"));
}
@Test
public void parsesBigPercent() throws ParseException {
DecimalFormat format = createFormat("0.#E0%");
format.setParseBigDecimal(true);
assertEquals(BigDecimal.valueOf(23, 2), format.parse("23%"));
assertEquals(BigDecimal.valueOf(23, 0), format.parse("2300%"));
}
@Test
public void parsesSpecial() throws ParseException {
DecimalFormat format = createFormat("0.#E0");
assertEquals(Double.POSITIVE_INFINITY, format.parse(""));
assertEquals(Double.NEGATIVE_INFINITY, format.parse("-∞"));
assertEquals(-0.0, format.parse("-0"));
}
private DecimalFormat createFormat(String format) {
return new DecimalFormat(format, symbols);
}
}

View File

@ -0,0 +1,475 @@
/*
* Copyright 2015 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.text;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Currency;
import java.util.Locale;
import org.junit.Test;
/**
*
* @author Alexey Andreev
*/
public class DecimalFormatTest {
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
@Test
public void parsesIntegerPattern() {
DecimalFormat format = createFormat("00");
assertEquals(2, format.getMinimumIntegerDigits());
assertFalse(format.isDecimalSeparatorAlwaysShown());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(0, format.getMinimumFractionDigits());
assertEquals(0, format.getMaximumFractionDigits());
format = createFormat("##");
assertEquals(0, format.getMinimumIntegerDigits());
assertFalse(format.isDecimalSeparatorAlwaysShown());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(0, format.getMinimumFractionDigits());
assertEquals(0, format.getMaximumFractionDigits());
format = createFormat("#,##0");
assertEquals(1, format.getMinimumIntegerDigits());
assertFalse(format.isDecimalSeparatorAlwaysShown());
assertTrue(format.isGroupingUsed());
assertEquals(3, format.getGroupingSize());
assertEquals(0, format.getMinimumFractionDigits());
assertEquals(0, format.getMaximumFractionDigits());
}
@Test
public void selectsLastGrouping() {
DecimalFormat format = new DecimalFormat("#,0,000");
assertEquals(4, format.getMinimumIntegerDigits());
assertTrue(format.isGroupingUsed());
assertEquals(3, format.getGroupingSize());
}
@Test
public void parsesPrefixAndSuffixInPattern() {
DecimalFormat format = createFormat("(00)");
assertEquals(2, format.getMinimumIntegerDigits());
assertEquals("(", format.getPositivePrefix());
assertEquals(")", format.getPositiveSuffix());
assertEquals("-(", format.getNegativePrefix());
assertEquals(")", format.getNegativeSuffix());
format = createFormat("+(00);-{#}");
assertEquals(2, format.getMinimumIntegerDigits());
assertEquals("+(", format.getPositivePrefix());
assertEquals(")", format.getPositiveSuffix());
assertEquals("-{", format.getNegativePrefix());
}
@Test
public void parsesFractionalPattern() {
DecimalFormat format = createFormat("#.");
assertEquals(1, format.getMinimumIntegerDigits());
assertTrue(format.isDecimalSeparatorAlwaysShown());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(0, format.getMinimumFractionDigits());
assertEquals(0, format.getMaximumFractionDigits());
format = createFormat("#.00");
assertEquals(0, format.getMinimumIntegerDigits());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(2, format.getMinimumFractionDigits());
assertEquals(2, format.getMaximumFractionDigits());
format = createFormat("#.00##");
assertEquals(0, format.getMinimumIntegerDigits());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(2, format.getMinimumFractionDigits());
assertEquals(4, format.getMaximumFractionDigits());
format = createFormat("#00.00##");
assertEquals(2, format.getMinimumIntegerDigits());
assertFalse(format.isGroupingUsed());
assertEquals(0, format.getGroupingSize());
assertEquals(2, format.getMinimumFractionDigits());
assertEquals(4, format.getMaximumFractionDigits());
format = createFormat("#,#00.00##");
assertEquals(2, format.getMinimumIntegerDigits());
assertTrue(format.isGroupingUsed());
assertEquals(3, format.getGroupingSize());
assertEquals(2, format.getMinimumFractionDigits());
assertEquals(4, format.getMaximumFractionDigits());
}
@Test
public void parsesExponentialPattern() {
DecimalFormat format = createFormat("##0E00");
assertEquals(1, format.getMinimumIntegerDigits());
assertEquals(0, format.getGroupingSize());
assertEquals(0, format.getMinimumFractionDigits());
assertEquals(0, format.getMaximumFractionDigits());
}
@Test
public void formatsIntegerPart() {
DecimalFormat format = createFormat("00");
assertEquals("02", format.format(2));
assertEquals("23", format.format(23));
assertEquals("23", format.format(23.2));
assertEquals("24", format.format(23.7));
}
@Test
public void formatsBigIntegerPart() {
DecimalFormat format = createFormat("00");
assertEquals("02", format.format(new BigInteger("2")));
assertEquals("23", format.format(new BigInteger("23")));
assertEquals("23", format.format(new BigDecimal("23.2")));
assertEquals("24", format.format(new BigDecimal("23.7")));
}
@Test
public void formatsNumber() {
DecimalFormat format = createFormat("0.0");
assertEquals("23.0", format.format(23));
assertEquals("23.2", format.format(23.2));
assertEquals("23.2", format.format(23.23));
assertEquals("23.3", format.format(23.27));
assertEquals("0.0", format.format(0.0001));
format = createFormat("00000000000000000000000000.0");
assertEquals("00000000000000000000000023.0", format.format(23));
assertEquals("00002300000000000000000000.0", format.format(23E20));
assertEquals("23000000000000000000000000.0", format.format(23E24));
format = createFormat("0.00000000000000000000000000");
assertEquals("23.00000000000000000000000000", format.format(23));
assertEquals("0.23000000000000000000000000", format.format(0.23));
assertEquals("0.00230000000000000000000000", format.format(0.0023));
assertEquals("0.00000000000000000000230000", format.format(23E-22));
assertEquals("0.00000000000000000000000023", format.format(23E-26));
}
@Test
public void formatsBigNumber() {
DecimalFormat format = createFormat("0.0");
assertEquals("23.0", format.format(BigInteger.valueOf(23)));
assertEquals("23.2", format.format(new BigDecimal("23.2")));
assertEquals("23.2", format.format(new BigDecimal("23.23")));
assertEquals("23.3", format.format(new BigDecimal("23.27")));
assertEquals("0.0", format.format(new BigDecimal("0.0001")));
format = createFormat("00000000000000000000000000.0");
assertEquals("00000000000000000000000023.0", format.format(new BigInteger("23")));
assertEquals("00002300000000000000000000.0", format.format(new BigInteger("2300000000000000000000")));
assertEquals("23000000000000000000000000.0", format.format(new BigInteger("23000000000000000000000000")));
format = createFormat("0.00000000000000000000000000");
assertEquals("23.00000000000000000000000000", format.format(new BigInteger("23")));
assertEquals("0.23000000000000000000000000", format.format(new BigDecimal("0.23")));
assertEquals("0.00230000000000000000000000", format.format(new BigDecimal("0.0023")));
assertEquals("0.00000000000000000000230000", format.format(new BigDecimal("0.0000000000000000000023")));
assertEquals("0.00000000000000000000000023", format.format(new BigDecimal("0.00000000000000000000000023")));
}
@Test
public void formatsFractionalPart() {
DecimalFormat format = createFormat("0.0000####");
assertEquals("0.00001235", format.format(0.0000123456));
assertEquals("0.00012346", format.format(0.000123456));
assertEquals("0.00123456", format.format(0.00123456));
assertEquals("0.0123456", format.format(0.0123456));
assertEquals("0.1200", format.format(0.12));
assertEquals("0.1230", format.format(0.123));
assertEquals("0.1234", format.format(0.1234));
assertEquals("0.12345", format.format(0.12345));
format = createFormat("0.##");
assertEquals("23", format.format(23));
assertEquals("2.3", format.format(2.3));
assertEquals("0.23", format.format(0.23));
assertEquals("0.02", format.format(0.023));
}
@Test
public void roundingWorks() {
DecimalFormat format = createFormat("0");
format.setRoundingMode(RoundingMode.UP);
assertEquals("3", format.format(2.3));
assertEquals("3", format.format(2.7));
assertEquals("-3", format.format(-2.3));
assertEquals("-3", format.format(-2.7));
format.setRoundingMode(RoundingMode.DOWN);
assertEquals("2", format.format(2.3));
assertEquals("2", format.format(2.7));
assertEquals("-2", format.format(-2.3));
assertEquals("-2", format.format(-2.7));
format.setRoundingMode(RoundingMode.FLOOR);
assertEquals("2", format.format(2.3));
assertEquals("2", format.format(2.7));
assertEquals("-3", format.format(-2.3));
assertEquals("-3", format.format(-2.7));
format.setRoundingMode(RoundingMode.CEILING);
assertEquals("3", format.format(2.3));
assertEquals("3", format.format(2.7));
assertEquals("-2", format.format(-2.3));
assertEquals("-2", format.format(-2.7));
format.setRoundingMode(RoundingMode.HALF_DOWN);
assertEquals("2", format.format(2.3));
assertEquals("3", format.format(2.7));
assertEquals("2", format.format(2.5));
assertEquals("3", format.format(3.5));
assertEquals("-2", format.format(-2.5));
assertEquals("-3", format.format(-3.5));
format.setRoundingMode(RoundingMode.HALF_UP);
assertEquals("2", format.format(2.3));
assertEquals("3", format.format(2.7));
assertEquals("3", format.format(2.5));
assertEquals("4", format.format(3.5));
assertEquals("-3", format.format(-2.5));
assertEquals("-4", format.format(-3.5));
format.setRoundingMode(RoundingMode.HALF_EVEN);
assertEquals("2", format.format(2.3));
assertEquals("3", format.format(2.7));
assertEquals("2", format.format(2.5));
assertEquals("4", format.format(3.5));
assertEquals("-2", format.format(-2.5));
assertEquals("-4", format.format(-3.5));
}
@Test
public void bigRoundingWorks() {
DecimalFormat format = createFormat("0");
format.setRoundingMode(RoundingMode.UP);
assertEquals("3", format.format(new BigDecimal("2.3")));
assertEquals("3", format.format(new BigDecimal("2.7")));
assertEquals("-3", format.format(new BigDecimal("-2.3")));
assertEquals("-3", format.format(new BigDecimal("-2.7")));
format.setRoundingMode(RoundingMode.DOWN);
assertEquals("2", format.format(new BigDecimal("2.3")));
assertEquals("2", format.format(new BigDecimal("2.7")));
assertEquals("-2", format.format(new BigDecimal("-2.3")));
assertEquals("-2", format.format(new BigDecimal("-2.7")));
format.setRoundingMode(RoundingMode.FLOOR);
assertEquals("2", format.format(new BigDecimal("2.3")));
assertEquals("2", format.format(new BigDecimal("2.7")));
assertEquals("-3", format.format(new BigDecimal("-2.3")));
assertEquals("-3", format.format(new BigDecimal("-2.7")));
format.setRoundingMode(RoundingMode.CEILING);
assertEquals("3", format.format(new BigDecimal("2.3")));
assertEquals("3", format.format(new BigDecimal("2.7")));
assertEquals("-2", format.format(new BigDecimal("-2.3")));
assertEquals("-2", format.format(new BigDecimal("-2.7")));
format.setRoundingMode(RoundingMode.HALF_DOWN);
assertEquals("2", format.format(new BigDecimal("2.3")));
assertEquals("3", format.format(new BigDecimal("2.7")));
assertEquals("2", format.format(new BigDecimal("2.5")));
assertEquals("3", format.format(new BigDecimal("3.5")));
assertEquals("-2", format.format(new BigDecimal("-2.5")));
assertEquals("-3", format.format(new BigDecimal("-3.5")));
format.setRoundingMode(RoundingMode.HALF_UP);
assertEquals("2", format.format(new BigDecimal("2.3")));
assertEquals("3", format.format(new BigDecimal("2.7")));
assertEquals("3", format.format(new BigDecimal("2.5")));
assertEquals("4", format.format(new BigDecimal("3.5")));
assertEquals("-3", format.format(new BigDecimal("-2.5")));
assertEquals("-4", format.format(new BigDecimal("-3.5")));
format.setRoundingMode(RoundingMode.HALF_EVEN);
assertEquals("2", format.format(new BigDecimal("2.3")));
assertEquals("3", format.format(new BigDecimal("2.7")));
assertEquals("2", format.format(new BigDecimal("2.5")));
assertEquals("4", format.format(new BigDecimal("3.5")));
assertEquals("-2", format.format(new BigDecimal("-2.5")));
assertEquals("-4", format.format(new BigDecimal("-3.5")));
}
@Test
public void formatsWithGroups() {
DecimalFormat format = createFormat("#,###.0");
assertEquals("23.0", format.format(23));
assertEquals("2,300.0", format.format(2300));
assertEquals("2,300,000,000,000,000,000,000.0", format.format(23E20));
assertEquals("23,000,000,000,000,000,000,000,000.0", format.format(23E24));
format = createFormat("000,000,000,000,000,000,000");
assertEquals("000,000,000,000,000,000,023", format.format(23));
}
@Test
public void formatsBigWithGroups() {
DecimalFormat format = createFormat("#,###.0");
assertEquals("23.0", format.format(BigInteger.valueOf(23)));
assertEquals("2,300.0", format.format(BigInteger.valueOf(2300)));
assertEquals("2,300,000,000,000,000,000,000.0", format.format(new BigInteger("2300000000000000000000")));
assertEquals("23,000,000,000,000,000,000,000,000.0", format.format(
new BigInteger("23000000000000000000000000")));
format = createFormat("000,000,000,000,000,000,000");
assertEquals("000,000,000,000,000,000,023", format.format(BigInteger.valueOf(23)));
}
@Test
public void formatsLargeValues() {
DecimalFormat format = createFormat("0");
assertEquals("9223372036854775807", format.format(9223372036854775807L));
assertEquals("-9223372036854775808", format.format(-9223372036854775808L));
}
@Test
public void formatsExponent() {
DecimalFormat format = createFormat("000E0");
assertEquals("230E-1", format.format(23));
assertEquals("230E0", format.format(230));
assertEquals("230E1", format.format(2300));
assertEquals("123E1", format.format(1234));
assertEquals("-123E1", format.format(-1234));
format = createFormat("0.00E0");
assertEquals("2.00E1", format.format(20));
assertEquals("2.30E1", format.format(23));
assertEquals("2.30E2", format.format(230));
assertEquals("1.23E3", format.format(1234));
format = createFormat("000000000000000000000.00E0");
assertEquals("230000000000000000000.00E-19", format.format(23));
format = createFormat("0.0000000000000000000000E0");
assertEquals("2.3000000000000000000000E1", format.format(23));
format = createFormat("0.0##E0");
assertEquals("1.0E0", format.format(1));
assertEquals("1.2E1", format.format(12));
assertEquals("1.23E2", format.format(123));
assertEquals("1.234E3", format.format(1234));
assertEquals("1.234E4", format.format(12345));
}
@Test
public void formatsBigExponent() {
DecimalFormat format = createFormat("000E0");
assertEquals("230E-1", format.format(BigInteger.valueOf(23)));
assertEquals("230E0", format.format(BigInteger.valueOf(230)));
assertEquals("230E1", format.format(BigInteger.valueOf(2300)));
assertEquals("123E1", format.format(BigInteger.valueOf(1234)));
assertEquals("-123E1", format.format(BigInteger.valueOf(-1234)));
format = createFormat("0.00E0");
assertEquals("2.00E1", format.format(BigInteger.valueOf(20)));
assertEquals("2.30E1", format.format(BigInteger.valueOf(23)));
assertEquals("2.30E2", format.format(BigInteger.valueOf(230)));
assertEquals("1.23E3", format.format(BigInteger.valueOf(1234)));
format = createFormat("000000000000000000000.00E0");
assertEquals("230000000000000000000.00E-19", format.format(BigInteger.valueOf(23)));
format = createFormat("0.0000000000000000000000E0");
assertEquals("2.3000000000000000000000E1", format.format(BigInteger.valueOf(23)));
format = createFormat("0.0##E0");
assertEquals("1.0E0", format.format(BigInteger.valueOf(1)));
assertEquals("1.2E1", format.format(BigInteger.valueOf(12)));
assertEquals("1.23E2", format.format(BigInteger.valueOf(123)));
assertEquals("1.234E3", format.format(BigInteger.valueOf(1234)));
assertEquals("1.234E4", format.format(BigInteger.valueOf(12345)));
}
@Test
public void formatsExponentWithMultiplier() {
DecimalFormat format = createFormat("##0.00E0");
assertEquals("2.30E0", format.format(2.3));
assertEquals("23.0E0", format.format(23));
assertEquals("230E0", format.format(230));
assertEquals("2.30E3", format.format(2300));
assertEquals("23.0E3", format.format(23000));
}
@Test
public void formatsBigExponentWithMultiplier() {
DecimalFormat format = createFormat("##0.00E0");
assertEquals("2.30E0", format.format(new BigDecimal("2.3")));
assertEquals("23.0E0", format.format(new BigDecimal("23")));
assertEquals("230E0", format.format(new BigDecimal("230")));
assertEquals("2.30E3", format.format(new BigDecimal("2300")));
assertEquals("23.0E3", format.format(new BigDecimal("23000")));
}
@Test
public void formatsSpecialValues() {
DecimalFormat format = createFormat("0");
assertEquals("", format.format(Double.POSITIVE_INFINITY));
assertEquals("-∞", format.format(Double.NEGATIVE_INFINITY));
}
@Test
public void formatsWithMultiplier() {
DecimalFormat format = createFormat("0");
format.setMultiplier(2);
assertEquals("18446744073709551614", format.format(9223372036854775807L));
assertEquals("46", format.format(BigInteger.valueOf(23)));
format.setMultiplier(100);
assertEquals("2300", format.format(23));
assertEquals("2300", format.format(BigInteger.valueOf(23)));
format = createFormat("00E0");
format.setMultiplier(2);
assertEquals("18E18", format.format(9223372036854775807L));
assertEquals("46E0", format.format(BigInteger.valueOf(23)));
format.setMultiplier(100);
assertEquals("23E2", format.format(23));
assertEquals("23E2", format.format(BigInteger.valueOf(23)));
}
@Test
public void formatsSpecial() {
DecimalFormat format = createFormat("0%");
assertEquals("23%", format.format(0.23));
format = createFormat("0‰");
assertEquals("230‰", format.format(0.23));
format = createFormat("0.00 ¤");
format.setCurrency(Currency.getInstance("RUB"));
assertEquals("23.00 RUB", format.format(23));
}
private DecimalFormat createFormat(String format) {
return new DecimalFormat(format, symbols);
}
}