EventRecurrence.java revision a991358d94947cc0dbd6a8d75dd77b5fd377734b
10d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden/* 20d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Copyright (C) 2006 The Android Open Source Project 30d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 40d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Licensed under the Apache License, Version 2.0 (the "License"); 50d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * you may not use this file except in compliance with the License. 60d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * You may obtain a copy of the License at 70d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 80d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * http://www.apache.org/licenses/LICENSE-2.0 90d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Unless required by applicable law or agreed to in writing, software 110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * distributed under the License is distributed on an "AS IS" BASIS, 120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * See the License for the specific language governing permissions and 140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * limitations under the License. 150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenpackage com.android.calendarcommon; 180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport android.text.TextUtils; 200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport android.text.format.Time; 210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport android.util.Log; 220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport android.util.TimeFormatException; 230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport java.util.Calendar; 250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenimport java.util.HashMap; 260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden/** 280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Event recurrence utility functions. 290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFaddenpublic class EventRecurrence { 310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static String TAG = "EventRecur"; 320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int SECONDLY = 1; 340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int MINUTELY = 2; 350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int HOURLY = 3; 360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int DAILY = 4; 370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int WEEKLY = 5; 380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int MONTHLY = 6; 390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int YEARLY = 7; 400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int SU = 0x00010000; 420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int MO = 0x00020000; 430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int TU = 0x00040000; 440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int WE = 0x00080000; 450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int TH = 0x00100000; 460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int FR = 0x00200000; 470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static final int SA = 0x00400000; 480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public Time startDate; // set by setStartDate(), not parse() 500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int freq; // SECONDLY, MINUTELY, etc. 520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public String until; 530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int count; 540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int interval; 550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int wkst; // SU, MO, TU, etc. 560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* lists with zero entries may be null references */ 580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] bysecond; 590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int bysecondCount; 600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] byminute; 610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int byminuteCount; 620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] byhour; 630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int byhourCount; 640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] byday; 650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] bydayNum; 660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int bydayCount; 670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] bymonthday; 680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int bymonthdayCount; 690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] byyearday; 700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int byyeardayCount; 710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] byweekno; 720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int byweeknoCount; 730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] bymonth; 740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int bymonthCount; 750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int[] bysetpos; 760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public int bysetposCount; 770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** maps a part string to a parser object */ 790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static HashMap<String,PartParser> sParsePartMap; 800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden static { 810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap = new HashMap<String,PartParser>(); 820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("FREQ", new ParseFreq()); 830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("UNTIL", new ParseUntil()); 840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("COUNT", new ParseCount()); 850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("INTERVAL", new ParseInterval()); 860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYSECOND", new ParseBySecond()); 870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYMINUTE", new ParseByMinute()); 880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYHOUR", new ParseByHour()); 890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYDAY", new ParseByDay()); 900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay()); 910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYYEARDAY", new ParseByYearDay()); 920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYWEEKNO", new ParseByWeekNo()); 930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYMONTH", new ParseByMonth()); 940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("BYSETPOS", new ParseBySetPos()); 950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParsePartMap.put("WKST", new ParseWkst()); 960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* values for bit vector that keeps track of what we have already seen */ 990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_FREQ = 1 << 0; 1000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_UNTIL = 1 << 1; 1010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_COUNT = 1 << 2; 1020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_INTERVAL = 1 << 3; 1030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYSECOND = 1 << 4; 1040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYMINUTE = 1 << 5; 1050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYHOUR = 1 << 6; 1060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYDAY = 1 << 7; 1070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYMONTHDAY = 1 << 8; 1080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYYEARDAY = 1 << 9; 1090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYWEEKNO = 1 << 10; 1100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYMONTH = 1 << 11; 1110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_BYSETPOS = 1 << 12; 1120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final int PARSED_WKST = 1 << 13; 1130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** maps a FREQ value to an integer constant */ 1150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>(); 1160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden static { 1170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("SECONDLY", SECONDLY); 1180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("MINUTELY", MINUTELY); 1190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("HOURLY", HOURLY); 1200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("DAILY", DAILY); 1210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("WEEKLY", WEEKLY); 1220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("MONTHLY", MONTHLY); 1230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseFreqMap.put("YEARLY", YEARLY); 1240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** maps a two-character weekday string to an integer constant */ 1270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>(); 1280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden static { 1290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("SU", SU); 1300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("MO", MO); 1310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("TU", TU); 1320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("WE", WE); 1330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("TH", TH); 1340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("FR", FR); 1350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden sParseWeekdayMap.put("SA", SA); 1360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** If set, allow lower-case recurrence rule strings. Minor performance impact. */ 139996f12565f11ea0130cbdc570c0853ae53073f18Michael Chan private static final boolean ALLOW_LOWER_CASE = true; 1400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** If set, validate the value of UNTIL parts. Minor performance impact. */ 1420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final boolean VALIDATE_UNTIL = false; 1430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */ 1450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static final boolean ONLY_ONE_UNTIL_COUNT = false; 1460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 1490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Thrown when a recurrence string provided can not be parsed according 1500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * to RFC2445. 1510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 1520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static class InvalidFormatException extends RuntimeException { 1530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden InvalidFormatException(String s) { 1540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden super(s); 1550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public void setStartDate(Time date) { 1600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden startDate = date; 1610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 1640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc. 1650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * constants. btw, I think we should switch to those here too, to 1660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * get rid of this function, if possible. 1670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 1680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int calendarDay2Day(int day) 1690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 1700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (day) 1710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 1720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.SUNDAY: 1730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return SU; 1740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.MONDAY: 1750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return MO; 1760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.TUESDAY: 1770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return TU; 1780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.WEDNESDAY: 1790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return WE; 1800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.THURSDAY: 1810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return TH; 1820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.FRIDAY: 1830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return FR; 1840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Calendar.SATURDAY: 1850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return SA; 1860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden default: 1870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new RuntimeException("bad day of week: " + day); 1880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 1900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 1910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int timeDay2Day(int day) 1920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 1930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (day) 1940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 1950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.SUNDAY: 1960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return SU; 1970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.MONDAY: 1980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return MO; 1990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.TUESDAY: 2000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return TU; 2010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.WEDNESDAY: 2020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return WE; 2030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.THURSDAY: 2040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return TH; 2050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.FRIDAY: 2060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return FR; 2070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case Time.SATURDAY: 2080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return SA; 2090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden default: 2100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new RuntimeException("bad day of week: " + day); 2110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int day2TimeDay(int day) 2140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 2150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (day) 2160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 2170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SU: 2180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.SUNDAY; 2190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case MO: 2200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.MONDAY; 2210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TU: 2220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.TUESDAY; 2230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case WE: 2240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.WEDNESDAY; 2250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TH: 2260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.THURSDAY; 2270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case FR: 2280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.FRIDAY; 2290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SA: 2300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Time.SATURDAY; 2310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden default: 2320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new RuntimeException("bad day of week: " + day); 2330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 2360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 2370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Converts one of the SU, MO, etc. constants to the Calendar.SUNDAY 2380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * constants. btw, I think we should switch to those here too, to 2390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * get rid of this function, if possible. 2400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 2410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int day2CalendarDay(int day) 2420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 2430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (day) 2440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 2450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SU: 2460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.SUNDAY; 2470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case MO: 2480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.MONDAY; 2490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TU: 2500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.TUESDAY; 2510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case WE: 2520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.WEDNESDAY; 2530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TH: 2540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.THURSDAY; 2550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case FR: 2560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.FRIDAY; 2570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SA: 2580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return Calendar.SATURDAY; 2590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden default: 2600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new RuntimeException("bad day of week: " + day); 2610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 2640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 2650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Converts one of the internal day constants (SU, MO, etc.) to the 2660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * two-letter string representing that constant. 2670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 2680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param day one the internal constants SU, MO, etc. 2690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @return the two-letter string for the day ("SU", "MO", etc.) 2700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 2710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @throws IllegalArgumentException Thrown if the day argument is not one of 2720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * the defined day constants. 2730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 2740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static String day2String(int day) { 2750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (day) { 2760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SU: 2770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "SU"; 2780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case MO: 2790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "MO"; 2800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TU: 2810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "TU"; 2820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case WE: 2830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "WE"; 2840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case TH: 2850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "TH"; 2860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case FR: 2870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "FR"; 2880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SA: 2890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return "SA"; 2900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden default: 2910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new IllegalArgumentException("bad day argument: " + day); 2920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 2940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 2950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static void appendNumbers(StringBuilder s, String label, 2960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int count, int[] values) 2970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 2980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (count > 0) { 2990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(label); 3000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden count--; 3010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i=0; i<count; i++) { 3020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(values[i]); 3030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(","); 3040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(values[count]); 3060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private void appendByDay(StringBuilder s, int i) 3100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 3110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int n = this.bydayNum[i]; 3120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (n != 0) { 3130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(n); 3140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String str = day2String(this.byday[i]); 3170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(str); 3180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override 3210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public String toString() 3220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 3230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden StringBuilder s = new StringBuilder(); 3240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("FREQ="); 3260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden switch (this.freq) 3270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden { 3280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case SECONDLY: 3290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("SECONDLY"); 3300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case MINUTELY: 3320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("MINUTELY"); 3330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case HOURLY: 3350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("HOURLY"); 3360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case DAILY: 3380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("DAILY"); 3390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case WEEKLY: 3410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("WEEKLY"); 3420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case MONTHLY: 3440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("MONTHLY"); 3450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden case YEARLY: 3470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append("YEARLY"); 3480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden break; 3490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (!TextUtils.isEmpty(this.until)) { 3520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(";UNTIL="); 3530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(until); 3540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this.count != 0) { 3570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(";COUNT="); 3580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(this.count); 3590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this.interval != 0) { 3620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(";INTERVAL="); 3630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(this.interval); 3640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this.wkst != 0) { 3670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(";WKST="); 3680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(day2String(this.wkst)); 3690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYSECOND=", this.bysecondCount, this.bysecond); 3720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYMINUTE=", this.byminuteCount, this.byminute); 3730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYSECOND=", this.byhourCount, this.byhour); 3740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // day 3760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int count = this.bydayCount; 3770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (count > 0) { 3780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(";BYDAY="); 3790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden count--; 3800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i=0; i<count; i++) { 3810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendByDay(s, i); 3820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden s.append(","); 3830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendByDay(s, count); 3850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYMONTHDAY=", this.bymonthdayCount, this.bymonthday); 3880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYYEARDAY=", this.byyeardayCount, this.byyearday); 3890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYWEEKNO=", this.byweeknoCount, this.byweekno); 3900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYMONTH=", this.bymonthCount, this.bymonth); 3910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden appendNumbers(s, ";BYSETPOS=", this.bysetposCount, this.bysetpos); 3920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return s.toString(); 3940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 3950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 3960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public boolean repeatsOnEveryWeekDay() { 3970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this.freq != WEEKLY) { 3980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 3990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int count = this.bydayCount; 4020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (count != 5) { 4030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i = 0 ; i < count ; i++) { 4070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int day = byday[i]; 4080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (day == SU || day == SA) { 4090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return true; 4140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 4170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Determines whether this rule specifies a simple monthly rule by weekday, such as 4180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * "FREQ=MONTHLY;BYDAY=3TU" (the 3rd Tuesday of every month). 4190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 4200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Negative days, e.g. "FREQ=MONTHLY;BYDAY=-1TU" (the last Tuesday of every month), 4210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * will cause "false" to be returned. 4220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 4230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Rules that fire every week, such as "FREQ=MONTHLY;BYDAY=TU" (every Tuesday of every 4240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * month) will cause "false" to be returned. (Note these are usually expressed as 4250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * WEEKLY rules, and hence are uncommon.) 4260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 4270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @return true if this rule is of the appropriate form 4280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 4290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public boolean repeatsMonthlyOnDayCount() { 4300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this.freq != MONTHLY) { 4310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (bydayCount != 1 || bymonthdayCount != 0) { 4350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (bydayNum[0] <= 0) { 4390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return true; 4430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 4460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Determines whether two integer arrays contain identical elements. 4470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 4480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * The native implementation over-allocated the arrays (and may have stuff left over from 4490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * a previous run), so we can't just check the arrays -- the separately-maintained count 4500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * field also matters. We assume that a null array will have a count of zero, and that the 4510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * array can hold as many elements as the associated count indicates. 4520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 4530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * TODO: replace this with Arrays.equals() when the old parser goes away. 4540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 4550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) { 4560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (count1 != count2) { 4570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i = 0; i < count1; i++) { 4610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (array1[i] != array2[i]) 4620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return true; 4660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override 4690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public boolean equals(Object obj) { 4700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (this == obj) { 4710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return true; 4720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (!(obj instanceof EventRecurrence)) { 4740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return false; 4750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden EventRecurrence er = (EventRecurrence) obj; 4780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return (startDate == null ? 4790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.startDate == null : Time.compare(startDate, er.startDate) == 0) && 4800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden freq == er.freq && 4810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden (until == null ? er.until == null : until.equals(er.until)) && 4820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden count == er.count && 4830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden interval == er.interval && 4840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden wkst == er.wkst && 4850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) && 4860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) && 4870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) && 4880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(byday, bydayCount, er.byday, er.bydayCount) && 4890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) && 4900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) && 4910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) && 4920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) && 4930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) && 4940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount); 4950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 4960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 4970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int hashCode() { 4980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // We overrode equals, so we must override hashCode(). Nobody seems to need this though. 4990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new UnsupportedOperationException(); 5000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 5010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 5020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 5030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Resets parser-modified fields to their initial state. Does not alter startDate. 5040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 5050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * The original parser always set all of the "count" fields, "wkst", and "until", 5060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * essentially allowing the same object to be used multiple times by calling parse(). 5070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * It's unclear whether this behavior was intentional. For now, be paranoid and 5080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * preserve the existing behavior by resetting the fields. 5090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * <p> 5100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * We don't need to touch the integer arrays; they will either be ignored or 5110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * overwritten. The "startDate" field is not set by the parser, so we ignore it here. 5120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 5130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private void resetFields() { 5140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden until = null; 5150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden freq = count = interval = bysecondCount = byminuteCount = byhourCount = 5160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount = 5170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bysetposCount = 0; 5180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 5190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 5200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 5210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse 5220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * malformed input will result in an EventRecurrence.InvalidFormatException. 5230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param recur The recurrence rule to parse (in un-folded form). 5250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 5260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public void parse(String recur) { 5270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* 5280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * From RFC 2445 section 4.3.10: 5290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * recur = "FREQ"=freq *( 5310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ; either UNTIL or COUNT may appear in a 'recur', 5320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ; but UNTIL and COUNT MUST NOT occur in the same 'recur' 5330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "UNTIL" "=" enddate ) / 5350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "COUNT" "=" 1*DIGIT ) / 5360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ; the rest of these keywords are optional, 5380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ; but MUST NOT occur more than once 5390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "INTERVAL" "=" 1*DIGIT ) / 5410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYSECOND" "=" byseclist ) / 5420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYMINUTE" "=" byminlist ) / 5430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYHOUR" "=" byhrlist ) / 5440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYDAY" "=" bywdaylist ) / 5450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYMONTHDAY" "=" bymodaylist ) / 5460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYYEARDAY" "=" byyrdaylist ) / 5470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYWEEKNO" "=" bywknolist ) / 5480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYMONTH" "=" bymolist ) / 5490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "BYSETPOS" "=" bysplist ) / 5500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" "WKST" "=" weekday ) / 5510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ( ";" x-name "=" text ) 5520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * ) 5530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5546d5684cdf6886a46ee993c8ec986d306472cd5b0Andy McFadden * The rule parts are not ordered in any particular sequence. 5556d5684cdf6886a46ee993c8ec986d306472cd5b0Andy McFadden * 5560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Examples: 5570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU 5580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8 5590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Strategy: 5610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * (1) Split the string at ';' boundaries to get an array of rule "parts". 5620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * (2) For each part, find substrings for left/right sides of '=' (name/value). 5630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * (3) Call a <name>-specific parsing function to parse the <value> into an 5640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * output field. 5650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * By keeping track of which names we've seen in a bit vector, we can verify the 5670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * constraints indicated above (FREQ appears first, none of them appear more than once -- 5680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * though x-[name] would require special treatment), and we have either UNTIL or COUNT 5690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * but not both. 5700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must 5720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * be handled in a case-insensitive fashion, but case may be significant for other 5730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * properties. We don't have any case-sensitive values in RRULE, except possibly 5740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially 5750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * convert the entire string to upper case and then use simple comparisons. 5760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 5770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Differences from previous version: 5780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - allows lower-case property and enumeration values [optional] 5790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - enforces that FREQ appears first 5800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - enforces that only one of UNTIL and COUNT may be specified 5810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - allows (but ignores) X-* parts 5820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - improved validation on various values (e.g. UNTIL timestamps) 5830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * - error messages are more specific 5849a91eb9d6c3c28b54223dae453c9d456b0c87355Andy McFadden * 5859a91eb9d6c3c28b54223dae453c9d456b0c87355Andy McFadden * TODO: enforce additional constraints listed in RFC 5545, notably the "N/A" entries 5869a91eb9d6c3c28b54223dae453c9d456b0c87355Andy McFadden * in section 3.3.10. For example, if FREQ=WEEKLY, we should reject a rule that 5879a91eb9d6c3c28b54223dae453c9d456b0c87355Andy McFadden * includes a BYMONTHDAY part. 5880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 5890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 5900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* TODO: replace with "if (freq != 0) throw" if nothing requires this */ 5910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden resetFields(); 5920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 5930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int parseFlags = 0; 5940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String[] parts; 5950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (ALLOW_LOWER_CASE) { 5960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden parts = recur.toUpperCase().split(";"); 5970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 5980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden parts = recur.split(";"); 5990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (String part : parts) { 601a991358d94947cc0dbd6a8d75dd77b5fd377734bAlice Yang // allow empty part (e.g., double semicolon ";;") 602a991358d94947cc0dbd6a8d75dd77b5fd377734bAlice Yang if (TextUtils.isEmpty(part)) { 603a991358d94947cc0dbd6a8d75dd77b5fd377734bAlice Yang continue; 604a991358d94947cc0dbd6a8d75dd77b5fd377734bAlice Yang } 6050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int equalIndex = part.indexOf('='); 6060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (equalIndex <= 0) { 6070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* no '=' or no LHS */ 6080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Missing LHS in " + part); 6090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String lhs = part.substring(0, equalIndex); 6120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String rhs = part.substring(equalIndex + 1); 6130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (rhs.length() == 0) { 6140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Missing RHS in " + part); 6150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* 6180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * In lieu of a "switch" statement that allows string arguments, we use a 6190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * map from strings to parsing functions. 6200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 6210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden PartParser parser = sParsePartMap.get(lhs); 6220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (parser == null) { 6230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (lhs.startsWith("X-")) { 6240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden //Log.d(TAG, "Ignoring custom part " + lhs); 6250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden continue; 6260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Couldn't find parser for " + lhs); 6280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 6290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int flag = parser.parsePart(rhs, this); 6300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if ((parseFlags & flag) != 0) { 6310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Part " + lhs + " was specified twice"); 6320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden parseFlags |= flag; 6340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // If not specified, week starts on Monday. 6380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if ((parseFlags & PARSED_WKST) == 0) { 6390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden wkst = MO; 6400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // FREQ is mandatory. 6430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if ((parseFlags & PARSED_FREQ) == 0) { 6440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Must specify a FREQ value"); 6450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // Can't have both UNTIL and COUNT. 6480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) { 6490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (ONLY_ONE_UNTIL_COUNT) { 6500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur); 6510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 6520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur); 6530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 6580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Base class for the RRULE part parsers. 6590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 6600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden abstract static class PartParser { 6610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 6620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Parses a single part. 6630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 6640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param value The right-hand-side of the part. 6650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param er The EventRecurrence into which the result is stored. 6660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @return A bit value indicating which part was parsed. 6670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 6680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public abstract int parsePart(String value, EventRecurrence er); 6690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 6710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Parses an integer, with range-checking. 6720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 6730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param str The string to parse. 6740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param minVal Minimum allowed value. 6750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param maxVal Maximum allowed value. 6760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param allowZero Is 0 allowed? 6770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @return The parsed value. 6780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 6790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) { 6800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden try { 6810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (str.charAt(0) == '+') { 6820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // Integer.parseInt does not allow a leading '+', so skip it manually. 6830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden str = str.substring(1); 6840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int val = Integer.parseInt(str); 6860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (val < minVal || val > maxVal || (val == 0 && !allowZero)) { 6870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Integer value out of range: " + str); 6880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return val; 6900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } catch (NumberFormatException nfe) { 6910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Invalid integer value: " + str); 6920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 6940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 6950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** 6960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * Parses a comma-separated list of integers, with range-checking. 6970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * 6980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param listStr The string to parse. 6990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param minVal Minimum allowed value. 7000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param maxVal Maximum allowed value. 7010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @param allowZero Is 0 allowed? 7020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden * @return A new array with values, sized to hold the exact number of elements. 7030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden */ 7040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden public static int[] parseNumberList(String listStr, int minVal, int maxVal, 7050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden boolean allowZero) { 7060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] values; 7070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 7080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (listStr.indexOf(",") < 0) { 7090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // Common case: only one entry, skip split() overhead. 7100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden values = new int[1]; 7110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden values[0] = parseIntRange(listStr, minVal, maxVal, allowZero); 7120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 7130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String[] valueStrs = listStr.split(","); 7140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int len = valueStrs.length; 7150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden values = new int[len]; 7160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i = 0; i < len; i++) { 7170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero); 7180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return values; 7210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 7240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses FREQ={SECONDLY,MINUTELY,...} */ 7250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseFreq extends PartParser { 7260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden Integer freq = sParseFreqMap.get(value); 7280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (freq == null) { 7290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Invalid FREQ value: " + value); 7300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.freq = freq; 7320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_FREQ; 7330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses UNTIL=enddate, e.g. "19970829T021400" */ 7360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseUntil extends PartParser { 7370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (VALIDATE_UNTIL) { 7390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden try { 7400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden // Parse the time to validate it. The result isn't retained. 7410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden Time until = new Time(); 7420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden until.parse(value); 7430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } catch (TimeFormatException tfe) { 7440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Invalid UNTIL value: " + value); 7450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.until = value; 7480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_UNTIL; 7490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses COUNT=[non-negative-integer] */ 7520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseCount extends PartParser { 7530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7540335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan er.count = parseIntRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true); 7550335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan if (er.count < 0) { 7560335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan Log.d(TAG, "Invalid Count. Forcing COUNT to 1 from " + value); 7570335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan er.count = 1; // invalid count. assume one time recurrence. 7580335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan } 7590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_COUNT; 7600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses INTERVAL=[non-negative-integer] */ 7630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseInterval extends PartParser { 7640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7650335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan er.interval = parseIntRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true); 7660335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan if (er.interval < 1) { 7670335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan Log.d(TAG, "Invalid Interval. Forcing INTERVAL to 1 from " + value); 7680335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan er.interval = 1; 7690335d9f524da1da1f4d15bba37a1576a9480a36dMichael Chan } 7700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_INTERVAL; 7710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYSECOND=byseclist */ 7740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseBySecond extends PartParser { 7750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] bysecond = parseNumberList(value, 0, 59, true); 7770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bysecond = bysecond; 7780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bysecondCount = bysecond.length; 7790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYSECOND; 7800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYMINUTE=byminlist */ 7830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByMinute extends PartParser { 7840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] byminute = parseNumberList(value, 0, 59, true); 7860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byminute = byminute; 7870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byminuteCount = byminute.length; 7880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYMINUTE; 7890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYHOUR=byhrlist */ 7920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByHour extends PartParser { 7930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 7940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] byhour = parseNumberList(value, 0, 23, true); 7950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byhour = byhour; 7960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byhourCount = byhour.length; 7970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYHOUR; 7980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 7990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */ 8010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByDay extends PartParser { 8020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] byday; 8040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] bydayNum; 8050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int bydayCount; 8060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 8070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (value.indexOf(",") < 0) { 8080d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* only one entry, skip split() overhead */ 8090d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayCount = 1; 8100d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden byday = new int[1]; 8110d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayNum = new int[1]; 8120d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden parseWday(value, byday, bydayNum, 0); 8130d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 8140d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String[] wdays = value.split(","); 8150d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int len = wdays.length; 8160d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayCount = len; 8170d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden byday = new int[len]; 8180d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayNum = new int[len]; 8190d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden for (int i = 0; i < len; i++) { 8200d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden parseWday(wdays[i], byday, bydayNum, i); 8210d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8220d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8230d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byday = byday; 8240d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bydayNum = bydayNum; 8250d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bydayCount = bydayCount; 8260d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYDAY; 8270d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8280d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 8290d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses [int]weekday, putting the pieces into parallel array entries */ 8300d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static void parseWday(String str, int[] byday, int[] bydayNum, int index) { 8310d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int wdayStrStart = str.length() - 2; 8320d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String wdayStr; 8330d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden 8340d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (wdayStrStart > 0) { 8350d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* number is included; parse it out and advance to weekday */ 8360d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden String numPart = str.substring(0, wdayStrStart); 8370d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int num = parseIntRange(numPart, -53, 53, false); 8380d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden bydayNum[index] = num; 8390d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden wdayStr = str.substring(wdayStrStart); 8400d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } else { 8410d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /* just the weekday string */ 8420d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden wdayStr = str; 8430d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8440d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden Integer wday = sParseWeekdayMap.get(wdayStr); 8450d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (wday == null) { 8460d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Invalid BYDAY value: " + str); 8470d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8480d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden byday[index] = wday; 8490d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8500d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8510d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYMONTHDAY=bymodaylist */ 8520d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByMonthDay extends PartParser { 8530d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8540d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] bymonthday = parseNumberList(value, -31, 31, false); 8550d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bymonthday = bymonthday; 8560d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bymonthdayCount = bymonthday.length; 8570d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYMONTHDAY; 8580d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8590d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8600d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYYEARDAY=byyrdaylist */ 8610d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByYearDay extends PartParser { 8620d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8630d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] byyearday = parseNumberList(value, -366, 366, false); 8640d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byyearday = byyearday; 8650d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byyeardayCount = byyearday.length; 8660d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYYEARDAY; 8670d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8680d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8690d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYWEEKNO=bywknolist */ 8700d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByWeekNo extends PartParser { 8710d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8720d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] byweekno = parseNumberList(value, -53, 53, false); 8730d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byweekno = byweekno; 8740d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.byweeknoCount = byweekno.length; 8750d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYWEEKNO; 8760d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8770d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8780d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYMONTH=bymolist */ 8790d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseByMonth extends PartParser { 8800d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8810d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] bymonth = parseNumberList(value, 1, 12, false); 8820d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bymonth = bymonth; 8830d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bymonthCount = bymonth.length; 8840d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYMONTH; 8850d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8860d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8870d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses BYSETPOS=bysplist */ 8880d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseBySetPos extends PartParser { 8890d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8900d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true); 8910d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bysetpos = bysetpos; 8920d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.bysetposCount = bysetpos.length; 8930d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_BYSETPOS; 8940d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8950d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 8960d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden /** parses WKST={SU,MO,...} */ 8970d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden private static class ParseWkst extends PartParser { 8980d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden @Override public int parsePart(String value, EventRecurrence er) { 8990d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden Integer wkst = sParseWeekdayMap.get(value); 9000d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden if (wkst == null) { 9010d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden throw new InvalidFormatException("Invalid WKST value: " + value); 9020d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 9030d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden er.wkst = wkst; 9040d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden return PARSED_WKST; 9050d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 9060d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden } 9070d3524562e330e74f150a17c4dc4dd66a0faae46Andy McFadden} 908