Here are three patches to add Tasks and Memos database records. Obviously there is a lot of code duplicated from Calendar in Tasks to handle the recurrent data fields. It probably isn't used in any other databases so we could live with it, but I would propose making an class to handle the parsing and printing (for Dump()) of this data.
I would also propose refactoring the records stuff a little to reduce the repetition of a bunch of member functions in each class. I'd like to do this before adding any more database record classes, maybe even break each database type out into their own files? -edge
### Eclipse Workspace Patch 1.0 #P barry Index: src/record.cc =================================================================== RCS file: /cvsroot/barry/barry/src/record.cc,v retrieving revision 1.21 diff -u -r1.21 record.cc --- src/record.cc 11 May 2007 19:58:20 -0000 1.21 +++ src/record.cc 18 May 2007 17:57:31 -0000 @@ -1451,6 +1451,483 @@ os << Unknowns; } +/////////////////////////////////////////////////////////////////////////////// +// Memo Class + +// Memo Field Codes +#define MEMFC_TITLE 0x01 +#define MEMFC_BODY 0x02 +#define MEMFC_CATEGORY 0x04 +#define MEMFC_END 0xffff + +FieldLink<Memos> MemosFieldLinks[] = { + { MEMFC_TITLE, "Title", 0, 0, &Memos::Title, 0, 0 }, + { MEMFC_BODY, "Body", 0, 0, &Memos::Body, 0, 0 }, + { MEMFC_CATEGORY, "Category", 0, 0, &Memos::Category, 0, 0 }, + { MEMFC_END, "End of List", 0, 0, 0, 0, 0 } +}; + +Memos::Memos() +{ + Clear(); +} + +Memos::~Memos() +{ +} + +const unsigned char* Memos::ParseField(const unsigned char *begin, + const unsigned char *end) +{ + const CommonField *field = (const CommonField *) begin; + + // advance and check size + begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size); + if( begin > end ) // if begin==end, we are ok + return begin; + + if( !btohs(field->size) ) // if field has no size, something's up + return begin; + + // cycle through the type table + for( FieldLink<Memos> *b = MemosFieldLinks; + b->type != MEMFC_END; + b++ ) + { + if( b->type == field->type ) { + if( b->strMember ) { + std::string &s = this->*(b->strMember); + s.assign((const char *)field->u.raw, btohs(field->size)-1); + return begin; // done! + } + else if( b->timeMember && btohs(field->size) == 4 ) { + time_t &t = this->*(b->timeMember); + t = min2time(field->u.min1900); + return begin; + } + } + } + // if still not handled, add to the Unknowns list + UnknownField uf; + uf.type = field->type; + uf.data.assign((const char*)field->u.raw, btohs(field->size)); + Unknowns.push_back(uf); + + // return new pointer for next field + return begin; +} + +void Memos::ParseHeader(const Data &data, size_t &offset) +{ + // no header in Memos records +} + +void Memos::ParseFields(const Data &data, size_t &offset) +{ + const unsigned char *finish = ParseCommonFields(*this, + data.GetData() + offset, data.GetData() + data.GetSize()); + offset += finish - (data.GetData() + offset); +} + + +void Memos::Dump(std::ostream &os) const +{ + os << "Memos entry: 0x" << setbase(16) << RecordId + << " (" << (unsigned int)RecType << ")\n"; + os << " Title: " << Title << "\n"; + os << " Body: " << Body << "\n"; + os << " Category: " << Category << "\n"; + + os << Unknowns; + os << "\n\n"; +} + +void Memos::Clear() +{ + Title.clear(); + Body.clear(); + Category.clear(); + + Unknowns.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Tasks Class + +// Tasks Field Codes +#define TSKFC_TITLE 0x02 +#define TSKFC_NOTES 0x03 +#define TSKFC_START_TIME 0x05 +#define TSKFC_DUE_TIME 0x06 +#define TSKFC_STATUS 0x09 +#define TSKFC_PRIORITY 0x0a +#define TSKFC_RECURRENCE_DATA 0x0c +#define TSKFC_ALARM_TIME 0x0f +#define TSKFC_TIMEZONE_CODE 0x10 +#define TSKFC_CATEGORIES 0x11 +#define TSKFC_END 0xffff + +FieldLink<Tasks> TasksFieldLinks[] = { + { TSKFC_TITLE, "Summary", 0, 0, &Tasks::Summary, 0, 0 }, + { TSKFC_NOTES, "Notes", 0, 0, &Tasks::Notes, 0, 0 }, + { TSKFC_START_TIME, "Start Time", 0, 0, 0, 0, &Tasks::StartTime }, + { TSKFC_DUE_TIME, "Due Time", 0, 0, 0, 0, &Tasks::DueTime }, + { TSKFC_ALARM_TIME, "Alarm Time", 0, 0, 0, 0, &Tasks::AlarmTime }, + { TSKFC_CATEGORIES, "Categories", 0, 0, &Tasks::Categories, 0, 0 }, + { TSKFC_END, "End of List", 0, 0, 0, 0, 0 }, +}; + +Tasks::Tasks() +{ + Clear(); +} + +Tasks::~Tasks() +{ +} + +const unsigned char* Tasks::ParseField(const unsigned char *begin, + const unsigned char *end) +{ + const CommonField *field = (const CommonField *) begin; + + // advance and check size + begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size); + if( begin > end ) // if begin==end, we are ok + return begin; + + if( !btohs(field->size) ) // if field has no size, something's up + return begin; + + // cycle through the type table + for( FieldLink<Tasks> *b = TasksFieldLinks; + b->type != TSKFC_END; + b++ ) + { + if( b->type == field->type ) { + if( b->strMember ) { + std::string &s = this->*(b->strMember); + s.assign((const char *)field->u.raw, btohs(field->size)-1); + return begin; // done! + } + else if( b->timeMember && btohs(field->size) == 4 ) { + time_t &t = this->*(b->timeMember); + t = min2time(field->u.min1900); + return begin; + } + } + } + // handle special cases + switch( field->type ) + { + case TSKFC_PRIORITY: + if( field->u.raw[0] > Low ) { + throw Error( "Task::ParseField: priority field out of bounds" ); + } + else { + PriorityType = (PriorityTypeFlag)field->u.raw[0]; + } + return begin; + + case TSKFC_STATUS: + if( field->u.raw[0] > Deferred ) { + throw Error( "Task::ParseField: priority field out of bounds" ); + } + else { + StatusType = (StatusTypeFlag)field->u.raw[0]; + } + return begin; + case TSKFC_TIMEZONE_CODE: + if( btohs(field->size) == 4 ) { + TimeZoneCode = btohs(field->u.code); + } + else { + throw Error("Task::ParseField: not enough data in time zone code field"); + } + return begin; + + case TSKFC_RECURRENCE_DATA: + if( btohs(field->size) >= CALENDAR_RECURRENCE_DATA_FIELD_SIZE ) { + Recurring = true; + ParseRecurrenceData(&field->u.raw[0]); + } + else { + throw Error("Task::ParseField: not enough data in recurrence data field"); + } + return begin; + } + // if still not handled, add to the Unknowns list + UnknownField uf; + uf.type = field->type; + uf.data.assign((const char*)field->u.raw, btohs(field->size)); + Unknowns.push_back(uf); + + // return new pointer for next field + return begin; +} + +// this function assumes the size has already been checked +void Tasks::ParseRecurrenceData(const void *data) +{ + const CalendarRecurrenceDataField *rec = + (const CalendarRecurrenceDataField*) data; + + Interval = btohs(rec->interval); + if( Interval < 1 ) + Interval = 1; // must always be >= 1 + + if( rec->endTime == 0xffffffff ) { + Perpetual = true; + } + else { + RecurringEndTime = min2time(rec->endTime); + Perpetual = false; + } + + switch( rec->type ) + { + case CRDF_TYPE_DAY: + RecurringType = Day; + // no extra data + break; + + case CRDF_TYPE_MONTH_BY_DATE: + RecurringType = MonthByDate; + DayOfMonth = rec->u.month_by_date.monthDay; + break; + + case CRDF_TYPE_MONTH_BY_DAY: + RecurringType = MonthByDay; + DayOfWeek = rec->u.month_by_day.weekDay; + WeekOfMonth = rec->u.month_by_day.week; + break; + + case CRDF_TYPE_YEAR_BY_DATE: + RecurringType = YearByDate; + DayOfMonth = rec->u.year_by_date.monthDay; + MonthOfYear = rec->u.year_by_date.month; + break; + + case CRDF_TYPE_YEAR_BY_DAY: + RecurringType = YearByDay; + DayOfWeek = rec->u.year_by_day.weekDay; + WeekOfMonth = rec->u.year_by_day.week; + MonthOfYear = rec->u.year_by_day.month; + break; + + case CRDF_TYPE_WEEK: + RecurringType = Week; + + // Note: this simple copy is only possible since + // the CAL_WD_* constants are the same as CRDF_WD_* constants. + // If this ever changes, this code will need to change. + WeekDays = rec->u.week.days; + break; + + default: + eout("Unknown recurrence data type: " << rec->type); + throw Error("Unknown recurrence data type"); + } +} + +// this function assumes there is CALENDAR_RECURRENCE_DATA_FIELD_SIZE bytes +// available in data +void Tasks::BuildRecurrenceData(void *data) +{ + if( !Recurring ) + throw Error("Task::BuildRecurrenceData: Attempting to build recurrence data on non-recurring record."); + + CalendarRecurrenceDataField *rec = (CalendarRecurrenceDataField*) data; + + // set all to zero + memset(data, 0, CALENDAR_RECURRENCE_DATA_FIELD_SIZE); + + rec->interval = htobs(Interval); + rec->startTime = time2min(StartTime); + if( Perpetual ) + rec->endTime = 0xffffffff; + else + rec->endTime = time2min(RecurringEndTime); + + switch( RecurringType ) + { + case Day: + rec->type = CRDF_TYPE_DAY; + // no extra data + break; + + case MonthByDate: + rec->type = CRDF_TYPE_MONTH_BY_DATE; + rec->u.month_by_date.monthDay = DayOfMonth; + break; + + case MonthByDay: + rec->type = CRDF_TYPE_MONTH_BY_DAY; + rec->u.month_by_day.weekDay = DayOfWeek; + rec->u.month_by_day.week = WeekOfMonth; + break; + + case YearByDate: + rec->type = CRDF_TYPE_YEAR_BY_DATE; + rec->u.year_by_date.monthDay = DayOfMonth; + rec->u.year_by_date.month = MonthOfYear; + break; + + case YearByDay: + rec->type = CRDF_TYPE_YEAR_BY_DAY; + rec->u.year_by_day.weekDay = DayOfWeek; + rec->u.year_by_day.week = WeekOfMonth; + rec->u.year_by_day.month = MonthOfYear; + break; + + case Week: + rec->type = CRDF_TYPE_WEEK; + + // Note: this simple copy is only possible since + // the CAL_WD_* constants are the same as CRDF_WD_* constants. + // If this ever changes, this code will need to change. + rec->u.week.days = WeekDays; + break; + + default: + eout("Task::BuildRecurrenceData: " + "Unknown recurrence data type: " << rec->type); + throw Error("Task::BuildRecurrenceData: Unknown recurrence data type"); + } +} + +void Tasks::ParseHeader(const Data &data, size_t &offset) +{ + // no header in Tasks records +} + +void Tasks::ParseFields(const Data &data, size_t &offset) +{ + const unsigned char *finish = ParseCommonFields(*this, + data.GetData() + offset, data.GetData() + data.GetSize()); + offset += finish - (data.GetData() + offset); +} +void Tasks::Clear() +{ + Summary.clear(); + Notes.clear(); + Categories.clear(); + StartTime = DueTime = AlarmTime = 0; + + PriorityType = (PriorityTypeFlag)0; + StatusType = (StatusTypeFlag)0; + + Recurring = false; + + TimeZoneCode = GetTimeZoneCode( 0, 0 ); + + Unknowns.clear(); +} + +void Tasks::Dump(std::ostream &os) const +{ + static const char *PriorityName[] = { + "High", "Normal", "Low" }; + static const char *StatusName[] = { + "Not Started", "In Progress", "Completed", "Waiting", "Deferred" }; + static const char *DayNames[] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" }; + static const char *MonthNames[] = { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + os << "Tasks entry: 0x" << setbase(16) << RecordId + << " (" << (unsigned int)RecType << ")\n"; + // cycle through the type table + for( const FieldLink<Tasks> *b = TasksFieldLinks; + b->type != TSKFC_END; + b++ ) + { + if( b->strMember ) { + const std::string &s = this->*(b->strMember); + if( s.size() ) + os << " " << b->name << ": " << s << "\n"; + } + else if( b->timeMember ) { + time_t t = this->*(b->timeMember); + if( t > 0 ) + os << " " << b->name << ": " << ctime(&t); + } + } + + os << " Priority: " << PriorityName[PriorityType] << "\n"; + os << " Status: " << StatusName[StatusType] << "\n"; + // print recurrence data if available + os << " Recurring: " << (Recurring ? "yes" : "no") << "\n"; + if( Recurring ) { + switch( RecurringType ) + { + case Day: + os << " Every day.\n"; + break; + + case MonthByDate: + os << " Every month on the " + << DayOfMonth + << (DayOfMonth == 1 ? "st" : "") + << (DayOfMonth == 2 ? "nd" : "") + << (DayOfMonth == 3 ? "rd" : "") + << (DayOfMonth > 3 ? "th" : "") + << "\n"; + break; + + case MonthByDay: + os << " Every month on the " + << DayNames[DayOfWeek] + << " of week " + << WeekOfMonth + << "\n"; + break; + + case YearByDate: + os << " Every year on " + << MonthNames[MonthOfYear-1] + << " " << DayOfMonth << "\n"; + break; + + case YearByDay: + os << " Every year in " << MonthNames[MonthOfYear-1] + << " on " + << DayNames[DayOfWeek] + << " of week " << WeekOfMonth << "\n"; + break; + + case Week: + os << " Every week on: "; + if( WeekDays & CAL_WD_SUN ) os << "Sun "; + if( WeekDays & CAL_WD_MON ) os << "Mon "; + if( WeekDays & CAL_WD_TUE ) os << "Tue "; + if( WeekDays & CAL_WD_WED ) os << "Wed "; + if( WeekDays & CAL_WD_THU ) os << "Thu "; + if( WeekDays & CAL_WD_FRI ) os << "Fri "; + if( WeekDays & CAL_WD_SAT ) os << "Sat "; + os << "\n"; + break; + + default: + os << " Unknown recurrence type\n"; + break; + } + + os << " Interval: " << Interval << "\n"; + + if( Perpetual ) + os << " Ends: never\n"; + else + os << " Ends: " + << ctime(&RecurringEndTime); + } + os << Unknowns; + + os << "\n\n"; + +} + + /////////////////////////////////////////////////////////////////////////////// // ServiceBookConfig class
Index: src/record.h =================================================================== RCS file: /cvsroot/barry/barry/src/record.h,v retrieving revision 1.19 diff -u -r1.19 record.h --- src/record.h 11 May 2007 19:58:20 -0000 1.19 +++ src/record.h 18 May 2007 17:57:57 -0000 @@ -473,6 +473,142 @@ return os; } +class Memos +{ +public: + typedef std::vector<UnknownField> UnknownsType; + + uint8_t RecType; + uint32_t RecordId; + + std::string Title; + std::string Body; + std::string Category; + + UnknownsType Unknowns; + +public: + const unsigned char* ParseField(const unsigned char *begin, + const unsigned char *end); +public: + Memos(); + ~Memos(); + + // Parser / Builder API (see parser.h / builder.h) + uint8_t GetRecType() const { return RecType; } + uint32_t GetUniqueId() const { return RecordId; } + void SetIds(uint8_t Type, uint32_t Id) { RecType = Type; RecordId = Id; } + void ParseHeader(const Data &data, size_t &offset); + void ParseFields(const Data &data, size_t &offset); + void BuildHeader(Data &data, size_t &offset) const; + + void Clear(); + + void Dump(std::ostream &os) const; + + // database name + static const char * GetDBName() { return "Memos"; } + static uint8_t GetDefaultRecType() { return 0; } // or 0? +}; +inline std::ostream& operator<<(std::ostream &os, const Memos &msg) { + msg.Dump(os); + return os; +} + +class Tasks +{ +public: + typedef std::vector<UnknownField> UnknownsType; + uint8_t RecType; + uint32_t RecordId; + + std::string Summary; + std::string Notes; + std::string Categories; + std::string UID; + + time_t StartTime; + time_t DueTime; + time_t AlarmTime; + int TimeZoneCode; + unsigned short Interval; + enum RecurringCodeType { + Day = 1, //< eg. every day + //< set: nothing + MonthByDate = 3, //< eg. every month on the 12th + //< set: DayOfMonth + MonthByDay = 4, //< eg. every month on 3rd Wed + //< set: DayOfWeek and WeekOfMonth + YearByDate = 5, //< eg. every year on March 5 + //< set: DayOfMonth and MonthOfYear + YearByDay = 6, //< eg. every year on 3rd Wed of Jan + //< set: DayOfWeek, WeekOfMonth, and + //< MonthOfYear + Week = 12 //< eg. every week on Mon and Fri + //< set: WeekDays + }; + RecurringCodeType RecurringType; + time_t RecurringEndTime; + unsigned short // recurring details, depending on type + DayOfWeek, // 0-6 + WeekOfMonth, // 1-5 + DayOfMonth, // 1-31 + MonthOfYear; // 1-12 + unsigned char WeekDays; // bitmask, bit 0 = sunday + + int ClassType; + enum PriorityTypeFlag + { + High = 0, + Normal, + Low + }; + PriorityTypeFlag PriorityType; + + enum StatusTypeFlag + { + NotStarted = 0, + InProgress, + Completed, + Waiting, + Deferred + }; + StatusTypeFlag StatusType; + + bool Recurring; + bool Perpetual; + + + UnknownsType Unknowns; + +public: + Tasks(); + ~Tasks(); + + const unsigned char* ParseField(const unsigned char *begin, + const unsigned char *end); + void ParseRecurrenceData(const void *data); + void BuildRecurrenceData(void *data); + uint8_t GetRecType() const { return RecType; } + uint32_t GetUniqueId() const { return RecordId; } + void SetIds(uint8_t Type, uint32_t Id) { RecType = Type; RecordId = Id; } + void ParseHeader(const Data &data, size_t &offset); + void ParseFields(const Data &data, size_t &offset); + void BuildHeader(Data &data, size_t &offset) const; + + void Clear(); + + void Dump(std::ostream &os) const; + + // database name + static const char * GetDBName() { return "Tasks"; } + static uint8_t GetDefaultRecType() { return 2; } + +}; +inline std::ostream& operator<<(std::ostream &os, const Tasks &msg) { + msg.Dump(os); + return os; +} // This is a packed field, which is a group of fields packed in // variable length records inside one larger field of a normal record.
Index: tools/btool.cc =================================================================== RCS file: /cvsroot/barry/barry/tools/btool.cc,v retrieving revision 1.14 diff -u -r1.14 btool.cc --- tools/btool.cc 24 Feb 2007 04:48:05 -0000 1.14 +++ tools/btool.cc 18 May 2007 17:58:43 -0000 @@ -218,6 +218,16 @@ new RecordParser<ServiceBook, Store<ServiceBook> > ( new Store<ServiceBook>(filename, false))); } + else if( name == "Memos" ) { + return auto_ptr<Parser>( + new RecordParser<Memos, Store<Memos> > ( + new Store<Memos>(filename, false ))); + } + else if( name == "Tasks" ) { + return auto_ptr<Parser>( + new RecordParser<Tasks, Store<Tasks> > ( + new Store<Tasks>(filename, false ))); + } else { // unknown database, use null parser return auto_ptr<Parser>( new DataDumpParser );
pgpdhLmyysmfX.pgp
Description: PGP signature
------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/
_______________________________________________ Barry-devel mailing list Barry-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/barry-devel