2013-04-18 23:35:02 +02:00
|
|
|
package net.osmand.util;
|
2015-05-10 00:50:44 +02:00
|
|
|
/* Can be commented out in order to run the main function separately */
|
2013-04-18 23:35:02 +02:00
|
|
|
|
2015-08-21 12:13:51 +02:00
|
|
|
import java.io.Serializable;
|
2016-02-12 16:50:27 +01:00
|
|
|
import java.text.DateFormatSymbols;
|
2013-04-18 23:35:02 +02:00
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Calendar;
|
2016-02-12 16:50:27 +01:00
|
|
|
import java.util.Locale;
|
2013-04-18 23:35:02 +02:00
|
|
|
|
2015-12-02 16:48:33 +01:00
|
|
|
import gnu.trove.list.array.TIntArrayList;
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Class used to parse opening hours
|
2016-02-12 15:28:39 +01:00
|
|
|
* <p/>
|
2013-07-25 22:16:45 +02:00
|
|
|
* the method "parseOpenedHours" will parse an OSM opening_hours string and
|
|
|
|
* return an object of the type OpeningHours. That object can be used to check
|
2016-02-12 15:28:39 +01:00
|
|
|
* if the OSM feature is open at a certain time.
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
|
|
|
public class OpeningHoursParser {
|
2016-02-12 16:50:27 +01:00
|
|
|
private static final String[] daysStr;
|
|
|
|
private static final String[] localDaysStr;
|
|
|
|
private static final String[] monthsStr;
|
|
|
|
private static final String[] localMothsStr;
|
|
|
|
|
|
|
|
static {
|
|
|
|
DateFormatSymbols dateFormatSymbols = DateFormatSymbols.getInstance(Locale.US);
|
|
|
|
monthsStr = dateFormatSymbols.getShortMonths();
|
|
|
|
daysStr = getTwoLettersStringArray(dateFormatSymbols.getShortWeekdays());
|
|
|
|
dateFormatSymbols = DateFormatSymbols.getInstance();
|
|
|
|
localMothsStr = dateFormatSymbols.getShortMonths();
|
|
|
|
localDaysStr = getTwoLettersStringArray(dateFormatSymbols.getShortWeekdays());
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
2013-07-25 22:16:45 +02:00
|
|
|
* Default values for sunrise and sunset. Might be computed afterwards, not final.
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
|
|
|
private static String sunrise = "07:00", sunset = "21:00";
|
2013-07-25 22:16:45 +02:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
2013-07-25 22:16:45 +02:00
|
|
|
* Hour of when you would expect a day to be ended.
|
2016-02-12 15:28:39 +01:00
|
|
|
* This is to be used when no end hour is known (like pubs that open at a certain time,
|
2013-04-18 23:35:02 +02:00
|
|
|
* but close at a variable time, depending on the number of clients).
|
|
|
|
* OsmAnd needs to show a value, so there is some arbitrary default value chosen.
|
|
|
|
*/
|
|
|
|
private static String endOfDay = "24:00";
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2016-02-12 16:50:27 +01:00
|
|
|
private static String[] getTwoLettersStringArray(String[] strings) {
|
|
|
|
String[] newStrings = new String[strings.length];
|
|
|
|
for (int i = 0; i < strings.length; i++) {
|
|
|
|
if (strings[i] != null) {
|
|
|
|
if (strings[i].length() > 2) {
|
|
|
|
newStrings[i] = strings[i].substring(0, 2);
|
|
|
|
} else {
|
|
|
|
newStrings[i] = strings[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newStrings;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int getDayIndex(int i) {
|
|
|
|
switch (i) {
|
|
|
|
case 0: return Calendar.MONDAY;
|
|
|
|
case 1: return Calendar.TUESDAY;
|
|
|
|
case 2: return Calendar.WEDNESDAY;
|
|
|
|
case 3: return Calendar.THURSDAY;
|
|
|
|
case 4: return Calendar.FRIDAY;
|
|
|
|
case 5: return Calendar.SATURDAY;
|
|
|
|
case 6: return Calendar.SUNDAY;
|
|
|
|
default: return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
2016-02-12 15:28:39 +01:00
|
|
|
* This class contains the entire OpeningHours schema and
|
2013-04-18 23:35:02 +02:00
|
|
|
* offers methods to check directly weather something is open
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @author sander
|
|
|
|
*/
|
2015-08-25 14:21:53 +02:00
|
|
|
public static class OpeningHours implements Serializable {
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* list of the different rules
|
|
|
|
*/
|
|
|
|
private ArrayList<OpeningHoursRule> rules;
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Constructor
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param rules List of OpeningHoursRule to be given
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
|
|
|
public OpeningHours(ArrayList<OpeningHoursRule> rules) {
|
|
|
|
this.rules = rules;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Empty constructor
|
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public OpeningHours() {
|
2013-04-18 23:35:02 +02:00
|
|
|
rules = new ArrayList<OpeningHoursRule>();
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* add a rule to the opening hours
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param r rule to add
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public void addRule(OpeningHoursRule r) {
|
2013-04-18 23:35:02 +02:00
|
|
|
rules.add(r);
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* return the list of rules
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return the rules
|
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public ArrayList<OpeningHoursRule> getRules() {
|
2013-04-18 23:35:02 +02:00
|
|
|
return rules;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* check if the feature is opened at time "cal"
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param cal the time to check
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return true if feature is open
|
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public boolean isOpenedForTime(Calendar cal) {
|
2013-04-18 23:35:02 +02:00
|
|
|
/*
|
|
|
|
* first check for rules that contain the current day
|
|
|
|
* afterwards check for rules that contain the previous
|
|
|
|
* day with overlapping times (times after midnight)
|
|
|
|
*/
|
|
|
|
boolean isOpenDay = false;
|
2016-02-12 15:28:39 +01:00
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
if (r.containsDay(cal) && r.containsMonth(cal)) {
|
2013-04-18 23:35:02 +02:00
|
|
|
isOpenDay = r.isOpenedForTime(cal, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
boolean isOpenPrevious = false;
|
2016-02-12 15:28:39 +01:00
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
if (r.containsPreviousDay(cal) && r.containsMonth(cal)) {
|
2013-04-18 23:35:02 +02:00
|
|
|
isOpenPrevious = r.isOpenedForTime(cal, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return isOpenDay || isOpenPrevious;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2015-04-07 00:34:11 +02:00
|
|
|
public String getCurrentRuleTime(Calendar cal) {
|
|
|
|
String ruleOpen = null;
|
|
|
|
String ruleClosed = null;
|
2016-02-12 15:28:39 +01:00
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
if (r.containsPreviousDay(cal) && r.containsMonth(cal)) {
|
|
|
|
if (r.isOpenedForTime(cal, true)) {
|
2015-04-07 00:47:02 +02:00
|
|
|
ruleOpen = r.toRuleString(true);
|
2015-04-07 00:34:11 +02:00
|
|
|
} else {
|
2015-04-07 00:47:02 +02:00
|
|
|
ruleClosed = r.toRuleString(true);
|
2015-04-07 00:34:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
if (r.containsDay(cal) && r.containsMonth(cal)) {
|
|
|
|
if (r.isOpenedForTime(cal, false)) {
|
2015-04-07 00:47:02 +02:00
|
|
|
ruleOpen = r.toRuleString(true);
|
2015-04-07 00:34:11 +02:00
|
|
|
} else {
|
2015-04-07 00:47:02 +02:00
|
|
|
ruleClosed = r.toRuleString(true);
|
2015-04-07 00:34:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
if (ruleOpen != null) {
|
2015-04-07 00:34:11 +02:00
|
|
|
return ruleOpen;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
return ruleClosed;
|
2015-04-07 00:34:11 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
@Override
|
2016-02-12 15:28:39 +01:00
|
|
|
public String toString() {
|
2013-04-18 23:35:02 +02:00
|
|
|
StringBuilder s = new StringBuilder();
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
if (rules.isEmpty()) {
|
|
|
|
return "";
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
s.append(r.toString()).append("; ");
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
return s.substring(0, s.length() - 2);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-04-07 00:34:11 +02:00
|
|
|
|
2015-08-26 09:59:39 +02:00
|
|
|
public String toStringNoMonths() {
|
2015-08-25 16:48:08 +02:00
|
|
|
StringBuilder s = new StringBuilder();
|
|
|
|
if (rules.isEmpty()) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
s.append(r.toRuleString(true)).append("; ");
|
|
|
|
}
|
|
|
|
|
2016-02-12 15:28:39 +01:00
|
|
|
return s.substring(0, s.length() - 2);
|
2015-08-25 16:48:08 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2016-02-12 16:50:27 +01:00
|
|
|
public String toLocalStringNoMonths() {
|
|
|
|
StringBuilder s = new StringBuilder();
|
|
|
|
if (rules.isEmpty()) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (OpeningHoursRule r : rules) {
|
|
|
|
s.append(r.toLocalRuleString()).append("; ");
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.substring(0, s.length() - 2);
|
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Interface to represent a single rule
|
2016-02-12 15:28:39 +01:00
|
|
|
* <p/>
|
|
|
|
* A rule consist out of
|
|
|
|
* - a collection of days/dates
|
|
|
|
* - a time range
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
2015-08-25 14:21:53 +02:00
|
|
|
public static interface OpeningHoursRule extends Serializable {
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Check if, for this rule, the feature is opened for time "cal"
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param cal the time to check
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param checkPrevious only check for overflowing times (after midnight) or don't check for it
|
|
|
|
* @return true if the feature is open
|
|
|
|
*/
|
|
|
|
public boolean isOpenedForTime(Calendar cal, boolean checkPrevious);
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Check if the previous day before "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param cal; the time to check
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return true if the previous day is part of the rule
|
|
|
|
*/
|
|
|
|
public boolean containsPreviousDay(Calendar cal);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the day of "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param cal the time to check
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return true if the day is part of the rule
|
|
|
|
*/
|
|
|
|
public boolean containsDay(Calendar cal);
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
/**
|
|
|
|
* Check if the month of "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-06-25 12:11:58 +02:00
|
|
|
* @param cal the time to check
|
|
|
|
* @return true if the month is part of the rule
|
|
|
|
*/
|
|
|
|
public boolean containsMonth(Calendar cal);
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
|
2015-04-07 00:47:02 +02:00
|
|
|
public String toRuleString(boolean avoidMonths);
|
2016-02-12 16:50:27 +01:00
|
|
|
|
|
|
|
public String toLocalRuleString();
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* implementation of the basic OpeningHoursRule
|
2016-02-12 15:28:39 +01:00
|
|
|
* <p/>
|
2013-06-25 12:11:58 +02:00
|
|
|
* This implementation only supports month, day of weeks and numeral times, or the value "off"
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public static class BasicOpeningHourRule implements OpeningHoursRule {
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* represents the list on which days it is open.
|
|
|
|
* Day number 0 is MONDAY
|
|
|
|
*/
|
|
|
|
private boolean[] days = new boolean[7];
|
2013-07-25 22:16:45 +02:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
/**
|
2013-07-25 22:16:45 +02:00
|
|
|
* represents the list on which month it is open.
|
2015-12-29 16:11:55 +01:00
|
|
|
* Day number 0 is JANUARY.
|
2013-06-25 12:11:58 +02:00
|
|
|
*/
|
|
|
|
private boolean[] months = new boolean[12];
|
2013-07-25 22:16:45 +02:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* lists of equal size representing the start and end times
|
|
|
|
*/
|
2015-12-29 16:11:55 +01:00
|
|
|
private TIntArrayList startTimes = new TIntArrayList(), endTimes = new TIntArrayList();
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* return an array representing the days of the rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return the days of the rule
|
|
|
|
*/
|
|
|
|
public boolean[] getDays() {
|
|
|
|
return days;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
/**
|
|
|
|
* return an array representing the months of the rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-06-25 12:11:58 +02:00
|
|
|
* @return the months of the rule
|
|
|
|
*/
|
|
|
|
public boolean[] getMonths() {
|
|
|
|
return months;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* set a single start time, erase all previously added start times
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param s startTime to set
|
|
|
|
*/
|
|
|
|
public void setStartTime(int s) {
|
2015-12-29 16:11:55 +01:00
|
|
|
setSingleValueForArrayList(startTimes, s);
|
2016-02-12 15:28:39 +01:00
|
|
|
if (endTimes.size() != 1) {
|
2015-12-29 16:11:55 +01:00
|
|
|
setSingleValueForArrayList(endTimes, 0);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|
2015-12-29 16:11:55 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* set a single end time, erase all previously added end times
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param e endTime to set
|
|
|
|
*/
|
|
|
|
public void setEndTime(int e) {
|
2015-12-29 16:11:55 +01:00
|
|
|
setSingleValueForArrayList(endTimes, e);
|
2016-02-12 15:28:39 +01:00
|
|
|
if (startTimes.size() != 1) {
|
2015-12-29 16:11:55 +01:00
|
|
|
setSingleValueForArrayList(startTimes, 0);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|
2015-12-03 12:36:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set single start time. If position exceeds index of last item by one
|
|
|
|
* then new value will be added.
|
|
|
|
* If value is between 0 and last index, then value in the position p will be overwritten
|
|
|
|
* with new one.
|
|
|
|
* Else exception will be thrown.
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param s - value
|
2015-12-03 12:36:30 +01:00
|
|
|
* @param position - position to add
|
|
|
|
*/
|
|
|
|
public void setStartTime(int s, int position) {
|
2015-12-29 16:11:55 +01:00
|
|
|
if (position == startTimes.size()) {
|
|
|
|
startTimes.add(s);
|
|
|
|
endTimes.add(0);
|
2015-12-03 12:36:30 +01:00
|
|
|
} else {
|
2015-12-29 16:11:55 +01:00
|
|
|
startTimes.set(position, s);
|
2015-12-03 12:36:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set single end time. If position exceeds index of last item by one
|
|
|
|
* then new value will be added.
|
|
|
|
* If value is between 0 and last index, then value in the position p will be overwritten
|
|
|
|
* with new one.
|
|
|
|
* Else exception will be thrown.
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param s - value
|
2015-12-03 12:36:30 +01:00
|
|
|
* @param position - position to add
|
|
|
|
*/
|
|
|
|
public void setEndTime(int s, int position) {
|
2015-12-29 16:11:55 +01:00
|
|
|
if (position == startTimes.size()) {
|
|
|
|
endTimes.add(s);
|
|
|
|
startTimes.add(0);
|
2015-12-03 12:36:30 +01:00
|
|
|
} else {
|
2015-12-29 16:11:55 +01:00
|
|
|
endTimes.set(position, s);
|
2015-12-03 12:36:30 +01:00
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* get a single start time
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return a single start time
|
|
|
|
*/
|
|
|
|
public int getStartTime() {
|
2016-02-12 15:28:39 +01:00
|
|
|
if (startTimes.size() == 0) {
|
2013-04-18 23:35:02 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2015-12-29 16:11:55 +01:00
|
|
|
return startTimes.get(0);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-12-03 12:36:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* get a single start time in position
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2015-12-03 12:36:30 +01:00
|
|
|
* @param position position to get value from
|
|
|
|
* @return a single start time
|
|
|
|
*/
|
|
|
|
public int getStartTime(int position) {
|
2015-12-29 16:11:55 +01:00
|
|
|
return startTimes.get(position);
|
2015-12-03 12:36:30 +01:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* get a single end time
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @return a single end time
|
|
|
|
*/
|
|
|
|
public int getEndTime() {
|
2016-02-12 15:28:39 +01:00
|
|
|
if (endTimes.size() == 0) {
|
2013-04-18 23:35:02 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2015-12-29 16:11:55 +01:00
|
|
|
return endTimes.get(0);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-12-02 16:48:33 +01:00
|
|
|
|
2015-12-03 12:36:30 +01:00
|
|
|
/**
|
|
|
|
* get a single end time in position
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2015-12-03 12:36:30 +01:00
|
|
|
* @param position position to get value from
|
|
|
|
* @return a single end time
|
|
|
|
*/
|
|
|
|
public int getEndTime(int position) {
|
2015-12-29 16:11:55 +01:00
|
|
|
return endTimes.get(position);
|
2015-12-03 12:36:30 +01:00
|
|
|
}
|
|
|
|
|
2015-12-02 16:48:33 +01:00
|
|
|
/**
|
|
|
|
* get all start times as independent list
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2015-12-02 16:48:33 +01:00
|
|
|
* @return all start times
|
|
|
|
*/
|
|
|
|
public TIntArrayList getStartTimes() {
|
|
|
|
return new TIntArrayList(startTimes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get all end times as independent list
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2015-12-02 16:48:33 +01:00
|
|
|
* @return all end times
|
|
|
|
*/
|
|
|
|
public TIntArrayList getEndTimes() {
|
|
|
|
return new TIntArrayList(endTimes);
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Check if the weekday of time "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param cal the time to check
|
|
|
|
* @return true if this day is part of the rule
|
|
|
|
*/
|
|
|
|
@Override
|
2016-02-12 15:28:39 +01:00
|
|
|
public boolean containsDay(Calendar cal) {
|
2013-04-18 23:35:02 +02:00
|
|
|
int i = cal.get(Calendar.DAY_OF_WEEK);
|
|
|
|
int d = (i + 5) % 7;
|
2013-07-25 22:16:45 +02:00
|
|
|
if (days[d]) {
|
|
|
|
return true;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Check if the previous weekday of time "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param cal the time to check
|
|
|
|
* @return true if the previous day is part of the rule
|
|
|
|
*/
|
|
|
|
@Override
|
2016-02-12 15:28:39 +01:00
|
|
|
public boolean containsPreviousDay(Calendar cal) {
|
2013-04-18 23:35:02 +02:00
|
|
|
int i = cal.get(Calendar.DAY_OF_WEEK);
|
|
|
|
int p = (i + 4) % 7;
|
2013-07-30 21:28:19 +02:00
|
|
|
if (days[p]) {
|
|
|
|
return true;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2013-07-30 21:28:19 +02:00
|
|
|
return false;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
/**
|
|
|
|
* Check if the month of "cal" is part of this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-06-25 12:11:58 +02:00
|
|
|
* @param cal the time to check
|
|
|
|
* @return true if the month is part of the rule
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public boolean containsMonth(Calendar cal) {
|
|
|
|
int i = cal.get(Calendar.MONTH);
|
|
|
|
if (months[i]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Check if this rule says the feature is open at time "cal"
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param cal the time to check
|
|
|
|
* @return false in all other cases, also if only day is wrong
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public boolean isOpenedForTime(Calendar cal, boolean checkPrevious) {
|
|
|
|
int i = cal.get(Calendar.DAY_OF_WEEK);
|
|
|
|
int d = (i + 5) % 7;
|
|
|
|
int p = d - 1;
|
|
|
|
if (p < 0) {
|
|
|
|
p += 7;
|
|
|
|
}
|
2013-07-25 22:16:45 +02:00
|
|
|
int time = cal.get(Calendar.HOUR_OF_DAY) * 60 + cal.get(Calendar.MINUTE); // Time in minutes
|
2015-12-29 16:11:55 +01:00
|
|
|
for (i = 0; i < startTimes.size(); i++) {
|
|
|
|
int startTime = this.startTimes.get(i);
|
|
|
|
int endTime = this.endTimes.get(i);
|
2013-04-18 23:35:02 +02:00
|
|
|
if (startTime < endTime || endTime == -1) {
|
2013-05-09 14:59:53 +02:00
|
|
|
// one day working like 10:00-20:00 (not 20:00-04:00)
|
2013-04-18 23:35:02 +02:00
|
|
|
if (days[d] && !checkPrevious) {
|
|
|
|
if (time >= startTime && (endTime == -1 || time <= endTime)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-05-09 14:59:53 +02:00
|
|
|
// opening_hours includes day wrap like
|
|
|
|
// "We 20:00-03:00" or "We 07:00-07:00"
|
|
|
|
if (time >= startTime && days[d] && !checkPrevious) {
|
2013-04-18 23:35:02 +02:00
|
|
|
return true;
|
2013-05-09 14:59:53 +02:00
|
|
|
} else if (time < endTime && days[p] && checkPrevious) {
|
|
|
|
// check in previous day
|
2013-04-18 23:35:02 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
@Override
|
2015-04-07 00:47:02 +02:00
|
|
|
public String toRuleString(boolean avoidMonths) {
|
2016-02-12 16:50:27 +01:00
|
|
|
return toRuleString(avoidMonths, daysStr, monthsStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
private String toRuleString(boolean avoidMonths, String[] dayNames, String[] monthNames) {
|
2013-04-18 23:35:02 +02:00
|
|
|
StringBuilder b = new StringBuilder(25);
|
2013-07-30 21:28:19 +02:00
|
|
|
// Month
|
2016-02-12 15:28:39 +01:00
|
|
|
boolean dash = false;
|
2013-04-18 23:35:02 +02:00
|
|
|
boolean first = true;
|
2015-04-07 00:47:02 +02:00
|
|
|
if (!avoidMonths) {
|
|
|
|
for (int i = 0; i < 12; i++) {
|
|
|
|
if (months[i]) {
|
|
|
|
if (i > 0 && months[i - 1] && i < 11 && months[i + 1]) {
|
|
|
|
if (!dash) {
|
|
|
|
dash = true;
|
|
|
|
b.append("-"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
continue;
|
2013-07-30 21:28:19 +02:00
|
|
|
}
|
2015-04-07 00:47:02 +02:00
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else if (!dash) {
|
|
|
|
b.append(", "); //$NON-NLS-1$
|
|
|
|
}
|
2016-02-12 16:50:27 +01:00
|
|
|
b.append(monthNames[i]);
|
2015-04-07 00:47:02 +02:00
|
|
|
dash = false;
|
2013-07-30 21:28:19 +02:00
|
|
|
}
|
|
|
|
}
|
2015-04-07 00:47:02 +02:00
|
|
|
if (b.length() != 0) {
|
|
|
|
b.append(": ");
|
|
|
|
}
|
2013-07-30 21:28:19 +02:00
|
|
|
}
|
|
|
|
// Day
|
2013-04-18 23:35:02 +02:00
|
|
|
boolean open24_7 = true;
|
|
|
|
for (int i = 0; i < 7; i++) {
|
2015-08-25 14:21:53 +02:00
|
|
|
if (!days[i]) {
|
2013-04-18 23:35:02 +02:00
|
|
|
open24_7 = false;
|
2015-08-25 14:21:53 +02:00
|
|
|
break;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-12 16:50:27 +01:00
|
|
|
appendDaysString(b, dayNames);
|
2013-06-25 12:11:58 +02:00
|
|
|
// Time
|
2016-02-12 15:28:39 +01:00
|
|
|
if (startTimes == null || startTimes.size() == 0) {
|
2013-04-18 23:35:02 +02:00
|
|
|
b.append(" off ");
|
|
|
|
} else {
|
2016-02-12 15:28:39 +01:00
|
|
|
for (int i = 0; i < startTimes.size(); i++) {
|
2015-12-29 16:11:55 +01:00
|
|
|
int startTime = startTimes.get(i);
|
|
|
|
int endTime = endTimes.get(i);
|
2013-04-18 23:35:02 +02:00
|
|
|
if (open24_7 && startTime == 0 && endTime / 60 == 24) {
|
|
|
|
return "24/7";
|
|
|
|
}
|
|
|
|
b.append(" "); //$NON-NLS-1$
|
|
|
|
int stHour = startTime / 60;
|
|
|
|
int stTime = startTime - stHour * 60;
|
|
|
|
int enHour = endTime / 60;
|
|
|
|
int enTime = endTime - enHour * 60;
|
|
|
|
formatTime(stHour, stTime, b);
|
|
|
|
b.append("-"); //$NON-NLS-1$
|
|
|
|
formatTime(enHour, enTime, b);
|
|
|
|
b.append(",");
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
return b.substring(0, b.length() - 1);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-08-25 14:21:53 +02:00
|
|
|
|
2016-02-12 16:50:27 +01:00
|
|
|
@Override
|
|
|
|
public String toLocalRuleString() {
|
|
|
|
return toRuleString(true, localDaysStr, localMothsStr);
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
2015-04-07 00:47:02 +02:00
|
|
|
return toRuleString(false);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
|
2015-08-25 14:21:53 +02:00
|
|
|
public void appendDaysString(StringBuilder builder) {
|
2016-02-12 16:50:27 +01:00
|
|
|
appendDaysString(builder, daysStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void appendDaysString(StringBuilder builder, String[] daysNames) {
|
2016-02-12 15:28:39 +01:00
|
|
|
boolean dash = false;
|
2015-08-25 14:21:53 +02:00
|
|
|
boolean first = true;
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
|
|
if (days[i]) {
|
|
|
|
if (i > 0 && days[i - 1] && i < 6 && days[i + 1]) {
|
|
|
|
if (!dash) {
|
|
|
|
dash = true;
|
|
|
|
builder.append("-"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else if (!dash) {
|
|
|
|
builder.append(", "); //$NON-NLS-1$
|
|
|
|
}
|
2016-02-12 16:50:27 +01:00
|
|
|
builder.append(daysNames[getDayIndex(i)]);
|
2015-08-25 14:21:53 +02:00
|
|
|
dash = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Add a time range (startTime-endTime) to this rule
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param startTime startTime to add
|
2016-02-12 15:28:39 +01:00
|
|
|
* @param endTime endTime to add
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
|
|
|
public void addTimeRange(int startTime, int endTime) {
|
2015-12-29 16:11:55 +01:00
|
|
|
startTimes.add(startTime);
|
|
|
|
endTimes.add(endTime);
|
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
|
2015-12-30 17:29:29 +01:00
|
|
|
public int timesSize() {
|
|
|
|
return startTimes.size();
|
|
|
|
}
|
|
|
|
|
2015-12-29 16:11:55 +01:00
|
|
|
public void deleteTimeRange(int position) {
|
|
|
|
startTimes.removeAt(position);
|
|
|
|
endTimes.removeAt(position);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-12-03 12:36:30 +01:00
|
|
|
|
2015-12-29 16:11:55 +01:00
|
|
|
private static void setSingleValueForArrayList(TIntArrayList arrayList, int s) {
|
|
|
|
if (arrayList.size() > 0) {
|
|
|
|
arrayList.remove(0, arrayList.size());
|
|
|
|
}
|
|
|
|
arrayList.add(s);
|
2015-12-03 12:36:30 +01:00
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
|
|
|
|
public static class UnparseableRule implements OpeningHoursParser.OpeningHoursRule {
|
|
|
|
private String ruleString;
|
|
|
|
|
|
|
|
public UnparseableRule(String ruleString) {
|
|
|
|
this.ruleString = ruleString;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOpenedForTime(Calendar cal, boolean checkPrevious) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsPreviousDay(Calendar cal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsDay(Calendar cal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean containsMonth(Calendar cal) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toRuleString(boolean avoidMonths) {
|
|
|
|
return ruleString;
|
|
|
|
}
|
2016-01-05 12:18:03 +01:00
|
|
|
|
2016-02-12 16:50:27 +01:00
|
|
|
@Override
|
|
|
|
public String toLocalRuleString() {
|
|
|
|
return toRuleString(false);
|
|
|
|
}
|
|
|
|
|
2016-01-05 12:18:03 +01:00
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return toRuleString(false);
|
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* Parse an opening_hours string from OSM to an OpeningHours object which can be used to check
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param r the string to parse
|
2015-09-30 18:27:37 +02:00
|
|
|
* @return BasicRule if the String is successfully parsed and UnparseableRule otherwise
|
2013-04-18 23:35:02 +02:00
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public static OpeningHoursParser.OpeningHoursRule parseRule(String r) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// replace words "sunrise" and "sunset" by real hours
|
2016-02-07 19:06:10 +01:00
|
|
|
r = r.toLowerCase();
|
2016-02-12 15:28:39 +01:00
|
|
|
final String[] daysStr = new String[]{"mo", "tu", "we", "th", "fr", "sa", "su"};
|
|
|
|
final String[] monthsStr = new String[]{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
|
|
|
|
final String[] holidayStr = new String[]{"ph", "sh"};
|
2015-09-30 18:27:37 +02:00
|
|
|
String sunrise = "07:00";
|
|
|
|
String sunset = "21:00";
|
|
|
|
String endOfDay = "24:00";
|
|
|
|
|
|
|
|
String localRuleString = r.replaceAll("sunset", sunset).replaceAll("sunrise", sunrise)
|
|
|
|
.replaceAll("\\+", "-" + endOfDay);
|
2016-02-12 15:28:39 +01:00
|
|
|
int startDay = -1;
|
|
|
|
int previousDay = -1;
|
|
|
|
int startMonth = -1;
|
2013-06-25 12:11:58 +02:00
|
|
|
int previousMonth = -1;
|
2016-02-12 15:28:39 +01:00
|
|
|
int k = 0; // Position in opening_hours string
|
2013-07-25 22:16:45 +02:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
BasicOpeningHourRule basic = new BasicOpeningHourRule();
|
2016-02-12 15:28:39 +01:00
|
|
|
boolean[] days = basic.getDays();
|
2013-06-25 12:11:58 +02:00
|
|
|
boolean[] months = basic.getMonths();
|
2013-07-25 22:16:45 +02:00
|
|
|
// check 24/7
|
2016-02-12 15:28:39 +01:00
|
|
|
if ("24/7".equals(localRuleString)) {
|
2013-04-18 23:35:02 +02:00
|
|
|
Arrays.fill(days, true);
|
2015-09-30 18:27:37 +02:00
|
|
|
Arrays.fill(months, true);
|
|
|
|
basic.addTimeRange(0, 24 * 60);
|
|
|
|
return basic;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
|
|
|
|
for (; k < localRuleString.length(); k++) {
|
|
|
|
char ch = localRuleString.charAt(k);
|
2013-04-18 23:35:02 +02:00
|
|
|
if (Character.isDigit(ch)) {
|
|
|
|
// time starts
|
|
|
|
break;
|
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
if ((k + 2 < localRuleString.length())
|
|
|
|
&& localRuleString.substring(k, k + 3).equals("off")) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// value "off" is found
|
|
|
|
break;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (Character.isWhitespace(ch) || ch == ',') {
|
2015-09-30 18:27:37 +02:00
|
|
|
} else if (ch == '-') {
|
2016-02-12 15:28:39 +01:00
|
|
|
if (previousDay != -1) {
|
2013-06-25 12:11:58 +02:00
|
|
|
startDay = previousDay;
|
|
|
|
} else if (previousMonth != -1) {
|
|
|
|
startMonth = previousMonth;
|
2013-04-18 23:35:02 +02:00
|
|
|
} else {
|
2015-09-30 18:27:37 +02:00
|
|
|
return new UnparseableRule(r);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2013-07-25 22:16:45 +02:00
|
|
|
} else if (k < r.length() - 1) {
|
2013-04-18 23:35:02 +02:00
|
|
|
int i = 0;
|
2016-02-12 15:28:39 +01:00
|
|
|
for (String s : daysStr) {
|
|
|
|
if (s.charAt(0) == ch && s.charAt(1) == r.charAt(k + 1)) {
|
2013-04-18 23:35:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (i < daysStr.length) {
|
|
|
|
if (startDay != -1) {
|
2013-04-18 23:35:02 +02:00
|
|
|
for (int j = startDay; j <= i; j++) {
|
|
|
|
days[j] = true;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (startDay > i) {// overflow handling, e.g. Su-We
|
2013-04-18 23:35:02 +02:00
|
|
|
for (int j = startDay; j <= 6; j++) {
|
|
|
|
days[j] = true;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
for (int j = 0; j <= i; j++) {
|
2013-04-18 23:35:02 +02:00
|
|
|
days[j] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
startDay = -1;
|
|
|
|
} else {
|
|
|
|
days[i] = true;
|
|
|
|
}
|
|
|
|
previousDay = i;
|
2013-06-25 12:11:58 +02:00
|
|
|
} else {
|
|
|
|
// Read Month
|
|
|
|
int m = 0;
|
|
|
|
for (String s : monthsStr) {
|
|
|
|
if (s.charAt(0) == ch && s.charAt(1) == r.charAt(k + 1)
|
|
|
|
&& s.charAt(2) == r.charAt(k + 2)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m++;
|
|
|
|
}
|
|
|
|
if (m < monthsStr.length) {
|
|
|
|
if (startMonth != -1) {
|
|
|
|
for (int j = startMonth; j <= m; j++) {
|
|
|
|
months[j] = true;
|
|
|
|
}
|
|
|
|
if (startMonth > m) {// overflow handling, e.g. Oct-Mar
|
|
|
|
for (int j = startMonth; j <= 11; j++) {
|
|
|
|
months[j] = true;
|
|
|
|
}
|
|
|
|
for (int j = 0; j <= m; j++) {
|
|
|
|
months[j] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
startMonth = -1;
|
|
|
|
} else {
|
|
|
|
months[m] = true;
|
|
|
|
}
|
|
|
|
previousMonth = m;
|
|
|
|
}
|
2016-01-05 12:18:03 +01:00
|
|
|
if (previousMonth == -1) {
|
|
|
|
int h = 0;
|
2016-02-12 15:28:39 +01:00
|
|
|
for (String s : holidayStr) {
|
|
|
|
if (s.charAt(0) == ch && s.charAt(1) == r.charAt(k + 1)) {
|
2016-01-05 12:18:03 +01:00
|
|
|
return new UnparseableRule(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
} else {
|
2015-09-30 18:27:37 +02:00
|
|
|
return new UnparseableRule(r);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (previousDay == -1) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// no days given => take all days.
|
2016-02-12 15:28:39 +01:00
|
|
|
for (int i = 0; i < 7; i++) {
|
2013-04-18 23:35:02 +02:00
|
|
|
days[i] = true;
|
|
|
|
}
|
|
|
|
}
|
2013-06-25 12:11:58 +02:00
|
|
|
if (previousMonth == -1) {
|
|
|
|
// no month given => take all months.
|
|
|
|
for (int i = 0; i < 12; i++) {
|
|
|
|
months[i] = true;
|
|
|
|
}
|
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
String timeSubstr = localRuleString.substring(k);
|
2013-04-18 23:35:02 +02:00
|
|
|
String[] times = timeSubstr.split(",");
|
|
|
|
boolean timesExist = true;
|
2015-08-27 16:03:32 +02:00
|
|
|
for (int i = 0; i < times.length; i++) {
|
|
|
|
String time = times[i];
|
2013-04-18 23:35:02 +02:00
|
|
|
time = time.trim();
|
2016-02-12 15:28:39 +01:00
|
|
|
if (time.length() == 0) {
|
2013-04-18 23:35:02 +02:00
|
|
|
continue;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (time.equals("off")) {
|
2013-04-18 23:35:02 +02:00
|
|
|
break; // add no time values
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (time.equals("24/7")) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// for some reason, this is used. See tagwatch.
|
2016-02-12 15:28:39 +01:00
|
|
|
basic.addTimeRange(0, 24 * 60);
|
2013-04-18 23:35:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
String[] stEnd = time.split("-"); //$NON-NLS-1$
|
|
|
|
if (stEnd.length != 2) {
|
2015-08-27 16:03:32 +02:00
|
|
|
if (i == times.length - 1 && basic.getStartTime() == 0 && basic.getEndTime() == 0) {
|
2015-09-30 18:27:37 +02:00
|
|
|
return new UnparseableRule(r);
|
2015-08-27 16:03:32 +02:00
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
timesExist = true;
|
|
|
|
int st;
|
|
|
|
int end;
|
|
|
|
try {
|
|
|
|
int i1 = stEnd[0].indexOf(':');
|
|
|
|
int i2 = stEnd[1].indexOf(':');
|
|
|
|
int startHour, startMin, endHour, endMin;
|
2016-02-12 15:28:39 +01:00
|
|
|
if (i1 == -1) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// if no minutes are given, try complete value as hour
|
|
|
|
startHour = Integer.parseInt(stEnd[0].trim());
|
2016-02-12 15:28:39 +01:00
|
|
|
startMin = 0;
|
2013-04-18 23:35:02 +02:00
|
|
|
} else {
|
|
|
|
startHour = Integer.parseInt(stEnd[0].substring(0, i1).trim());
|
2016-02-12 15:28:39 +01:00
|
|
|
startMin = Integer.parseInt(stEnd[0].substring(i1 + 1).trim());
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (i2 == -1) {
|
2013-04-18 23:35:02 +02:00
|
|
|
// if no minutes are given, try complete value as hour
|
|
|
|
endHour = Integer.parseInt(stEnd[1].trim());
|
2016-02-12 15:28:39 +01:00
|
|
|
endMin = 0;
|
2013-04-18 23:35:02 +02:00
|
|
|
} else {
|
|
|
|
endHour = Integer.parseInt(stEnd[1].substring(0, i2).trim());
|
2016-02-12 15:28:39 +01:00
|
|
|
endMin = Integer.parseInt(stEnd[1].substring(i2 + 1).trim());
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
st = startHour * 60 + startMin;
|
|
|
|
end = endHour * 60 + endMin;
|
2013-04-18 23:35:02 +02:00
|
|
|
} catch (NumberFormatException e) {
|
2015-09-30 18:27:37 +02:00
|
|
|
return new UnparseableRule(r);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
basic.addTimeRange(st, end);
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
if (!timesExist) {
|
2015-09-30 18:27:37 +02:00
|
|
|
return new UnparseableRule(r);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
return basic;
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
/**
|
|
|
|
* parse OSM opening_hours string to an OpeningHours object
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2013-04-18 23:35:02 +02:00
|
|
|
* @param format the string to parse
|
|
|
|
* @return null when parsing was unsuccessful
|
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public static OpeningHours parseOpenedHours(String format) {
|
|
|
|
if (format == null) {
|
2015-04-10 01:34:46 +02:00
|
|
|
return null;
|
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
// split the OSM string in multiple rules
|
|
|
|
String[] rules = format.split(";"); //$NON-NLS-1$
|
2013-07-30 21:28:19 +02:00
|
|
|
// FIXME: What if the semicolon is inside a quoted string?
|
2013-04-18 23:35:02 +02:00
|
|
|
OpeningHours rs = new OpeningHours();
|
2016-02-12 15:28:39 +01:00
|
|
|
for (String r : rules) {
|
2013-04-18 23:35:02 +02:00
|
|
|
r = r.trim();
|
2013-07-30 21:28:19 +02:00
|
|
|
if (r.length() == 0) {
|
2013-04-18 23:35:02 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// check if valid
|
2015-09-30 18:27:37 +02:00
|
|
|
final OpeningHoursRule r1 = parseRule(r);
|
2015-10-21 22:49:36 +02:00
|
|
|
boolean rule = r1 instanceof BasicOpeningHourRule;
|
|
|
|
if (rule) {
|
|
|
|
rs.addRule(r1);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2015-09-30 18:27:37 +02:00
|
|
|
}
|
|
|
|
return rs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse OSM opening_hours string to an OpeningHours object.
|
|
|
|
* Does not return null when parsing unsuccessful. When parsing rule is unsuccessful,
|
|
|
|
* such rule is stored as UnparseableRule.
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
2015-09-30 18:27:37 +02:00
|
|
|
* @param format the string to parse
|
|
|
|
* @return the OpeningHours object
|
|
|
|
*/
|
2016-02-12 15:28:39 +01:00
|
|
|
public static OpeningHoursParser.OpeningHours parseOpenedHoursHandleErrors(String format) {
|
|
|
|
if (format == null) {
|
2015-09-30 18:27:37 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
String[] rules = format.split(";"); //$NON-NLS-1$
|
|
|
|
OpeningHoursParser.OpeningHours rs = new OpeningHoursParser.OpeningHours();
|
2016-02-12 15:28:39 +01:00
|
|
|
for (String r : rules) {
|
2015-09-30 18:27:37 +02:00
|
|
|
r = r.trim();
|
|
|
|
if (r.length() == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// check if valid
|
|
|
|
rs.addRule(OpeningHoursParser.parseRule(r));
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
return rs;
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
private static void formatTime(int h, int t, StringBuilder b) {
|
2013-07-30 21:28:19 +02:00
|
|
|
if (h < 10) {
|
2013-04-18 23:35:02 +02:00
|
|
|
b.append("0"); //$NON-NLS-1$
|
2013-07-30 21:28:19 +02:00
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
b.append(h).append(":"); //$NON-NLS-1$
|
2013-07-30 21:28:19 +02:00
|
|
|
if (t < 10) {
|
2013-04-18 23:35:02 +02:00
|
|
|
b.append("0"); //$NON-NLS-1$
|
|
|
|
}
|
|
|
|
b.append(t);
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
|
|
|
|
2013-07-30 21:28:19 +02:00
|
|
|
/**
|
|
|
|
* test if the calculated opening hours are what you expect
|
2016-02-12 15:28:39 +01:00
|
|
|
*
|
|
|
|
* @param time the time to test in the format "dd.MM.yyyy HH:mm"
|
|
|
|
* @param hours the OpeningHours object
|
2013-07-30 21:28:19 +02:00
|
|
|
* @param expected the expected state
|
|
|
|
*/
|
2013-04-18 23:35:02 +02:00
|
|
|
private static void testOpened(String time, OpeningHours hours, boolean expected) throws ParseException {
|
|
|
|
Calendar cal = Calendar.getInstance();
|
2016-02-12 16:50:27 +01:00
|
|
|
cal.setTime(new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.US).parse(time));
|
2013-08-07 18:55:56 +02:00
|
|
|
boolean calculated = hours.isOpenedForTime(cal);
|
2015-09-10 00:30:31 +02:00
|
|
|
System.out.printf(" %sok: Expected %s: %b = %b (rule %s)\n",
|
2016-01-05 12:18:03 +01:00
|
|
|
((calculated != expected) ? "NOT " : ""), time, expected, calculated, hours.getCurrentRuleTime(cal));
|
2016-02-12 15:28:39 +01:00
|
|
|
if (calculated != expected) {
|
|
|
|
throw new IllegalArgumentException("BUG!!!");
|
|
|
|
}
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
2016-01-05 12:18:03 +01:00
|
|
|
|
|
|
|
private static void testParsedAndAssembledCorrectly(String timeString, OpeningHours hours) {
|
|
|
|
String assembledString = hours.toStringNoMonths();
|
2016-02-12 16:50:27 +01:00
|
|
|
boolean isCorrect = assembledString.equalsIgnoreCase(timeString);
|
2016-01-05 12:18:03 +01:00
|
|
|
System.out.printf(" %sok: Expected: \"%s\" got: \"%s\"\n",
|
|
|
|
(isCorrect ? "NOT " : ""), timeString, assembledString);
|
|
|
|
if (!isCorrect) {
|
|
|
|
throw new IllegalArgumentException("BUG!!!");
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
public static void main(String[] args) throws ParseException {
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-07-30 21:28:19 +02:00
|
|
|
// Test basic case
|
2016-02-12 15:28:39 +01:00
|
|
|
OpeningHours hours = parseOpenedHours("Mo-Fr 08:30-14:40"); //$NON-NLS-1$
|
2013-04-18 23:35:02 +02:00
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("09.08.2012 11:00", hours, true);
|
|
|
|
testOpened("09.08.2012 16:00", hours, false);
|
2016-02-07 19:06:10 +01:00
|
|
|
hours = parseOpenedHours("mo-fr 07:00-19:00; sa 12:00-18:00");
|
|
|
|
System.out.println(hours);
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2015-09-10 00:30:31 +02:00
|
|
|
hours = parseOpenedHours("Mo-Fr 11:30-15:00,17:30-23:00; Sa-Su,PH 11:30-23:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("7.09.2015 14:54", hours, true);
|
|
|
|
testOpened("7.09.2015 15:05", hours, false);
|
2016-02-12 15:28:39 +01:00
|
|
|
|
2013-07-30 21:28:19 +02:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
// two time and date ranges
|
|
|
|
hours = parseOpenedHours("Mo-We, Fr 08:30-14:40,15:00-19:00"); //$NON-NLS-1$
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("08.08.2012 14:00", hours, true);
|
|
|
|
testOpened("08.08.2012 14:50", hours, false);
|
2013-07-30 21:28:19 +02:00
|
|
|
testOpened("10.08.2012 15:00", hours, true);
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
// test exception on general schema
|
|
|
|
hours = parseOpenedHours("Mo-Sa 08:30-14:40; Tu 08:00 - 14:00"); //$NON-NLS-1$
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("07.08.2012 14:20", hours, false);
|
2013-07-30 21:28:19 +02:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
// test off value
|
|
|
|
hours = parseOpenedHours("Mo-Sa 09:00-18:25; Th off"); //$NON-NLS-1$
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("08.08.2012 12:00", hours, true);
|
|
|
|
testOpened("09.08.2012 12:00", hours, false);
|
2013-07-30 21:28:19 +02:00
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
//test 24/7
|
|
|
|
hours = parseOpenedHours("24/7"); //$NON-NLS-1$
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("08.08.2012 23:59", hours, true);
|
2013-07-30 21:28:19 +02:00
|
|
|
testOpened("08.08.2012 12:23", hours, true);
|
|
|
|
testOpened("08.08.2012 06:23", hours, true);
|
|
|
|
|
2013-04-18 23:35:02 +02:00
|
|
|
// some people seem to use the following syntax:
|
|
|
|
hours = parseOpenedHours("Sa-Su 24/7");
|
|
|
|
System.out.println(hours);
|
|
|
|
hours = parseOpenedHours("Mo-Fr 9-19");
|
|
|
|
System.out.println(hours);
|
|
|
|
hours = parseOpenedHours("09:00-17:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
hours = parseOpenedHours("sunrise-sunset");
|
|
|
|
System.out.println(hours);
|
|
|
|
hours = parseOpenedHours("10:00+");
|
|
|
|
System.out.println(hours);
|
|
|
|
hours = parseOpenedHours("Su-Th sunset-24:00, 04:00-sunrise; Fr-Sa sunset-sunrise");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("12.08.2012 04:00", hours, true);
|
|
|
|
testOpened("12.08.2012 23:00", hours, true);
|
|
|
|
testOpened("08.08.2012 12:00", hours, false);
|
|
|
|
testOpened("08.08.2012 05:00", hours, true);
|
2013-05-09 23:47:39 +02:00
|
|
|
|
|
|
|
// test simple day wrap
|
|
|
|
hours = parseOpenedHours("Mo 20:00-02:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("05.05.2013 10:30", hours, false);
|
|
|
|
testOpened("05.05.2013 23:59", hours, false);
|
|
|
|
testOpened("06.05.2013 10:30", hours, false);
|
|
|
|
testOpened("06.05.2013 20:30", hours, true);
|
|
|
|
testOpened("06.05.2013 23:59", hours, true);
|
|
|
|
testOpened("07.05.2013 00:00", hours, true);
|
|
|
|
testOpened("07.05.2013 00:30", hours, true);
|
|
|
|
testOpened("07.05.2013 01:59", hours, true);
|
|
|
|
testOpened("07.05.2013 20:30", hours, false);
|
|
|
|
|
|
|
|
// test maximum day wrap
|
|
|
|
hours = parseOpenedHours("Su 10:00-10:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("05.05.2013 09:59", hours, false);
|
|
|
|
testOpened("05.05.2013 10:00", hours, true);
|
|
|
|
testOpened("05.05.2013 23:59", hours, true);
|
|
|
|
testOpened("06.05.2013 00:00", hours, true);
|
|
|
|
testOpened("06.05.2013 09:59", hours, true);
|
|
|
|
testOpened("06.05.2013 10:00", hours, false);
|
|
|
|
|
|
|
|
// test day wrap as seen on OSM
|
2015-05-10 00:50:44 +02:00
|
|
|
// Incorrectly evaluated: https://wiki.openstreetmap.org/w/index.php?title=Key:opening_hours/specification#explain:additional_rule_separator
|
|
|
|
// <normal_rule_separator> does overwrite previous definitions.
|
2015-05-10 01:25:23 +02:00
|
|
|
// VICTOR: Do we have a test for incorrectly evaluated?
|
2013-05-09 23:47:39 +02:00
|
|
|
hours = parseOpenedHours("Tu-Th 07:00-2:00; Fr 17:00-4:00; Sa 18:00-05:00; Su,Mo off");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("05.05.2013 04:59", hours, true);
|
|
|
|
testOpened("05.05.2013 05:00", hours, false);
|
|
|
|
testOpened("05.05.2013 12:30", hours, false);
|
|
|
|
testOpened("06.05.2013 10:30", hours, false);
|
|
|
|
testOpened("07.05.2013 01:00", hours, false);
|
|
|
|
testOpened("07.05.2013 20:25", hours, true);
|
|
|
|
testOpened("07.05.2013 23:59", hours, true);
|
|
|
|
testOpened("08.05.2013 00:00", hours, true);
|
|
|
|
testOpened("08.05.2013 02:00", hours, false);
|
2013-06-25 12:11:58 +02:00
|
|
|
|
2015-05-10 00:50:44 +02:00
|
|
|
// test day wrap as seen on OSM
|
2015-05-10 01:25:23 +02:00
|
|
|
hours = parseOpenedHours("Mo-Th 09:00-03:00; Fr-Sa 09:00-04:00; Su off");
|
2015-05-10 00:50:44 +02:00
|
|
|
testOpened("11.05.2015 08:59", hours, false);
|
2015-05-10 01:25:23 +02:00
|
|
|
testOpened("11.05.2015 09:01", hours, true);
|
2015-05-10 00:50:44 +02:00
|
|
|
testOpened("12.05.2015 02:59", hours, true);
|
|
|
|
testOpened("12.05.2015 03:00", hours, false);
|
2016-02-12 15:28:39 +01:00
|
|
|
testOpened("16.05.2015 03:59", hours, true);
|
2015-05-10 01:25:23 +02:00
|
|
|
testOpened("16.05.2015 04:01", hours, false);
|
|
|
|
testOpened("17.05.2015 01:00", hours, true);
|
|
|
|
testOpened("17.05.2015 04:01", hours, false);
|
2015-05-10 00:50:44 +02:00
|
|
|
|
2015-05-10 01:25:23 +02:00
|
|
|
hours = parseOpenedHours("Tu-Th 07:00-2:00; Fr 17:00-4:00; Sa 18:00-05:00; Su,Mo off");
|
|
|
|
testOpened("11.05.2015 08:59", hours, false);
|
|
|
|
testOpened("11.05.2015 09:01", hours, false);
|
|
|
|
testOpened("12.05.2015 02:59", hours, false);
|
|
|
|
testOpened("12.05.2015 03:00", hours, false);
|
2016-02-12 15:28:39 +01:00
|
|
|
testOpened("16.05.2015 03:59", hours, true);
|
2015-05-10 01:25:23 +02:00
|
|
|
testOpened("16.05.2015 04:01", hours, false);
|
|
|
|
testOpened("17.05.2015 01:00", hours, true);
|
|
|
|
testOpened("17.05.2015 05:01", hours, false);
|
2015-05-10 00:50:44 +02:00
|
|
|
|
2013-06-25 12:11:58 +02:00
|
|
|
// tests single month value
|
|
|
|
hours = parseOpenedHours("May: 07:00-19:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("05.05.2013 12:00", hours, true);
|
|
|
|
testOpened("05.05.2013 05:00", hours, false);
|
|
|
|
testOpened("05.05.2013 21:00", hours, false);
|
|
|
|
testOpened("05.01.2013 12:00", hours, false);
|
|
|
|
testOpened("05.01.2013 05:00", hours, false);
|
|
|
|
|
|
|
|
// tests multi month value
|
|
|
|
hours = parseOpenedHours("Apr-Sep: 8:00-22:00; Oct-Mar: 10:00-18:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("05.03.2013 15:00", hours, true);
|
|
|
|
testOpened("05.03.2013 20:00", hours, false);
|
|
|
|
|
|
|
|
testOpened("05.05.2013 20:00", hours, true);
|
|
|
|
testOpened("05.05.2013 23:00", hours, false);
|
|
|
|
|
|
|
|
testOpened("05.10.2013 15:00", hours, true);
|
|
|
|
testOpened("05.10.2013 20:00", hours, false);
|
2015-12-02 16:48:33 +01:00
|
|
|
|
|
|
|
// Test time with breaks
|
|
|
|
hours = parseOpenedHours("Mo-Fr: 9:00-13:00, 14:00-18:00");
|
|
|
|
System.out.println(hours);
|
|
|
|
testOpened("02.12.2015 12:00", hours, true);
|
|
|
|
testOpened("02.12.2015 13:30", hours, false);
|
|
|
|
testOpened("02.12.2015 16:00", hours, true);
|
|
|
|
|
|
|
|
testOpened("05.12.2015 16:00", hours, false);
|
2016-01-05 12:18:03 +01:00
|
|
|
|
|
|
|
// Test holidays
|
2016-02-07 19:06:10 +01:00
|
|
|
String hoursString = "mo-fr 11:00-21:00; ph off";
|
2016-01-05 12:18:03 +01:00
|
|
|
hours = parseOpenedHoursHandleErrors(hoursString);
|
|
|
|
testParsedAndAssembledCorrectly(hoursString, hours);
|
2013-04-18 23:35:02 +02:00
|
|
|
}
|
|
|
|
}
|