package er.chronic.handlers;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import er.chronic.Options;
import er.chronic.repeaters.EnumRepeaterDayPortion;
import er.chronic.repeaters.IntegerRepeaterDayPortion;
import er.chronic.repeaters.Repeater;
import er.chronic.repeaters.RepeaterDayName;
import er.chronic.repeaters.RepeaterDayPortion;
import er.chronic.repeaters.RepeaterMonthName;
import er.chronic.repeaters.RepeaterTime;
import er.chronic.tags.Grabber;
import er.chronic.tags.Ordinal;
import er.chronic.tags.OrdinalDay;
import er.chronic.tags.Pointer;
import er.chronic.tags.Pointer.PointerType;
import er.chronic.tags.Scalar;
import er.chronic.tags.ScalarDay;
import er.chronic.tags.ScalarMonth;
import er.chronic.tags.ScalarYear;
import er.chronic.tags.Separator;
import er.chronic.tags.SeparatorAt;
import er.chronic.tags.SeparatorComma;
import er.chronic.tags.SeparatorIn;
import er.chronic.tags.SeparatorOn;
import er.chronic.tags.SeparatorSlashOrDash;
import er.chronic.tags.Tag;
import er.chronic.tags.TimeZone;
import er.chronic.utils.EndianPrecedence;
import er.chronic.utils.Range;
import er.chronic.utils.Span;
import er.chronic.utils.Time;
import er.chronic.utils.Token;
public class Handler {
private static Map<Handler.HandlerType, List<Handler>> _definitions;
public static enum HandlerType {
TIME, DATE, ANCHOR, ARROW, NARROW
}
private HandlerPattern[] _patterns;
private IHandler _handler;
private boolean _compatible;
public Handler(IHandler handler, HandlerPattern... patterns) {
this(handler, true, patterns);
}
public Handler(IHandler handler, boolean compatible, HandlerPattern... patterns) {
_handler = handler;
_compatible = compatible;
_patterns = patterns;
}
public boolean isCompatible(Options options) {
return !options.isCompatibilityMode() || _compatible;
}
public IHandler getHandler() {
return _handler;
}
public boolean match(List<Token> tokens, Map<Handler.HandlerType, List<Handler>> definitions) {
return matchCount(tokens, definitions) != null;
}
public Range matchCount(List<Token> tokens, Map<Handler.HandlerType, List<Handler>> definitions) {
Range range = _matchCount(tokens, definitions);
return (range != null && range.getEnd() == tokens.size()) ? range : null;
}
// MS: Switching to count offsets allows us to have variable length times at the front instead of
// the end ... for instance, 5pm 2/10 instead of 2/10 5pm
public Range _matchCount(List<Token> tokens, Map<Handler.HandlerType, List<Handler>> definitions) {
int tokenStartIndex = 0;
int tokenIndex = 0;
for (HandlerPattern pattern : _patterns) {
//System.out.println("Handler.match: pattern = " + pattern + " (tokenIndex = " + tokenIndex + ", " + tokens.get(tokenIndex) + ")");
boolean optional = pattern.isOptional();
if (pattern instanceof TagPattern) {
boolean match = (tokenIndex < tokens.size() && tokens.get(tokenIndex).getTags(((TagPattern) pattern).getTagClass()).size() > 0);
// System.out.println("Handler.match: " + pattern + "=" + match);
if (!match && !optional) {
return null;
//return false;
}
if (match) {
tokenIndex++;
}
// next if !match && optional ?
}
else if (pattern instanceof HandlerTypePattern) {
if (optional && tokenIndex == tokens.size()) {
return new Range(0, tokens.size());
}
List<Handler> subHandlers = definitions.get(((HandlerTypePattern) pattern).getType());
for (Handler subHandler : subHandlers) {
Range range = subHandler._matchCount(tokens.subList(tokenIndex, tokens.size()), definitions);
if (range != null) {
if (tokenIndex == 0) {
tokenStartIndex = (int)range.getEnd();
}
tokenIndex += range.getEnd();
}
}
//return false;
}
else {
throw new IllegalArgumentException("Unknown pattern: " + pattern);
}
}
//if (tokenIndex != tokens.size()) {
//return -1;
//return false;
//}
return new Range(tokenStartIndex, tokenIndex);
}
@Override
public String toString() {
return "[Handler: " + _handler + "]";
}
public static synchronized Map<Handler.HandlerType, List<Handler>> definitions(Options options) {
Map<Handler.HandlerType, List<Handler>> definitions = _definitions;
// MS: technically we can't cache any longer because endian precendence mucks with the settings ...
List<EndianPrecedence> defaultEndianPrecedences = Arrays.asList(EndianPrecedence.Middle, EndianPrecedence.Little);
if (definitions == null || (options.getEndianPrecedence() != null && !options.getEndianPrecedence().equals(defaultEndianPrecedences))) {
List<EndianPrecedence> endianPrecendence = options.getEndianPrecedence();
if (endianPrecendence == null) {
endianPrecendence = defaultEndianPrecedences;
}
// ensure the endian precedence is exactly two elements long
if (endianPrecendence.size() != 2) {
throw new IllegalArgumentException("More than two elements specified for endian precedence array: " + endianPrecendence + ".");
}
Map<EndianPrecedence, Handler> endianHandlers = new HashMap<>();
// handler for dd/mm/yyyy
endianHandlers.put(EndianPrecedence.Little, new Handler(new SdSmSyHandler(), new TagPattern(ScalarDay.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
// handler for mm/dd/yyyy
endianHandlers.put(EndianPrecedence.Middle, new Handler(new SmSdSyHandler(), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
// ensure we have valid endian values
// MS: generics guarantee this for us
definitions = new HashMap<Handler.HandlerType, List<Handler>>();
List<Handler> timeHandlers = new LinkedList<>();
timeHandlers.add(new Handler(null, new TagPattern(RepeaterTime.class), new TagPattern(Grabber.class, true), new TagPattern(RepeaterDayPortion.class, true)));
definitions.put(Handler.HandlerType.TIME, timeHandlers);
List<Handler> dateHandlers = new LinkedList<>();
dateHandlers.add(new Handler(new RdnRmnSdTTzSyHandler(), new TagPattern(RepeaterDayName.class), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarDay.class), new TagPattern(RepeaterTime.class), new TagPattern(SeparatorSlashOrDash.class, true), new TagPattern(TimeZone.class), new TagPattern(ScalarYear.class)));
// DIFF: We add scalar year as a standalone match
dateHandlers.add(new Handler(new SyHandler(), new TagPattern(ScalarYear.class)));
// DIFF: We add an optional comma to MDY
dateHandlers.add(new Handler(new RmnSdSyHandler(), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorComma.class, true), new TagPattern(ScalarYear.class)));
dateHandlers.add(new Handler(new RmnSdSyHandler(), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorComma.class, true), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
dateHandlers.add(new Handler(new RmnSdHandler(), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
for (EndianPrecedence precedence : endianPrecendence) {
dateHandlers.add(endianHandlers.get(precedence));
}
dateHandlers.add(new Handler(new RmnSdOnHandler(), new TagPattern(RepeaterTime.class), new TagPattern(RepeaterDayPortion.class, true), new TagPattern(SeparatorOn.class, true), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarDay.class)));
dateHandlers.add(new Handler(new RmnOdHandler(), new TagPattern(RepeaterMonthName.class), new TagPattern(OrdinalDay.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
dateHandlers.add(new Handler(new RmnOdOnHandler(), new TagPattern(RepeaterTime.class), new TagPattern(RepeaterDayPortion.class, true), new TagPattern(SeparatorOn.class, true), new TagPattern(RepeaterMonthName.class), new TagPattern(OrdinalDay.class)));
dateHandlers.add(new Handler(new RmnSyHandler(), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarYear.class)));
dateHandlers.add(new Handler(new SdRmnSyHandler(), new TagPattern(ScalarDay.class), new TagPattern(RepeaterMonthName.class), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
dateHandlers.add(new Handler(new SmSdSyHandler(), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
dateHandlers.add(new Handler(new SdSmSyHandler(), new TagPattern(ScalarDay.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarYear.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
dateHandlers.add(new Handler(new SySmSdHandler(), new TagPattern(ScalarYear.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
// DIFF: We make 05/06 interpret as month/day before month/year
dateHandlers.add(new Handler(new SmSdHandler(), false, new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarDay.class), new TagPattern(SeparatorAt.class, true), new HandlerTypePattern(Handler.HandlerType.TIME, true)));
// DIFF: Also, we support a leading time -- for 5pm 2/10
dateHandlers.add(new Handler(new SmSdHandler(), false, new HandlerTypePattern(Handler.HandlerType.TIME, true), new TagPattern(SeparatorOn.class, true), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarDay.class)));
dateHandlers.add(new Handler(new SmSyHandler(), new TagPattern(ScalarMonth.class), new TagPattern(SeparatorSlashOrDash.class), new TagPattern(ScalarYear.class)));
definitions.put(Handler.HandlerType.DATE, dateHandlers);
// tonight at 7pm
List<Handler> anchorHandlers = new LinkedList<>();
anchorHandlers.add(new Handler(new RHandler(), new TagPattern(Grabber.class, true), new TagPattern(Repeater.class), new TagPattern(SeparatorAt.class, true), new TagPattern(Repeater.class, true), new TagPattern(Repeater.class, true)));
// DIFF: Add support for "next" and "last" grabbers
anchorHandlers.add(new Handler(new RHandler(), new TagPattern(Grabber.class, true), new TagPattern(Repeater.class), new TagPattern(Repeater.class), new TagPattern(SeparatorAt.class, true), new TagPattern(Grabber.class, true), new TagPattern(Repeater.class, true), new TagPattern(Repeater.class, true)));
anchorHandlers.add(new Handler(new RGRHandler(), new TagPattern(Repeater.class), new TagPattern(Grabber.class), new TagPattern(Repeater.class)));
definitions.put(Handler.HandlerType.ANCHOR, anchorHandlers);
// 3 weeks from now, in 2 months
List<Handler> arrowHandlers = new LinkedList<>();
arrowHandlers.add(new Handler(new SRPHandler(), new TagPattern(Scalar.class), new TagPattern(Repeater.class), new TagPattern(Pointer.class)));
arrowHandlers.add(new Handler(new PSRHandler(), new TagPattern(Pointer.class), new TagPattern(Scalar.class), new TagPattern(Repeater.class)));
arrowHandlers.add(new Handler(new SRPAHandler(), new TagPattern(Scalar.class), new TagPattern(Repeater.class), new TagPattern(Pointer.class), new HandlerTypePattern(Handler.HandlerType.ANCHOR)));
definitions.put(Handler.HandlerType.ARROW, arrowHandlers);
// 3rd week in march
List<Handler> narrowHandlers = new LinkedList<>();
narrowHandlers.add(new Handler(new ORSRHandler(), new TagPattern(Ordinal.class), new TagPattern(Repeater.class), new TagPattern(SeparatorIn.class), new TagPattern(Repeater.class)));
narrowHandlers.add(new Handler(new ORGRHandler(), new TagPattern(Ordinal.class), new TagPattern(Repeater.class), new TagPattern(Grabber.class), new TagPattern(Repeater.class)));
definitions.put(Handler.HandlerType.NARROW, narrowHandlers);
if (_definitions == null) {
_definitions = definitions;
}
}
return definitions;
}
public static Span tokensToSpan(List<Token> tokens, Options options) {
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: " + tokens);
}
Range range = null;
// maybe it's a specific date
Map<Handler.HandlerType, List<Handler>> definitions = Handler.definitions(options);
for (Handler handler : definitions.get(Handler.HandlerType.DATE)) {
if (handler.isCompatible(options) && (range = handler.matchCount(tokens, definitions)) != null) {
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: date " + handler);
}
List<Token> goodTokens = new LinkedList<>();
for (Token token : range.subList(tokens)) {
if (token.getTag(Separator.class) == null) {
goodTokens.add(token);
}
}
return handler.getHandler().handle(goodTokens, options);
}
}
// I guess it's not a specific date, maybe it's just an anchor
for (Handler handler : definitions.get(Handler.HandlerType.ANCHOR)) {
if (handler.isCompatible(options) && (range = handler.matchCount(tokens, definitions)) != null) {
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: anchor " + handler);
}
List<Token> goodTokens = new LinkedList<>();
for (Token token : range.subList(tokens)) {
if (token.getTag(Separator.class) == null) {
goodTokens.add(token);
}
}
return handler.getHandler().handle(goodTokens, options);
}
}
// not an anchor, perhaps it's an arrow
for (Handler handler : definitions.get(Handler.HandlerType.ARROW)) {
if (handler.isCompatible(options) && (range = handler.matchCount(tokens, definitions)) != null) {
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: arrow " + handler);
}
List<Token> goodTokens = new LinkedList<>();
for (Token token : range.subList(tokens)) {
if (token.getTag(SeparatorAt.class) == null && token.getTag(SeparatorSlashOrDash.class) == null && token.getTag(SeparatorComma.class) == null) {
goodTokens.add(token);
}
}
return handler.getHandler().handle(goodTokens, options);
}
}
// not an arrow, let's hope it's a narrow
for (Handler handler : definitions.get(Handler.HandlerType.NARROW)) {
if (handler.isCompatible(options) && (range = handler.matchCount(tokens, definitions)) != null) {
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: narrow " + handler);
}
//List<Token> goodTokens = new LinkedList<>();
//for (Token token : tokens) {
//if (token.getTag(Separator.class) == null) {
// goodTokens.add(token);
//}
//}
return handler.getHandler().handle(range.subList(tokens), options);
}
}
// I guess you're out of luck!
if (options.isDebug()) {
System.out.println("Chronic.tokensToSpan: none");
}
return null;
}
public static List<Repeater<?>> getRepeaters(List<Token> tokens) {
List<Repeater<?>> repeaters = new LinkedList<Repeater<?>>();
for (Token token : tokens) {
Repeater<?> tag = token.getTag(Repeater.class);
if (tag != null) {
repeaters.add(tag);
}
}
Collections.sort(repeaters);
Collections.reverse(repeaters);
return repeaters;
}
public static Span getAnchor(List<Token> tokens, Options options) {
Grabber grabber;
// DIFF: If your pointer is PAST, you want "last Monday" not "this Monday"
Pointer.PointerType pointer;
if (options.getContext() == PointerType.PAST) {
grabber = new Grabber(Grabber.Relative.LAST);
}
else {
grabber = new Grabber(Grabber.Relative.THIS);
}
pointer = Pointer.PointerType.FUTURE;
List<Repeater<?>> repeaters = getRepeaters(tokens);
for (int i = 0; i < repeaters.size(); i++) {
tokens.remove(tokens.size() - 1);
}
if (!tokens.isEmpty() && tokens.get(0).getTag(Grabber.class) != null) {
grabber = tokens.get(0).getTag(Grabber.class);
tokens.remove(tokens.size() - 1);
}
Repeater<?> head = repeaters.remove(0);
head.setStart((Calendar) options.getNow().clone());
Span outerSpan;
Grabber.Relative grabberType = grabber.getType();
if (grabberType == Grabber.Relative.LAST) {
outerSpan = head.nextSpan(Pointer.PointerType.PAST);
}
else if (grabberType == Grabber.Relative.THIS) {
if (repeaters.size() > 0) {
outerSpan = head.thisSpan(PointerType.NONE);
}
else {
outerSpan = head.thisSpan(options.getContext());
}
}
else if (grabberType == Grabber.Relative.NEXT) {
outerSpan = head.nextSpan(Pointer.PointerType.FUTURE);
}
else {
throw new IllegalArgumentException("Invalid grabber type " + grabberType + ".");
}
if (options.isDebug()) {
System.out.println("Chronic.getAnchor: grabber = " + grabber + "; repeaters = " + repeaters + "; outerSpan = " + outerSpan);
}
Span anchor = findWithin(repeaters, outerSpan, pointer, options);
return anchor;
}
// MS: Not necessary because we changed the impl to be more javaish
//def apply_endian_precedences(precedences)
//def endian_variable_name_for(e)
//def swap(arr, a, b); arr[a], arr[b] = arr[b], arr[a]; end
public static Span parseTime(List<Token> tokens, int timeTokenOffset, int year, int month, int day, Options options) {
// MS: properly parse time in this format
Span span;
try {
List<Token> timeTokens = tokens.subList(timeTokenOffset, tokens.size());
Calendar dayStart = Time.construct(year, month, day);
span = Handler.dayOrTime(dayStart, timeTokens, options);
}
catch (IllegalArgumentException e) {
if (options.isDebug()) {
e.printStackTrace(System.out);
}
span = null;
}
return span;
}
public static Span dayOrTime(Calendar dayStart, List<Token> timeTokens, Options options) {
Span outerSpan = new Span(dayStart, Time.cloneAndAdd(dayStart, Calendar.DAY_OF_MONTH, 1));
if (!timeTokens.isEmpty()) {
// /** SUPER HACK MODE FOR TIMES **/
// Tag<?> dayPortionTag = null;
// Tag<?> timeTag = null;
// for (Token token : timeTokens) {
// Tag<?> tempDayPortionTag = token.getTag(RepeaterDayPortion.class);
// if (tempDayPortionTag != null) {
// dayPortionTag = tempDayPortionTag;
// }
//
// Tag<?> tempTimeTag = token.getTag(RepeaterTime.class);
// if (tempTimeTag != null) {
// timeTag = tempTimeTag;
// }
// }
//
// if (timeTag != null && dayPortionTag != null) {
// Tick tick = (Tick)timeTag.getType();
// RepeaterDayPortion.DayPortion dayPortion = (RepeaterDayPortion.DayPortion)dayPortionTag.getType();
// if (tick.intValue() <= (RepeaterDay.DAY_SECONDS / 2)) {
// if (dayPortion == RepeaterDayPortion.DayPortion.PM) {
// if (tick.intValue() == (12 * 60 * 60)) {
// Calendar exactTime = Time.cloneAndAdd(dayStart, Calendar.SECOND, tick.intValue());
// return new Span(exactTime, exactTime);
// }
// Calendar exactTime = Time.cloneAndAdd(dayStart, Calendar.SECOND, tick.intValue() + RepeaterDay.DAY_SECONDS / 2);
// return new Span(exactTime, exactTime);
// }
// else if (dayPortion == RepeaterDayPortion.DayPortion.AM) {
// if (tick.intValue() == (12 * 60 * 60)) {
// Calendar exactTime = dayStart;
// return new Span(exactTime, exactTime);
// }
// Calendar exactTime = Time.cloneAndAdd(dayStart, Calendar.SECOND, tick.intValue());
// return new Span(exactTime, exactTime);
// }
// }
// }
// /** SUPER HACK MODE FOR TIMES **/
// DIFF: If you're generating past dates, you want to use the end of the day as the offset so "5pm" ends up on the same day as the date you chose
if (options.getContext() == PointerType.PAST) {
options.setNow(outerSpan.getEndCalendar());
}
else {
options.setNow(outerSpan.getBeginCalendar());
}
Span time = getAnchor(dealiasAndDisambiguateTimes(timeTokens, options), options);
return time;
}
return outerSpan;
}
/**
* Recursively finds repeaters within other repeaters.
* Returns a Span representing the innermost time span
* or nil if no repeater union could be found
*/
public static Span findWithin(List<Repeater<?>> tags, Span span, Pointer.PointerType pointer, Options options) {
if (options.isDebug()) {
System.out.println("Chronic.findWithin: " + tags + " in " + span);
}
if (tags.isEmpty()) {
return span;
}
Repeater<?> head = tags.get(0);
List<Repeater<?>> rest = (tags.size() > 1) ? tags.subList(1, tags.size()) : new LinkedList<Repeater<?>>();
head.setStart((pointer == Pointer.PointerType.FUTURE) ? span.getBeginCalendar() : span.getEndCalendar());
Span h = head.thisSpan(PointerType.NONE);
if (span.contains(h.getBegin()) || span.contains(h.getEnd())) {
return findWithin(rest, h, pointer, options);
}
return null;
}
@SuppressWarnings("unchecked")
public static List<Token> dealiasAndDisambiguateTimes(List<Token> tokens, Options options) {
// handle aliases of am/pm
// 5:00 in the morning => 5:00 am
// 7:00 in the evening => 7:00 pm
int dayPortionIndex = -1;
int tokenSize = tokens.size();
for (int i = 0; dayPortionIndex == -1 && i < tokenSize; i++) {
Token t = tokens.get(i);
if (t.getTag(RepeaterDayPortion.class) != null) {
dayPortionIndex = i;
}
}
int timeIndex = -1;
for (int i = 0; timeIndex == -1 && i < tokenSize; i++) {
Token t = tokens.get(i);
if (t.getTag(RepeaterTime.class) != null) {
timeIndex = i;
}
}
if (dayPortionIndex != -1 && timeIndex != -1) {
Token t1 = tokens.get(dayPortionIndex);
Tag<RepeaterDayPortion<?>> t1Tag = t1.getTag(RepeaterDayPortion.class);
Object t1TagType = t1Tag.getType();
if (RepeaterDayPortion.DayPortion.MORNING.equals(t1TagType)) {
if (options.isDebug()) {
System.out.println("Chronic.dealiasAndDisambiguateTimes: morning->am");
}
t1.untag(RepeaterDayPortion.class);
t1.tag(new EnumRepeaterDayPortion(RepeaterDayPortion.DayPortion.AM));
}
else if (RepeaterDayPortion.DayPortion.AFTERNOON.equals(t1TagType) || RepeaterDayPortion.DayPortion.EVENING.equals(t1TagType) || RepeaterDayPortion.DayPortion.NIGHT.equals(t1TagType)) {
if (options.isDebug()) {
System.out.println("Chronic.dealiasAndDisambiguateTimes: " + t1TagType + "->pm");
}
t1.untag(RepeaterDayPortion.class);
t1.tag(new EnumRepeaterDayPortion(RepeaterDayPortion.DayPortion.PM));
}
}
// int tokenSize = tokens.size();
// for (int i = 0; i < tokenSize; i++) {
// Token t0 = tokens.get(i);
// if (i < tokenSize - 1) {
// Token t1 = tokens.get(i + 1);
// RepeaterDayPortion<?> t1Tag = t1.getTag(RepeaterDayPortion.class);
// if (t1Tag != null && t0.getTag(RepeaterTime.class) != null) {
// if (t1Tag.getType() == RepeaterDayPortion.DayPortion.MORNING) {
// t1.untag(RepeaterDayPortion.class);
// t1.tag(new EnumRepeaterDayPortion(RepeaterDayPortion.DayPortion.AM));
// }
// else if (t1Tag.getType() == RepeaterDayPortion.DayPortion.AFTERNOON || t1Tag.getType() == RepeaterDayPortion.DayPortion.EVENING || t1Tag.getType() == RepeaterDayPortion.DayPortion.NIGHT) {
// t1.untag(RepeaterDayPortion.class);
// t1.tag(new EnumRepeaterDayPortion(RepeaterDayPortion.DayPortion.PM));
// }
// }
// }
// }
// handle ambiguous times if :ambiguous_time_range is specified
if (options.getAmbiguousTimeRange() != 0) {
List<Token> ttokens = new LinkedList<>();
for (int i = 0; i < tokenSize; i++) {
Token t0 = tokens.get(i);
ttokens.add(t0);
Token t1 = null;
if (i < tokenSize - 1) {
t1 = tokens.get(i + 1);
}
if (t0.getTag(RepeaterTime.class) != null && t0.getTag(RepeaterTime.class).getType().isAmbiguous() && (t1 == null || t1.getTag(RepeaterDayPortion.class) == null)) {
Token distoken = new Token("disambiguator");
distoken.tag(new IntegerRepeaterDayPortion(Integer.valueOf(options.getAmbiguousTimeRange())));
ttokens.add(distoken);
}
}
tokens = ttokens;
}
if (options.isDebug()) {
System.out.println("Chronic.dealiasAndDisambiguateTimes: " + tokens);
}
return tokens;
}
}