From 54b6cfa9a9e5b861a9930af873580d6dc20f773c Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: Initial Contribution --- libs/ui/EventRecurrence.cpp | 484 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 libs/ui/EventRecurrence.cpp (limited to 'libs/ui/EventRecurrence.cpp') diff --git a/libs/ui/EventRecurrence.cpp b/libs/ui/EventRecurrence.cpp new file mode 100644 index 000000000000..b436b506f321 --- /dev/null +++ b/libs/ui/EventRecurrence.cpp @@ -0,0 +1,484 @@ +/* + * Copyright 2006 The Android Open Source Project + */ + +#include +#include +#include +#include + +namespace android { + +#define FAIL_HERE() do { \ + printf("Parsing failed at line %d\n", __LINE__); \ + return UNKNOWN_ERROR; \ + } while(0) + +EventRecurrence::EventRecurrence() + :freq((freq_t)0), + until(), + count(0), + interval(0), + bysecond(0), + bysecondCount(0), + byminute(0), + byminuteCount(0), + byhour(0), + byhourCount(0), + byday(0), + bydayNum(0), + bydayCount(0), + bymonthday(0), + bymonthdayCount(0), + byyearday(0), + byyeardayCount(0), + byweekno(0), + byweeknoCount(0), + bymonth(0), + bymonthCount(0), + bysetpos(0), + bysetposCount(0), + wkst(0) +{ +} + +EventRecurrence::~EventRecurrence() +{ + delete[] bysecond; + delete[] byminute; + delete[] byhour; + delete[] byday; + delete[] bydayNum; + delete[] byyearday; + delete[] bymonthday; + delete[] byweekno; + delete[] bymonth; + delete[] bysetpos; +} + +enum LHS { + NONE_LHS = 0, + FREQ, + UNTIL, + COUNT, + INTERVAL, + BYSECOND, + BYMINUTE, + BYHOUR, + BYDAY, + BYMONTHDAY, + BYYEARDAY, + BYWEEKNO, + BYMONTH, + BYSETPOS, + WKST +}; + +struct LHSProc +{ + const char16_t* text; + size_t textSize; + uint32_t value; +}; + +const char16_t FREQ_text[] = { 'F', 'R', 'E', 'Q' }; +const char16_t UNTIL_text[] = { 'U', 'N', 'T', 'I', 'L' }; +const char16_t COUNT_text[] = { 'C', 'O', 'U', 'N', 'T' }; +const char16_t INTERVAL_text[] = { 'I', 'N', 'T', 'E', 'R', 'V', 'A', 'L'}; +const char16_t BYSECOND_text[] = { 'B', 'Y', 'S', 'E', 'C', 'O', 'N', 'D' }; +const char16_t BYMINUTE_text[] = { 'B', 'Y', 'M', 'I', 'N', 'U', 'T', 'E' }; +const char16_t BYHOUR_text[] = { 'B', 'Y', 'H', 'O', 'U', 'R' }; +const char16_t BYDAY_text[] = { 'B', 'Y', 'D', 'A', 'Y' }; +const char16_t BYMONTHDAY_text[] = { 'B','Y','M','O','N','T','H','D','A','Y' }; +const char16_t BYYEARDAY_text[] = { 'B','Y','Y','E','A','R','D','A','Y' }; +const char16_t BYWEEKNO_text[] = { 'B', 'Y', 'W', 'E', 'E', 'K', 'N', 'O' }; +const char16_t BYMONTH_text[] = { 'B', 'Y', 'M', 'O', 'N', 'T', 'H' }; +const char16_t BYSETPOS_text[] = { 'B', 'Y', 'S', 'E', 'T', 'P', 'O', 'S' }; +const char16_t WKST_text[] = { 'W', 'K', 'S', 'T' }; + +#define SIZ(x) (sizeof(x)/sizeof(x[0])) + +const LHSProc LHSPROC[] = { + { FREQ_text, SIZ(FREQ_text), FREQ }, + { UNTIL_text, SIZ(UNTIL_text), UNTIL }, + { COUNT_text, SIZ(COUNT_text), COUNT }, + { INTERVAL_text, SIZ(INTERVAL_text), INTERVAL }, + { BYSECOND_text, SIZ(BYSECOND_text), BYSECOND }, + { BYMINUTE_text, SIZ(BYMINUTE_text), BYMINUTE }, + { BYHOUR_text, SIZ(BYHOUR_text), BYHOUR }, + { BYDAY_text, SIZ(BYDAY_text), BYDAY }, + { BYMONTHDAY_text, SIZ(BYMONTHDAY_text), BYMONTHDAY }, + { BYYEARDAY_text, SIZ(BYYEARDAY_text), BYYEARDAY }, + { BYWEEKNO_text, SIZ(BYWEEKNO_text), BYWEEKNO }, + { BYMONTH_text, SIZ(BYMONTH_text), BYMONTH }, + { BYSETPOS_text, SIZ(BYSETPOS_text), BYSETPOS }, + { WKST_text, SIZ(WKST_text), WKST }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SECONDLY_text[] = { 'S','E','C','O','N','D','L','Y' }; +const char16_t MINUTELY_text[] = { 'M','I','N','U','T','E','L','Y' }; +const char16_t HOURLY_text[] = { 'H','O','U','R','L','Y' }; +const char16_t DAILY_text[] = { 'D','A','I','L','Y' }; +const char16_t WEEKLY_text[] = { 'W','E','E','K','L','Y' }; +const char16_t MONTHLY_text[] = { 'M','O','N','T','H','L','Y' }; +const char16_t YEARLY_text[] = { 'Y','E','A','R','L','Y' }; + +typedef LHSProc FreqProc; + +const FreqProc FREQPROC[] = { + { SECONDLY_text, SIZ(SECONDLY_text), EventRecurrence::SECONDLY }, + { MINUTELY_text, SIZ(MINUTELY_text), EventRecurrence::MINUTELY }, + { HOURLY_text, SIZ(HOURLY_text), EventRecurrence::HOURLY }, + { DAILY_text, SIZ(DAILY_text), EventRecurrence::DAILY }, + { WEEKLY_text, SIZ(WEEKLY_text), EventRecurrence::WEEKLY }, + { MONTHLY_text, SIZ(MONTHLY_text), EventRecurrence::MONTHLY }, + { YEARLY_text, SIZ(YEARLY_text), EventRecurrence::YEARLY }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SU_text[] = { 'S','U' }; +const char16_t MO_text[] = { 'M','O' }; +const char16_t TU_text[] = { 'T','U' }; +const char16_t WE_text[] = { 'W','E' }; +const char16_t TH_text[] = { 'T','H' }; +const char16_t FR_text[] = { 'F','R' }; +const char16_t SA_text[] = { 'S','A' }; + +const FreqProc WEEKDAYPROC[] = { + { SU_text, SIZ(SU_text), EventRecurrence::SU }, + { MO_text, SIZ(MO_text), EventRecurrence::MO }, + { TU_text, SIZ(TU_text), EventRecurrence::TU }, + { WE_text, SIZ(WE_text), EventRecurrence::WE }, + { TH_text, SIZ(TH_text), EventRecurrence::TH }, + { FR_text, SIZ(FR_text), EventRecurrence::FR }, + { SA_text, SIZ(SA_text), EventRecurrence::SA }, + { NULL, 0, NONE_LHS }, +}; + +// returns the index into LHSPROC for the match or -1 if not found +inline static int +match_proc(const LHSProc* p, const char16_t* str, size_t len) +{ + int i = 0; + while (p->text != NULL) { + if (p->textSize == len) { + if (0 == memcmp(p->text, str, len*sizeof(char16_t))) { + return i; + } + } + p++; + i++; + } + return -1; +} + +// rangeMin and rangeMax are inclusive +static status_t +parse_int(const char16_t* str, size_t len, int* out, + int rangeMin, int rangeMax, bool zeroOK) +{ + char16_t c; + size_t i=0; + + if (len == 0) { + FAIL_HERE(); + } + bool negative = false; + c = str[0]; + if (c == '-' ) { + negative = true; + i++; + } + else if (c == '+') { + i++; + } + int n = 0; + for (; i '9') { + FAIL_HERE(); + } + int prev = n; + n *= 10; + // the spec doesn't address how big these numbers can be, + // so we're not going to worry about not being able to represent + // INT_MIN, and if we're going to wrap, we'll just clamp to + // INT_MAX instead + if (n < prev) { + n = INT_MAX; + } else { + n += c - '0'; + } + } + if (negative) { + n = -n; + } + if (n < rangeMin || n > rangeMax) { + FAIL_HERE(); + } + if (!zeroOK && n == 0) { + FAIL_HERE(); + } + *out = n; + return NO_ERROR; +} + +static status_t +parse_int_list(const char16_t* str, size_t len, int* countOut, int** listOut, + int rangeMin, int rangeMax, bool zeroOK, + status_t (*func)(const char16_t*,size_t,int*,int,int,bool)=parse_int) +{ + status_t err; + + if (len == 0) { + *countOut = 0; + *listOut = NULL; + return NO_ERROR; + } + + // make one pass through looking for commas so we know how big to make our + // out array. + int count = 1; + for (size_t i=0; i 0) { + char16_t c = s[0]; + if (c == '-' || c == '+' || (c >= '0' && c <= '9')) { + if (len > 1) { + size_t nlen = 0; + c = s[nlen]; + while (nlen < len + && (c == '-' || c == '+' || (c >= '0' && c <= '9'))) { + c = s[nlen]; + nlen++; + } + if (nlen > 0) { + nlen--; + err = parse_int(s, nlen, &n, rangeMin, rangeMax, zeroOK); + if (err != NO_ERROR) { + FAIL_HERE(); + } + p += nlen; + plen -= nlen; + } + } + } + + int index = match_proc(WEEKDAYPROC, p, plen); + if (index >= 0) { + *out = (0xffff0000 & WEEKDAYPROC[index].value) + | (0x0000ffff & n); + return NO_ERROR; + } + } + return UNKNOWN_ERROR; +} + +static void +postprocess_byday(int count, int* byday, int** bydayNum) +{ + int* bdn = new int[count]; + *bydayNum = bdn; + for (int i=0; i= 0) { + break; + } + } + FAIL_HERE(); + case ';': + { + switch (LHSPROC[lhsIndex].value) + { + case FREQ: + if (this->freq != 0) { + FAIL_HERE(); + } + index = match_proc(FREQPROC, s, slen); + if (index >= 0) { + this->freq = (freq_t)FREQPROC[index].value; + } + break; + case UNTIL: + // XXX should check that this is a valid time + until.setTo(String16(s, slen)); + break; + case COUNT: + if (count != 0 + || NO_ERROR != parse_int(s, slen, + &count, INT_MIN, INT_MAX, true)) { + FAIL_HERE(); + } + break; + case INTERVAL: + if (interval != 0 + || NO_ERROR != parse_int(s, slen, + &interval, INT_MIN, INT_MAX, false)) { + FAIL_HERE(); + } + break; + case BYSECOND: + PARSE_INT_LIST_CHECKED(bysecond, 0, 59, true) + break; + case BYMINUTE: + PARSE_INT_LIST_CHECKED(byminute, 0, 59, true) + break; + case BYHOUR: + PARSE_INT_LIST_CHECKED(byhour, 0, 23, true) + break; + case BYDAY: + if (bydayCount != 0 || NO_ERROR != + parse_int_list(s, slen, &bydayCount, + &byday, -53, 53, false, + parse_byday)) { + FAIL_HERE(); + } + postprocess_byday(bydayCount, byday, &bydayNum); + break; + case BYMONTHDAY: + PARSE_INT_LIST_CHECKED(bymonthday, -31, 31, + false) + break; + case BYYEARDAY: + PARSE_INT_LIST_CHECKED(byyearday, -366, 366, + false) + break; + case BYWEEKNO: + PARSE_INT_LIST_CHECKED(byweekno, -53, 53, + false) + break; + case BYMONTH: + PARSE_INT_LIST_CHECKED(bymonth, 1, 12, false) + break; + case BYSETPOS: + PARSE_INT_LIST_CHECKED(bysetpos, + INT_MIN, INT_MAX, true) + break; + case WKST: + if (this->wkst != 0) { + FAIL_HERE(); + } + index = match_proc(WEEKDAYPROC, s, slen); + if (index >= 0) { + this->wkst = (int)WEEKDAYPROC[index].value; + } + break; + default: + FAIL_HERE(); + } + lhsIndex = NONE_LHS; + break; + } + } + + start = i+1; + } + } + } + + // enforce that there was a FREQ + if (freq == 0) { + FAIL_HERE(); + } + + // default wkst to MO if it wasn't specified + if (wkst == 0) { + wkst = MO; + } + + return NO_ERROR; +} + + +}; // namespace android + + -- cgit v1.2.3-59-g8ed1b