java.time: reduce generated code size

This commit is contained in:
Alexey Andreev 2020-05-06 10:44:51 +03:00 committed by Alexey Andreev
parent 1b31d6da9a
commit e9d0ed5c67
10 changed files with 338 additions and 140 deletions

View File

@ -1,7 +1,11 @@
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN" <!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<module name="Checker"> <module name="Checker">
<module name="SuppressionCommentFilter"/> <module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ?([\w\|]+)"/>
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ?([\w\|]+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="TreeWalker"> <module name="TreeWalker">
<module name="FileContentsHolder"/> <module name="FileContentsHolder"/>
<module name="AnnotationUseStyle"/> <module name="AnnotationUseStyle"/>

View File

@ -61,8 +61,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.threeten.bp.format.DateTimeParseException; import org.threeten.bp.format.DateTimeParseException;
import org.threeten.bp.jdk8.Jdk8Methods; import org.threeten.bp.jdk8.Jdk8Methods;
import org.threeten.bp.temporal.ChronoField; import org.threeten.bp.temporal.ChronoField;
@ -117,15 +115,7 @@ public final class Duration
/** /**
* Constant for nanos per second. * Constant for nanos per second.
*/ */
private static final BigInteger BI_NANOS_PER_SECOND = BigInteger.valueOf(NANOS_PER_SECOND); private static BigInteger bigIntNanosPerSecond;
/**
* The pattern for parsing.
*/
// TODO: get rid of regexp
private final static Pattern PATTERN =
Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)D)?"
+ "(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?",
Pattern.CASE_INSENSITIVE);
/** /**
* The number of seconds in the duration. * The number of seconds in the duration.
@ -137,6 +127,13 @@ public final class Duration
*/ */
private final int nanos; private final int nanos;
private static BigInteger getBigIntNanosPerSecond() {
if (bigIntNanosPerSecond == null) {
bigIntNanosPerSecond = BigInteger.valueOf(NANOS_PER_SECOND);
}
return bigIntNanosPerSecond;
}
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
/** /**
* Obtains an instance of {@code Duration} from a number of standard 24 hour days. * Obtains an instance of {@code Duration} from a number of standard 24 hour days.
@ -398,60 +395,156 @@ public final class Duration
*/ */
public static Duration parse(CharSequence text) { public static Duration parse(CharSequence text) {
Objects.requireNonNull(text, "text"); Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text); Parser parser = new Parser(text);
if (matcher.matches()) { if (!parser.parse() || !parser.hasOneField) {
// check for letter T but no time sections throw new DateTimeParseException("Text cannot be parsed to a Duration", text, parser.ptr);
if (!"T".equals(matcher.group(3))) { }
boolean negate = "-".equals(matcher.group(1)); return create(parser.negative, parser.days * SECONDS_PER_DAY, parser.hours * SECONDS_PER_HOUR,
String dayMatch = matcher.group(2); parser.minutes * SECONDS_PER_MINUTE, parser.seconds, parser.nanos);
String hourMatch = matcher.group(4); }
String minuteMatch = matcher.group(5);
String secondMatch = matcher.group(6); static class Parser {
String fractionMatch = matcher.group(7); private int ptr;
if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { private CharSequence text;
long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); private int days;
long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); private int hours;
long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); private int minutes;
long seconds = parseNumber(text, secondMatch, 1, "seconds"); private int seconds;
boolean negativeSecs = secondMatch != null && secondMatch.charAt(0) == '-'; private int nanos;
int nanos = parseFraction(text, fractionMatch, negativeSecs ? -1 : 1); private boolean negative;
try { private boolean hasOneField;
return create(negate, daysAsSecs, hoursAsSecs, minsAsSecs, seconds, nanos); private int parsedNumber;
} catch (ArithmeticException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Duration: overflow", text, 0, ex); Parser(CharSequence text) {
} this.text = text;
}
boolean parse() {
negative = sign();
if (eof() || text.charAt(ptr) != 'P') {
return false;
}
ptr++;
if (eof()) {
return false;
}
if (text.charAt(ptr) != 'T') {
if (!tryParseDays()) {
return false;
}
if (eof()) {
return true;
}
if (text.charAt(ptr) != 'T') {
return false;
}
++ptr;
hasOneField = false;
} else {
++ptr;
}
int state = 0;
loop: do {
if (!number()) {
break;
}
if (eof()) {
return false;
}
hasOneField = true;
char c = text.charAt(ptr);
//CHECKSTYLE.OFF: FallThrough
switch (state) {
case 0:
if (c == 'H') {
++ptr;
hours = parsedNumber;
state = 1;
break;
}
case 1:
if (c == 'M') {
++ptr;
minutes = parsedNumber;
state = 2;
break;
}
case 2:
if (c == 'S') {
++ptr;
seconds = parsedNumber;
break loop;
} else if (c == '.') {
seconds = parsedNumber;
if (!number()) {
return false;
}
nanos = parsedNumber;
if (eof() || text.charAt(ptr) != 'S') {
return false;
}
++ptr;
break loop;
}
default:
return false;
}
//CHECKSTYLE.ON: FallThrough
} while (true);
return eof() && hasOneField;
}
private boolean tryParseDays() {
if (!number()) {
return false;
}
days = parsedNumber;
hasOneField = true;
if (ptr >= text.length() || text.charAt(ptr) != 'D') {
return false;
}
++ptr;
return true;
}
boolean eof() {
return ptr >= text.length();
}
boolean sign() {
if (!eof()) {
if (text.charAt(ptr) == '-') {
ptr++;
return true;
} else if (text.charAt(ptr) == '+') {
ptr++;
} }
} }
return false;
} }
throw new DateTimeParseException("Text cannot be parsed to a Duration", text, 0);
}
private static long parseNumber(CharSequence text, String parsed, int multiplier, String errorText) { boolean number() {
// regex limits to [-+]?[0-9]+ boolean negative = sign();
if (parsed == null) { parsedNumber = 0;
return 0; boolean hasDigits = false;
} while (ptr < text.length()) {
try { char c = text.charAt(ptr);
if (parsed.startsWith("+")) { if (c < '0' || c >= '9') {
parsed = parsed.substring(1); break;
}
++ptr;
hasDigits = true;
parsedNumber = parsedNumber * 10 + c - '0';
} }
long val = Long.parseLong(parsed); if (negative) {
return Jdk8Methods.safeMultiply(val, multiplier); parsedNumber = -parsedNumber;
} catch (NumberFormatException | ArithmeticException ex) { }
throw new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0, ex); return hasDigits;
}
}
private static int parseFraction(CharSequence text, String parsed, int negate) {
// regex limits to [0-9]{0,9}
if (parsed == null || parsed.length() == 0) {
return 0;
}
try {
parsed = (parsed + "000000000").substring(0, 9);
return Integer.parseInt(parsed) * negate;
} catch (NumberFormatException | ArithmeticException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Duration: fraction", text, 0, ex);
} }
} }
@ -951,7 +1044,7 @@ public final class Duration
*/ */
private static Duration create(BigDecimal seconds) { private static Duration create(BigDecimal seconds) {
BigInteger nanos = seconds.movePointRight(9).toBigIntegerExact(); BigInteger nanos = seconds.movePointRight(9).toBigIntegerExact();
BigInteger[] divRem = nanos.divideAndRemainder(BI_NANOS_PER_SECOND); BigInteger[] divRem = nanos.divideAndRemainder(getBigIntNanosPerSecond());
if (divRem[0].bitLength() > 63) { if (divRem[0].bitLength() > 63) {
throw new ArithmeticException("Exceeds capacity of Duration: " + nanos); throw new ArithmeticException("Exceeds capacity of Duration: " + nanos);
} }

View File

@ -58,7 +58,9 @@ import static org.threeten.bp.temporal.ChronoUnit.NANOS;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
import org.threeten.bp.format.DateTimeFormatter; import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;
import org.threeten.bp.format.DateTimeParseException; import org.threeten.bp.format.DateTimeParseException;
import org.threeten.bp.format.DateTimePrintContext;
import org.threeten.bp.jdk8.Jdk8Methods; import org.threeten.bp.jdk8.Jdk8Methods;
import org.threeten.bp.temporal.ChronoField; import org.threeten.bp.temporal.ChronoField;
import org.threeten.bp.temporal.ChronoUnit; import org.threeten.bp.temporal.ChronoUnit;
@ -1166,6 +1168,9 @@ public final class Instant
*/ */
@Override @Override
public String toString() { public String toString() {
return DateTimeFormatter.ISO_INSTANT.format(this); StringBuilder sb = new StringBuilder();
new DateTimeFormatterBuilder.InstantPrinterParser(-2).print(
new DateTimePrintContext(this, null, null), sb);
return sb.toString();
} }
} }

View File

@ -96,15 +96,6 @@ import org.threeten.bp.temporal.ValueRange;
*/ */
public final class MonthDay public final class MonthDay
implements TemporalAccessor, TemporalAdjuster, Comparable<MonthDay>, Serializable { implements TemporalAccessor, TemporalAdjuster, Comparable<MonthDay>, Serializable {
/**
* Parser.
*/
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.appendLiteral("--")
.appendValue(MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(DAY_OF_MONTH, 2)
.toFormatter();
/** /**
* The month-of-year, not null. * The month-of-year, not null.
@ -254,7 +245,13 @@ public final class MonthDay
* @throws DateTimeParseException if the text cannot be parsed * @throws DateTimeParseException if the text cannot be parsed
*/ */
public static MonthDay parse(CharSequence text) { public static MonthDay parse(CharSequence text) {
return parse(text, PARSER); // TODO: get rid of DateTimeFormatterBuilder
return parse(text, new DateTimeFormatterBuilder()
.appendLiteral("--")
.appendValue(MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(DAY_OF_MONTH, 2)
.toFormatter());
} }
/** /**

View File

@ -54,8 +54,6 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.threeten.bp.chrono.ChronoLocalDate; import org.threeten.bp.chrono.ChronoLocalDate;
import org.threeten.bp.chrono.ChronoPeriod; import org.threeten.bp.chrono.ChronoPeriod;
import org.threeten.bp.chrono.Chronology; import org.threeten.bp.chrono.Chronology;
@ -110,12 +108,6 @@ public final class Period
* A constant for a period of zero. * A constant for a period of zero.
*/ */
public static final Period ZERO = new Period(0, 0, 0); public static final Period ZERO = new Period(0, 0, 0);
/**
* The pattern for parsing.
*/
private final static Pattern PATTERN =
Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?",
Pattern.CASE_INSENSITIVE);
/** /**
* The number of years. * The number of years.
@ -312,38 +304,126 @@ public final class Period
*/ */
public static Period parse(CharSequence text) { public static Period parse(CharSequence text) {
Objects.requireNonNull(text, "text"); Objects.requireNonNull(text, "text");
Matcher matcher = PATTERN.matcher(text); Parser parser = new Parser(text);
if (matcher.matches()) { if (!parser.parse() || !parser.hasOneField) {
int negate = "-".equals(matcher.group(1)) ? -1 : 1; throw new DateTimeParseException("Text cannot be parsed to a Period", text, parser.ptr);
String yearMatch = matcher.group(2);
String monthMatch = matcher.group(3);
String weekMatch = matcher.group(4);
String dayMatch = matcher.group(5);
if (yearMatch != null || monthMatch != null || weekMatch != null || dayMatch != null) {
try {
int years = parseNumber(text, yearMatch, negate);
int months = parseNumber(text, monthMatch, negate);
int weeks = parseNumber(text, weekMatch, negate);
int days = parseNumber(text, dayMatch, negate);
days = Jdk8Methods.safeAdd(days, Jdk8Methods.safeMultiply(weeks, 7));
return create(years, months, days);
} catch (NumberFormatException ex) {
throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);
}
}
} }
throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0); if (parser.negative) {
parser.years = -parser.years;
parser.months = -parser.months;
parser.weeks = -parser.weeks;
parser.days = -parser.days;
}
int days = Jdk8Methods.safeAdd(parser.days, Jdk8Methods.safeMultiply(parser.weeks, 7));
return create(parser.years, parser.months, days);
} }
private static int parseNumber(CharSequence text, String str, int negate) { static class Parser {
if (str == null) { private int ptr;
return 0; private CharSequence text;
private int years;
private int months;
private int weeks;
private int days;
private boolean negative;
private boolean hasOneField;
private int parsedNumber;
Parser(CharSequence text) {
this.text = text;
} }
int val = Integer.parseInt(str);
try { boolean parse() {
return Jdk8Methods.safeMultiply(val, negate); negative = sign();
} catch (ArithmeticException ex) { if (eof() || text.charAt(ptr) != 'P') {
throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex); return false;
}
ptr++;
if (eof()) {
return false;
}
int state = 0;
while (number()) {
if (eof()) {
return false;
}
hasOneField = true;
char c = text.charAt(ptr);
//CHECKSTYLE.OFF: FallThrough
switch (state) {
case 0:
if (c == 'Y') {
++ptr;
years = parsedNumber;
state = 1;
break;
}
case 1:
if (c == 'M') {
++ptr;
months = parsedNumber;
state = 2;
break;
}
case 2:
if (c == 'W') {
++ptr;
weeks = parsedNumber;
state = 3;
break;
}
case 3:
if (c == 'D') {
++ptr;
days = parsedNumber;
state = 4;
break;
}
default:
return false;
}
//CHECKSTYLE.ON: FallThrough
}
return eof() && hasOneField;
}
boolean eof() {
return ptr >= text.length();
}
boolean sign() {
if (!eof()) {
if (text.charAt(ptr) == '-') {
ptr++;
return true;
} else if (text.charAt(ptr) == '+') {
ptr++;
}
}
return false;
}
boolean number() {
boolean negative = sign();
parsedNumber = 0;
boolean hasDigits = false;
while (ptr < text.length()) {
char c = text.charAt(ptr);
if (c < '0' || c >= '9') {
break;
}
++ptr;
hasDigits = true;
parsedNumber = parsedNumber * 10 + c - '0';
}
if (negative) {
parsedNumber = -parsedNumber;
}
return hasDigits;
} }
} }

View File

@ -115,13 +115,6 @@ public final class Year
*/ */
public static final int MAX_VALUE = 999999999; public static final int MAX_VALUE = 999999999;
/**
* Parser.
*/
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.toFormatter();
/** /**
* The year being represented. * The year being represented.
*/ */
@ -239,7 +232,10 @@ public final class Year
* @throws DateTimeParseException if the text cannot be parsed * @throws DateTimeParseException if the text cannot be parsed
*/ */
public static Year parse(CharSequence text) { public static Year parse(CharSequence text) {
return parse(text, PARSER); // TODO: Get rid of DateTimeFormatterBuilder
return parse(text, new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.toFormatter());
} }
/** /**

View File

@ -102,15 +102,6 @@ import org.threeten.bp.temporal.ValueRange;
public final class YearMonth public final class YearMonth
implements Temporal, TemporalAdjuster, Comparable<YearMonth>, Serializable, TemporalAccessor { implements Temporal, TemporalAdjuster, Comparable<YearMonth>, Serializable, TemporalAccessor {
/**
* Parser.
*/
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendLiteral('-')
.appendValue(MONTH_OF_YEAR, 2)
.toFormatter();
/** /**
* The year. * The year.
*/ */
@ -243,7 +234,12 @@ public final class YearMonth
* @throws DateTimeParseException if the text cannot be parsed * @throws DateTimeParseException if the text cannot be parsed
*/ */
public static YearMonth parse(CharSequence text) { public static YearMonth parse(CharSequence text) {
return parse(text, PARSER); // TODO: get rid of format
return parse(text, new DateTimeFormatterBuilder()
.appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendLiteral('-')
.appendValue(MONTH_OF_YEAR, 2)
.toFormatter());
} }
/** /**

View File

@ -48,7 +48,6 @@ package org.threeten.bp;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern;
import org.threeten.bp.zone.ZoneRules; import org.threeten.bp.zone.ZoneRules;
import org.threeten.bp.zone.ZoneRulesException; import org.threeten.bp.zone.ZoneRulesException;
import org.threeten.bp.zone.ZoneRulesProvider; import org.threeten.bp.zone.ZoneRulesProvider;
@ -73,11 +72,6 @@ import org.threeten.bp.zone.ZoneRulesProvider;
*/ */
final class ZoneRegion extends ZoneId implements Serializable { final class ZoneRegion extends ZoneId implements Serializable {
/**
* The regex pattern for region IDs.
*/
private static final Pattern PATTERN = Pattern.compile("[A-Za-z][A-Za-z0-9~/._+-]+");
/** /**
* The time-zone ID, not null. * The time-zone ID, not null.
*/ */
@ -139,7 +133,7 @@ final class ZoneRegion extends ZoneId implements Serializable {
*/ */
static ZoneRegion ofId(String zoneId, boolean checkAvailable) { static ZoneRegion ofId(String zoneId, boolean checkAvailable) {
Objects.requireNonNull(zoneId, "zoneId"); Objects.requireNonNull(zoneId, "zoneId");
if (zoneId.length() < 2 || !PATTERN.matcher(zoneId).matches()) { if (!isValidId(zoneId)) {
throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId); throw new DateTimeException("Invalid ID for region-based ZoneId, invalid format: " + zoneId);
} }
ZoneRules rules = null; ZoneRules rules = null;
@ -157,6 +151,39 @@ final class ZoneRegion extends ZoneId implements Serializable {
return new ZoneRegion(zoneId, rules); return new ZoneRegion(zoneId, rules);
} }
private static boolean isValidId(String id) {
if (id.length() < 2) {
return false;
}
if (!isIdStart(id.charAt(0))) {
return false;
}
for (int i = 1; i < id.length(); ++i) {
if (!isIdPart(id.charAt(i))) {
return false;
}
}
return true;
}
private static boolean isIdPart(char c) {
switch (c) {
case '~':
case '/':
case '.':
case '_':
case '+':
case '-':
return true;
default:
return isIdStart(c) || c >= '0' && c <= '9';
}
}
private static boolean isIdStart(char c) {
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
/** /**
* Constructor. * Constructor.

View File

@ -2931,7 +2931,7 @@ public final class DateTimeFormatterBuilder {
/** /**
* Prints or parses an ISO-8601 instant. * Prints or parses an ISO-8601 instant.
*/ */
static final class InstantPrinterParser implements DateTimePrinterParser { public static final class InstantPrinterParser implements DateTimePrinterParser {
// days in a 400 year cycle = 146097 // days in a 400 year cycle = 146097
// days in a 10,000 year cycle = 146097 * 25 // days in a 10,000 year cycle = 146097 * 25
// seconds per day = 86400 // seconds per day = 86400
@ -2940,7 +2940,7 @@ public final class DateTimeFormatterBuilder {
private final int fractionalDigits; private final int fractionalDigits;
InstantPrinterParser(int fractionalDigits) { public InstantPrinterParser(int fractionalDigits) {
this.fractionalDigits = fractionalDigits; this.fractionalDigits = fractionalDigits;
} }

View File

@ -111,9 +111,9 @@ public class LinkedHashMapTest {
assertNull("Empty LinkedHashMap access", empty.get("nothing")); assertNull("Empty LinkedHashMap access", empty.get("nothing"));
empty.put("something", "here"); empty.put("something", "here");
//CHECKSTYLE:OFF //CHECKSTYLE.OFF:StringLiteralEquality
assertTrue("cannot get element", empty.get("something") == "here"); assertTrue("cannot get element", empty.get("something") == "here");
//CHECKSTYLE:ON //CHECKSTYLE.ON:StringLiteralEquality
} }
@Test @Test
@ -131,9 +131,9 @@ public class LinkedHashMapTest {
assertNull("Empty hashtable access", empty.get("nothing")); assertNull("Empty hashtable access", empty.get("nothing"));
empty.put("something", "here"); empty.put("something", "here");
//CHECKSTYLE:OFF //CHECKSTYLE.OFF:StringLiteralEquality
assertTrue("cannot get element", empty.get("something") == "here"); assertTrue("cannot get element", empty.get("something") == "here");
// CHECKSTYLE: ON //CHECKSTYLE.ON:StringLiteralEquality
} }
@Test @Test