fixes for Sonarqube
This commit is contained in:
parent
2aead18a0f
commit
1d11478360
48 changed files with 1475 additions and 1175 deletions
18
CREDITS.txt
Normal file
18
CREDITS.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
org.xbib.time is based upon the following software:
|
||||
|
||||
- Chronic https://github.com/samtingleff/jchronic (MIT License)
|
||||
|
||||
- org.joda.time.format https://github.com/JodaOrg/joda-time (Apache 2.0)
|
||||
|
||||
- prettytime https://github.com/ocpsoft/prettytime (Apache 2.0)
|
||||
|
||||
with improvements by Jörg Prante including
|
||||
|
||||
- converted to Java 8 java.time API
|
||||
|
||||
- fixing bugs reported by Sonarqube
|
||||
|
||||
- refactoring classes
|
||||
|
||||
- rewritten code to simplify and ease development
|
|
@ -3,17 +3,26 @@ package org.xbib.time.chronic;
|
|||
import org.xbib.time.chronic.handlers.Handler;
|
||||
import org.xbib.time.chronic.numerizer.Numerizer;
|
||||
import org.xbib.time.chronic.repeaters.Repeater;
|
||||
import org.xbib.time.chronic.tags.*;
|
||||
import org.xbib.time.chronic.tags.Grabber;
|
||||
import org.xbib.time.chronic.tags.Ordinal;
|
||||
import org.xbib.time.chronic.tags.Pointer;
|
||||
import org.xbib.time.chronic.tags.Scalar;
|
||||
import org.xbib.time.chronic.tags.Separator;
|
||||
import org.xbib.time.chronic.tags.TimeZone;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Chronic {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Chronic.class.getName());
|
||||
|
||||
private Chronic() {
|
||||
}
|
||||
|
||||
|
@ -74,7 +83,8 @@ public class Chronic {
|
|||
try {
|
||||
tokens = (List<Token>) optionScannerClass.getMethod("scan", List.class, Options.class)
|
||||
.invoke(null, tokens, options);
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
throw new ParseException("failed to scan tokens", 0);
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +99,8 @@ public class Chronic {
|
|||
try {
|
||||
tokens = (List<Token>) scannerClass.getMethod("scan", List.class, Options.class)
|
||||
.invoke(null, tokens, options);
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
throw new ParseException("failed to scan tokens", 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,19 @@ public class Span extends Range {
|
|||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ zoneId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Span &&
|
||||
((Span) obj).getBegin().equals(getBegin()) &&
|
||||
((Span) obj).getEnd().equals(getEnd()) &&
|
||||
((Span) obj).zoneId.equals(zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + DateTimeFormatter.ISO_INSTANT.format(getBeginCalendar())
|
||||
|
|
|
@ -62,7 +62,7 @@ public class Token {
|
|||
public <T extends Tag<?>> T getTag(Class<T> tagClass) {
|
||||
List<T> matches = getTags(tagClass);
|
||||
T matchingTag = null;
|
||||
if (matches.size() > 0) {
|
||||
if (!matches.isEmpty()) {
|
||||
matchingTag = matches.get(0);
|
||||
}
|
||||
return matchingTag;
|
||||
|
|
|
@ -30,11 +30,7 @@ import org.xbib.time.chronic.tags.TimeZone;
|
|||
import java.text.ParseException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -57,7 +53,7 @@ public class Handler {
|
|||
|
||||
public static synchronized Map<HandlerType, List<Handler>> definitions() {
|
||||
if (definitions == null) {
|
||||
Map<HandlerType, List<Handler>> definitions = new HashMap<>();
|
||||
Map<HandlerType, List<Handler>> definitions = new EnumMap<>(HandlerType.class);
|
||||
|
||||
List<Handler> timeHandlers = new LinkedList<>();
|
||||
timeHandlers.add(new Handler(null, new TagPattern(RepeaterTime.class),
|
||||
|
@ -227,7 +223,7 @@ public class Handler {
|
|||
if (grabberType == Grabber.Relative.LAST) {
|
||||
outerSpan = head.nextSpan(PointerType.PAST);
|
||||
} else if (grabberType == Grabber.Relative.THIS) {
|
||||
if (repeaters.size() > 0) {
|
||||
if (!repeaters.isEmpty()) {
|
||||
outerSpan = head.thisSpan(PointerType.NONE);
|
||||
} else {
|
||||
outerSpan = head.thisSpan(options.getContext());
|
||||
|
@ -273,11 +269,11 @@ public class Handler {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<Token> dealiasAndDisambiguateTimes(List<Token> tokens, Options options) {
|
||||
public static List<Token> dealiasAndDisambiguateTimes(List<Token> tokenList, Options options) {
|
||||
// handle aliases of am/pm
|
||||
// 5:00 in the morning => 5:00 am
|
||||
// 7:00 in the evening => 7:00 pm
|
||||
|
||||
List<Token> tokens = tokenList;
|
||||
int dayPortionIndex = -1;
|
||||
int tokenSize = tokens.size();
|
||||
for (int i = 0; dayPortionIndex == -1 && i < tokenSize; i++) {
|
||||
|
@ -347,8 +343,8 @@ public class Handler {
|
|||
for (HandlerPattern pattern : patterns) {
|
||||
boolean optional = pattern.isOptional();
|
||||
if (pattern instanceof TagPattern) {
|
||||
boolean match = (tokenIndex < tokens.size() &&
|
||||
tokens.get(tokenIndex).getTags(((TagPattern) pattern).getTagClass()).size() > 0);
|
||||
boolean match = tokenIndex < tokens.size() &&
|
||||
tokens.get(tokenIndex).getTags(((TagPattern) pattern).getTagClass()).size() > 0;
|
||||
if (!match && !optional) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.List;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IHandler {
|
||||
Span handle(List<Token> tokens, Options options);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
*/
|
||||
public class ORGRHandler extends ORRHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
Span outerSpan = Handler.getAnchor(tokens.subList(2, 4), options);
|
||||
return handle(tokens.subList(0, 2), outerSpan, options);
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
*/
|
||||
public class ORSRHandler extends ORRHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
Span outerSpan = Handler.getAnchor(tokens.subList(3, 4), options);
|
||||
return handle(tokens.subList(0, 2), outerSpan, options);
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
*/
|
||||
public class RHandler implements IHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
List<Token> ddTokens = Handler.dealiasAndDisambiguateTimes(tokens, options);
|
||||
return Handler.getAnchor(ddTokens, options);
|
||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
|||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RdnRmnSdTTzSyHandler implements IHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RdnRmnSdTTzSyHandler.class.getName());
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(1).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||
int day = tokens.get(2).getTag(ScalarDay.class).getType();
|
||||
|
@ -25,6 +30,7 @@ public class RdnRmnSdTTzSyHandler implements IHandler {
|
|||
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
span = null;
|
||||
}
|
||||
return span;
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.List;
|
|||
*
|
||||
*/
|
||||
public class RmnOdHandler extends MDHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
||||
tokens.get(1).getTag(OrdinalDay.class), tokens.subList(2, tokens.size()), options);
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.List;
|
|||
*
|
||||
*/
|
||||
public class RmnSdHandler extends MDHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
||||
tokens.get(1).getTag(ScalarDay.class), tokens.subList(2, tokens.size()), options);
|
||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
|||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RmnSdSyHandler implements IHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RmnSdSyHandler.class.getName());
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||
int day = tokens.get(1).getTag(ScalarDay.class).getType();
|
||||
|
@ -25,6 +30,7 @@ public class RmnSdSyHandler implements IHandler {
|
|||
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
span = null;
|
||||
}
|
||||
return span;
|
||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RmnSyHandler implements IHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RmnSyHandler.class.getName());
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||
int year = tokens.get(1).getTag(ScalarYear.class).getType();
|
||||
|
@ -24,6 +29,7 @@ public class RmnSyHandler implements IHandler {
|
|||
ZonedDateTime end = start.plus(1, ChronoUnit.MONTHS);
|
||||
span = new Span(start, end);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
span = null;
|
||||
}
|
||||
return span;
|
||||
|
|
|
@ -14,6 +14,8 @@ import java.util.List;
|
|||
*
|
||||
*/
|
||||
public class SmSdHandler implements IHandler {
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||
int day = tokens.get(1).getTag(ScalarDay.class).getType();
|
||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
|||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SmSdSyHandler implements IHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SmSdSyHandler.class.getName());
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||
int day = tokens.get(1).getTag(ScalarDay.class).getType();
|
||||
|
@ -25,6 +30,7 @@ public class SmSdSyHandler implements IHandler {
|
|||
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
span = null;
|
||||
}
|
||||
return span;
|
||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SmSyHandler implements IHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SmSyHandler.class.getName());
|
||||
|
||||
@Override
|
||||
public Span handle(List<Token> tokens, Options options) {
|
||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||
int year = tokens.get(1).getTag(ScalarYear.class).getType();
|
||||
|
@ -24,6 +29,7 @@ public class SmSyHandler implements IHandler {
|
|||
ZonedDateTime end = start.plus(1, ChronoUnit.MONTHS);
|
||||
span = new Span(start, end);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
span = null;
|
||||
}
|
||||
return span;
|
||||
|
|
|
@ -125,10 +125,10 @@ public class Numerizer {
|
|||
}
|
||||
|
||||
private static String andition(String str) {
|
||||
StringBuffer anditionStr = new StringBuffer(str);
|
||||
StringBuilder anditionStr = new StringBuilder(str);
|
||||
Matcher matcher = Numerizer.ANDITION_PATTERN.matcher(anditionStr);
|
||||
while (matcher.find()) {
|
||||
if (matcher.group(2).equalsIgnoreCase(" and ") || matcher.group(1).length() > matcher.group(3).length()) {
|
||||
if (" and ".equalsIgnoreCase(matcher.group(2)) || matcher.group(1).length() > matcher.group(3).length()) {
|
||||
anditionStr.replace(matcher.start(), matcher.end(),
|
||||
String.valueOf(Integer.parseInt(matcher.group(1).trim()) +
|
||||
Integer.parseInt(matcher.group(3).trim())));
|
||||
|
@ -141,7 +141,7 @@ public class Numerizer {
|
|||
/**
|
||||
*
|
||||
*/
|
||||
static class DirectNum {
|
||||
private static class DirectNum {
|
||||
private Pattern name;
|
||||
private String number;
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ public abstract class Repeater<T> extends Tag<T> implements Comparable<Repeater<
|
|||
return tokens;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Repeater<?> other) {
|
||||
return Integer.compare(getWidth(), other.getWidth());
|
||||
}
|
||||
|
@ -84,6 +85,18 @@ public abstract class Repeater<T> extends Tag<T> implements Comparable<Repeater<
|
|||
|
||||
public abstract Span getOffset(Span span, int amount, Pointer.PointerType pointer);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getType().hashCode() ^ getNow().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof Repeater &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "repeater";
|
||||
|
|
|
@ -64,6 +64,18 @@ public class RepeaterDay extends RepeaterUnit {
|
|||
return RepeaterDay.DAY_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterDay &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-day";
|
||||
|
|
|
@ -15,7 +15,7 @@ import java.util.regex.Pattern;
|
|||
*
|
||||
*/
|
||||
public class RepeaterDayName extends Repeater<RepeaterDayName.DayName> {
|
||||
public static final int DAY_SECONDS = 86400; // (24 * 60 * 60);
|
||||
private static final int DAY_SECONDS = 86400;
|
||||
private static final Pattern MON_PATTERN = Pattern.compile("^m[ou]n(day)?$");
|
||||
private static final Pattern TUE_PATTERN = Pattern.compile("^t(ue|eu|oo|u|)s(day)?$");
|
||||
private static final Pattern TUE_PATTERN_1 = Pattern.compile("^tue$");
|
||||
|
@ -69,17 +69,14 @@ public class RepeaterDayName extends Repeater<RepeaterDayName.DayName> {
|
|||
currentDayStart = currentDayStart.plus(direction, ChronoUnit.DAYS);
|
||||
}
|
||||
} else {
|
||||
currentDayStart = currentDayStart.plus(direction * 7, ChronoUnit.DAYS);
|
||||
currentDayStart = currentDayStart.plus(direction * 7L, ChronoUnit.DAYS);
|
||||
}
|
||||
return new Span(currentDayStart, ChronoUnit.DAYS, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span internalThisSpan(PointerType pointer) {
|
||||
if (pointer == PointerType.NONE) {
|
||||
pointer = PointerType.FUTURE;
|
||||
}
|
||||
return super.nextSpan(pointer);
|
||||
return super.nextSpan(pointer == PointerType.NONE ? PointerType.FUTURE : pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,6 +89,18 @@ public class RepeaterDayName extends Repeater<RepeaterDayName.DayName> {
|
|||
return RepeaterDayName.DAY_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterDayName &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-dayname-" + getType();
|
||||
|
|
|
@ -58,7 +58,6 @@ public abstract class RepeaterDayPortion<T> extends Repeater<T> {
|
|||
@Override
|
||||
protected Span internalNextSpan(PointerType pointer) {
|
||||
ZonedDateTime rangeStart;
|
||||
ZonedDateTime rangeEnd;
|
||||
if (currentSpan == null) {
|
||||
long nowSeconds = getNow().toInstant().getEpochSecond() - ymd(getNow()).toInstant().getEpochSecond();
|
||||
if (nowSeconds < range.getBegin()) {
|
||||
|
@ -133,6 +132,18 @@ public abstract class RepeaterDayPortion<T> extends Repeater<T> {
|
|||
|
||||
protected abstract Range createRange(T type);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterDayPortion &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-dayportion-" + getType();
|
||||
|
|
|
@ -35,7 +35,7 @@ public class RepeaterFortnight extends RepeaterUnit {
|
|||
Span lastSundaySpan = sundayRepeater.nextSpan(PointerType.PAST);
|
||||
currentFortnightStart = lastSundaySpan.getBeginCalendar();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unable to handle pointer " + pointer + ".");
|
||||
throw new IllegalArgumentException("Unable to handle pointer " + pointer);
|
||||
}
|
||||
} else {
|
||||
long direction = (pointer == PointerType.FUTURE) ? 1L : -1L;
|
||||
|
@ -47,11 +47,11 @@ public class RepeaterFortnight extends RepeaterUnit {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Span internalThisSpan(PointerType pointer) {
|
||||
protected Span internalThisSpan(PointerType pointerType) {
|
||||
PointerType pointer = pointerType;
|
||||
if (pointer == null) {
|
||||
pointer = PointerType.FUTURE;
|
||||
}
|
||||
|
||||
Span span;
|
||||
if (pointer == PointerType.FUTURE) {
|
||||
ZonedDateTime thisFortnightStart = ymdh(getNow()).plus(RepeaterHour.HOUR_SECONDS, ChronoUnit.SECONDS);
|
||||
|
@ -86,6 +86,18 @@ public class RepeaterFortnight extends RepeaterUnit {
|
|||
return RepeaterFortnight.FORTNIGHT_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterFortnight &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-fortnight";
|
||||
|
|
|
@ -71,6 +71,18 @@ public class RepeaterHour extends RepeaterUnit {
|
|||
return RepeaterHour.HOUR_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterHour &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-hour";
|
||||
|
|
|
@ -66,6 +66,18 @@ public class RepeaterMinute extends RepeaterUnit {
|
|||
return RepeaterMinute.MINUTE_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterMinute &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-minute";
|
||||
|
|
|
@ -66,6 +66,18 @@ public class RepeaterMonth extends RepeaterUnit {
|
|||
return RepeaterMonth.MONTH_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterMonth &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-month";
|
||||
|
|
|
@ -76,14 +76,14 @@ public class RepeaterMonthName extends Repeater<RepeaterMonthName.MonthName> {
|
|||
} else if (pointer == PointerType.NONE) {
|
||||
if (nowMonth <= targetMonth) {
|
||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone());
|
||||
} else if (nowMonth > targetMonth) {
|
||||
} else {
|
||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone())
|
||||
.plus(1, ChronoUnit.YEARS);
|
||||
}
|
||||
} else if (pointer == PointerType.PAST) {
|
||||
if (nowMonth > targetMonth) {
|
||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone());
|
||||
} else if (nowMonth <= targetMonth) {
|
||||
} else {
|
||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone())
|
||||
.minus(1, ChronoUnit.YEARS);
|
||||
}
|
||||
|
@ -129,6 +129,18 @@ public class RepeaterMonthName extends Repeater<RepeaterMonthName.MonthName> {
|
|||
return RepeaterMonthName.MONTH_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterMonthName &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-monthname-" + getType();
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.time.temporal.ChronoUnit;
|
|||
*
|
||||
*/
|
||||
public class RepeaterSecond extends RepeaterUnit {
|
||||
public static final int SECOND_SECONDS = 1; // (60 * 60);
|
||||
private static final int SECOND_SECONDS = 1;
|
||||
|
||||
private ZonedDateTime secondStart;
|
||||
|
||||
|
@ -42,6 +42,18 @@ public class RepeaterSecond extends RepeaterUnit {
|
|||
return RepeaterSecond.SECOND_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterSecond &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-second";
|
||||
|
|
|
@ -37,8 +37,8 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
int minutesInSeconds = Integer.parseInt(t.substring(1)) * 60;
|
||||
type = new Tick(hoursInSeconds + minutesInSeconds, true);
|
||||
} else if (length == 4) {
|
||||
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||
Integer.parseInt(t.substring(0, 2)) <= 12);
|
||||
boolean ambiguous = time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||
Integer.parseInt(t.substring(0, 2)) <= 12;
|
||||
int hours = Integer.parseInt(t.substring(0, 2));
|
||||
int hoursInSeconds = hours * 60 * 60;
|
||||
int minutesInSeconds = Integer.parseInt(t.substring(2)) * 60;
|
||||
|
@ -53,8 +53,8 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
int seconds = Integer.parseInt(t.substring(3));
|
||||
type = new Tick(hoursInSeconds + minutesInSeconds + seconds, true);
|
||||
} else if (length == 6) {
|
||||
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||
Integer.parseInt(t.substring(0, 2)) <= 12);
|
||||
boolean ambiguous = time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||
Integer.parseInt(t.substring(0, 2)) <= 12;
|
||||
int hours = Integer.parseInt(t.substring(0, 2));
|
||||
int hoursInSeconds = hours * 60 * 60;
|
||||
int minutesInSeconds = Integer.parseInt(t.substring(2, 4)) * 60;
|
||||
|
@ -136,7 +136,7 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
if (tick.isAmbiguous()) {
|
||||
List<ZonedDateTime> futureDates = new LinkedList<>();
|
||||
futureDates.add(midnight.plus(tick.intValue(), ChronoUnit.SECONDS));
|
||||
futureDates.add(midnight.plus(halfDay + tick.intValue(), ChronoUnit.SECONDS));
|
||||
futureDates.add(midnight.plus(halfDay + (long) tick.intValue(), ChronoUnit.SECONDS));
|
||||
futureDates.add(tomorrowMidnight.plus(tick.intValue(), ChronoUnit.SECONDS));
|
||||
for (ZonedDateTime futureDate : futureDates) {
|
||||
if (futureDate.isAfter(now) || futureDate.equals(now)) {
|
||||
|
@ -160,9 +160,9 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
} else {
|
||||
if (tick.isAmbiguous()) {
|
||||
List<ZonedDateTime> pastDates = new LinkedList<>();
|
||||
pastDates.add(midnight.plus(halfDay + tick.intValue(), ChronoUnit.SECONDS));
|
||||
pastDates.add(midnight.plus(halfDay + (long) tick.intValue(), ChronoUnit.SECONDS));
|
||||
pastDates.add(midnight.plus(tick.intValue(), ChronoUnit.SECONDS));
|
||||
pastDates.add(yesterdayMidnight.plus(tick.intValue() * 2, ChronoUnit.SECONDS));
|
||||
pastDates.add(yesterdayMidnight.plus(tick.intValue() * 2L, ChronoUnit.SECONDS));
|
||||
for (ZonedDateTime pastDate : pastDates) {
|
||||
if (pastDate.isBefore(now) || pastDate.equals(now)) {
|
||||
currentTime = pastDate;
|
||||
|
@ -190,8 +190,8 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
}
|
||||
|
||||
if (!first) {
|
||||
int increment = (tick.isAmbiguous()) ? halfDay : fullDay;
|
||||
int direction = (pointer == PointerType.FUTURE) ? 1 : -1;
|
||||
int increment = tick.isAmbiguous() ? halfDay : fullDay;
|
||||
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||
currentTime = currentTime.plus(direction * increment, ChronoUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
@ -200,10 +200,7 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
|
||||
@Override
|
||||
protected Span internalThisSpan(PointerType pointer) {
|
||||
if (pointer == PointerType.NONE) {
|
||||
pointer = PointerType.FUTURE;
|
||||
}
|
||||
return nextSpan(pointer);
|
||||
return nextSpan(pointer == PointerType.NONE ? PointerType.FUTURE : pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -216,6 +213,18 @@ public class RepeaterTime extends Repeater<Tick> {
|
|||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterTime &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-time-" + getType();
|
||||
|
|
|
@ -47,11 +47,23 @@ public abstract class RepeaterUnit extends Repeater<Object> {
|
|||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to create RepeaterUnit.", t);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to create RepeaterUnit", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterUnit &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.time.temporal.ChronoUnit;
|
|||
*
|
||||
*/
|
||||
public class RepeaterWeek extends RepeaterUnit {
|
||||
public static final int WEEK_SECONDS = 604800; // (7 * 24 * 60 * 60);
|
||||
public static final int WEEK_SECONDS = 604800;
|
||||
public static final int WEEK_DAYS = 7;
|
||||
|
||||
private ZonedDateTime currentWeekStart;
|
||||
|
@ -38,7 +38,7 @@ public class RepeaterWeek extends RepeaterUnit {
|
|||
throw new IllegalArgumentException("Unable to handle pointer " + pointer + ".");
|
||||
}
|
||||
} else {
|
||||
int direction = (pointer == PointerType.FUTURE) ? 1 : -1;
|
||||
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||
currentWeekStart = currentWeekStart.plus(RepeaterWeek.WEEK_DAYS * direction, ChronoUnit.DAYS);
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,18 @@ public class RepeaterWeek extends RepeaterUnit {
|
|||
return RepeaterWeek.WEEK_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterWeek &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-week";
|
||||
|
|
|
@ -16,26 +16,35 @@ public class RepeaterWeekend extends RepeaterUnit {
|
|||
|
||||
@Override
|
||||
protected Span internalNextSpan(PointerType pointer) {
|
||||
if (currentWeekStart == null) {
|
||||
if (pointer == PointerType.FUTURE) {
|
||||
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
||||
saturdayRepeater.setNow(getNow());
|
||||
Span nextSaturdaySpan = saturdayRepeater.nextSpan(PointerType.FUTURE);
|
||||
currentWeekStart = nextSaturdaySpan.getBeginCalendar();
|
||||
} else if (pointer == PointerType.PAST) {
|
||||
if (currentWeekStart != null) {
|
||||
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||
currentWeekStart = currentWeekStart.plus(direction * RepeaterWeek.WEEK_SECONDS, ChronoUnit.SECONDS);
|
||||
ZonedDateTime c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||
return new Span(currentWeekStart, c);
|
||||
}
|
||||
ZonedDateTime c;
|
||||
switch (pointer) {
|
||||
case PAST: {
|
||||
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
||||
saturdayRepeater.setNow(getNow().plus(RepeaterDay.DAY_SECONDS, ChronoUnit.SECONDS));
|
||||
Span lastSaturdaySpan = saturdayRepeater.nextSpan(PointerType.PAST);
|
||||
currentWeekStart = lastSaturdaySpan.getBeginCalendar();
|
||||
}
|
||||
} else {
|
||||
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||
currentWeekStart = currentWeekStart.plus(direction * RepeaterWeek.WEEK_SECONDS, ChronoUnit.SECONDS);
|
||||
}
|
||||
assert currentWeekStart != null;
|
||||
ZonedDateTime c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||
c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||
return new Span(currentWeekStart, c);
|
||||
}
|
||||
case FUTURE: {
|
||||
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
||||
saturdayRepeater.setNow(getNow());
|
||||
Span nextSaturdaySpan = saturdayRepeater.nextSpan(PointerType.FUTURE);
|
||||
currentWeekStart = nextSaturdaySpan.getBeginCalendar();
|
||||
c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||
return new Span(currentWeekStart, c);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("pointer type not expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Span internalThisSpan(PointerType pointer) {
|
||||
|
@ -73,6 +82,18 @@ public class RepeaterWeekend extends RepeaterUnit {
|
|||
return (int) RepeaterWeekend.WEEKEND_SECONDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterWeekend &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-weekend";
|
||||
|
|
|
@ -70,6 +70,18 @@ public class RepeaterYear extends RepeaterUnit {
|
|||
return (365 * 24 * 60 * 60);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof RepeaterYear &&
|
||||
((Repeater) other).getType().equals(getType()) &&
|
||||
((Repeater) other).getNow().equals(getNow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-year";
|
||||
|
|
109
src/main/java/org/xbib/time/format/Composite.java
Normal file
109
src/main/java/org/xbib/time/format/Composite.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Period;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Composite implementation that merges other fields to create a full pattern.
|
||||
*/
|
||||
class Composite implements PeriodPrinter, PeriodParser {
|
||||
|
||||
private final PeriodPrinter[] iPrinters;
|
||||
private final PeriodParser[] iParsers;
|
||||
|
||||
Composite(List<Object> elementPairs) {
|
||||
List<Object> printerList = new ArrayList<>();
|
||||
List<Object> parserList = new ArrayList<>();
|
||||
|
||||
decompose(elementPairs, printerList, parserList);
|
||||
|
||||
if (printerList.isEmpty()) {
|
||||
iPrinters = null;
|
||||
} else {
|
||||
iPrinters = printerList.toArray(new PeriodPrinter[printerList.size()]);
|
||||
}
|
||||
|
||||
if (parserList.isEmpty()) {
|
||||
iParsers = null;
|
||||
} else {
|
||||
iParsers = parserList.toArray(new PeriodParser[parserList.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
public int countFieldsToPrint(Period period, int stopAt, Locale locale) {
|
||||
int sum = 0;
|
||||
PeriodPrinter[] printers = iPrinters;
|
||||
for (int i = printers.length; sum < stopAt && --i >= 0; ) {
|
||||
sum += printers[i].countFieldsToPrint(period, Integer.MAX_VALUE, locale);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(Period period, Locale locale) {
|
||||
int sum = 0;
|
||||
PeriodPrinter[] printers = iPrinters;
|
||||
for (int i = printers.length; --i >= 0; ) {
|
||||
sum += printers[i].calculatePrintedLength(period, locale);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, Period period, Locale locale) {
|
||||
for (PeriodPrinter printer : iPrinters) {
|
||||
printer.printTo(buf, period, locale);
|
||||
}
|
||||
}
|
||||
|
||||
public void printTo(Writer out, Period period, Locale locale) throws IOException {
|
||||
for (PeriodPrinter printer : iPrinters) {
|
||||
printer.printTo(out, period, locale);
|
||||
}
|
||||
}
|
||||
|
||||
public int parseInto(PeriodAmount period, String periodStr, int pos, Locale locale) {
|
||||
int position = pos;
|
||||
PeriodParser[] parsers = iParsers;
|
||||
if (parsers == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
int len = parsers.length;
|
||||
for (int i = 0; i < len && position >= 0; i++) {
|
||||
position = parsers[i].parseInto(period, periodStr, position, locale);
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
private void decompose(List<Object> elementPairs, List<Object> printerList, List<Object> parserList) {
|
||||
int size = elementPairs.size();
|
||||
for (int i = 0; i < size; i += 2) {
|
||||
Object element = elementPairs.get(i);
|
||||
if (element instanceof PeriodPrinter) {
|
||||
if (element instanceof Composite) {
|
||||
addArrayToList(printerList, ((Composite) element).iPrinters);
|
||||
} else {
|
||||
printerList.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
element = elementPairs.get(i + 1);
|
||||
if (element instanceof PeriodParser) {
|
||||
if (element instanceof Composite) {
|
||||
addArrayToList(parserList, ((Composite) element).iParsers);
|
||||
} else {
|
||||
parserList.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addArrayToList(List<Object> list, Object[] array) {
|
||||
if (array != null) {
|
||||
Collections.addAll(list, array);
|
||||
}
|
||||
}
|
||||
}
|
82
src/main/java/org/xbib/time/format/CompositeAffix.java
Normal file
82
src/main/java/org/xbib/time/format/CompositeAffix.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Builds a composite affix by merging two other affix implementations.
|
||||
*/
|
||||
class CompositeAffix extends IgnorableAffix {
|
||||
private final PeriodFieldAffix iLeft;
|
||||
private final PeriodFieldAffix iRight;
|
||||
private final String[] iLeftRightCombinations;
|
||||
|
||||
CompositeAffix(PeriodFieldAffix left, PeriodFieldAffix right) {
|
||||
iLeft = left;
|
||||
iRight = right;
|
||||
|
||||
// We need to construct all possible combinations of left and right.
|
||||
// We are doing it once in constructor so that getAffixes() is quicker.
|
||||
Set<String> result = new HashSet<String>();
|
||||
for (String leftText : iLeft.getAffixes()) {
|
||||
for (String rightText : iRight.getAffixes()) {
|
||||
result.add(leftText + rightText);
|
||||
}
|
||||
}
|
||||
iLeftRightCombinations = result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculatePrintedLength(int value) {
|
||||
return iLeft.calculatePrintedLength(value)
|
||||
+ iRight.calculatePrintedLength(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printTo(StringBuilder buf, int value) {
|
||||
iLeft.printTo(buf, value);
|
||||
iRight.printTo(buf, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printTo(Writer out, int value) throws IOException {
|
||||
iLeft.printTo(out, value);
|
||||
iRight.printTo(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String periodStr, int position) {
|
||||
int pos = iLeft.parse(periodStr, position);
|
||||
if (pos >= 0) {
|
||||
pos = iRight.parse(periodStr, pos);
|
||||
if (pos >= 0 && matchesOtherAffix(parse(periodStr, pos) - pos, periodStr, position)) {
|
||||
return ~position;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scan(String periodStr, final int position) {
|
||||
int leftPosition = iLeft.scan(periodStr, position);
|
||||
if (leftPosition >= 0) {
|
||||
int rightPosition = iRight.scan(periodStr, iLeft.parse(periodStr, leftPosition));
|
||||
if (!(rightPosition >= 0 && matchesOtherAffix(iRight.parse(periodStr, rightPosition) -
|
||||
leftPosition, periodStr, position))) {
|
||||
if (leftPosition > 0) {
|
||||
return leftPosition;
|
||||
} else {
|
||||
return rightPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAffixes() {
|
||||
return iLeftRightCombinations.clone();
|
||||
}
|
||||
}
|
303
src/main/java/org/xbib/time/format/FieldFormatter.java
Normal file
303
src/main/java/org/xbib/time/format/FieldFormatter.java
Normal file
|
@ -0,0 +1,303 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Period;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Formats the numeric value of a field, potentially with prefix/suffix.
|
||||
*/
|
||||
class FieldFormatter implements PeriodPrinter, PeriodParser {
|
||||
private final int iMinPrintedDigits;
|
||||
//private final int iPrintZeroSetting;
|
||||
private final int iMaxParsedDigits;
|
||||
private final boolean iRejectSignedValues;
|
||||
|
||||
/**
|
||||
* The index of the field type, 0=year, etc.
|
||||
*/
|
||||
private final ChronoUnit unit;
|
||||
/**
|
||||
* The array of the latest formatter added for each type.
|
||||
* This is shared between all the field formatters in a formatter.
|
||||
*/
|
||||
//private final FieldFormatter[] iFieldFormatters;
|
||||
|
||||
private final PeriodFieldAffix iPrefix;
|
||||
private final PeriodFieldAffix iSuffix;
|
||||
|
||||
FieldFormatter(int minPrintedDigits,
|
||||
int maxParsedDigits, boolean rejectSignedValues,
|
||||
ChronoUnit chronoUnit,
|
||||
PeriodFieldAffix prefix, PeriodFieldAffix suffix) {
|
||||
iMinPrintedDigits = minPrintedDigits;
|
||||
iMaxParsedDigits = maxParsedDigits;
|
||||
iRejectSignedValues = rejectSignedValues;
|
||||
this.unit = chronoUnit;
|
||||
iPrefix = prefix;
|
||||
iSuffix = suffix;
|
||||
}
|
||||
|
||||
FieldFormatter(FieldFormatter field, PeriodFieldAffix periodFieldAffix) {
|
||||
PeriodFieldAffix suffix = periodFieldAffix;
|
||||
iMinPrintedDigits = field.iMinPrintedDigits;
|
||||
iMaxParsedDigits = field.iMaxParsedDigits;
|
||||
iRejectSignedValues = field.iRejectSignedValues;
|
||||
this.unit = field.unit;
|
||||
iPrefix = field.iPrefix;
|
||||
if (field.iSuffix != null) {
|
||||
suffix = new CompositeAffix(field.iSuffix, suffix);
|
||||
}
|
||||
iSuffix = suffix;
|
||||
}
|
||||
|
||||
public void finish(FieldFormatter[] fieldFormatters) {
|
||||
Set<PeriodFieldAffix> prefixesToIgnore = new HashSet<>();
|
||||
Set<PeriodFieldAffix> suffixesToIgnore = new HashSet<>();
|
||||
for (FieldFormatter fieldFormatter : fieldFormatters) {
|
||||
if (fieldFormatter != null && !this.equals(fieldFormatter)) {
|
||||
prefixesToIgnore.add(fieldFormatter.iPrefix);
|
||||
suffixesToIgnore.add(fieldFormatter.iSuffix);
|
||||
}
|
||||
}
|
||||
// if we have a prefix then allow ignore behaviour
|
||||
if (iPrefix != null) {
|
||||
iPrefix.finish(prefixesToIgnore);
|
||||
}
|
||||
// if we have a suffix then allow ignore behaviour
|
||||
if (iSuffix != null) {
|
||||
iSuffix.finish(suffixesToIgnore);
|
||||
}
|
||||
}
|
||||
|
||||
public int countFieldsToPrint(Period period, int stopAt, Locale locale) {
|
||||
if (stopAt <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (getFieldValue(period) != Long.MAX_VALUE) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(Period period, Locale locale) {
|
||||
long valueLong = getFieldValue(period);
|
||||
if (valueLong == Long.MAX_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sum = Math.max(FormatUtils.calculateDigitCount(valueLong), iMinPrintedDigits);
|
||||
int value = (int) valueLong;
|
||||
|
||||
if (iPrefix != null) {
|
||||
sum += iPrefix.calculatePrintedLength(value);
|
||||
}
|
||||
if (iSuffix != null) {
|
||||
sum += iSuffix.calculatePrintedLength(value);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, Period period, Locale locale) {
|
||||
long valueLong = getFieldValue(period);
|
||||
if (valueLong == Long.MAX_VALUE) {
|
||||
return;
|
||||
}
|
||||
int value = (int) valueLong;
|
||||
if (iPrefix != null) {
|
||||
iPrefix.printTo(buf, value);
|
||||
}
|
||||
int minDigits = iMinPrintedDigits;
|
||||
if (minDigits <= 1) {
|
||||
FormatUtils.appendUnpaddedInteger(buf, value);
|
||||
} else {
|
||||
FormatUtils.appendPaddedInteger(buf, value, minDigits);
|
||||
}
|
||||
if (iSuffix != null) {
|
||||
iSuffix.printTo(buf, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void printTo(Writer out, Period period, Locale locale) throws IOException {
|
||||
long valueLong = getFieldValue(period);
|
||||
if (valueLong == Long.MAX_VALUE) {
|
||||
return;
|
||||
}
|
||||
int value = (int) valueLong;
|
||||
if (iPrefix != null) {
|
||||
iPrefix.printTo(out, value);
|
||||
}
|
||||
int minDigits = iMinPrintedDigits;
|
||||
if (minDigits <= 1) {
|
||||
FormatUtils.writeUnpaddedInteger(out, value);
|
||||
} else {
|
||||
FormatUtils.writePaddedInteger(out, value, minDigits);
|
||||
}
|
||||
if (iSuffix != null) {
|
||||
iSuffix.printTo(out, value);
|
||||
}
|
||||
}
|
||||
|
||||
public int parseInto(PeriodAmount period, String text, int pos, Locale locale) {
|
||||
int position = pos;
|
||||
if (position >= text.length()) {
|
||||
return ~position;
|
||||
}
|
||||
|
||||
if (iPrefix != null) {
|
||||
position = iPrefix.parse(text, position);
|
||||
if (position < 0) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
int limit;
|
||||
limit = Math.min(iMaxParsedDigits, text.length() - position);
|
||||
int length = 0;
|
||||
boolean hasDigits = false;
|
||||
boolean negative;
|
||||
while (length < limit) {
|
||||
char c = text.charAt(position + length);
|
||||
if (length == 0 && (c == '-' || c == '+') && !iRejectSignedValues) {
|
||||
negative = c == '-';
|
||||
if (length + 1 >= limit ||
|
||||
(c = text.charAt(position + length + 1)) < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (negative) {
|
||||
length++;
|
||||
} else {
|
||||
position++;
|
||||
}
|
||||
limit = Math.min(limit + 1, text.length() - position);
|
||||
continue;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
hasDigits = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
length++;
|
||||
}
|
||||
|
||||
if (!hasDigits) {
|
||||
return ~position;
|
||||
}
|
||||
setFieldValue(period, unit, parseInt(text, position, length));
|
||||
position += length;
|
||||
if (position >= 0 && iSuffix != null) {
|
||||
position = iSuffix.parse(text, position);
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param text text to parse
|
||||
* @param pos position in text
|
||||
* @param len exact count of characters to parse
|
||||
* @return parsed int value
|
||||
*/
|
||||
private int parseInt(String text, int pos, int len) {
|
||||
int position = pos;
|
||||
int length = len;
|
||||
if (length >= 10) {
|
||||
return Integer.parseInt(text.substring(position, position + length));
|
||||
}
|
||||
if (length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int value = text.charAt(position++);
|
||||
length--;
|
||||
boolean negative;
|
||||
if (value == '-') {
|
||||
if (--length < 0) {
|
||||
return 0;
|
||||
}
|
||||
negative = true;
|
||||
value = text.charAt(position++);
|
||||
} else {
|
||||
negative = false;
|
||||
}
|
||||
value -= '0';
|
||||
while (length-- > 0) {
|
||||
value = ((value << 3) + (value << 1)) + text.charAt(position++) - '0';
|
||||
}
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Long.MAX_VALUE if nothing to print, otherwise value
|
||||
*/
|
||||
long getFieldValue(Period period) {
|
||||
long value;
|
||||
switch (unit) {
|
||||
default:
|
||||
return Long.MAX_VALUE;
|
||||
case YEARS:
|
||||
value = period.get(ChronoUnit.YEARS);
|
||||
break;
|
||||
case MONTHS:
|
||||
value = period.get(ChronoUnit.MONTHS);
|
||||
break;
|
||||
case WEEKS:
|
||||
value = period.get(ChronoUnit.WEEKS);
|
||||
break;
|
||||
case DAYS:
|
||||
value = period.get(ChronoUnit.DAYS);
|
||||
break;
|
||||
case HOURS:
|
||||
value = period.get(ChronoUnit.HOURS);
|
||||
break;
|
||||
case MINUTES:
|
||||
value = period.get(ChronoUnit.MINUTES);
|
||||
break;
|
||||
case SECONDS:
|
||||
value = period.get(ChronoUnit.SECONDS);
|
||||
break;
|
||||
case MILLIS:
|
||||
value = period.get(ChronoUnit.MILLIS);
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void setFieldValue(PeriodAmount period, ChronoUnit field, long value) {
|
||||
switch (field) {
|
||||
default:
|
||||
break;
|
||||
case YEARS:
|
||||
period.set(ChronoUnit.YEARS, value);
|
||||
break;
|
||||
case MONTHS:
|
||||
period.set(ChronoUnit.MONTHS, value);
|
||||
break;
|
||||
case WEEKS:
|
||||
period.set(ChronoUnit.WEEKS, value);
|
||||
break;
|
||||
case DAYS:
|
||||
period.set(ChronoUnit.DAYS, value);
|
||||
break;
|
||||
case HOURS:
|
||||
period.set(ChronoUnit.HOURS, value);
|
||||
break;
|
||||
case MINUTES:
|
||||
period.set(ChronoUnit.MINUTES, value);
|
||||
break;
|
||||
case SECONDS:
|
||||
period.set(ChronoUnit.SECONDS, value);
|
||||
break;
|
||||
case MILLIS:
|
||||
period.set(ChronoUnit.MILLIS, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ChronoUnit getFieldType() {
|
||||
return unit;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ package org.xbib.time.format;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Utility methods used by formatters.
|
||||
|
@ -9,6 +11,8 @@ import java.io.Writer;
|
|||
*/
|
||||
public class FormatUtils {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FormatUtils.class.getName());
|
||||
|
||||
private static final double LOG_10 = Math.log(10);
|
||||
|
||||
/**
|
||||
|
@ -31,7 +35,7 @@ public class FormatUtils {
|
|||
try {
|
||||
appendPaddedInteger((Appendable) buf, value, size);
|
||||
} catch (IOException e) {
|
||||
// StringBuilder does not throw IOException
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,11 +46,13 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param appenadble receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param size minimum amount of digits to append
|
||||
* @param intvalue value to convert to a string
|
||||
* @param digits minimum amount of digits to append
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void appendPaddedInteger(Appendable appenadble, int value, int size) throws IOException {
|
||||
public static void appendPaddedInteger(Appendable appenadble, int intvalue, int digits) throws IOException {
|
||||
int value = intvalue;
|
||||
int size = digits;
|
||||
if (value < 0) {
|
||||
appenadble.append('-');
|
||||
if (value != Integer.MIN_VALUE) {
|
||||
|
@ -55,7 +61,8 @@ public class FormatUtils {
|
|||
for (; size > 10; size--) {
|
||||
appenadble.append('0');
|
||||
}
|
||||
appenadble.append("" + -(long) Integer.MIN_VALUE);
|
||||
appenadble.append(Long.toString(Integer.MIN_VALUE));
|
||||
//.append("" + -(long) Integer.MIN_VALUE)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -76,15 +83,15 @@ public class FormatUtils {
|
|||
// Append remainder by calculating (value - d * 10).
|
||||
appenadble.append((char) (value - (d << 3) - (d << 1) + '0'));
|
||||
} else {
|
||||
int digits;
|
||||
int d;
|
||||
if (value < 1000) {
|
||||
digits = 3;
|
||||
d = 3;
|
||||
} else if (value < 10000) {
|
||||
digits = 4;
|
||||
d = 4;
|
||||
} else {
|
||||
digits = (int) (Math.log(value) / LOG_10) + 1;
|
||||
d = (int) (Math.log(value) / LOG_10) + 1;
|
||||
}
|
||||
for (; size > digits; size--) {
|
||||
for (; size > d; size--) {
|
||||
appenadble.append('0');
|
||||
}
|
||||
appenadble.append(Integer.toString(value));
|
||||
|
@ -105,7 +112,7 @@ public class FormatUtils {
|
|||
try {
|
||||
appendPaddedInteger((Appendable) buf, value, size);
|
||||
} catch (IOException e) {
|
||||
// StringBuilder does not throw IOException
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,12 +123,14 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param appendable receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param size minimum amount of digits to append
|
||||
* @param longvalue value to convert to a string
|
||||
* @param digits minimum amount of digits to append
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void appendPaddedInteger(Appendable appendable, long value, int size) throws IOException {
|
||||
public static void appendPaddedInteger(Appendable appendable, long longvalue, int digits) throws IOException {
|
||||
long value = longvalue;
|
||||
int intValue = (int) value;
|
||||
int size = digits;
|
||||
if (intValue == value) {
|
||||
appendPaddedInteger(appendable, intValue, size);
|
||||
} else if (size <= 19) {
|
||||
|
@ -139,8 +148,8 @@ public class FormatUtils {
|
|||
return;
|
||||
}
|
||||
}
|
||||
int digits = (int) (Math.log(value) / LOG_10) + 1;
|
||||
for (; size > digits; size--) {
|
||||
int d = (int) (Math.log(value) / LOG_10) + 1;
|
||||
for (; size > d; size--) {
|
||||
appendable.append('0');
|
||||
}
|
||||
appendable.append(Long.toString(value));
|
||||
|
@ -154,12 +163,14 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param out receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param size minimum amount of digits to append
|
||||
* @param val value to convert to a string
|
||||
* @param digits minimum amount of digits to append
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void writePaddedInteger(Writer out, int value, int size)
|
||||
public static void writePaddedInteger(Writer out, int val, int digits)
|
||||
throws IOException {
|
||||
int value = val;
|
||||
int size = digits;
|
||||
if (value < 0) {
|
||||
out.write('-');
|
||||
if (value != Integer.MIN_VALUE) {
|
||||
|
@ -168,7 +179,8 @@ public class FormatUtils {
|
|||
for (; size > 10; size--) {
|
||||
out.write('0');
|
||||
}
|
||||
out.write("" + -(long) Integer.MIN_VALUE);
|
||||
//out.write("" + -(long) Integer.MIN_VALUE);
|
||||
out.write(Long.toString(Integer.MIN_VALUE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -189,15 +201,15 @@ public class FormatUtils {
|
|||
// Append remainder by calculating (value - d * 10).
|
||||
out.write(value - (d << 3) - (d << 1) + '0');
|
||||
} else {
|
||||
int digits;
|
||||
int d;
|
||||
if (value < 1000) {
|
||||
digits = 3;
|
||||
d = 3;
|
||||
} else if (value < 10000) {
|
||||
digits = 4;
|
||||
d = 4;
|
||||
} else {
|
||||
digits = (int) (Math.log(value) / LOG_10) + 1;
|
||||
d = (int) (Math.log(value) / LOG_10) + 1;
|
||||
}
|
||||
for (; size > digits; size--) {
|
||||
for (; size > d; size--) {
|
||||
out.write('0');
|
||||
}
|
||||
out.write(Integer.toString(value));
|
||||
|
@ -211,12 +223,13 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param out receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param size minimum amount of digits to append
|
||||
* @param longvalue value to convert to a string
|
||||
* @param digits minimum amount of digits to append
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void writePaddedInteger(Writer out, long value, int size)
|
||||
throws IOException {
|
||||
public static void writePaddedInteger(Writer out, long longvalue, int digits) throws IOException {
|
||||
long value = longvalue;
|
||||
int size = digits;
|
||||
int intValue = (int) value;
|
||||
if (intValue == value) {
|
||||
writePaddedInteger(out, intValue, size);
|
||||
|
@ -235,8 +248,8 @@ public class FormatUtils {
|
|||
return;
|
||||
}
|
||||
}
|
||||
int digits = (int) (Math.log(value) / LOG_10) + 1;
|
||||
for (; size > digits; size--) {
|
||||
int d = (int) (Math.log(value) / LOG_10) + 1;
|
||||
for (; size > d; size--) {
|
||||
out.write('0');
|
||||
}
|
||||
out.write(Long.toString(value));
|
||||
|
@ -254,7 +267,7 @@ public class FormatUtils {
|
|||
try {
|
||||
appendUnpaddedInteger((Appendable) buf, value);
|
||||
} catch (IOException e) {
|
||||
// StringBuilder do not throw IOException
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,16 +277,17 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param appendable receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param val value to convert to a string
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void appendUnpaddedInteger(Appendable appendable, int value) throws IOException {
|
||||
public static void appendUnpaddedInteger(Appendable appendable, int val) throws IOException {
|
||||
int value = val;
|
||||
if (value < 0) {
|
||||
appendable.append('-');
|
||||
if (value != Integer.MIN_VALUE) {
|
||||
value = -value;
|
||||
} else {
|
||||
appendable.append("" + -(long) Integer.MIN_VALUE);
|
||||
appendable.append(Long.toString(Integer.MIN_VALUE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +317,7 @@ public class FormatUtils {
|
|||
try {
|
||||
appendUnpaddedInteger((Appendable) buf, value);
|
||||
} catch (IOException e) {
|
||||
// StringBuilder do not throw IOException
|
||||
logger.log(Level.FINE, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,17 +345,18 @@ public class FormatUtils {
|
|||
* This method is optimized for converting small values to strings.
|
||||
*
|
||||
* @param out receives integer converted to a string
|
||||
* @param value value to convert to a string
|
||||
* @param val value to convert to a string
|
||||
* @throws IOException exception
|
||||
*/
|
||||
public static void writeUnpaddedInteger(Writer out, int value)
|
||||
public static void writeUnpaddedInteger(Writer out, int val)
|
||||
throws IOException {
|
||||
int value = val;
|
||||
if (value < 0) {
|
||||
out.write('-');
|
||||
if (value != Integer.MIN_VALUE) {
|
||||
value = -value;
|
||||
} else {
|
||||
out.write("" + -(long) Integer.MIN_VALUE);
|
||||
out.write(Long.toString(Integer.MIN_VALUE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -394,12 +409,8 @@ public class FormatUtils {
|
|||
return 20;
|
||||
}
|
||||
}
|
||||
return
|
||||
(value < 10 ? 1 :
|
||||
(value < 100 ? 2 :
|
||||
(value < 1000 ? 3 :
|
||||
(value < 10000 ? 4 :
|
||||
((int) (Math.log(value) / LOG_10) + 1)))));
|
||||
return value < 10 ? 1 : value < 100 ? 2 : value < 1000 ? 3 : value < 10000 ? 4 :
|
||||
((int) (Math.log(value) / LOG_10) + 1);
|
||||
}
|
||||
|
||||
static int parseTwoDigits(CharSequence text, int position) {
|
||||
|
|
69
src/main/java/org/xbib/time/format/IgnorableAffix.java
Normal file
69
src/main/java/org/xbib/time/format/IgnorableAffix.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An affix that can be ignored.
|
||||
*/
|
||||
abstract class IgnorableAffix implements PeriodFieldAffix {
|
||||
private volatile String[] iOtherAffixes;
|
||||
|
||||
public void finish(Set<PeriodFieldAffix> periodFieldAffixesToIgnore) {
|
||||
if (iOtherAffixes == null) {
|
||||
// Calculate the shortest affix in this instance.
|
||||
int shortestAffixLength = Integer.MAX_VALUE;
|
||||
String shortestAffix = null;
|
||||
for (String affix : getAffixes()) {
|
||||
if (affix.length() < shortestAffixLength) {
|
||||
shortestAffixLength = affix.length();
|
||||
shortestAffix = affix;
|
||||
}
|
||||
}
|
||||
|
||||
// Pick only affixes that are longer than the shortest affix in this instance.
|
||||
// This will reduce the number of parse operations and thus speed up the PeriodFormatter.
|
||||
// also need to pick affixes that differ only in case (but not those that are identical)
|
||||
Set<String> affixesToIgnore = new HashSet<String>();
|
||||
for (PeriodFieldAffix periodFieldAffixToIgnore : periodFieldAffixesToIgnore) {
|
||||
if (periodFieldAffixToIgnore != null) {
|
||||
for (String affixToIgnore : periodFieldAffixToIgnore.getAffixes()) {
|
||||
if (affixToIgnore.length() > shortestAffixLength ||
|
||||
(affixToIgnore.equalsIgnoreCase(shortestAffix) &&
|
||||
!affixToIgnore.equals(shortestAffix))) {
|
||||
affixesToIgnore.add(affixToIgnore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iOtherAffixes = affixesToIgnore.toArray(new String[affixesToIgnore.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a match among the other affixes (stored internally)
|
||||
* that is longer than the passed value (textLength).
|
||||
*
|
||||
* @param textLength the length of the match
|
||||
* @param periodStr the Period string that will be parsed
|
||||
* @param position the position in the Period string at which the parsing should be started.
|
||||
* @return true if the other affixes (stored internally) contain a match
|
||||
* that is longer than the textLength parameter, false otherwise
|
||||
*/
|
||||
protected boolean matchesOtherAffix(int textLength, String periodStr, int position) {
|
||||
if (iOtherAffixes != null) {
|
||||
// ignore case when affix length differs
|
||||
// match case when affix length is same
|
||||
for (String affixToIgnore : iOtherAffixes) {
|
||||
int textToIgnoreLength = affixToIgnore.length();
|
||||
if ((textLength < textToIgnoreLength &&
|
||||
periodStr.regionMatches(true, position, affixToIgnore, 0, textToIgnoreLength)) ||
|
||||
(textLength == textToIgnoreLength &&
|
||||
periodStr.regionMatches(false, position, affixToIgnore, 0, textToIgnoreLength))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
42
src/main/java/org/xbib/time/format/Literal.java
Normal file
42
src/main/java/org/xbib/time/format/Literal.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Period;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Handles a simple literal piece of text.
|
||||
*/
|
||||
class Literal implements PeriodPrinter, PeriodParser {
|
||||
static final Literal EMPTY = new Literal("");
|
||||
private final String iText;
|
||||
|
||||
Literal(String text) {
|
||||
iText = text;
|
||||
}
|
||||
|
||||
public int countFieldsToPrint(Period period, int stopAt, Locale locale) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(Period period, Locale locale) {
|
||||
return iText.length();
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, Period period, Locale locale) {
|
||||
buf.append(iText);
|
||||
}
|
||||
|
||||
public void printTo(Writer out, Period period, Locale locale) throws IOException {
|
||||
out.write(iText);
|
||||
}
|
||||
|
||||
public int parseInto(PeriodAmount period, String periodStr,
|
||||
int position, Locale locale) {
|
||||
if (periodStr.regionMatches(true, position, iText, 0, iText.length())) {
|
||||
return position + iText.length();
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
}
|
46
src/main/java/org/xbib/time/format/PeriodFieldAffix.java
Normal file
46
src/main/java/org/xbib/time/format/PeriodFieldAffix.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Defines a formatted field's prefix or suffix text.
|
||||
* This can be used for fields such as 'n hours' or 'nH' or 'Hour:n'.
|
||||
*/
|
||||
interface PeriodFieldAffix {
|
||||
|
||||
int calculatePrintedLength(int value);
|
||||
|
||||
void printTo(StringBuilder buf, int value);
|
||||
|
||||
void printTo(Writer out, int value) throws IOException;
|
||||
|
||||
/**
|
||||
* @return new position after parsing affix, or ~position of failure
|
||||
*/
|
||||
int parse(String periodStr, int position);
|
||||
|
||||
/**
|
||||
* @return position where affix starts, or original ~position if not found
|
||||
*/
|
||||
int scan(String periodStr, int position);
|
||||
|
||||
/**
|
||||
* @return a copy of array of affixes
|
||||
*/
|
||||
String[] getAffixes();
|
||||
|
||||
/**
|
||||
* This method should be called only once.
|
||||
* After first call consecutive calls to this methods will have no effect.
|
||||
* Causes this affix to ignore a match (parse and scan
|
||||
* methods) if there is an affix in the passed list that holds
|
||||
* affix text which satisfy both following conditions:
|
||||
* - the affix text is also a match
|
||||
* - the affix text is longer than the match from this object
|
||||
*
|
||||
* @param affixesToIgnore affixes to ignore
|
||||
*/
|
||||
void finish(Set<PeriodFieldAffix> affixesToIgnore);
|
||||
}
|
File diff suppressed because it is too large
Load diff
82
src/main/java/org/xbib/time/format/PluralAffix.java
Normal file
82
src/main/java/org/xbib/time/format/PluralAffix.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Implements an affix where the text varies by the amount of the field.
|
||||
* Only singular (1) and plural (not 1) are supported.
|
||||
*/
|
||||
class PluralAffix extends IgnorableAffix {
|
||||
private final String iSingularText;
|
||||
private final String iPluralText;
|
||||
|
||||
PluralAffix(String singularText, String pluralText) {
|
||||
iSingularText = singularText;
|
||||
iPluralText = pluralText;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(int value) {
|
||||
return (value == 1 ? iSingularText : iPluralText).length();
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, int value) {
|
||||
buf.append(value == 1 ? iSingularText : iPluralText);
|
||||
}
|
||||
|
||||
public void printTo(Writer out, int value) throws IOException {
|
||||
out.write(value == 1 ? iSingularText : iPluralText);
|
||||
}
|
||||
|
||||
public int parse(String periodStr, int position) {
|
||||
String text1 = iPluralText;
|
||||
String text2 = iSingularText;
|
||||
if (text1.length() < text2.length()) {
|
||||
// Swap in order to match longer one first.
|
||||
String temp = text1;
|
||||
text1 = text2;
|
||||
text2 = temp;
|
||||
}
|
||||
if (periodStr.regionMatches(true, position, text1, 0, text1.length()) &&
|
||||
!matchesOtherAffix(text1.length(), periodStr, position)) {
|
||||
return position + text1.length();
|
||||
}
|
||||
if (periodStr.regionMatches(true, position, text2, 0, text2.length()) &&
|
||||
!matchesOtherAffix(text2.length(), periodStr, position)) {
|
||||
return position + text2.length();
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
public int scan(String periodStr, final int position) {
|
||||
String text1 = iPluralText;
|
||||
String text2 = iSingularText;
|
||||
|
||||
if (text1.length() < text2.length()) {
|
||||
// Swap in order to match longer one first.
|
||||
String temp = text1;
|
||||
text1 = text2;
|
||||
text2 = temp;
|
||||
}
|
||||
|
||||
int textLength1 = text1.length();
|
||||
int textLength2 = text2.length();
|
||||
|
||||
int sourceLength = periodStr.length();
|
||||
for (int pos = position; pos < sourceLength; pos++) {
|
||||
if (periodStr.regionMatches(true, pos, text1, 0, textLength1) &&
|
||||
!matchesOtherAffix(text1.length(), periodStr, pos)) {
|
||||
return pos;
|
||||
}
|
||||
if (periodStr.regionMatches(true, pos, text2, 0, textLength2) &&
|
||||
!matchesOtherAffix(text2.length(), periodStr, pos)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
public String[] getAffixes() {
|
||||
return new String[]{iSingularText, iPluralText};
|
||||
}
|
||||
}
|
97
src/main/java/org/xbib/time/format/RegExAffix.java
Normal file
97
src/main/java/org/xbib/time/format/RegExAffix.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Implements an affix where the text varies by the amount of the field.
|
||||
* Different amounts are supported based on the provided parameters.
|
||||
*/
|
||||
class RegExAffix extends IgnorableAffix {
|
||||
|
||||
private static final ConcurrentMap<String, Pattern> PATTERNS = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Comparator<String> LENGTH_DESC_COMPARATOR = (o1, o2) -> o2.length() - o1.length();
|
||||
|
||||
private final String[] iSuffixes;
|
||||
private final Pattern[] iPatterns;
|
||||
|
||||
// The parse method has to iterate over the suffixes from the longest one to the shortest one
|
||||
// Otherwise it might consume not enough characters.
|
||||
private final String[] iSuffixesSortedDescByLength;
|
||||
|
||||
RegExAffix(String[] regExes, String[] texts) {
|
||||
iSuffixes = texts.clone();
|
||||
iPatterns = new Pattern[regExes.length];
|
||||
for (int i = 0; i < regExes.length; i++) {
|
||||
Pattern pattern = PATTERNS.get(regExes[i]);
|
||||
if (pattern == null) {
|
||||
pattern = Pattern.compile(regExes[i]);
|
||||
Pattern p = PATTERNS.putIfAbsent(regExes[i], pattern);
|
||||
}
|
||||
iPatterns[i] = pattern;
|
||||
}
|
||||
iSuffixesSortedDescByLength = iSuffixes.clone();
|
||||
Arrays.sort(iSuffixesSortedDescByLength, LENGTH_DESC_COMPARATOR);
|
||||
}
|
||||
|
||||
private int selectSuffixIndex(int value) {
|
||||
String valueString = String.valueOf(value);
|
||||
for (int i = 0; i < iPatterns.length; i++) {
|
||||
if (iPatterns[i].matcher(valueString).matches()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return iPatterns.length - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int calculatePrintedLength(int value) {
|
||||
return iSuffixes[selectSuffixIndex(value)].length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printTo(StringBuilder buf, int value) {
|
||||
buf.append(iSuffixes[selectSuffixIndex(value)]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printTo(Writer out, int value) throws IOException {
|
||||
out.write(iSuffixes[selectSuffixIndex(value)]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parse(String periodStr, int position) {
|
||||
for (String text : iSuffixesSortedDescByLength) {
|
||||
if (periodStr.regionMatches(true, position, text, 0, text.length()) &&
|
||||
!matchesOtherAffix(text.length(), periodStr, position)) {
|
||||
return position + text.length();
|
||||
}
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int scan(String periodStr, final int position) {
|
||||
int sourceLength = periodStr.length();
|
||||
for (int pos = position; pos < sourceLength; pos++) {
|
||||
for (String text : iSuffixesSortedDescByLength) {
|
||||
if (periodStr.regionMatches(true, pos, text, 0, text.length()) &&
|
||||
!matchesOtherAffix(text.length(), periodStr, pos)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAffixes() {
|
||||
return iSuffixes.clone();
|
||||
}
|
||||
}
|
180
src/main/java/org/xbib/time/format/Separator.java
Normal file
180
src/main/java/org/xbib/time/format/Separator.java
Normal file
|
@ -0,0 +1,180 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.time.Period;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Handles a separator, that splits the fields into multiple parts.
|
||||
* For example, the 'T' in the ISO8601 standard.
|
||||
*/
|
||||
class Separator implements PeriodPrinter, PeriodParser {
|
||||
private final String iText;
|
||||
private final String iFinalText;
|
||||
private final String[] iParsedForms;
|
||||
|
||||
private final boolean iUseBefore;
|
||||
private final boolean iUseAfter;
|
||||
|
||||
private final PeriodPrinter iBeforePrinter;
|
||||
private final PeriodParser iBeforeParser;
|
||||
volatile PeriodPrinter iAfterPrinter;
|
||||
volatile PeriodParser iAfterParser;
|
||||
|
||||
Separator(String text, String finalText, String[] variants,
|
||||
PeriodPrinter beforePrinter, PeriodParser beforeParser,
|
||||
boolean useBefore, boolean useAfter) {
|
||||
iText = text;
|
||||
iFinalText = finalText;
|
||||
|
||||
if ((finalText == null || text.equals(finalText)) &&
|
||||
(variants == null || variants.length == 0)) {
|
||||
|
||||
iParsedForms = new String[]{text};
|
||||
} else {
|
||||
// Filter and reverse sort the parsed forms.
|
||||
TreeSet<String> parsedSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||
parsedSet.add(text);
|
||||
parsedSet.add(finalText);
|
||||
if (variants != null) {
|
||||
for (int i = variants.length; --i >= 0; ) {
|
||||
parsedSet.add(variants[i]);
|
||||
}
|
||||
}
|
||||
ArrayList<String> parsedList = new ArrayList<String>(parsedSet);
|
||||
Collections.reverse(parsedList);
|
||||
iParsedForms = parsedList.toArray(new String[parsedList.size()]);
|
||||
}
|
||||
|
||||
iBeforePrinter = beforePrinter;
|
||||
iBeforeParser = beforeParser;
|
||||
iUseBefore = useBefore;
|
||||
iUseAfter = useAfter;
|
||||
}
|
||||
|
||||
public int countFieldsToPrint(Period period, int stopAt, Locale locale) {
|
||||
int sum = iBeforePrinter.countFieldsToPrint(period, stopAt, locale);
|
||||
if (sum < stopAt) {
|
||||
sum += iAfterPrinter.countFieldsToPrint(period, stopAt, locale);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(Period period, Locale locale) {
|
||||
PeriodPrinter before = iBeforePrinter;
|
||||
PeriodPrinter after = iAfterPrinter;
|
||||
|
||||
int sum = before.calculatePrintedLength(period, locale)
|
||||
+ after.calculatePrintedLength(period, locale);
|
||||
|
||||
if (iUseBefore) {
|
||||
if (before.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
if (iUseAfter) {
|
||||
int afterCount = after.countFieldsToPrint(period, 2, locale);
|
||||
if (afterCount > 0) {
|
||||
sum += (afterCount > 1 ? iText : iFinalText).length();
|
||||
}
|
||||
} else {
|
||||
sum += iText.length();
|
||||
}
|
||||
}
|
||||
} else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
sum += iText.length();
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, Period period, Locale locale) {
|
||||
PeriodPrinter before = iBeforePrinter;
|
||||
PeriodPrinter after = iAfterPrinter;
|
||||
|
||||
before.printTo(buf, period, locale);
|
||||
if (iUseBefore) {
|
||||
if (before.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
if (iUseAfter) {
|
||||
int afterCount = after.countFieldsToPrint(period, 2, locale);
|
||||
if (afterCount > 0) {
|
||||
buf.append(afterCount > 1 ? iText : iFinalText);
|
||||
}
|
||||
} else {
|
||||
buf.append(iText);
|
||||
}
|
||||
}
|
||||
} else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
buf.append(iText);
|
||||
}
|
||||
after.printTo(buf, period, locale);
|
||||
}
|
||||
|
||||
public void printTo(Writer out, Period period, Locale locale) throws IOException {
|
||||
PeriodPrinter before = iBeforePrinter;
|
||||
PeriodPrinter after = iAfterPrinter;
|
||||
|
||||
before.printTo(out, period, locale);
|
||||
if (iUseBefore) {
|
||||
if (before.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
if (iUseAfter) {
|
||||
int afterCount = after.countFieldsToPrint(period, 2, locale);
|
||||
if (afterCount > 0) {
|
||||
out.write(afterCount > 1 ? iText : iFinalText);
|
||||
}
|
||||
} else {
|
||||
out.write(iText);
|
||||
}
|
||||
}
|
||||
} else if (iUseAfter && after.countFieldsToPrint(period, 1, locale) > 0) {
|
||||
out.write(iText);
|
||||
}
|
||||
after.printTo(out, period, locale);
|
||||
}
|
||||
|
||||
public int parseInto(PeriodAmount period, String periodStr,
|
||||
int pos, Locale locale) {
|
||||
int position = pos;
|
||||
int oldPos = position;
|
||||
position = iBeforeParser.parseInto(period, periodStr, position, locale);
|
||||
if (position < 0) {
|
||||
return position;
|
||||
}
|
||||
boolean found = false;
|
||||
int parsedFormLength = -1;
|
||||
if (position > oldPos) {
|
||||
for (String parsedForm : iParsedForms) {
|
||||
if ((parsedForm == null || parsedForm.length() == 0) ||
|
||||
periodStr.regionMatches(true, position, parsedForm, 0, parsedForm.length())) {
|
||||
parsedFormLength = (parsedForm == null ? 0 : parsedForm.length());
|
||||
position += parsedFormLength;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
oldPos = position;
|
||||
position = iAfterParser.parseInto(period, periodStr, position, locale);
|
||||
if (position < 0) {
|
||||
return position;
|
||||
}
|
||||
if (found && position == oldPos && parsedFormLength > 0) {
|
||||
// Separator should not have been supplied.
|
||||
return ~oldPos;
|
||||
}
|
||||
|
||||
if (position > oldPos && !found && !iUseBefore) {
|
||||
// Separator was required.
|
||||
return ~oldPos;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
Separator finish(PeriodPrinter afterPrinter, PeriodParser afterParser) {
|
||||
iAfterPrinter = afterPrinter;
|
||||
iAfterParser = afterParser;
|
||||
return this;
|
||||
}
|
||||
}
|
75
src/main/java/org/xbib/time/format/SimpleAffix.java
Normal file
75
src/main/java/org/xbib/time/format/SimpleAffix.java
Normal file
|
@ -0,0 +1,75 @@
|
|||
package org.xbib.time.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Implements an affix where the text does not vary by the amount.
|
||||
*/
|
||||
class SimpleAffix extends IgnorableAffix {
|
||||
private final String iText;
|
||||
|
||||
SimpleAffix(String text) {
|
||||
iText = text;
|
||||
}
|
||||
|
||||
public int calculatePrintedLength(int value) {
|
||||
return iText.length();
|
||||
}
|
||||
|
||||
public void printTo(StringBuilder buf, int value) {
|
||||
buf.append(iText);
|
||||
}
|
||||
|
||||
public void printTo(Writer out, int value) throws IOException {
|
||||
out.write(iText);
|
||||
}
|
||||
|
||||
public int parse(String periodStr, int position) {
|
||||
String text = iText;
|
||||
int textLength = text.length();
|
||||
if (periodStr.regionMatches(true, position, text, 0, textLength) &&
|
||||
!matchesOtherAffix(textLength, periodStr, position)) {
|
||||
return position + textLength;
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
public int scan(String periodStr, final int position) {
|
||||
String text = iText;
|
||||
int textLength = text.length();
|
||||
int sourceLength = periodStr.length();
|
||||
search:
|
||||
for (int pos = position; pos < sourceLength; pos++) {
|
||||
if (periodStr.regionMatches(true, pos, text, 0, textLength) &&
|
||||
!matchesOtherAffix(textLength, periodStr, pos)) {
|
||||
return pos;
|
||||
}
|
||||
// Only allow number characters to be skipped in search of suffix.
|
||||
switch (periodStr.charAt(pos)) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '.':
|
||||
case ',':
|
||||
case '+':
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
break search;
|
||||
}
|
||||
}
|
||||
return ~position;
|
||||
}
|
||||
|
||||
public String[] getAffixes() {
|
||||
return new String[]{iText};
|
||||
}
|
||||
}
|
|
@ -112,10 +112,7 @@ public class PrettyTime {
|
|||
* @return time unit quantity
|
||||
*/
|
||||
public TimeUnitQuantity approximateDuration(LocalDateTime then) {
|
||||
if (then == null) {
|
||||
then = LocalDateTime.now();
|
||||
}
|
||||
long difference = ChronoUnit.MILLIS.between(localDateTime, then);
|
||||
long difference = ChronoUnit.MILLIS.between(localDateTime, then == null ? LocalDateTime.now() : then);
|
||||
return calculateDuration(difference);
|
||||
}
|
||||
|
||||
|
@ -163,11 +160,8 @@ public class PrettyTime {
|
|||
* between compared dates.
|
||||
*/
|
||||
public List<TimeUnitQuantity> calculatePreciseDuration(LocalDateTime then) {
|
||||
if (then == null) {
|
||||
then = LocalDateTime.now();
|
||||
}
|
||||
List<TimeUnitQuantity> result = new ArrayList<>();
|
||||
long difference = ChronoUnit.MILLIS.between(localDateTime, then);
|
||||
long difference = ChronoUnit.MILLIS.between(localDateTime, then == null ? LocalDateTime.now() : then);
|
||||
TimeUnitQuantity timeUnitQuantity = calculateDuration(difference);
|
||||
result.add(timeUnitQuantity);
|
||||
while (timeUnitQuantity.getDelta() != 0L) {
|
||||
|
@ -191,10 +185,7 @@ public class PrettyTime {
|
|||
* @return A formatted string representing {@code then}
|
||||
*/
|
||||
public String format(LocalDateTime then) {
|
||||
if (then == null) {
|
||||
then = LocalDateTime.now();
|
||||
}
|
||||
return format(approximateDuration(then));
|
||||
return format(approximateDuration(then == null ? LocalDateTime.now() : then));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -103,7 +103,7 @@ public class Resources_fi extends ListResourceBundle implements TimeFormatProvid
|
|||
{"MillenniumPastSuffix", "sitten"},
|
||||
{"MillenniumFutureSuffix", "päästä"},
|
||||
};
|
||||
private volatile ConcurrentMap<TimeUnit, TimeFormat> formatMap = new ConcurrentHashMap<TimeUnit, TimeFormat>();
|
||||
private volatile ConcurrentMap<TimeUnit, TimeFormat> formatMap = new ConcurrentHashMap<>();
|
||||
|
||||
public Resources_fi() {
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ public class Resources_fi extends ListResourceBundle implements TimeFormatProvid
|
|||
|
||||
@Override
|
||||
public String decorate(TimeUnitQuantity timeUnitQuantity, String time) {
|
||||
String result = "";
|
||||
String result;
|
||||
if (timeUnitQuantity.getUnit() instanceof Day && Math.abs(timeUnitQuantity.getQuantity()) == 1) {
|
||||
result = time;
|
||||
} else {
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.time.ZonedDateTime;
|
|||
|
||||
public class ParserTest extends Assert {
|
||||
|
||||
private final static ZoneId ZONE_ID = ZoneId.of("GMT");
|
||||
private static final ZoneId ZONE_ID = ZoneId.of("GMT");
|
||||
private ZonedDateTime time_2006_08_16_14_00_00 = construct(2006, 8, 16, 14, 0, 0);
|
||||
|
||||
public static ZonedDateTime construct(int year, int month) {
|
||||
|
|
Loading…
Reference in a new issue