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.handlers.Handler;
|
||||||
import org.xbib.time.chronic.numerizer.Numerizer;
|
import org.xbib.time.chronic.numerizer.Numerizer;
|
||||||
import org.xbib.time.chronic.repeaters.Repeater;
|
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.text.ParseException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Chronic {
|
public class Chronic {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(Chronic.class.getName());
|
||||||
|
|
||||||
private Chronic() {
|
private Chronic() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +83,8 @@ public class Chronic {
|
||||||
try {
|
try {
|
||||||
tokens = (List<Token>) optionScannerClass.getMethod("scan", List.class, Options.class)
|
tokens = (List<Token>) optionScannerClass.getMethod("scan", List.class, Options.class)
|
||||||
.invoke(null, tokens, options);
|
.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);
|
throw new ParseException("failed to scan tokens", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +99,8 @@ public class Chronic {
|
||||||
try {
|
try {
|
||||||
tokens = (List<Token>) scannerClass.getMethod("scan", List.class, Options.class)
|
tokens = (List<Token>) scannerClass.getMethod("scan", List.class, Options.class)
|
||||||
.invoke(null, tokens, options);
|
.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);
|
throw new ParseException("failed to scan tokens", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,19 @@ public class Span extends Range {
|
||||||
return zoneId;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "(" + DateTimeFormatter.ISO_INSTANT.format(getBeginCalendar())
|
return "(" + DateTimeFormatter.ISO_INSTANT.format(getBeginCalendar())
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class Token {
|
||||||
public <T extends Tag<?>> T getTag(Class<T> tagClass) {
|
public <T extends Tag<?>> T getTag(Class<T> tagClass) {
|
||||||
List<T> matches = getTags(tagClass);
|
List<T> matches = getTags(tagClass);
|
||||||
T matchingTag = null;
|
T matchingTag = null;
|
||||||
if (matches.size() > 0) {
|
if (!matches.isEmpty()) {
|
||||||
matchingTag = matches.get(0);
|
matchingTag = matches.get(0);
|
||||||
}
|
}
|
||||||
return matchingTag;
|
return matchingTag;
|
||||||
|
|
|
@ -30,11 +30,7 @@ import org.xbib.time.chronic.tags.TimeZone;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -57,7 +53,7 @@ public class Handler {
|
||||||
|
|
||||||
public static synchronized Map<HandlerType, List<Handler>> definitions() {
|
public static synchronized Map<HandlerType, List<Handler>> definitions() {
|
||||||
if (definitions == null) {
|
if (definitions == null) {
|
||||||
Map<HandlerType, List<Handler>> definitions = new HashMap<>();
|
Map<HandlerType, List<Handler>> definitions = new EnumMap<>(HandlerType.class);
|
||||||
|
|
||||||
List<Handler> timeHandlers = new LinkedList<>();
|
List<Handler> timeHandlers = new LinkedList<>();
|
||||||
timeHandlers.add(new Handler(null, new TagPattern(RepeaterTime.class),
|
timeHandlers.add(new Handler(null, new TagPattern(RepeaterTime.class),
|
||||||
|
@ -227,7 +223,7 @@ public class Handler {
|
||||||
if (grabberType == Grabber.Relative.LAST) {
|
if (grabberType == Grabber.Relative.LAST) {
|
||||||
outerSpan = head.nextSpan(PointerType.PAST);
|
outerSpan = head.nextSpan(PointerType.PAST);
|
||||||
} else if (grabberType == Grabber.Relative.THIS) {
|
} else if (grabberType == Grabber.Relative.THIS) {
|
||||||
if (repeaters.size() > 0) {
|
if (!repeaters.isEmpty()) {
|
||||||
outerSpan = head.thisSpan(PointerType.NONE);
|
outerSpan = head.thisSpan(PointerType.NONE);
|
||||||
} else {
|
} else {
|
||||||
outerSpan = head.thisSpan(options.getContext());
|
outerSpan = head.thisSpan(options.getContext());
|
||||||
|
@ -273,11 +269,11 @@ public class Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@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
|
// handle aliases of am/pm
|
||||||
// 5:00 in the morning => 5:00 am
|
// 5:00 in the morning => 5:00 am
|
||||||
// 7:00 in the evening => 7:00 pm
|
// 7:00 in the evening => 7:00 pm
|
||||||
|
List<Token> tokens = tokenList;
|
||||||
int dayPortionIndex = -1;
|
int dayPortionIndex = -1;
|
||||||
int tokenSize = tokens.size();
|
int tokenSize = tokens.size();
|
||||||
for (int i = 0; dayPortionIndex == -1 && i < tokenSize; i++) {
|
for (int i = 0; dayPortionIndex == -1 && i < tokenSize; i++) {
|
||||||
|
@ -347,8 +343,8 @@ public class Handler {
|
||||||
for (HandlerPattern pattern : patterns) {
|
for (HandlerPattern pattern : patterns) {
|
||||||
boolean optional = pattern.isOptional();
|
boolean optional = pattern.isOptional();
|
||||||
if (pattern instanceof TagPattern) {
|
if (pattern instanceof TagPattern) {
|
||||||
boolean match = (tokenIndex < tokens.size() &&
|
boolean match = tokenIndex < tokens.size() &&
|
||||||
tokens.get(tokenIndex).getTags(((TagPattern) pattern).getTagClass()).size() > 0);
|
tokens.get(tokenIndex).getTags(((TagPattern) pattern).getTagClass()).size() > 0;
|
||||||
if (!match && !optional) {
|
if (!match && !optional) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
public interface IHandler {
|
public interface IHandler {
|
||||||
Span handle(List<Token> tokens, Options options);
|
Span handle(List<Token> tokens, Options options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class ORGRHandler extends ORRHandler {
|
public class ORGRHandler extends ORRHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
Span outerSpan = Handler.getAnchor(tokens.subList(2, 4), options);
|
Span outerSpan = Handler.getAnchor(tokens.subList(2, 4), options);
|
||||||
return handle(tokens.subList(0, 2), outerSpan, options);
|
return handle(tokens.subList(0, 2), outerSpan, options);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class ORSRHandler extends ORRHandler {
|
public class ORSRHandler extends ORRHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
Span outerSpan = Handler.getAnchor(tokens.subList(3, 4), options);
|
Span outerSpan = Handler.getAnchor(tokens.subList(3, 4), options);
|
||||||
return handle(tokens.subList(0, 2), outerSpan, options);
|
return handle(tokens.subList(0, 2), outerSpan, options);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class RHandler implements IHandler {
|
public class RHandler implements IHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
List<Token> ddTokens = Handler.dealiasAndDisambiguateTimes(tokens, options);
|
List<Token> ddTokens = Handler.dealiasAndDisambiguateTimes(tokens, options);
|
||||||
return Handler.getAnchor(ddTokens, options);
|
return Handler.getAnchor(ddTokens, options);
|
||||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RdnRmnSdTTzSyHandler implements IHandler {
|
public class RdnRmnSdTTzSyHandler implements IHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(RdnRmnSdTTzSyHandler.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(1).getTag(RepeaterMonthName.class).getType().ordinal();
|
int month = tokens.get(1).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||||
int day = tokens.get(2).getTag(ScalarDay.class).getType();
|
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());
|
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.log(Level.FINE, e.getMessage(), e);
|
||||||
span = null;
|
span = null;
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
|
|
|
@ -12,6 +12,8 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RmnOdHandler extends MDHandler {
|
public class RmnOdHandler extends MDHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
||||||
tokens.get(1).getTag(OrdinalDay.class), tokens.subList(2, tokens.size()), options);
|
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 {
|
public class RmnSdHandler extends MDHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
return handle(tokens.get(0).getTag(RepeaterMonthName.class),
|
||||||
tokens.get(1).getTag(ScalarDay.class), tokens.subList(2, tokens.size()), options);
|
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.time.ZonedDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RmnSdSyHandler implements IHandler {
|
public class RmnSdSyHandler implements IHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(RmnSdSyHandler.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||||
int day = tokens.get(1).getTag(ScalarDay.class).getType();
|
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());
|
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.log(Level.FINE, e.getMessage(), e);
|
||||||
span = null;
|
span = null;
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RmnSyHandler implements IHandler {
|
public class RmnSyHandler implements IHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(RmnSyHandler.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
int month = tokens.get(0).getTag(RepeaterMonthName.class).getType().ordinal();
|
||||||
int year = tokens.get(1).getTag(ScalarYear.class).getType();
|
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);
|
ZonedDateTime end = start.plus(1, ChronoUnit.MONTHS);
|
||||||
span = new Span(start, end);
|
span = new Span(start, end);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.log(Level.FINE, e.getMessage(), e);
|
||||||
span = null;
|
span = null;
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
|
|
|
@ -14,6 +14,8 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SmSdHandler implements IHandler {
|
public class SmSdHandler implements IHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||||
int day = tokens.get(1).getTag(ScalarDay.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.time.ZonedDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SmSdSyHandler implements IHandler {
|
public class SmSdSyHandler implements IHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(SmSdSyHandler.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||||
int day = tokens.get(1).getTag(ScalarDay.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());
|
ZonedDateTime dayStart = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, options.getZoneId());
|
||||||
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
span = Handler.dayOrTime(dayStart, timeTokens, options);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.log(Level.FINE, e.getMessage(), e);
|
||||||
span = null;
|
span = null;
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
|
|
|
@ -9,12 +9,17 @@ import org.xbib.time.chronic.tags.ScalarYear;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SmSyHandler implements IHandler {
|
public class SmSyHandler implements IHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(SmSyHandler.class.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
public Span handle(List<Token> tokens, Options options) {
|
public Span handle(List<Token> tokens, Options options) {
|
||||||
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
int month = tokens.get(0).getTag(ScalarMonth.class).getType();
|
||||||
int year = tokens.get(1).getTag(ScalarYear.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);
|
ZonedDateTime end = start.plus(1, ChronoUnit.MONTHS);
|
||||||
span = new Span(start, end);
|
span = new Span(start, end);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.log(Level.FINE, e.getMessage(), e);
|
||||||
span = null;
|
span = null;
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
|
|
|
@ -125,10 +125,10 @@ public class Numerizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String andition(String str) {
|
private static String andition(String str) {
|
||||||
StringBuffer anditionStr = new StringBuffer(str);
|
StringBuilder anditionStr = new StringBuilder(str);
|
||||||
Matcher matcher = Numerizer.ANDITION_PATTERN.matcher(anditionStr);
|
Matcher matcher = Numerizer.ANDITION_PATTERN.matcher(anditionStr);
|
||||||
while (matcher.find()) {
|
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(),
|
anditionStr.replace(matcher.start(), matcher.end(),
|
||||||
String.valueOf(Integer.parseInt(matcher.group(1).trim()) +
|
String.valueOf(Integer.parseInt(matcher.group(1).trim()) +
|
||||||
Integer.parseInt(matcher.group(3).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 Pattern name;
|
||||||
private String number;
|
private String number;
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ public abstract class Repeater<T> extends Tag<T> implements Comparable<Repeater<
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int compareTo(Repeater<?> other) {
|
public int compareTo(Repeater<?> other) {
|
||||||
return Integer.compare(getWidth(), other.getWidth());
|
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);
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "repeater";
|
return "repeater";
|
||||||
|
|
|
@ -64,6 +64,18 @@ public class RepeaterDay extends RepeaterUnit {
|
||||||
return RepeaterDay.DAY_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-day";
|
return super.toString() + "-day";
|
||||||
|
|
|
@ -15,7 +15,7 @@ import java.util.regex.Pattern;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RepeaterDayName extends Repeater<RepeaterDayName.DayName> {
|
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 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 = Pattern.compile("^t(ue|eu|oo|u|)s(day)?$");
|
||||||
private static final Pattern TUE_PATTERN_1 = Pattern.compile("^tue$");
|
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);
|
currentDayStart = currentDayStart.plus(direction, ChronoUnit.DAYS);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentDayStart = currentDayStart.plus(direction * 7, ChronoUnit.DAYS);
|
currentDayStart = currentDayStart.plus(direction * 7L, ChronoUnit.DAYS);
|
||||||
}
|
}
|
||||||
return new Span(currentDayStart, ChronoUnit.DAYS, 1);
|
return new Span(currentDayStart, ChronoUnit.DAYS, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span internalThisSpan(PointerType pointer) {
|
protected Span internalThisSpan(PointerType pointer) {
|
||||||
if (pointer == PointerType.NONE) {
|
return super.nextSpan(pointer == PointerType.NONE ? PointerType.FUTURE : pointer);
|
||||||
pointer = PointerType.FUTURE;
|
|
||||||
}
|
|
||||||
return super.nextSpan(pointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,6 +89,18 @@ public class RepeaterDayName extends Repeater<RepeaterDayName.DayName> {
|
||||||
return RepeaterDayName.DAY_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-dayname-" + getType();
|
return super.toString() + "-dayname-" + getType();
|
||||||
|
|
|
@ -58,7 +58,6 @@ public abstract class RepeaterDayPortion<T> extends Repeater<T> {
|
||||||
@Override
|
@Override
|
||||||
protected Span internalNextSpan(PointerType pointer) {
|
protected Span internalNextSpan(PointerType pointer) {
|
||||||
ZonedDateTime rangeStart;
|
ZonedDateTime rangeStart;
|
||||||
ZonedDateTime rangeEnd;
|
|
||||||
if (currentSpan == null) {
|
if (currentSpan == null) {
|
||||||
long nowSeconds = getNow().toInstant().getEpochSecond() - ymd(getNow()).toInstant().getEpochSecond();
|
long nowSeconds = getNow().toInstant().getEpochSecond() - ymd(getNow()).toInstant().getEpochSecond();
|
||||||
if (nowSeconds < range.getBegin()) {
|
if (nowSeconds < range.getBegin()) {
|
||||||
|
@ -133,6 +132,18 @@ public abstract class RepeaterDayPortion<T> extends Repeater<T> {
|
||||||
|
|
||||||
protected abstract Range createRange(T type);
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-dayportion-" + getType();
|
return super.toString() + "-dayportion-" + getType();
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class RepeaterFortnight extends RepeaterUnit {
|
||||||
Span lastSundaySpan = sundayRepeater.nextSpan(PointerType.PAST);
|
Span lastSundaySpan = sundayRepeater.nextSpan(PointerType.PAST);
|
||||||
currentFortnightStart = lastSundaySpan.getBeginCalendar();
|
currentFortnightStart = lastSundaySpan.getBeginCalendar();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unable to handle pointer " + pointer + ".");
|
throw new IllegalArgumentException("Unable to handle pointer " + pointer);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long direction = (pointer == PointerType.FUTURE) ? 1L : -1L;
|
long direction = (pointer == PointerType.FUTURE) ? 1L : -1L;
|
||||||
|
@ -47,11 +47,11 @@ public class RepeaterFortnight extends RepeaterUnit {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span internalThisSpan(PointerType pointer) {
|
protected Span internalThisSpan(PointerType pointerType) {
|
||||||
|
PointerType pointer = pointerType;
|
||||||
if (pointer == null) {
|
if (pointer == null) {
|
||||||
pointer = PointerType.FUTURE;
|
pointer = PointerType.FUTURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span span;
|
Span span;
|
||||||
if (pointer == PointerType.FUTURE) {
|
if (pointer == PointerType.FUTURE) {
|
||||||
ZonedDateTime thisFortnightStart = ymdh(getNow()).plus(RepeaterHour.HOUR_SECONDS, ChronoUnit.SECONDS);
|
ZonedDateTime thisFortnightStart = ymdh(getNow()).plus(RepeaterHour.HOUR_SECONDS, ChronoUnit.SECONDS);
|
||||||
|
@ -86,6 +86,18 @@ public class RepeaterFortnight extends RepeaterUnit {
|
||||||
return RepeaterFortnight.FORTNIGHT_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-fortnight";
|
return super.toString() + "-fortnight";
|
||||||
|
|
|
@ -71,6 +71,18 @@ public class RepeaterHour extends RepeaterUnit {
|
||||||
return RepeaterHour.HOUR_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-hour";
|
return super.toString() + "-hour";
|
||||||
|
|
|
@ -66,6 +66,18 @@ public class RepeaterMinute extends RepeaterUnit {
|
||||||
return RepeaterMinute.MINUTE_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-minute";
|
return super.toString() + "-minute";
|
||||||
|
|
|
@ -66,6 +66,18 @@ public class RepeaterMonth extends RepeaterUnit {
|
||||||
return RepeaterMonth.MONTH_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-month";
|
return super.toString() + "-month";
|
||||||
|
|
|
@ -76,14 +76,14 @@ public class RepeaterMonthName extends Repeater<RepeaterMonthName.MonthName> {
|
||||||
} else if (pointer == PointerType.NONE) {
|
} else if (pointer == PointerType.NONE) {
|
||||||
if (nowMonth <= targetMonth) {
|
if (nowMonth <= targetMonth) {
|
||||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone());
|
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())
|
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone())
|
||||||
.plus(1, ChronoUnit.YEARS);
|
.plus(1, ChronoUnit.YEARS);
|
||||||
}
|
}
|
||||||
} else if (pointer == PointerType.PAST) {
|
} else if (pointer == PointerType.PAST) {
|
||||||
if (nowMonth > targetMonth) {
|
if (nowMonth > targetMonth) {
|
||||||
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone());
|
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())
|
currentMonthBegin = ZonedDateTime.of(getNow().getYear(), targetMonth, 1, 0, 0, 0, 0, getNow().getZone())
|
||||||
.minus(1, ChronoUnit.YEARS);
|
.minus(1, ChronoUnit.YEARS);
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,18 @@ public class RepeaterMonthName extends Repeater<RepeaterMonthName.MonthName> {
|
||||||
return RepeaterMonthName.MONTH_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-monthname-" + getType();
|
return super.toString() + "-monthname-" + getType();
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.time.temporal.ChronoUnit;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RepeaterSecond extends RepeaterUnit {
|
public class RepeaterSecond extends RepeaterUnit {
|
||||||
public static final int SECOND_SECONDS = 1; // (60 * 60);
|
private static final int SECOND_SECONDS = 1;
|
||||||
|
|
||||||
private ZonedDateTime secondStart;
|
private ZonedDateTime secondStart;
|
||||||
|
|
||||||
|
@ -42,6 +42,18 @@ public class RepeaterSecond extends RepeaterUnit {
|
||||||
return RepeaterSecond.SECOND_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-second";
|
return super.toString() + "-second";
|
||||||
|
|
|
@ -37,8 +37,8 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
int minutesInSeconds = Integer.parseInt(t.substring(1)) * 60;
|
int minutesInSeconds = Integer.parseInt(t.substring(1)) * 60;
|
||||||
type = new Tick(hoursInSeconds + minutesInSeconds, true);
|
type = new Tick(hoursInSeconds + minutesInSeconds, true);
|
||||||
} else if (length == 4) {
|
} else if (length == 4) {
|
||||||
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
boolean ambiguous = time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||||
Integer.parseInt(t.substring(0, 2)) <= 12);
|
Integer.parseInt(t.substring(0, 2)) <= 12;
|
||||||
int hours = Integer.parseInt(t.substring(0, 2));
|
int hours = Integer.parseInt(t.substring(0, 2));
|
||||||
int hoursInSeconds = hours * 60 * 60;
|
int hoursInSeconds = hours * 60 * 60;
|
||||||
int minutesInSeconds = Integer.parseInt(t.substring(2)) * 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));
|
int seconds = Integer.parseInt(t.substring(3));
|
||||||
type = new Tick(hoursInSeconds + minutesInSeconds + seconds, true);
|
type = new Tick(hoursInSeconds + minutesInSeconds + seconds, true);
|
||||||
} else if (length == 6) {
|
} else if (length == 6) {
|
||||||
boolean ambiguous = (time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
boolean ambiguous = time.contains(":") && Integer.parseInt(t.substring(0, 1)) != 0 &&
|
||||||
Integer.parseInt(t.substring(0, 2)) <= 12);
|
Integer.parseInt(t.substring(0, 2)) <= 12;
|
||||||
int hours = Integer.parseInt(t.substring(0, 2));
|
int hours = Integer.parseInt(t.substring(0, 2));
|
||||||
int hoursInSeconds = hours * 60 * 60;
|
int hoursInSeconds = hours * 60 * 60;
|
||||||
int minutesInSeconds = Integer.parseInt(t.substring(2, 4)) * 60;
|
int minutesInSeconds = Integer.parseInt(t.substring(2, 4)) * 60;
|
||||||
|
@ -136,7 +136,7 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
if (tick.isAmbiguous()) {
|
if (tick.isAmbiguous()) {
|
||||||
List<ZonedDateTime> futureDates = new LinkedList<>();
|
List<ZonedDateTime> futureDates = new LinkedList<>();
|
||||||
futureDates.add(midnight.plus(tick.intValue(), ChronoUnit.SECONDS));
|
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));
|
futureDates.add(tomorrowMidnight.plus(tick.intValue(), ChronoUnit.SECONDS));
|
||||||
for (ZonedDateTime futureDate : futureDates) {
|
for (ZonedDateTime futureDate : futureDates) {
|
||||||
if (futureDate.isAfter(now) || futureDate.equals(now)) {
|
if (futureDate.isAfter(now) || futureDate.equals(now)) {
|
||||||
|
@ -160,9 +160,9 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
} else {
|
} else {
|
||||||
if (tick.isAmbiguous()) {
|
if (tick.isAmbiguous()) {
|
||||||
List<ZonedDateTime> pastDates = new LinkedList<>();
|
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(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) {
|
for (ZonedDateTime pastDate : pastDates) {
|
||||||
if (pastDate.isBefore(now) || pastDate.equals(now)) {
|
if (pastDate.isBefore(now) || pastDate.equals(now)) {
|
||||||
currentTime = pastDate;
|
currentTime = pastDate;
|
||||||
|
@ -190,8 +190,8 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!first) {
|
if (!first) {
|
||||||
int increment = (tick.isAmbiguous()) ? halfDay : fullDay;
|
int increment = tick.isAmbiguous() ? halfDay : fullDay;
|
||||||
int direction = (pointer == PointerType.FUTURE) ? 1 : -1;
|
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||||
currentTime = currentTime.plus(direction * increment, ChronoUnit.SECONDS);
|
currentTime = currentTime.plus(direction * increment, ChronoUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,10 +200,7 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span internalThisSpan(PointerType pointer) {
|
protected Span internalThisSpan(PointerType pointer) {
|
||||||
if (pointer == PointerType.NONE) {
|
return nextSpan(pointer == PointerType.NONE ? PointerType.FUTURE : pointer);
|
||||||
pointer = PointerType.FUTURE;
|
|
||||||
}
|
|
||||||
return nextSpan(pointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -216,6 +213,18 @@ public class RepeaterTime extends Repeater<Tick> {
|
||||||
return 1;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-time-" + getType();
|
return super.toString() + "-time-" + getType();
|
||||||
|
|
|
@ -47,11 +47,23 @@ public abstract class RepeaterUnit extends Repeater<Object> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable t) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to create RepeaterUnit.", t);
|
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 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;
|
public static final int WEEK_DAYS = 7;
|
||||||
|
|
||||||
private ZonedDateTime currentWeekStart;
|
private ZonedDateTime currentWeekStart;
|
||||||
|
@ -38,7 +38,7 @@ public class RepeaterWeek extends RepeaterUnit {
|
||||||
throw new IllegalArgumentException("Unable to handle pointer " + pointer + ".");
|
throw new IllegalArgumentException("Unable to handle pointer " + pointer + ".");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int direction = (pointer == PointerType.FUTURE) ? 1 : -1;
|
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||||
currentWeekStart = currentWeekStart.plus(RepeaterWeek.WEEK_DAYS * direction, ChronoUnit.DAYS);
|
currentWeekStart = currentWeekStart.plus(RepeaterWeek.WEEK_DAYS * direction, ChronoUnit.DAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,18 @@ public class RepeaterWeek extends RepeaterUnit {
|
||||||
return RepeaterWeek.WEEK_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-week";
|
return super.toString() + "-week";
|
||||||
|
|
|
@ -16,25 +16,34 @@ public class RepeaterWeekend extends RepeaterUnit {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Span internalNextSpan(PointerType pointer) {
|
protected Span internalNextSpan(PointerType pointer) {
|
||||||
if (currentWeekStart == null) {
|
if (currentWeekStart != null) {
|
||||||
if (pointer == PointerType.FUTURE) {
|
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
||||||
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
currentWeekStart = currentWeekStart.plus(direction * RepeaterWeek.WEEK_SECONDS, ChronoUnit.SECONDS);
|
||||||
saturdayRepeater.setNow(getNow());
|
ZonedDateTime c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||||
Span nextSaturdaySpan = saturdayRepeater.nextSpan(PointerType.FUTURE);
|
return new Span(currentWeekStart, c);
|
||||||
currentWeekStart = nextSaturdaySpan.getBeginCalendar();
|
}
|
||||||
} else if (pointer == PointerType.PAST) {
|
ZonedDateTime c;
|
||||||
|
switch (pointer) {
|
||||||
|
case PAST: {
|
||||||
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
||||||
saturdayRepeater.setNow(getNow().plus(RepeaterDay.DAY_SECONDS, ChronoUnit.SECONDS));
|
saturdayRepeater.setNow(getNow().plus(RepeaterDay.DAY_SECONDS, ChronoUnit.SECONDS));
|
||||||
Span lastSaturdaySpan = saturdayRepeater.nextSpan(PointerType.PAST);
|
Span lastSaturdaySpan = saturdayRepeater.nextSpan(PointerType.PAST);
|
||||||
currentWeekStart = lastSaturdaySpan.getBeginCalendar();
|
currentWeekStart = lastSaturdaySpan.getBeginCalendar();
|
||||||
|
c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
||||||
|
return new Span(currentWeekStart, c);
|
||||||
}
|
}
|
||||||
} else {
|
case FUTURE: {
|
||||||
long direction = pointer == PointerType.FUTURE ? 1L : -1L;
|
RepeaterDayName saturdayRepeater = new RepeaterDayName(RepeaterDayName.DayName.SATURDAY);
|
||||||
currentWeekStart = currentWeekStart.plus(direction * RepeaterWeek.WEEK_SECONDS, ChronoUnit.SECONDS);
|
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;
|
||||||
}
|
}
|
||||||
assert currentWeekStart != null;
|
throw new IllegalArgumentException("pointer type not expected");
|
||||||
ZonedDateTime c = currentWeekStart.plus(RepeaterWeekend.WEEKEND_SECONDS, ChronoUnit.SECONDS);
|
|
||||||
return new Span(currentWeekStart, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,6 +82,18 @@ public class RepeaterWeekend extends RepeaterUnit {
|
||||||
return (int) RepeaterWeekend.WEEKEND_SECONDS;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-weekend";
|
return super.toString() + "-weekend";
|
||||||
|
|
|
@ -70,6 +70,18 @@ public class RepeaterYear extends RepeaterUnit {
|
||||||
return (365 * 24 * 60 * 60);
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return super.toString() + "-year";
|
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.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods used by formatters.
|
* Utility methods used by formatters.
|
||||||
|
@ -9,6 +11,8 @@ import java.io.Writer;
|
||||||
*/
|
*/
|
||||||
public class FormatUtils {
|
public class FormatUtils {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(FormatUtils.class.getName());
|
||||||
|
|
||||||
private static final double LOG_10 = Math.log(10);
|
private static final double LOG_10 = Math.log(10);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +35,7 @@ public class FormatUtils {
|
||||||
try {
|
try {
|
||||||
appendPaddedInteger((Appendable) buf, value, size);
|
appendPaddedInteger((Appendable) buf, value, size);
|
||||||
} catch (IOException e) {
|
} 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.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param appenadble receives integer converted to a string
|
* @param appenadble receives integer converted to a string
|
||||||
* @param value value to convert to a string
|
* @param intvalue value to convert to a string
|
||||||
* @param size minimum amount of digits to append
|
* @param digits minimum amount of digits to append
|
||||||
* @throws IOException exception
|
* @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) {
|
if (value < 0) {
|
||||||
appenadble.append('-');
|
appenadble.append('-');
|
||||||
if (value != Integer.MIN_VALUE) {
|
if (value != Integer.MIN_VALUE) {
|
||||||
|
@ -55,7 +61,8 @@ public class FormatUtils {
|
||||||
for (; size > 10; size--) {
|
for (; size > 10; size--) {
|
||||||
appenadble.append('0');
|
appenadble.append('0');
|
||||||
}
|
}
|
||||||
appenadble.append("" + -(long) Integer.MIN_VALUE);
|
appenadble.append(Long.toString(Integer.MIN_VALUE));
|
||||||
|
//.append("" + -(long) Integer.MIN_VALUE)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,15 +83,15 @@ public class FormatUtils {
|
||||||
// Append remainder by calculating (value - d * 10).
|
// Append remainder by calculating (value - d * 10).
|
||||||
appenadble.append((char) (value - (d << 3) - (d << 1) + '0'));
|
appenadble.append((char) (value - (d << 3) - (d << 1) + '0'));
|
||||||
} else {
|
} else {
|
||||||
int digits;
|
int d;
|
||||||
if (value < 1000) {
|
if (value < 1000) {
|
||||||
digits = 3;
|
d = 3;
|
||||||
} else if (value < 10000) {
|
} else if (value < 10000) {
|
||||||
digits = 4;
|
d = 4;
|
||||||
} else {
|
} 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('0');
|
||||||
}
|
}
|
||||||
appenadble.append(Integer.toString(value));
|
appenadble.append(Integer.toString(value));
|
||||||
|
@ -105,7 +112,7 @@ public class FormatUtils {
|
||||||
try {
|
try {
|
||||||
appendPaddedInteger((Appendable) buf, value, size);
|
appendPaddedInteger((Appendable) buf, value, size);
|
||||||
} catch (IOException e) {
|
} 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.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param appendable receives integer converted to a string
|
* @param appendable receives integer converted to a string
|
||||||
* @param value value to convert to a string
|
* @param longvalue value to convert to a string
|
||||||
* @param size minimum amount of digits to append
|
* @param digits minimum amount of digits to append
|
||||||
* @throws IOException exception
|
* @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 intValue = (int) value;
|
||||||
|
int size = digits;
|
||||||
if (intValue == value) {
|
if (intValue == value) {
|
||||||
appendPaddedInteger(appendable, intValue, size);
|
appendPaddedInteger(appendable, intValue, size);
|
||||||
} else if (size <= 19) {
|
} else if (size <= 19) {
|
||||||
|
@ -139,8 +148,8 @@ public class FormatUtils {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int digits = (int) (Math.log(value) / LOG_10) + 1;
|
int d = (int) (Math.log(value) / LOG_10) + 1;
|
||||||
for (; size > digits; size--) {
|
for (; size > d; size--) {
|
||||||
appendable.append('0');
|
appendable.append('0');
|
||||||
}
|
}
|
||||||
appendable.append(Long.toString(value));
|
appendable.append(Long.toString(value));
|
||||||
|
@ -154,12 +163,14 @@ public class FormatUtils {
|
||||||
* This method is optimized for converting small values to strings.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param out receives integer converted to a string
|
* @param out receives integer converted to a string
|
||||||
* @param value value to convert to a string
|
* @param val value to convert to a string
|
||||||
* @param size minimum amount of digits to append
|
* @param digits minimum amount of digits to append
|
||||||
* @throws IOException exception
|
* @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 {
|
throws IOException {
|
||||||
|
int value = val;
|
||||||
|
int size = digits;
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
out.write('-');
|
out.write('-');
|
||||||
if (value != Integer.MIN_VALUE) {
|
if (value != Integer.MIN_VALUE) {
|
||||||
|
@ -168,7 +179,8 @@ public class FormatUtils {
|
||||||
for (; size > 10; size--) {
|
for (; size > 10; size--) {
|
||||||
out.write('0');
|
out.write('0');
|
||||||
}
|
}
|
||||||
out.write("" + -(long) Integer.MIN_VALUE);
|
//out.write("" + -(long) Integer.MIN_VALUE);
|
||||||
|
out.write(Long.toString(Integer.MIN_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,15 +201,15 @@ public class FormatUtils {
|
||||||
// Append remainder by calculating (value - d * 10).
|
// Append remainder by calculating (value - d * 10).
|
||||||
out.write(value - (d << 3) - (d << 1) + '0');
|
out.write(value - (d << 3) - (d << 1) + '0');
|
||||||
} else {
|
} else {
|
||||||
int digits;
|
int d;
|
||||||
if (value < 1000) {
|
if (value < 1000) {
|
||||||
digits = 3;
|
d = 3;
|
||||||
} else if (value < 10000) {
|
} else if (value < 10000) {
|
||||||
digits = 4;
|
d = 4;
|
||||||
} else {
|
} 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('0');
|
||||||
}
|
}
|
||||||
out.write(Integer.toString(value));
|
out.write(Integer.toString(value));
|
||||||
|
@ -211,12 +223,13 @@ public class FormatUtils {
|
||||||
* This method is optimized for converting small values to strings.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param out receives integer converted to a string
|
* @param out receives integer converted to a string
|
||||||
* @param value value to convert to a string
|
* @param longvalue value to convert to a string
|
||||||
* @param size minimum amount of digits to append
|
* @param digits minimum amount of digits to append
|
||||||
* @throws IOException exception
|
* @throws IOException exception
|
||||||
*/
|
*/
|
||||||
public static void writePaddedInteger(Writer out, long value, int size)
|
public static void writePaddedInteger(Writer out, long longvalue, int digits) throws IOException {
|
||||||
throws IOException {
|
long value = longvalue;
|
||||||
|
int size = digits;
|
||||||
int intValue = (int) value;
|
int intValue = (int) value;
|
||||||
if (intValue == value) {
|
if (intValue == value) {
|
||||||
writePaddedInteger(out, intValue, size);
|
writePaddedInteger(out, intValue, size);
|
||||||
|
@ -235,8 +248,8 @@ public class FormatUtils {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int digits = (int) (Math.log(value) / LOG_10) + 1;
|
int d = (int) (Math.log(value) / LOG_10) + 1;
|
||||||
for (; size > digits; size--) {
|
for (; size > d; size--) {
|
||||||
out.write('0');
|
out.write('0');
|
||||||
}
|
}
|
||||||
out.write(Long.toString(value));
|
out.write(Long.toString(value));
|
||||||
|
@ -254,7 +267,7 @@ public class FormatUtils {
|
||||||
try {
|
try {
|
||||||
appendUnpaddedInteger((Appendable) buf, value);
|
appendUnpaddedInteger((Appendable) buf, value);
|
||||||
} catch (IOException e) {
|
} 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.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param appendable receives integer converted to a string
|
* @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
|
* @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) {
|
if (value < 0) {
|
||||||
appendable.append('-');
|
appendable.append('-');
|
||||||
if (value != Integer.MIN_VALUE) {
|
if (value != Integer.MIN_VALUE) {
|
||||||
value = -value;
|
value = -value;
|
||||||
} else {
|
} else {
|
||||||
appendable.append("" + -(long) Integer.MIN_VALUE);
|
appendable.append(Long.toString(Integer.MIN_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,7 +317,7 @@ public class FormatUtils {
|
||||||
try {
|
try {
|
||||||
appendUnpaddedInteger((Appendable) buf, value);
|
appendUnpaddedInteger((Appendable) buf, value);
|
||||||
} catch (IOException e) {
|
} 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.
|
* This method is optimized for converting small values to strings.
|
||||||
*
|
*
|
||||||
* @param out receives integer converted to a string
|
* @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
|
* @throws IOException exception
|
||||||
*/
|
*/
|
||||||
public static void writeUnpaddedInteger(Writer out, int value)
|
public static void writeUnpaddedInteger(Writer out, int val)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
int value = val;
|
||||||
if (value < 0) {
|
if (value < 0) {
|
||||||
out.write('-');
|
out.write('-');
|
||||||
if (value != Integer.MIN_VALUE) {
|
if (value != Integer.MIN_VALUE) {
|
||||||
value = -value;
|
value = -value;
|
||||||
} else {
|
} else {
|
||||||
out.write("" + -(long) Integer.MIN_VALUE);
|
out.write(Long.toString(Integer.MIN_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,12 +409,8 @@ public class FormatUtils {
|
||||||
return 20;
|
return 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return value < 10 ? 1 : value < 100 ? 2 : value < 1000 ? 3 : value < 10000 ? 4 :
|
||||||
(value < 10 ? 1 :
|
((int) (Math.log(value) / LOG_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) {
|
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
|
* @return time unit quantity
|
||||||
*/
|
*/
|
||||||
public TimeUnitQuantity approximateDuration(LocalDateTime then) {
|
public TimeUnitQuantity approximateDuration(LocalDateTime then) {
|
||||||
if (then == null) {
|
long difference = ChronoUnit.MILLIS.between(localDateTime, then == null ? LocalDateTime.now() : then);
|
||||||
then = LocalDateTime.now();
|
|
||||||
}
|
|
||||||
long difference = ChronoUnit.MILLIS.between(localDateTime, then);
|
|
||||||
return calculateDuration(difference);
|
return calculateDuration(difference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,11 +160,8 @@ public class PrettyTime {
|
||||||
* between compared dates.
|
* between compared dates.
|
||||||
*/
|
*/
|
||||||
public List<TimeUnitQuantity> calculatePreciseDuration(LocalDateTime then) {
|
public List<TimeUnitQuantity> calculatePreciseDuration(LocalDateTime then) {
|
||||||
if (then == null) {
|
|
||||||
then = LocalDateTime.now();
|
|
||||||
}
|
|
||||||
List<TimeUnitQuantity> result = new ArrayList<>();
|
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);
|
TimeUnitQuantity timeUnitQuantity = calculateDuration(difference);
|
||||||
result.add(timeUnitQuantity);
|
result.add(timeUnitQuantity);
|
||||||
while (timeUnitQuantity.getDelta() != 0L) {
|
while (timeUnitQuantity.getDelta() != 0L) {
|
||||||
|
@ -191,10 +185,7 @@ public class PrettyTime {
|
||||||
* @return A formatted string representing {@code then}
|
* @return A formatted string representing {@code then}
|
||||||
*/
|
*/
|
||||||
public String format(LocalDateTime then) {
|
public String format(LocalDateTime then) {
|
||||||
if (then == null) {
|
return format(approximateDuration(then == null ? LocalDateTime.now() : then));
|
||||||
then = LocalDateTime.now();
|
|
||||||
}
|
|
||||||
return format(approximateDuration(then));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class Resources_fi extends ListResourceBundle implements TimeFormatProvid
|
||||||
{"MillenniumPastSuffix", "sitten"},
|
{"MillenniumPastSuffix", "sitten"},
|
||||||
{"MillenniumFutureSuffix", "päästä"},
|
{"MillenniumFutureSuffix", "päästä"},
|
||||||
};
|
};
|
||||||
private volatile ConcurrentMap<TimeUnit, TimeFormat> formatMap = new ConcurrentHashMap<TimeUnit, TimeFormat>();
|
private volatile ConcurrentMap<TimeUnit, TimeFormat> formatMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public Resources_fi() {
|
public Resources_fi() {
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ public class Resources_fi extends ListResourceBundle implements TimeFormatProvid
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String decorate(TimeUnitQuantity timeUnitQuantity, String time) {
|
public String decorate(TimeUnitQuantity timeUnitQuantity, String time) {
|
||||||
String result = "";
|
String result;
|
||||||
if (timeUnitQuantity.getUnit() instanceof Day && Math.abs(timeUnitQuantity.getQuantity()) == 1) {
|
if (timeUnitQuantity.getUnit() instanceof Day && Math.abs(timeUnitQuantity.getQuantity()) == 1) {
|
||||||
result = time;
|
result = time;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.time.ZonedDateTime;
|
||||||
|
|
||||||
public class ParserTest extends Assert {
|
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);
|
private ZonedDateTime time_2006_08_16_14_00_00 = construct(2006, 8, 16, 14, 0, 0);
|
||||||
|
|
||||||
public static ZonedDateTime construct(int year, int month) {
|
public static ZonedDateTime construct(int year, int month) {
|
||||||
|
|
Loading…
Reference in a new issue