This commit is contained in:
crimean 2018-10-08 12:52:22 +03:00
parent ea24dbd813
commit 7525c4f8d3
2 changed files with 283 additions and 60 deletions

View file

@ -636,6 +636,7 @@ public class OpeningHoursParser {
* Day number 0 is MONDAY * Day number 0 is MONDAY
*/ */
private boolean[] days = new boolean[7]; private boolean[] days = new boolean[7];
private boolean hasDays = false;
/** /**
* represents the list on which month it is open. * represents the list on which month it is open.
@ -646,7 +647,8 @@ public class OpeningHoursParser {
/** /**
* represents the list on which day it is open. * represents the list on which day it is open.
*/ */
private boolean[] dayMonths = new boolean[31]; private boolean[][] dayMonths = new boolean[12][31];
private boolean hasDayMonths = false;
/** /**
* lists of equal size representing the start and end times * lists of equal size representing the start and end times
@ -694,10 +696,14 @@ public class OpeningHoursParser {
/** /**
* @return the day months of the rule * @return the day months of the rule
*/ */
public boolean[] getDayMonths() { public boolean[][] getDayMonths() {
return dayMonths; return dayMonths;
} }
public boolean[] getDayMonths(int month) {
return dayMonths[month];
}
/** /**
* return an array representing the months of the rule * return an array representing the months of the rule
* *
@ -982,20 +988,108 @@ public class OpeningHoursParser {
break; break;
} }
} }
// Month boolean allDays = !hasDayMonths;
if (!allMonths) { if (!allDays) {
boolean dash = false;
boolean first = true;
int monthAdded = -1;
int excludedMonthEnd = -1;
int excludedDayEnd = -1;
int excludedMonthStart = -1;
int excludedDayStart = -1;
if (dayMonths[0][0] && dayMonths[11][30]) {
int prevMonth = 0;
int prevDay = 0;
for (int month = 0; month < dayMonths.length; month++) {
for (int day = 0; day < dayMonths[month].length; day++) {
if (day == 1) {
prevMonth = month;
}
if (!dayMonths[month][day]) {
excludedMonthEnd = prevMonth;
excludedDayEnd = prevDay;
break;
}
prevDay = day;
}
if (excludedDayEnd != -1) {
break;
}
}
prevMonth = dayMonths.length - 1;
prevDay = dayMonths[prevMonth].length - 1;
for (int month = dayMonths.length - 1; month >= 0; month--) {
for (int day = dayMonths[month].length - 1; day >= 0; day--) {
if (day == dayMonths[month].length - 2) {
prevMonth = month;
}
if (!dayMonths[month][day]) {
excludedMonthStart = prevMonth;
excludedDayStart = prevDay;
break;
}
prevDay = day;
}
if (excludedDayStart != -1) {
break;
}
}
}
for (int month = 0; month < dayMonths.length; month++) {
for (int day = 0; day < dayMonths[month].length; day++) {
if (excludedDayStart != -1 && excludedDayEnd != -1) {
if (month < excludedMonthEnd || (month == excludedMonthEnd && day <= excludedDayEnd)) {
continue;
} else if (month > excludedMonthStart || (month == excludedMonthStart && day >= excludedDayStart)) {
continue;
}
}
if (dayMonths[month][day]) {
if (day == 0 && dash) {
continue;
}
if (day > 0 && dayMonths[month][day - 1]
&& ((day < dayMonths[month].length - 1 && dayMonths[month][day + 1]) || (day == dayMonths[month].length - 1 && month < dayMonths.length - 1 && dayMonths[month + 1][0]))) {
if (!dash) {
dash = true;
if (!first) {
b.append("-");
}
}
continue;
}
if (first) {
first = false;
} else if (!dash) {
b.append(", ");
monthAdded = -1;
}
if (monthAdded != month) {
b.append(monthNames[month]).append(" ");
monthAdded = month;
}
b.append(day + 1);
dash = false;
}
}
}
if (excludedDayStart != -1 && excludedDayEnd != -1) {
if (first) {
first = false;
} else if (!dash) {
b.append(", ");
}
b.append(monthNames[excludedMonthStart]).append(" ").append(excludedDayStart + 1)
.append("-")
.append(monthNames[excludedMonthEnd]).append(" ").append(excludedDayEnd + 1);
}
if (!first) {
b.append(" ");
}
} else if (!allMonths) {
addArray(months, monthNames, b); addArray(months, monthNames, b);
} }
boolean allDays = true;
for (int i = 0; i < dayMonths.length; i++) {
if (!dayMonths[i]) {
allDays = false;
break;
}
}
if (!allDays) {
addArray(dayMonths, null, b);
}
// Day // Day
appendDaysString(b, dayNames); appendDaysString(b, dayNames);
// Time // Time
@ -1312,9 +1406,21 @@ public class OpeningHoursParser {
int i = cal.get(Calendar.DAY_OF_WEEK); int i = cal.get(Calendar.DAY_OF_WEEK);
int day = (i + 5) % 7; int day = (i + 5) % 7;
int previous = (day + 6) % 7; int previous = (day + 6) % 7;
boolean thisDay = days[day] || dayMonths[dmonth]; boolean thisDay = hasDays || hasDayMonths;
if (thisDay && hasDayMonths) {
thisDay = dayMonths[month][dmonth];
}
if (thisDay && hasDays) {
thisDay = days[day];
}
// potential error for Dec 31 12:00-01:00 // potential error for Dec 31 12:00-01:00
boolean previousDay = days[previous] || (dmonth > 0 && dayMonths[dmonth - 1]); boolean previousDay = hasDays || hasDayMonths;
if (previousDay && hasDayMonths && dmonth > 0) {
previousDay = dayMonths[month][dmonth - 1];
}
if (previousDay && hasDays) {
previousDay = days[previous];
}
if (!thisDay && !previousDay) { if (!thisDay && !previousDay) {
return 0; return 0;
} }
@ -1462,13 +1568,23 @@ public class OpeningHoursParser {
} catch(NumberFormatException e){ } catch(NumberFormatException e){
} }
} }
public Token(TokenType tokenType, int tokenMainNumber) {
type = tokenType;
mainNumber = tokenMainNumber;
text = Integer.toString(mainNumber);
}
int mainNumber = -1; int mainNumber = -1;
TokenType type; TokenType type;
String text; String text;
Token parent;
@Override @Override
public String toString() { public String toString() {
return text + " [" + type + "] "; if (parent != null) {
return parent.text + " [" + parent.type + "] (" + text + " [" + type + "]) ";
} else {
return text + " [" + type + "] ";
}
} }
} }
@ -1503,9 +1619,10 @@ public class OpeningHoursParser {
basic.setComment(comment); basic.setComment(comment);
boolean[] days = basic.getDays(); boolean[] days = basic.getDays();
boolean[] months = basic.getMonths(); boolean[] months = basic.getMonths();
boolean[] dayMonths = basic.getDayMonths(); boolean[][] dayMonths = basic.getDayMonths();
if ("24/7".equals(localRuleString)) { if ("24/7".equals(localRuleString)) {
Arrays.fill(days, true); Arrays.fill(days, true);
basic.hasDays = true;
Arrays.fill(months, true); Arrays.fill(months, true);
basic.addTimeRange(0, 24 * 60); basic.addTimeRange(0, 24 * 60);
rules.add(basic); rules.add(basic);
@ -1598,10 +1715,12 @@ public class OpeningHoursParser {
private static void buildRule(BasicOpeningHourRule basic, List<Token> tokens, List<OpeningHoursRule> rules) { private static void buildRule(BasicOpeningHourRule basic, List<Token> tokens, List<OpeningHoursRule> rules) {
// order MONTH MONTH_DAY DAY_WEEK HOUR_MINUTE OPEN_OFF // order MONTH MONTH_DAY DAY_WEEK HOUR_MINUTE OPEN_OFF
TokenType currentParse = TokenType.TOKEN_UNKNOWN; TokenType currentParse = TokenType.TOKEN_UNKNOWN;
TokenType currentParseParent = TokenType.TOKEN_UNKNOWN;
List<Token[]> listOfPairs = new ArrayList<>(); List<Token[]> listOfPairs = new ArrayList<>();
Set<TokenType> presentTokens = new HashSet<>(); Set<TokenType> presentTokens = new HashSet<>();
Token[] currentPair = new Token[2]; Token[] currentPair = new Token[2];
listOfPairs.add(currentPair); listOfPairs.add(currentPair);
Token prevToken = null;
int indexP = 0; int indexP = 0;
for(int i = 0; i <= tokens.size(); i++) { for(int i = 0; i <= tokens.size(); i++) {
Token t = i == tokens.size() ? null : tokens.get(i); Token t = i == tokens.size() ? null : tokens.get(i);
@ -1614,22 +1733,48 @@ public class OpeningHoursParser {
if (currentParse == TokenType.TOKEN_MONTH || currentParse == TokenType.TOKEN_DAY_MONTH if (currentParse == TokenType.TOKEN_MONTH || currentParse == TokenType.TOKEN_DAY_MONTH
|| currentParse == TokenType.TOKEN_DAY_WEEK || currentParse == TokenType.TOKEN_HOLIDAY) { || currentParse == TokenType.TOKEN_DAY_WEEK || currentParse == TokenType.TOKEN_HOLIDAY) {
boolean tokenDayMonth = currentParse == TokenType.TOKEN_DAY_MONTH;
boolean[] array = (currentParse == TokenType.TOKEN_MONTH) ? basic.getMonths() boolean[] array = (currentParse == TokenType.TOKEN_MONTH) ? basic.getMonths()
: (currentParse == TokenType.TOKEN_DAY_MONTH) ? basic.getDayMonths() : basic.getDays(); : tokenDayMonth ? null : basic.getDays();
for (Token[] pair : listOfPairs) { for (Token[] pair : listOfPairs) {
if (pair[0] != null && pair[1] != null) { if (pair[0] != null && pair[1] != null) {
if (pair[0].mainNumber <= pair[1].mainNumber) { Token firstMonthToken = pair[0].parent;
for (int j = pair[0].mainNumber; j <= pair[1].mainNumber && j < array.length; j++) { Token lastMonthToken = pair[1].parent;
array[j] = true; if (tokenDayMonth && firstMonthToken != null) {
} if (lastMonthToken != null && lastMonthToken.mainNumber != firstMonthToken.mainNumber) {
} else { Token[] p = new Token[]{firstMonthToken, lastMonthToken};
// overflow fillRuleArray(basic.getMonths(), p);
for (int j = pair[0].mainNumber; j < array.length; j++) {
array[j] = true; Token t1 = new Token(TokenType.TOKEN_DAY_MONTH, pair[0].mainNumber);
} Token t2 = new Token(TokenType.TOKEN_DAY_MONTH, 30);
for (int j = 0; j <= pair[1].mainNumber; j++) { p = new Token[]{t1, t2};
array[j] = true; array = basic.getDayMonths(firstMonthToken.mainNumber);
fillRuleArray(array, p);
t1 = new Token(TokenType.TOKEN_DAY_MONTH, 0);
t2 = new Token(TokenType.TOKEN_DAY_MONTH, pair[1].mainNumber);
p = new Token[]{t1, t2};
array = basic.getDayMonths(lastMonthToken.mainNumber);
fillRuleArray(array, p);
if (firstMonthToken.mainNumber <= lastMonthToken.mainNumber) {
for (int month = firstMonthToken.mainNumber + 1; month < lastMonthToken.mainNumber; month++) {
Arrays.fill(basic.getDayMonths(month), true);
}
} else {
for (int month = firstMonthToken.mainNumber + 1; month < 12; month++) {
Arrays.fill(basic.getDayMonths(month), true);
}
for (int month = 0; month < lastMonthToken.mainNumber; month++) {
Arrays.fill(basic.getDayMonths(month), true);
}
}
} else {
array = basic.getDayMonths(firstMonthToken.mainNumber);
fillRuleArray(array, pair);
} }
} else if (array != null) {
fillRuleArray(array, pair);
} }
} else if (pair[0] != null) { } else if (pair[0] != null) {
if (pair[0].type == TokenType.TOKEN_HOLIDAY) { if (pair[0].type == TokenType.TOKEN_HOLIDAY) {
@ -1641,7 +1786,13 @@ public class OpeningHoursParser {
basic.easter = true; basic.easter = true;
} }
} else if (pair[0].mainNumber >= 0) { } else if (pair[0].mainNumber >= 0) {
array[pair[0].mainNumber] = true; Token firstMonthToken = pair[0].parent;
if (tokenDayMonth && firstMonthToken != null) {
array = basic.getDayMonths(firstMonthToken.mainNumber);
}
if (array != null) {
array[pair[0].mainNumber] = true;
}
} }
} }
} }
@ -1664,14 +1815,19 @@ public class OpeningHoursParser {
currentPair[indexP++] = t; currentPair[indexP++] = t;
if (t != null) { if (t != null) {
currentParse = t.type; currentParse = t.type;
currentParseParent = currentParse;
if (t.type == TokenType.TOKEN_DAY_MONTH && prevToken != null && prevToken.type == TokenType.TOKEN_MONTH) {
t.parent = prevToken;
currentParseParent = prevToken.type;
}
} }
} else if (t.type.ord() < currentParse.ord() && indexP == 0 && tokens.size() > i) { } else if (t.type.ord() < currentParseParent.ord() && indexP == 0 && tokens.size() > i) {
BasicOpeningHourRule newRule = new BasicOpeningHourRule(basic.getSequenceIndex()); BasicOpeningHourRule newRule = new BasicOpeningHourRule(basic.getSequenceIndex());
newRule.setComment(basic.getComment()); newRule.setComment(basic.getComment());
buildRule(newRule, tokens.subList(i, tokens.size()), rules); buildRule(newRule, tokens.subList(i, tokens.size()), rules);
tokens = tokens.subList(0, i + 1); tokens = tokens.subList(0, i + 1);
} else if (t.type == TokenType.TOKEN_COMMA) { } else if (t.type == TokenType.TOKEN_COMMA) {
if (tokens.size() > i + 1 && tokens.get(i + 1) != null && tokens.get(i + 1).type.ord() < currentParse.ord()) { if (tokens.size() > i + 1 && tokens.get(i + 1) != null && tokens.get(i + 1).type.ord() < currentParseParent.ord()) {
indexP = 0; indexP = 0;
} else { } else {
currentPair = new Token[2]; currentPair = new Token[2];
@ -1681,21 +1837,48 @@ public class OpeningHoursParser {
} else if (t.type == TokenType.TOKEN_DASH) { } else if (t.type == TokenType.TOKEN_DASH) {
} else if (t.type.ord() == currentParse.ord()) { } else if (t.type.ord() == currentParse.ord()) {
if(indexP < 2) { if (indexP < 2) {
currentPair[indexP++] = t; currentPair[indexP++] = t;
if (t.type == TokenType.TOKEN_DAY_MONTH && prevToken != null && prevToken.type == TokenType.TOKEN_MONTH) {
t.parent = prevToken;
}
} }
} }
prevToken = t;
} }
if(!presentTokens.contains(TokenType.TOKEN_MONTH)) { if (!presentTokens.contains(TokenType.TOKEN_MONTH)) {
Arrays.fill(basic.getMonths(), true); Arrays.fill(basic.getMonths(), true);
} else {
if (presentTokens.contains(TokenType.TOKEN_DAY_MONTH)) {
basic.hasDayMonths = true;
}
} }
if(!presentTokens.contains(TokenType.TOKEN_DAY_WEEK) && !presentTokens.contains(TokenType.TOKEN_HOLIDAY) && if (!presentTokens.contains(TokenType.TOKEN_DAY_WEEK) && !presentTokens.contains(TokenType.TOKEN_HOLIDAY) &&
!presentTokens.contains(TokenType.TOKEN_DAY_MONTH)) { !presentTokens.contains(TokenType.TOKEN_DAY_MONTH)) {
Arrays.fill(basic.getDays(), true); Arrays.fill(basic.getDays(), true);
basic.hasDays = true;
} else if (presentTokens.contains(TokenType.TOKEN_DAY_WEEK)) {
basic.hasDays = true;
} }
rules.add(0, basic); rules.add(0, basic);
} }
private static void fillRuleArray(boolean[] array, Token[] pair) {
if (pair[0].mainNumber <= pair[1].mainNumber) {
for (int j = pair[0].mainNumber; j <= pair[1].mainNumber && j < array.length; j++) {
array[j] = true;
}
} else {
// overflow
for (int j = pair[0].mainNumber; j < array.length; j++) {
array[j] = true;
}
for (int j = 0; j <= pair[1].mainNumber; j++) {
array[j] = true;
}
}
}
private static void findInArray(Token t, String[] list, TokenType tokenType) { private static void findInArray(Token t, String[] list, TokenType tokenType) {
for(int i = 0; i < list.length; i++) { for(int i = 0; i < list.length; i++) {
if(list[i].equals(t.text)) { if(list[i].equals(t.text)) {

View file

@ -1,25 +1,14 @@
package net.osmand.util; package net.osmand.util;
import java.io.Serializable; import net.osmand.util.OpeningHoursParser.OpeningHours;
import java.text.DateFormatSymbols;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.junit.Test; import org.junit.Test;
import net.osmand.util.OpeningHoursParser.OpeningHours; import java.text.ParseException;
import junit.framework.Assert; import java.text.SimpleDateFormat;
import gnu.trove.list.array.TIntArrayList; import java.util.Calendar;
import java.util.List;
import java.util.Locale;
/** /**
* Class used to parse opening hours * Class used to parse opening hours
@ -121,14 +110,65 @@ public class OpeningHoursParserTest {
@Test @Test
public void testOpeningHours() throws ParseException { public void testOpeningHours() throws ParseException {
// 0. not supported MON DAY-MON DAY (only supported Feb 2-14 or Feb-Oct: 09:00-17:30) // 0. not properly supported
// parseOpenedHours("Feb 16-Oct 15: 09:00-18:30; Oct 16-Nov 15: 09:00-17:30; Nov 16-Feb 15: 09:00-16:30");
// 1. not properly supported
// hours = parseOpenedHours("Mo-Su (sunrise-00:30)-(sunset+00:30)"); // hours = parseOpenedHours("Mo-Su (sunrise-00:30)-(sunset+00:30)");
OpeningHours hours = parseOpenedHours("Apr 05-Oct 24: Fr 08:00-16:00");
System.out.println(hours);
testOpened("26.08.2018 15:00", hours, false);
testOpened("29.03.2019 15:00", hours, false);
testOpened("05.04.2019 11:00", hours, true);
hours = parseOpenedHours("Oct 24-Apr 05: Fr 08:00-16:00");
System.out.println(hours);
testOpened("26.08.2018 15:00", hours, false);
testOpened("29.03.2019 15:00", hours, true);
testOpened("26.04.2019 11:00", hours, false);
hours = parseOpenedHours("Oct 24-Apr 05, Jun 10-Jun 20, Jul 6-12: Fr 08:00-16:00");
System.out.println(hours);
//testOpened("26.08.2018 15:00", hours, false);
testOpened("02.01.2019 15:00", hours, false);
testOpened("29.03.2019 15:00", hours, true);
testOpened("26.04.2019 11:00", hours, false);
hours = parseOpenedHours("Apr 05-24: Fr 08:00-16:00");
System.out.println(hours);
testOpened("12.10.2018 11:00", hours, false);
testOpened("12.04.2019 15:00", hours, true);
testOpened("27.04.2019 15:00", hours, false);
hours = parseOpenedHours("Apr 5: Fr 08:00-16:00");
System.out.println(hours);
testOpened("05.04.2019 15:00", hours, true);
testOpened("06.04.2019 15:00", hours, false);
hours = parseOpenedHours("Apr 24-05: Fr 08:00-16:00");
System.out.println(hours);
testOpened("12.10.2018 11:00", hours, false);
testOpened("12.04.2018 15:00", hours, false);
hours = parseOpenedHours("Apr: Fr 08:00-16:00");
System.out.println(hours);
testOpened("12.10.2018 11:00", hours, false);
testOpened("12.04.2019 15:00", hours, true);
hours = parseOpenedHours("Apr-Oct: Fr 08:00-16:00");
System.out.println(hours);
testOpened("09.11.2018 11:00", hours, false);
testOpened("12.10.2018 11:00", hours, true);
testOpened("24.08.2018 15:00", hours, true);
testOpened("09.03.2018 15:00", hours, false);
hours = parseOpenedHours("Apr, Oct: Fr 08:00-16:00");
System.out.println(hours);
testOpened("09.11.2018 11:00", hours, false);
testOpened("12.10.2018 11:00", hours, true);
testOpened("24.08.2018 15:00", hours, false);
testOpened("12.04.2019 15:00", hours, true);
// test basic case // test basic case
OpeningHours hours = parseOpenedHours("Mo-Fr 08:30-14:40"); //$NON-NLS-1$ hours = parseOpenedHours("Mo-Fr 08:30-14:40"); //$NON-NLS-1$
System.out.println(hours); System.out.println(hours);
testOpened("09.08.2012 11:00", hours, true); testOpened("09.08.2012 11:00", hours, true);
testOpened("09.08.2012 16:00", hours, false); testOpened("09.08.2012 16:00", hours, false);