r_task.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_task.cc
00003 ///             Record parsing class for the task database.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2008, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2007, Brian Edginton
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include "r_task.h"
00024 #include "record-internal.h"
00025 #include "protostructs.h"
00026 #include "data.h"
00027 #include "time.h"
00028 #include "debug.h"
00029 #include <ostream>
00030 #include <iomanip>
00031 #include <string.h>
00032 
00033 using namespace std;
00034 using namespace Barry::Protocol;
00035 
00036 namespace Barry {
00037 
00038 ///////////////////////////////////////////////////////////////////////////////
00039 // Task Class
00040 
00041 // Task Field Codes
00042 #define TSKFC_TASK_TYPE         0x01
00043 #define TSKFC_TITLE             0x02
00044 #define TSKFC_NOTES             0x03
00045 #define TSKFC_START_TIME        0x05
00046 #define TSKFC_DUE_TIME          0x06
00047 #define TSKFC_DUE_FLAG          0x08
00048 #define TSKFC_STATUS            0x09
00049 #define TSKFC_PRIORITY          0x0a
00050 #define TSKFC_RECURRENCE_DATA   0x0c
00051 #define TSKFC_ALARM_TYPE        0x0e
00052 #define TSKFC_ALARM_TIME        0x0f
00053 #define TSKFC_TIMEZONE_CODE     0x10
00054 #define TSKFC_CATEGORIES        0x11
00055 #define TSKFC_END               0xffff
00056 
00057 FieldLink<Task> TaskFieldLinks[] = {
00058         { TSKFC_TITLE,      "Summary",     0, 0, &Task::Summary, 0, 0 },
00059         { TSKFC_NOTES,      "Notes",       0, 0, &Task::Notes, 0, 0 },
00060         { TSKFC_START_TIME, "Start Time",  0, 0, 0, 0, &Task::StartTime },
00061         { TSKFC_DUE_TIME,   "Due Time",    0, 0, 0, 0, &Task::DueTime },
00062         { TSKFC_ALARM_TIME, "Alarm Time",  0, 0, 0, 0, &Task::AlarmTime },
00063         { TSKFC_CATEGORIES, "Categories",  0, 0, &Task::Categories, 0, 0 },
00064         { TSKFC_END,        "End of List", 0, 0, 0, 0, 0 },
00065 };
00066 
00067 Task::Task()
00068 {
00069         Clear();
00070 }
00071 
00072 Task::~Task()
00073 {
00074 }
00075 
00076 const unsigned char* Task::ParseField(const unsigned char *begin,
00077                                       const unsigned char *end)
00078 {
00079         const CommonField *field = (const CommonField *) begin;
00080 
00081         // advance and check size
00082         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00083         if( begin > end )       // if begin==end, we are ok
00084                 return begin;
00085 
00086         if( !btohs(field->size) )   // if field has no size, something's up
00087                 return begin;
00088 
00089         if( field->type == TSKFC_TASK_TYPE ) {
00090                 if( ( TaskType = field->u.raw[0] ) != 't' ) {
00091                         throw Error("Task::ParseField: Task Type is not 't'");
00092                 }
00093                 return begin;
00094         }
00095 
00096         // cycle through the type table
00097         for(    FieldLink<Task> *b = TaskFieldLinks;
00098                 b->type != TSKFC_END;
00099                 b++ )
00100         {
00101                 if( b->type == field->type ) {
00102                         if( b->strMember ) {
00103                                 std::string &s = this->*(b->strMember);
00104                                 s = ParseFieldString(field);
00105                                 return begin;   // done!
00106                         }
00107                         else if( b->timeMember && btohs(field->size) == 4 ) {
00108                                 time_t &t = this->*(b->timeMember);
00109                                 t = min2time(field->u.min1900);
00110                                 return begin;
00111                         }
00112                 }
00113         }
00114         // handle special cases
00115         switch( field->type )
00116         {
00117         case TSKFC_PRIORITY:
00118                 if( field->u.raw[0] > Low ) {
00119                         throw Error( "Task::ParseField: priority field out of bounds" );
00120                 }
00121                 else {
00122                         PriorityFlag = (PriorityFlagType)field->u.raw[0];
00123                 }
00124                 return begin;
00125 
00126         case TSKFC_STATUS:
00127                 if( field->u.raw[0] > Deferred ) {
00128                         throw Error( "Task::ParseField: priority field out of bounds" );
00129                 }
00130                 else {
00131                         StatusFlag = (StatusFlagType)field->u.raw[0];
00132                 }
00133                 return begin;
00134 
00135         case TSKFC_TIMEZONE_CODE:
00136                 if( btohs(field->size) == 4 ) {
00137                         TimeZoneCode = btohs(field->u.code);
00138                 }
00139                 else {
00140                         throw Error("Task::ParseField: not enough data in time zone code field");
00141                 }
00142                 return begin;
00143 
00144         case TSKFC_RECURRENCE_DATA:
00145                 if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) {
00146                         Recurring = true;
00147                         ParseRecurrenceData(&field->u.raw[0]);
00148                 }
00149                 else {
00150                         throw Error("Task::ParseField: not enough data in recurrence data field");
00151                 }
00152                 return begin;
00153 
00154         case TSKFC_DUE_FLAG:
00155                 DueDateFlag = field->u.raw[0];
00156                 return begin;           
00157 
00158         case TSKFC_ALARM_TYPE:
00159                 if( field->u.raw[0] > Relative ) {
00160                         throw Error("Task::ParseField: AlarmType out of bounds" );
00161                 }
00162                 else {
00163                         AlarmType = (AlarmFlagType)field->u.raw[0];
00164                 }
00165                 return begin;
00166         }
00167 
00168         // if still not handled, add to the Unknowns list
00169         UnknownField uf;
00170         uf.type = field->type;
00171         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00172         Unknowns.push_back(uf);
00173 
00174         // return new pointer for next field
00175         return begin;
00176 }
00177 
00178 // this function assumes the size has already been checked
00179 void Task::ParseRecurrenceData(const void *data)
00180 {
00181         const CalendarRecurrenceDataField *rec =
00182                 (const CalendarRecurrenceDataField*) data;
00183 
00184         Interval = btohs(rec->interval);
00185         if( Interval < 1 )
00186                 Interval = 1;   // must always be >= 1
00187 
00188         if( rec->endTime == 0xffffffff ) {
00189                 Perpetual = true;
00190         }
00191         else {
00192                 RecurringEndTime = min2time(rec->endTime);
00193                 Perpetual = false;
00194         }
00195 
00196         switch( rec->type )
00197         {
00198         case CRDF_TYPE_DAY:
00199                 RecurringType = Day;
00200                 // no extra data
00201                 break;
00202 
00203         case CRDF_TYPE_MONTH_BY_DATE:
00204                 RecurringType = MonthByDate;
00205                 DayOfMonth = rec->u.month_by_date.monthDay;
00206                 break;
00207 
00208         case CRDF_TYPE_MONTH_BY_DAY:
00209                 RecurringType = MonthByDay;
00210                 DayOfWeek = rec->u.month_by_day.weekDay;
00211                 WeekOfMonth = rec->u.month_by_day.week;
00212                 break;
00213 
00214         case CRDF_TYPE_YEAR_BY_DATE:
00215                 RecurringType = YearByDate;
00216                 DayOfMonth = rec->u.year_by_date.monthDay;
00217                 MonthOfYear = rec->u.year_by_date.month;
00218                 break;
00219 
00220         case CRDF_TYPE_YEAR_BY_DAY:
00221                 RecurringType = YearByDay;
00222                 DayOfWeek = rec->u.year_by_day.weekDay;
00223                 WeekOfMonth = rec->u.year_by_day.week;
00224                 MonthOfYear = rec->u.year_by_day.month;
00225                 break;
00226 
00227         case CRDF_TYPE_WEEK:
00228                 RecurringType = Week;
00229 
00230                 // Note: this simple copy is only possible since
00231                 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
00232                 // If this ever changes, this code will need to change.
00233                 WeekDays = rec->u.week.days;
00234                 break;
00235 
00236         default:
00237                 eout("Unknown recurrence data type: 0x"
00238                         << setbase(16) << (unsigned int) rec->type);
00239                 throw Error("Unknown recurrence data type");
00240         }
00241 }
00242 
00243 // this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes
00244 // available in data
00245 void Task::BuildRecurrenceData(void *data)
00246 {
00247         if( !Recurring )
00248                 throw Error("Task::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record.");
00249 
00250         CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data;
00251 
00252         // set all to zero
00253         memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE);
00254 
00255         rec->interval = htobs(Interval);
00256         rec->startTime = time2min(StartTime);
00257         if( Perpetual )
00258                 rec->endTime = 0xffffffff;
00259         else
00260                 rec->endTime = time2min(RecurringEndTime);
00261 
00262         switch( RecurringType )
00263         {
00264         case Day:
00265                 rec->type = CRDF_TYPE_DAY;
00266                 // no extra data
00267                 break;
00268 
00269         case MonthByDate:
00270                 rec->type = CRDF_TYPE_MONTH_BY_DATE;
00271                 rec->u.month_by_date.monthDay = DayOfMonth;
00272                 break;
00273 
00274         case MonthByDay:
00275                 rec->type = CRDF_TYPE_MONTH_BY_DAY;
00276                 rec->u.month_by_day.weekDay = DayOfWeek;
00277                 rec->u.month_by_day.week = WeekOfMonth;
00278                 break;
00279 
00280         case YearByDate:
00281                 rec->type = CRDF_TYPE_YEAR_BY_DATE;
00282                 rec->u.year_by_date.monthDay = DayOfMonth;
00283                 rec->u.year_by_date.month = MonthOfYear;
00284                 break;
00285 
00286         case YearByDay:
00287                 rec->type = CRDF_TYPE_YEAR_BY_DAY;
00288                 rec->u.year_by_day.weekDay = DayOfWeek;
00289                 rec->u.year_by_day.week = WeekOfMonth;
00290                 rec->u.year_by_day.month = MonthOfYear;
00291                 break;
00292 
00293         case Week:
00294                 rec->type = CRDF_TYPE_WEEK;
00295 
00296                 // Note: this simple copy is only possible since
00297                 // the CAL_WD_* constants are the same as CRDF_WD_* constants.
00298                 // If this ever changes, this code will need to change.
00299                 rec->u.week.days = WeekDays;
00300                 break;
00301 
00302         default:
00303                 eout("Task::BuildRecurrenceData: "
00304                 "Unknown recurrence data type: " << rec->type);
00305                 throw Error("Task::BuildRecurrenceData: Unknown recurrence data type");
00306         }
00307 }
00308 
00309 void Task::ParseHeader(const Data &data, size_t &offset)
00310 {
00311         // no header in Task records
00312 }
00313 
00314 void Task::ParseFields(const Data &data, size_t &offset)
00315 {
00316         const unsigned char *finish = ParseCommonFields(*this,
00317         data.GetData() + offset, data.GetData() + data.GetSize());
00318         offset += finish - (data.GetData() + offset);
00319 }
00320 
00321 void Task::Clear()
00322 {
00323         Summary.clear();
00324         Notes.clear();
00325         Categories.clear();
00326         StartTime = DueTime = AlarmTime = 0;
00327 
00328         PriorityFlag = (PriorityFlagType)0;
00329         StatusFlag = (StatusFlagType)0;
00330         AlarmType = (AlarmFlagType)0;
00331 
00332         TaskType = 0;
00333 
00334         Perpetual = false;
00335         DueDateFlag = false;
00336         Recurring = false;
00337 
00338         TimeZoneCode = GetTimeZoneCode( 0, 0 );
00339 
00340         Unknowns.clear();
00341 }
00342 
00343 void Task::Dump(std::ostream &os) const
00344 {
00345         static const char *PriorityName[] = { "High", "Normal", "Low" };
00346         static const char *StatusName[] = { "Not Started", "In Progress",
00347                 "Completed", "Waiting", "Deferred" };
00348         static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
00349                 "Thu", "Fri", "Sat" };
00350         static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
00351                 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
00352         static const char *AlarmTypeName[] = { "None", "By Date", "Relative" };
00353 
00354         os << "Task entry: 0x" << setbase(16) << RecordId
00355            << " (" << (unsigned int)RecType << ")\n";
00356 
00357         // cycle through the type table
00358         for(    const FieldLink<Task> *b = TaskFieldLinks;
00359                 b->type != TSKFC_END;
00360                 b++ )
00361         {
00362                 if( b->strMember ) {
00363                         const std::string &s = this->*(b->strMember);
00364                         if( s.size() )
00365                                 os << "   " << b->name << ": " << s << "\n";
00366                 }
00367                 else if( b->timeMember ) {
00368                         time_t t = this->*(b->timeMember);
00369                         if( t > 0 )
00370                                 os << "   " << b->name << ": " << ctime(&t);
00371                 }
00372         }
00373 
00374         os << "   Priority: " << PriorityName[PriorityFlag] << "\n";
00375         os << "   Status: " << StatusName[StatusFlag] << "\n";
00376         if( AlarmType ) {
00377                 os << "   Alarm Type: " << AlarmTypeName[AlarmType] << "\n";
00378         }
00379 
00380         // print recurrence data if available
00381         os << "   Recurring: " << (Recurring ? "yes" : "no") << "\n";
00382         if( Recurring ) {
00383                 switch( RecurringType )
00384                 {
00385                 case Day:
00386                         os << "      Every day.\n";
00387                         break;
00388 
00389                 case MonthByDate:
00390                         os << "      Every month on the "
00391                            << DayOfMonth
00392                            << (DayOfMonth == 1 ? "st" : "")
00393                            << (DayOfMonth == 2 ? "nd" : "")
00394                            << (DayOfMonth == 3 ? "rd" : "")
00395                            << (DayOfMonth > 3  ? "th" : "")
00396                            << "\n";
00397                         break;
00398 
00399                 case MonthByDay:
00400                         os << "      Every month on the "
00401                            << DayNames[DayOfWeek]
00402                            << " of week "
00403                            << WeekOfMonth
00404                            << "\n";
00405                         break;
00406 
00407                 case YearByDate:
00408                         os << "      Every year on "
00409                            << MonthNames[MonthOfYear-1]
00410                            << " " << DayOfMonth << "\n";
00411                         break;
00412 
00413                 case YearByDay:
00414                         os << "      Every year in " << MonthNames[MonthOfYear-1]
00415                            << " on "
00416                            << DayNames[DayOfWeek]
00417                            << " of week " << WeekOfMonth << "\n";
00418                         break;
00419 
00420                 case Week:
00421                         os << "      Every week on: ";
00422                         if( WeekDays & CAL_WD_SUN ) os << "Sun ";
00423                         if( WeekDays & CAL_WD_MON ) os << "Mon ";
00424                         if( WeekDays & CAL_WD_TUE ) os << "Tue ";
00425                         if( WeekDays & CAL_WD_WED ) os << "Wed ";
00426                         if( WeekDays & CAL_WD_THU ) os << "Thu ";
00427                         if( WeekDays & CAL_WD_FRI ) os << "Fri ";
00428                         if( WeekDays & CAL_WD_SAT ) os << "Sat ";
00429                         os << "\n";
00430                         break;
00431 
00432                 default:
00433                         os << "      Unknown recurrence type\n";
00434                         break;
00435                 }
00436 
00437                 os << "      Interval: " << Interval << "\n";
00438 
00439                 if( Perpetual )
00440                         os << "      Ends: never\n";
00441                 else
00442                         os << "      Ends: " << ctime(&RecurringEndTime);
00443         }               
00444 
00445         os << Unknowns;
00446         os << "\n\n";
00447 }
00448 
00449 } // namespace Barry
00450 

Generated on Wed Sep 24 21:27:32 2008 for Barry by  doxygen 1.5.1