# 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-2011, 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 "r_calendar.h"                        // for CAL_* defines
    00025 #include "record-internal.h"
    00026 #include "protostructs.h"
    00027 #include "data.h"
    00028 #include "time.h"
    00029 #include "iconv.h"
    00030 #include "debug.h"
    00031 #include <ostream>
    00032 #include <iomanip>
    00033 #include <string.h>
    00034
    00035 using namespace std;
    00036 using namespace Barry::Protocol;
    00037
    00038 namespace Barry {
    00039
    00040
    00041 ///////////////////////////////////////////////////////////////////////////////
    00042 // Task Class, static members
    00043
    00044 //
    00045 // Note! These functions currently only pass the same values through.
    00046 //       In actuality, these are technically two different values:
    00047 //       one on the raw protocol side, and the other part of the
    00048 //       guaranteed Barry API.  If the Blackberry ever changes the
    00049 //       meanings for these codes, do the translation here.
    00050 //
    00051
    00052 Task::AlarmFlagType Task::AlarmProto2Rec(uint8_t a)
    00053 {
    00054         return (AlarmFlagType)a;
    00055 }
    00056
    00057 uint8_t Task::AlarmRec2Proto(AlarmFlagType a)
    00058 {
    00059         return a;
    00060 }
    00061
    00062 Task::PriorityFlagType Task::PriorityProto2Rec(uint8_t p)
    00063 {
    00064         return (PriorityFlagType)p;
    00065 }
    00066
    00067 uint8_t Task::PriorityRec2Proto(PriorityFlagType p)
    00068 {
    00069         return p;
    00070 }
    00071
    00072 Task::StatusFlagType Task::StatusProto2Rec(uint8_t s)
    00073 {
    00074         return (StatusFlagType)s;
    00075 }
    00076
    00077 uint8_t Task::StatusRec2Proto(StatusFlagType s)
    00078 {
    00079         return s;
    00080 }
    00081
    00082
    00083 ///////////////////////////////////////////////////////////////////////////////
    00084 // Task Class
    00085
    00086 // Task Field Codes
    00087 #define TSKFC_TASK_TYPE         0x01
    00088 #define TSKFC_TITLE             0x02
    00089 #define TSKFC_NOTES             0x03
    00090 #define TSKFC_DUE_TIME          0x05
    00091 #define TSKFC_START_TIME        0x06    // This is fuzzy... most devices seem
    00092                                         // to anchor this value == to DUE_TIME
    00093 #define TSKFC_DUE_FLAG          0x08
    00094 #define TSKFC_STATUS            0x09
    00095 #define TSKFC_PRIORITY          0x0a
    00096 #define TSKFC_ALARM_TYPE        0x0e
    00097 #define TSKFC_ALARM_TIME        0x0f
    00098 #define TSKFC_TIMEZONE_CODE     0x10
    00099 #define TSKFC_CATEGORIES        0x11
    00100 #define TSKFC_END               0xffff
    00101
    00102 static FieldLink<Task> TaskFieldLinks[] = {
    00103    { TSKFC_TITLE,      "Summary",     0, 0, &Task::Summary, 0, 0, 0, 0, true },
    00104    { TSKFC_NOTES,      "Notes",       0, 0, &Task::Notes, 0, 0, 0, 0, true },
    00105    { TSKFC_START_TIME, "Start Time",  0, 0, 0, 0, &Task::StartTime, 0, 0, false },
    00106    { TSKFC_DUE_TIME,   "Due Time",    0, 0, 0, 0, &Task::DueTime, 0, 0, false },
    00107    { TSKFC_ALARM_TIME, "Alarm Time",  0, 0, 0, 0, &Task::AlarmTime, 0, 0, false },
    00108    { TSKFC_END,        "End of List", 0, 0, 0, 0, 0, 0, 0, false },
    00109 };
    00110
    00111 Task::Task()
    00112 {
    00113         Clear();
    00114 }
    00115
    00116 Task::~Task()
    00117 {
    00118 }
    00119
    00120 const unsigned char* Task::ParseField(const unsigned char *begin,
    00121                                       const unsigned char *end,
    00122                                       const IConverter *ic)
    00123 {
    00124         const CommonField *field = (const CommonField *) begin;
    00125
    00126         // advance and check size
    00127         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
    00128         if( begin > end )       // if begin==end, we are ok
    00129                 return begin;
    00130
    00131         if( !btohs(field->size) )   // if field has no size, something's up
    00132                 return begin;
    00133
    00134         if( field->type == TSKFC_TASK_TYPE ) {
    00135                 if( field->u.raw[0] != 't' ) {
    00136                         throw Error("Task::ParseField: Task Type is not 't'");
    00137                 }
    00138                 return begin;
    00139         }
    00140
    00141         // cycle through the type table
    00142         for(    FieldLink<Task> *b = TaskFieldLinks;
    00143                 b->type != TSKFC_END;
    00144                 b++ )
    00145         {
    00146                 if( b->type == field->type ) {
    00147                         if( b->strMember ) {
    00148                                 std::string &s = this->*(b->strMember);
    00149                                 s = ParseFieldString(field);
    00150                                 if( b->iconvNeeded && ic )
    00151                                         s = ic->FromBB(s);
    00152                                 return begin;   // done!
    00153                         }
    00154                         else if( b->timeMember && btohs(field->size) == 4 ) {
    00155                                 time_t &t = this->*(b->timeMember);
    00156                                 t = min2time(field->u.min1900);
    00157                                 return begin;
    00158                         }
    00159                 }
    00160         }
    00161         // handle special cases
    00162         switch( field->type )
    00163         {
    00164         case TSKFC_PRIORITY:
    00165                 if( field->u.raw[0] > TR_PRIORITY_RANGE_HIGH ) {
    00166                         throw Error( "Task::ParseField: priority field out of bounds" );
    00167                 }
    00168                 else {
    00169                         PriorityFlag = PriorityProto2Rec(field->u.raw[0]);
    00170                 }
    00171                 return begin;
    00172
    00173         case TSKFC_STATUS:
    00174                 if( field->u.raw[0] > TR_STATUS_RANGE_HIGH ) {
    00175                         throw Error( "Task::ParseField: priority field out of bounds" );
    00176                 }
    00177                 else {
    00178                         StatusFlag = StatusProto2Rec(field->u.raw[0]);
    00179                 }
    00180                 return begin;
    00181
    00182         case TSKFC_TIMEZONE_CODE:
    00183                 if( btohs(field->size) == 4 ) {
    00184                         TimeZoneCode = btohs(field->u.code);
    00185                         TimeZoneValid = true;
    00186                 }
    00187                 else {
    00188                         throw Error("Task::ParseField: not enough data in time zone code field");
    00189                 }
    00190                 return begin;
    00191
    00192         case TSKFC_DUE_FLAG:
    00193                 DueDateFlag = field->u.raw[0];
    00194                 return begin;
    00195
    00196         case TSKFC_ALARM_TYPE:
    00197                 if( field->u.raw[0] > TR_ALARM_RANGE_HIGH ) {
    00198                         throw Error("Task::ParseField: AlarmType out of bounds" );
    00199                 }
    00200                 else {
    00201                         AlarmType = AlarmProto2Rec(field->u.raw[0]);
    00202                 }
    00203                 return begin;
    00204
    00205         case TSKFC_CATEGORIES:
    00206                 {
    00207                         std::string catstring = ParseFieldString(field);
    00208                         if( ic )
    00209                                 catstring = ic->FromBB(catstring);
    00210                         Categories.CategoryStr2List(catstring);
    00211                 }
    00212                 return begin;
    00213         }
    00214         // base class handles recurring data
    00215         if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
    00216                 return begin;
    00217
    00218         // if still not handled, add to the Unknowns list
    00219         UnknownField uf;
    00220         uf.type = field->type;
    00221         uf.data.assign((const char*)field->u.raw, btohs(field->size));
    00222         Unknowns.push_back(uf);
    00223
    00224         // return new pointer for next field
    00225         return begin;
    00226 }
    00227
    00228 void Task::ParseHeader(const Data &data, size_t &offset)
    00229 {
    00230         // no header in Task records
    00231 }
    00232
    00233 void Task::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
    00234 {
    00235         const unsigned char *finish = ParseCommonFields(*this,
    00236                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
    00237         offset += finish - (data.GetData() + offset);
    00238 }
    00239
    00240
    00241 void Task::BuildHeader(Data &data, size_t &offset) const
    00242 {
    00243         // no header in Task records
    00244 }
    00245
    00246
    00247 //
    00248 // Build
    00249 //
    00250 /// Build fields part of record.
    00251 ///
    00252 void Task::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
    00253 {
    00254         data.Zap();
    00255
    00256         // tack on the 't' task type field first
    00257         BuildField(data, offset, TSKFC_TASK_TYPE, 't');
    00258
    00259         BuildField(data, offset, TSKFC_STATUS, StatusRec2Proto(StatusFlag));
    00260         BuildField(data, offset, TSKFC_PRIORITY, PriorityRec2Proto(PriorityFlag));
    00261         BuildField(data, offset, TSKFC_ALARM_TYPE, AlarmRec2Proto(AlarmType));
    00262
    00263         if ( DueDateFlag )
    00264                 BuildField(data, offset, TSKFC_DUE_FLAG, (char) 1);
    00265         else
    00266                 BuildField(data, offset, TSKFC_DUE_FLAG, (char) 0);
    00267
    00268         if( TimeZoneValid ) {
    00269                 // the time zone code field is 4 bytes, but we only use
    00270                 // the first two... pad it with zeros
    00271                 uint32_t code = TimeZoneCode;
    00272                 BuildField(data, offset, TSKFC_TIMEZONE_CODE, code);
    00273         }
    00274
    00275         // cycle through the type table
    00276         for(    FieldLink<Task> *b = TaskFieldLinks;
    00277                 b->type != TSKFC_END;
    00278                 b++ )
    00279         {
    00280                 // print only fields with data
    00281                 if( b->strMember ) {
    00282                         const std::string &field = this->*(b->strMember);
    00283                         if( field.size() ) {
    00284                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
    00285                                 BuildField(data, offset, b->type, s);
    00286                         }
    00287                 }
    00288                 else if( b->timeMember ) {
    00289                         time_t t = this->*(b->timeMember);
    00290                         if( t > 0 )
    00291                                 BuildField1900(data, offset, b->type, t);
    00292                 }
    00293                 else if( b->postMember && b->postField ) {
    00294                         const std::string &field = (this->*(b->postMember)).*(b->postField);
    00295                         if( field.size() ) {
    00296                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
    00297                                 BuildField(data, offset, b->type, s);
    00298                         }
    00299                 }
    00300         }
    00301
    00302         // Categories
    00303         if( Categories.size() ) {
    00304                 string store;
    00305                 Categories.CategoryList2Str(store);
    00306                 BuildField(data, offset, TSKFC_CATEGORIES, ic ? ic->ToBB(store) : store);
    00307         }
    00308
    00309         // and finally save unknowns
    00310         UnknownsType::const_iterator
    00311                 ub = Unknowns.begin(), ue = Unknowns.end();
    00312         for( ; ub != ue; ub++ ) {
    00313                 BuildField(data, offset, *ub);
    00314         }
    00315
    00316         data.ReleaseBuffer(offset);
    00317 }
    00318
    00319
    00320
    00321 void Task::Clear()
    00322 {
    00323         // clear the base class first
    00324         RecurBase::Clear();
    00325
    00326         // our variables...
    00327         RecType = GetDefaultRecType();
    00328         RecordId = 0;
    00329
    00330         Summary.clear();
    00331         Notes.clear();
    00332         Categories.clear();
    00333         UID.clear();
    00334
    00335         StartTime = DueTime = AlarmTime = 0;
    00336         TimeZoneCode = GetTimeZoneCode( 0, 0 ); // default to GMT
    00337         TimeZoneValid = false;
    00338
    00339         AlarmType = (AlarmFlagType)0;
    00340         PriorityFlag = (PriorityFlagType)0;
    00341         StatusFlag = (StatusFlagType)0;
    00342
    00343         DueDateFlag = false;
    00344
    00345         Unknowns.clear();
    00346 }
    00347
    00348 std::string Task::GetDescription() const
    00349 {
    00350         return Summary;
    00351 }
    00352
    00353 void Task::Dump(std::ostream &os) const
    00354 {
    00355         static const char *PriorityName[] = { "High", "Normal", "Low" };
    00356         static const char *StatusName[] = { "Not Started", "In Progress",
    00357                 "Completed", "Waiting", "Deferred" };
    00358         static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed",
    00359                 "Thu", "Fri", "Sat" };
    00360         static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr",
    00361                 "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    00362         static const char *AlarmTypeName[] = { "None", "By Date", "Relative" };
    00363
    00364         os << "Task entry: 0x" << setbase(16) << RecordId
    00365            << " (" << (unsigned int)RecType << ")\n";
    00366
    00367         // cycle through the type table
    00368         for(    const FieldLink<Task> *b = TaskFieldLinks;
    00369                 b->type != TSKFC_END;
    00370                 b++ )
    00371         {
    00372                 if( b->strMember ) {
    00373                         const std::string &s = this->*(b->strMember);
    00374                         if( s.size() )
    00375                                 os << "   " << b->name << ": " << s << "\n";
    00376                 }
    00377                 else if( b->timeMember ) {
    00378                         time_t t = this->*(b->timeMember);
    00379                         if( t > 0 )
    00380                                 os << "   " << b->name << ": " << ctime(&t);
    00381                 }
    00382         }
    00383
    00384         os << "   Due Date Flag: " << (DueDateFlag ? "true" : "false") << "\n";
    00385         os << "   Priority: " << PriorityName[PriorityFlag] << "\n";
    00386         os << "   Status: " << StatusName[StatusFlag] << "\n";
    00387         if( AlarmType ) {
    00388                 os << "   Alarm Type: " << AlarmTypeName[AlarmType] << "\n";
    00389         }
    00390         if( TimeZoneValid )
    00391                 os << "   Time Zone: " << GetTimeZone(TimeZoneCode)->Name << "\n";
    00392
    00393         // print recurrence data if available
    00394         os << "   Recurring: " << (Recurring ? "yes" : "no") << "\n";
    00395         if( Recurring ) {
    00396                 switch( RecurringType )
    00397                 {
    00398                 case Day:
    00399                         os << "      Every day.\n";
    00400                         break;
    00401
    00402                 case MonthByDate:
    00403                         os << "      Every month on the "
    00404                            << DayOfMonth
    00405                            << (DayOfMonth == 1 ? "st" : "")
    00406                            << (DayOfMonth == 2 ? "nd" : "")
    00407                            << (DayOfMonth == 3 ? "rd" : "")
    00408                            << (DayOfMonth > 3  ? "th" : "")
    00409                            << "\n";
    00410                         break;
    00411
    00412                 case MonthByDay:
    00413                         os << "      Every month on the "
    00414                            << DayNames[DayOfWeek]
    00415                            << " of week "
    00416                            << WeekOfMonth
    00417                            << "\n";
    00418                         break;
    00419
    00420                 case YearByDate:
    00421                         os << "      Every year on "
    00422                            << MonthNames[MonthOfYear-1]
    00423                            << " " << DayOfMonth << "\n";
    00424                         break;
    00425
    00426                 case YearByDay:
    00427                         os << "      Every year in " << MonthNames[MonthOfYear-1]
    00428                            << " on "
    00429                            << DayNames[DayOfWeek]
    00430                            << " of week " << WeekOfMonth << "\n";
    00431                         break;
    00432
    00433                 case Week:
    00434                         os << "      Every week on: ";
    00435                         if( WeekDays & CAL_WD_SUN ) os << "Sun ";
    00436                         if( WeekDays & CAL_WD_MON ) os << "Mon ";
    00437                         if( WeekDays & CAL_WD_TUE ) os << "Tue ";
    00438                         if( WeekDays & CAL_WD_WED ) os << "Wed ";
    00439                         if( WeekDays & CAL_WD_THU ) os << "Thu ";
    00440                         if( WeekDays & CAL_WD_FRI ) os << "Fri ";
    00441                         if( WeekDays & CAL_WD_SAT ) os << "Sat ";
    00442                         os << "\n";
    00443                         break;
    00444
    00445                 default:
    00446                         os << "      Unknown recurrence type\n";
    00447                         break;
    00448                 }
    00449
    00450                 os << "      Interval: " << Interval << "\n";
    00451
    00452                 if( Perpetual )
    00453                         os << "      Ends: never\n";
    00454                 else
    00455                         os << "      Ends: " << ctime(&RecurringEndTime);
    00456         }
    00457
    00458         if( Categories.size() ) {
    00459                 string display;
    00460                 Categories.CategoryList2Str(display);
    00461                 os << "    Categories: " << display << "\n";
    00462         }
    00463
    00464         os << Unknowns;
    00465         os << "\n\n";
    00466 }
    00467
    00468 bool Task::operator<(const Task &other) const
    00469 {
    00470         if( StartTime != other.StartTime )
    00471                 return StartTime < other.StartTime;
    00472         if( AlarmTime != other.AlarmTime )
    00473                 return AlarmTime < other.AlarmTime;
    00474
    00475         int cmp = Summary.compare(other.Summary);
    00476         if( cmp == 0 )
    00477                 cmp = Notes.compare(other.Notes);
    00478         return cmp < 0;
    00479 }
    00480
    00481 } // namespace Barry
    00482