// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.ohe; import java.util.ArrayList; import org.openstreetmap.josm.plugins.ohe.gui.TimeRect; import org.openstreetmap.josm.plugins.ohe.parser.OpeningTimeCompiler; /** * Collection of utility methods. */ public final class OpeningTimeUtils { private OpeningTimeUtils() { // Hide default constructors for utilities classes } /** * Implements the subtraction of daytimes in spans of days when a day in the list occurs direct afterwards */ public static ArrayList<int[]> convert(ArrayList<DateTime> dateTimes) { ArrayList<int[]> ret = new ArrayList<>(); // the list which is // returned for (int i = 0; i < dateTimes.size(); ++i) { // iterate over every entry DateTime dateTime = dateTimes.get(i); ArrayList<DateTime> newDateTimes = new ArrayList<>(); // test if the given entry is a single dayspan if (dateTime.daySpans.size() == 1 && dateTime.daySpans.get(0).isSpan()) { ArrayList<DaySpan> partDaySpans = new ArrayList<>(); int start_day = dateTime.daySpans.get(0).startDay; // look in every entry behind while (i + 1 < dateTimes.size()) { ArrayList<DaySpan> following = dateTimes.get(i + 1).daySpans; if (following.size() == 1 && following.get(0).startDay > dateTime.daySpans.get(0).startDay && following.get(0).endDay < dateTime.daySpans.get(0).endDay) { partDaySpans.add(new DaySpan(start_day, following.get(0).startDay - 1)); start_day = following.get(0).endDay + 1; newDateTimes.add(dateTimes.get(i + 1)); i++; } else { break; } } partDaySpans.add(new DaySpan(start_day, dateTime.daySpans.get(0).endDay)); newDateTimes.add(new DateTime(partDaySpans, dateTime.daytimeSpans)); } if (newDateTimes.isEmpty()) { newDateTimes.add(dateTime); } // create the int-array for (int j = 0; j < newDateTimes.size(); ++j) { DateTime dateTime2 = newDateTimes.get(j); for (DaySpan dayspan : dateTime2.daySpans) { for (DaytimeSpan timespan : dateTime2.daytimeSpans) { if (!timespan.isOff()) { ret.add(new int[] {dayspan.startDay, dayspan.endDay, timespan.startMinute, timespan.endMinute }); } } } } } return ret; } public static class DaySpan { public int startDay; public int endDay; public DaySpan(int startDay, int endDay) { this.startDay = startDay; this.endDay = endDay; } public boolean isSpan() { return endDay > startDay; } public boolean isSingleDay() { return startDay == endDay; } } public static class DaytimeSpan { public int startMinute; public int endMinute; public DaytimeSpan(int startMinute, int endMinute) { this.startMinute = startMinute; this.endMinute = endMinute; } public boolean isOff() { return startMinute == -1; } public boolean isSpan() { return endMinute > startMinute; } } public static class DateTime { public ArrayList<DaySpan> daySpans; public ArrayList<DaytimeSpan> daytimeSpans; public DateTime(ArrayList<DaySpan> daySpans, ArrayList<DaytimeSpan> daytimeSpans) { this.daySpans = daySpans; this.daytimeSpans = daytimeSpans; } } /** * Returns a String (e.g "Mo-Sa 10:00-20:00; Tu off") representing the TimeRects */ public static String makeStringFromRects(ArrayList<TimeRect> givenTimeRects) { // create an array of booleans representing every minute on all the days in a week boolean[][] minuteArray = new boolean[7][24 * 60 + 2]; for (int day = 0; day < 7; ++day) { for (int minute = 0; minute < 24 * 60 + 2; ++minute) { minuteArray[day][minute] = false; } } for (TimeRect timeRect : givenTimeRects) { for (int day = timeRect.getDayStart(); day <= timeRect.getDayEnd(); ++day) { for (int minute = timeRect.getMinuteStart(); minute <= timeRect.getMinuteEnd(); ++minute) { minuteArray[day][minute] = true; } } } String ret = ""; int[] days = new int[7]; // an array representing the status of the days // 0 means nothing done with this day yet // 8 means the day is off // 0<x<8 means the day have the openinghours of day x // -8<x<0 means nothing done with this day yet, but it intersects a // range of days with same opening_hours for (int i = 0; i < 7; ++i) { String add = ""; if (isArrayEmpty(minuteArray[i]) && days[i] == 0) { days[i] = 8; } else if (isArrayEmpty(minuteArray[i]) && days[i] < 0) { add = OpeningTimeCompiler.WEEKDAYS[i] + " off"; days[i] = -8; } else if (days[i] <= 0) { days[i] = i + 1; int lastSameDay = i; int sameDayCount = 1; for (int j = i + 1; j < 7; ++j) { if (arraysEqual(minuteArray[i], minuteArray[j])) { days[j] = i + 1; lastSameDay = j; sameDayCount++; } } if (sameDayCount == 1) { // a single Day with this special opening_hours add = OpeningTimeCompiler.WEEKDAYS[i] + " " + makeStringFromMinuteArray(minuteArray[i]); } else if (sameDayCount == 2) { // exactly two Days with this special opening_hours add = OpeningTimeCompiler.WEEKDAYS[i] + "," + OpeningTimeCompiler.WEEKDAYS[lastSameDay] + " " + makeStringFromMinuteArray(minuteArray[i]); } else if (sameDayCount > 2) { // more than two Days with this special opening_hours add = OpeningTimeCompiler.WEEKDAYS[i] + "-" + OpeningTimeCompiler.WEEKDAYS[lastSameDay] + " " + makeStringFromMinuteArray(minuteArray[i]); for (int j = i + 1; j < lastSameDay; ++j) { if (days[j] == 0) { days[j] = -i - 1; } } } } if (!add.isEmpty()) { if (!ret.isEmpty()) { ret += "; "; } ret += add; } } return ret; } /** * Returns a String representing the openinghours on one special day (e.g. "10:00-20:00") */ private static String makeStringFromMinuteArray(boolean[] minutes) { String ret = ""; for (int i = 0; i < minutes.length; ++i) { if (minutes[i]) { int start = i; while (i < minutes.length && minutes[i]) { ++i; } String addString = timeString(start); if (i - 1 == 24 * 60 + 1) { addString += "+"; } else if (start != i - 1) { addString += "-" + timeString(i - 1); } if (!ret.isEmpty()) { ret += ","; } ret += addString; } } return ret; } public static String timeString(int minutes) { return timeString(minutes, ClockSystem.TWENTYFOUR_HOURS); } public static String timeString(int minutes, ClockSystem hourMode) { return timeString(minutes, hourMode, false); } /** * * @param minutes integer in range from 0 and 24*60 inclusive * @param hourMode 12 or 24 hour clock * @param showPeriod if 12 hour clock is chosen, the "AM"/"PM" will be shown * @return a formatted string of the time (for example "01:45 PM" or "13:45") */ public static String timeString(int minutes, ClockSystem hourMode, boolean showPeriod) { int h = minutes / 60; String period = ""; if (hourMode == ClockSystem.TWELVE_HOURS) { if (h == 24) return "midnight"; else { if (showPeriod) { period = h < 12 ? " AM" : " PM"; } h %= 12; if (h == 0) { h = 12; } } } int m = minutes % 60; return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + period; } private static boolean isArrayEmpty(boolean[] bs) { for (int i = 0; i < bs.length; i++) { if (bs[i]) return false; } return true; } private static boolean arraysEqual(boolean[] bs, boolean[] bs2) { boolean ret = true; for (int i = 0; i < bs.length; i++) { ret &= bs[i] == bs2[i]; } return ret; } /** * Ensures the given day is comprised between 0 and 6. * @param day The day to check * @param paramName The parameter name, used in error message * @throws IllegalArgumentException if the day is invalid */ public static void ensureValidDay(int day, String paramName) throws IllegalArgumentException { if (day < 0 || day > 6) { throw new IllegalArgumentException(paramName + " is not a valid day (0-6). Given value is " + day); } } /** * Ensures the given minute is comprised between 0 and 24*60+1. * @param minute The minute to check * @param paramName The parameter name, used in error message * @throws IllegalArgumentException if the minute is invalid */ public static void ensureValidMinute(int minute, String paramName) throws IllegalArgumentException { if (minute < 0 || minute > 24*60+1) { throw new IllegalArgumentException(paramName + " is not a valid minute (0-1441). Given value is " + minute); } } }