r_contact.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       r_contact.cc
00003 ///             Blackberry database record parser class for contact records.
00004 ///
00005 
00006 /*
00007     Copyright (C) 2005-2009, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "r_contact.h"
00023 #include "record-internal.h"
00024 #include "protocol.h"
00025 #include "protostructs.h"
00026 #include "data.h"
00027 #include "time.h"
00028 #include "error.h"
00029 #include "endian.h"
00030 #include "iconv.h"
00031 #include <ostream>
00032 #include <iomanip>
00033 #include <time.h>
00034 #include <stdexcept>
00035 
00036 #define __DEBUG_MODE__
00037 #include "debug.h"
00038 
00039 using namespace std;
00040 using namespace Barry::Protocol;
00041 
00042 namespace Barry {
00043 
00044 
00045 
00046 ///////////////////////////////////////////////////////////////////////////////
00047 // Contact class
00048 
00049 // Contact field codes
00050 #define CFC_EMAIL               1
00051 #define CFC_PHONE               2
00052 #define CFC_FAX                 3
00053 #define CFC_WORK_PHONE          6
00054 #define CFC_HOME_PHONE          7
00055 #define CFC_MOBILE_PHONE        8
00056 #define CFC_PAGER               9
00057 #define CFC_PIN                 10
00058 #define CFC_RADIO               14      // 0x0e
00059 #define CFC_WORK_PHONE_2        16      // 0x10
00060 #define CFC_HOME_PHONE_2        17      // 0x11
00061 #define CFC_OTHER_PHONE         18      // 0x12
00062 #define CFC_NAME                32      // 0x20 used twice, in first/last name order
00063 #define CFC_COMPANY             33
00064 #define CFC_DEFAULT_COMM_METHOD 34
00065 #define CFC_ADDRESS1            35
00066 #define CFC_ADDRESS2            36
00067 #define CFC_ADDRESS3            37
00068 #define CFC_CITY                38
00069 #define CFC_PROVINCE            39
00070 #define CFC_POSTAL_CODE         40
00071 #define CFC_COUNTRY             41
00072 #define CFC_TITLE               42      // 0x2a
00073 #define CFC_PUBLIC_KEY          43
00074 #define CFC_GROUP_FLAG          44
00075 #define CFC_GROUP_LINK          52
00076 #define CFC_URL                 54      // 0x36
00077 #define CFC_PREFIX              55      // 0x37
00078 #define CFC_CATEGORY            59      // 0x3B
00079 #define CFC_HOME_ADDRESS1       61      // 0x3D
00080 #define CFC_HOME_ADDRESS2       62      // 0x3E
00081   // If the address 3 isn't mapped then it appears
00082   // in the same field as address2 with a space
00083 #define CFC_HOME_ADDRESS3       63      // 0x3F
00084 #define CFC_NOTES               64      // 0x40
00085 #define CFC_USER_DEFINED_1      65      // 0x41
00086 #define CFC_USER_DEFINED_2      66      // 0x42
00087 #define CFC_USER_DEFINED_3      67      // 0x43
00088 #define CFC_USER_DEFINED_4      68      // 0x44
00089 #define CFC_HOME_CITY           69      // 0x45
00090 #define CFC_HOME_PROVINCE       70      // 0x46
00091 #define CFC_HOME_POSTAL_CODE    71      // 0x47
00092 #define CFC_HOME_COUNTRY        72      // 0x48
00093 #define CFC_IMAGE               77      // 0x4D
00094 #define CFC_BIRTHDAY            82      // 0x52
00095 #define CFC_ANNIVERSARY         83      // 0x53
00096 #define CFC_INVALID_FIELD       255
00097 
00098 // Contact code to field table
00099 static FieldLink<Contact> ContactFieldLinks[] = {
00100    { CFC_PHONE,        "Phone",      0,0,                 &Contact::Phone, 0, 0, 0, 0, true },
00101    { CFC_FAX,          "Fax",        "facsimileTelephoneNumber",0, &Contact::Fax, 0, 0, 0, 0, true },
00102    { CFC_WORK_PHONE,   "WorkPhone",  "telephoneNumber",0, &Contact::WorkPhone, 0, 0, 0, 0, true },
00103    { CFC_HOME_PHONE,   "HomePhone",  "homePhone",0,       &Contact::HomePhone, 0, 0, 0, 0, true },
00104    { CFC_MOBILE_PHONE, "MobilePhone","mobile",0,          &Contact::MobilePhone, 0, 0, 0, 0, true },
00105    { CFC_PAGER,        "Pager",      "pager",0,           &Contact::Pager, 0, 0, 0, 0, true },
00106    { CFC_PIN,          "PIN",        0,0,                 &Contact::PIN, 0, 0, 0, 0, true },
00107    { CFC_RADIO,        "Radio",      0,0,                 &Contact::Radio, 0, 0, 0, 0, true },
00108    { CFC_WORK_PHONE_2, "WorkPhone2", 0,0,                 &Contact::WorkPhone2, 0, 0, 0, 0, true },
00109    { CFC_HOME_PHONE_2, "HomePhone2", 0,0,                 &Contact::HomePhone2, 0, 0, 0, 0, true },
00110    { CFC_OTHER_PHONE,  "OtherPhone", 0,0,                 &Contact::OtherPhone, 0, 0, 0, 0, true },
00111    { CFC_COMPANY,      "Company",    "o",0,               &Contact::Company, 0, 0, 0, 0, true },
00112    { CFC_DEFAULT_COMM_METHOD,"DefaultCommMethod",0,0,     &Contact::DefaultCommunicationsMethod, 0, 0, 0, 0, true },
00113    { CFC_ADDRESS1,     "WorkAddress1",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address1, true },
00114    { CFC_ADDRESS2,     "WorkAddress2",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address2, true },
00115    { CFC_ADDRESS3,     "WorkAddress3",   0,0,             0, 0, 0, &Contact::WorkAddress, &PostalAddress::Address3, true },
00116    { CFC_CITY,         "WorkCity",       "l",0,           0, 0, 0, &Contact::WorkAddress, &PostalAddress::City, true },
00117    { CFC_PROVINCE,     "WorkProvince",   "st",0,          0, 0, 0, &Contact::WorkAddress, &PostalAddress::Province, true },
00118    { CFC_POSTAL_CODE,  "WorkPostalCode", "postalCode",0,  0, 0, 0, &Contact::WorkAddress, &PostalAddress::PostalCode, true },
00119    { CFC_COUNTRY,      "WorkCountry",    "c", "country",  0, 0, 0, &Contact::WorkAddress, &PostalAddress::Country, true },
00120    { CFC_TITLE,        "JobTitle",   "title",0,           &Contact::JobTitle, 0, 0, 0, 0, true },
00121    { CFC_PUBLIC_KEY,   "PublicKey",  0,0,                 &Contact::PublicKey, 0, 0, 0, 0, false },
00122    { CFC_URL,          "URL",        0,0,                 &Contact::URL, 0, 0, 0, 0, true },
00123    { CFC_PREFIX,       "Prefix",     0,0,                 &Contact::Prefix, 0, 0, 0, 0, true },
00124    { CFC_HOME_ADDRESS1,"HomeAddress1", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address1, true },
00125    { CFC_HOME_ADDRESS2,"HomeAddress2", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address2, true },
00126    { CFC_HOME_ADDRESS3,"HomeAddress3", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Address3, true },
00127    { CFC_NOTES,        "Notes",      0,0,                 &Contact::Notes, 0, 0, 0, 0, true },
00128    { CFC_USER_DEFINED_1, "UserDefined1", 0,0,             &Contact::UserDefined1, 0, 0, 0, 0, true },
00129    { CFC_USER_DEFINED_2, "UserDefined2", 0,0,             &Contact::UserDefined2, 0, 0, 0, 0, true },
00130    { CFC_USER_DEFINED_3, "UserDefined3", 0,0,             &Contact::UserDefined3, 0, 0, 0, 0, true },
00131    { CFC_USER_DEFINED_4, "UserDefined4", 0,0,             &Contact::UserDefined4, 0, 0, 0, 0, true },
00132    { CFC_HOME_CITY,    "HomeCity",   0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::City, true },
00133    { CFC_HOME_PROVINCE,"HomeProvince", 0,0,               0, 0, 0, &Contact::HomeAddress, &PostalAddress::Province, true },
00134    { CFC_HOME_POSTAL_CODE, "HomePostalCode", 0,0,         0, 0, 0, &Contact::HomeAddress, &PostalAddress::PostalCode, true },
00135    { CFC_HOME_COUNTRY, "HomeCountry",0,0,                 0, 0, 0, &Contact::HomeAddress, &PostalAddress::Country, true },
00136    { CFC_IMAGE,        "Image",      0,0,                 &Contact::Image, 0, 0, 0, 0, false },
00137    { CFC_INVALID_FIELD,"EndOfList",  0, 0, 0, 0, 0, 0, 0, false }
00138 };
00139 
00140 Contact::Contact()
00141         : RecType(Contact::GetDefaultRecType()),
00142         RecordId(0),
00143         m_FirstNameSeen(false)
00144 {
00145 }
00146 
00147 Contact::~Contact()
00148 {
00149 }
00150 
00151 const unsigned char* Contact::ParseField(const unsigned char *begin,
00152                                          const unsigned char *end,
00153                                          const IConverter *ic)
00154 {
00155         const CommonField *field = (const CommonField *) begin;
00156 
00157         // advance and check size
00158         begin += COMMON_FIELD_HEADER_SIZE + btohs(field->size);
00159         if( begin > end )               // if begin==end, we are ok
00160                 return begin;
00161 
00162         if( !btohs(field->size) )       // if field has no size, something's up
00163                 return begin;
00164 
00165         // cycle through the type table
00166         for(    FieldLink<Contact> *b = ContactFieldLinks;
00167                 b->type != CFC_INVALID_FIELD;
00168                 b++ )
00169         {
00170                 if( b->type == field->type ) {
00171                         if( b->strMember ) {
00172                                 std::string &s = this->*(b->strMember);
00173                                 s = ParseFieldString(field);
00174                                 if( b->iconvNeeded && ic )
00175                                         s = ic->FromBB(s);
00176                                 return begin;   // done!
00177                         }
00178                         else if( b->postMember && b->postField ) {
00179                                 std::string &s = (this->*(b->postMember)).*(b->postField);
00180                                 s = ParseFieldString(field);
00181                                 if( b->iconvNeeded && ic )
00182                                         s = ic->FromBB(s);
00183                                 return begin;
00184                         }
00185                         else {
00186                                 break;  // fall through to special handling
00187                         }
00188                 }
00189         }
00190 
00191         // if not found in the type table, check for special handling
00192         switch( field->type )
00193         {
00194         case CFC_EMAIL: {
00195                 std::string s = ParseFieldString(field);
00196                 if( ic )
00197                         s = ic->FromBB(s);
00198                 EmailAddresses.push_back( s );
00199                 }
00200                 return begin;
00201 
00202         case CFC_NAME: {
00203                 // can be used multiple times, for first/last names
00204                 std::string *name;
00205                 if( FirstName.size() || m_FirstNameSeen ) {
00206                         // first name already filled, use last name
00207                         name = &LastName;
00208                         m_FirstNameSeen = false;
00209                 }
00210                 else {
00211                         name = &FirstName;
00212                         m_FirstNameSeen = true;
00213                 }
00214 
00215                 *name = ParseFieldString(field);
00216                 if( ic )
00217                         *name = ic->FromBB(*name);
00218                 }
00219                 return begin;
00220 
00221         case CFC_GROUP_LINK:
00222                 // just add the unique ID to the list
00223                 GroupLinks.push_back(
00224                         GroupLink(field->u.link.uniqueId,
00225                                 field->u.link.unknown));
00226                 return begin;
00227 
00228         case CFC_GROUP_FLAG:
00229                 // ignore the group flag... the presense of group link items
00230                 // behaves as the flag in this class
00231                 return begin;
00232 
00233         case CFC_CATEGORY: {
00234                 std::string catstring = ParseFieldString(field);
00235                 if( ic )
00236                         catstring = ic->FromBB(catstring);
00237                 CategoryStr2List(catstring, Categories);
00238                 }
00239                 return begin;
00240 
00241         case CFC_BIRTHDAY: {
00242                 std::string bstring = ParseFieldString(field);
00243                 Birthday.FromBBString(bstring);
00244                 }
00245                 return begin;
00246 
00247         case CFC_ANNIVERSARY: {
00248                 std::string astring = ParseFieldString(field);
00249                 Anniversary.FromBBString(astring);
00250                 }
00251                 return begin;
00252         }
00253 
00254         // if still not handled, add to the Unknowns list
00255         UnknownField uf;
00256         uf.type = field->type;
00257         uf.data.assign((const char*)field->u.raw, btohs(field->size));
00258         Unknowns.push_back(uf);
00259 
00260         // return new pointer for next field
00261         return begin;
00262 }
00263 
00264 void Contact::ParseHeader(const Data &data, size_t &offset)
00265 {
00266         // no header to parse in Contact records
00267 }
00268 
00269 // this is called by the RecordParser<> class, which checks size for us
00270 void Contact::ParseFields(const Data &data, size_t &offset, const IConverter *ic)
00271 {
00272         const unsigned char *finish = ParseCommonFields(*this,
00273                 data.GetData() + offset, data.GetData() + data.GetSize(), ic);
00274         offset += finish - (data.GetData() + offset);
00275 }
00276 
00277 void Contact::BuildHeader(Data &data, size_t &offset) const
00278 {
00279         // no header in Contact records
00280 }
00281 
00282 //
00283 // BuildFields
00284 //
00285 /// Build fields part of record
00286 ///
00287 void Contact::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
00288 {
00289         data.Zap();
00290 
00291         // Sanity check: the Blackberry requires at least a name or
00292         // a company name for each address record.
00293         if( !GetFullName().size() && !Company.size() )
00294                 throw BadData("Contact must have name or company name.");
00295 
00296         // check if this is a group link record, and if so, output
00297         // the group flag
00298         if( GroupLinks.size() )
00299                 BuildField(data, offset, CFC_GROUP_FLAG, 'G');
00300 
00301         // special fields not in type table
00302         if( FirstName.size() ) {
00303                 std::string s = ic ? ic->ToBB(FirstName) : FirstName;
00304                 BuildField(data, offset, CFC_NAME, s);
00305         }
00306         if( LastName.size() ) {
00307                 if( !FirstName.size() ) {
00308                         // order matters with first/last name, and if
00309                         // last name exists, and first name doesn't,
00310                         // insert blank first name ahead of it
00311                         BuildField(data, offset, CFC_NAME, "");
00312                 }
00313                 BuildField(data, offset, CFC_NAME, ic ? ic->ToBB(LastName) : LastName);
00314         }
00315 
00316         // add all email addresses
00317         EmailList::const_iterator eai = EmailAddresses.begin();
00318         for( ; eai != EmailAddresses.end(); ++eai ) {
00319                 if( eai->size() ) {
00320                         BuildField(data, offset, CFC_EMAIL, ic ? ic->ToBB(*eai) : *eai);
00321                 }
00322         }
00323 
00324         // cycle through the type table
00325         for(    FieldLink<Contact> *b = ContactFieldLinks;
00326                 b->type != CFC_INVALID_FIELD;
00327                 b++ )
00328         {
00329                 // print only fields with data
00330                 if( b->strMember ) {
00331                         const std::string &field = this->*(b->strMember);
00332                         if( field.size() ) {
00333                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00334                                 BuildField(data, offset, b->type, s);
00335                         }
00336                 }
00337                 else if( b->postMember && b->postField ) {
00338                         const std::string &field = (this->*(b->postMember)).*(b->postField);
00339                         if( field.size() ) {
00340                                 std::string s = (b->iconvNeeded && ic) ? ic->ToBB(field) : field;
00341                                 BuildField(data, offset, b->type, s);
00342                         }
00343                 }
00344         }
00345 
00346         // save any group links
00347         GroupLinksType::const_iterator
00348                 gb = GroupLinks.begin(), ge = GroupLinks.end();
00349         for( ; gb != ge; gb++ ) {
00350                 Barry::Protocol::GroupLink link;
00351                 link.uniqueId = htobl(gb->Link);
00352                 link.unknown = htobs(gb->Unknown);
00353                 BuildField(data, offset, CFC_GROUP_LINK, link);
00354         }
00355 
00356         // save categories
00357         if( Categories.size() ) {
00358                 string store;
00359                 CategoryList2Str(Categories, store);
00360                 BuildField(data, offset, CFC_CATEGORY, ic ? ic->ToBB(store) : store);
00361         }
00362 
00363         // save Birthday and Anniversary
00364         if( Birthday.HasData() )
00365                 BuildField(data, offset, CFC_BIRTHDAY, Birthday.ToBBString());
00366         if( Anniversary.HasData() )
00367                 BuildField(data, offset, CFC_ANNIVERSARY, Anniversary.ToBBString());
00368 
00369         // and finally save unknowns
00370         UnknownsType::const_iterator
00371                 ub = Unknowns.begin(), ue = Unknowns.end();
00372         for( ; ub != ue; ub++ ) {
00373                 BuildField(data, offset, *ub);
00374         }
00375 
00376         data.ReleaseBuffer(offset);
00377 }
00378 
00379 void Contact::Clear()
00380 {
00381         RecType = Contact::GetDefaultRecType();
00382 
00383         EmailAddresses.clear();
00384         Phone.clear();
00385         Fax.clear();
00386         WorkPhone.clear();
00387         HomePhone.clear();
00388         MobilePhone.clear();
00389         Pager.clear();
00390         PIN.clear();
00391         Radio.clear();
00392         WorkPhone2.clear();
00393         HomePhone2.clear();
00394         OtherPhone.clear();
00395         FirstName.clear();
00396         LastName.clear();
00397         Company.clear();
00398         DefaultCommunicationsMethod.clear();
00399         JobTitle.clear();
00400         PublicKey.clear();
00401         URL.clear();
00402         Prefix.clear();
00403         Notes.clear();
00404         UserDefined1.clear();
00405         UserDefined2.clear();
00406         UserDefined3.clear();
00407         UserDefined4.clear();
00408         Image.clear();
00409 
00410         Birthday.Clear();
00411         Anniversary.Clear();
00412 
00413         WorkAddress.Clear();
00414         HomeAddress.Clear();
00415 
00416         Categories.clear();
00417 
00418         GroupLinks.clear();
00419         Unknowns.clear();
00420 
00421         m_FirstNameSeen = false;
00422 }
00423 
00424 //
00425 // GetFullName
00426 //
00427 /// Helper function that returns a formatted full name
00428 ///
00429 std::string Contact::GetFullName() const
00430 {
00431         std::string Full = FirstName;
00432         if( Full.size() && LastName.size() )
00433                 Full += " ";
00434         Full += LastName;
00435         return Full;
00436 }
00437 
00438 //
00439 // GetEmail
00440 //
00441 /// Helper function that always returns a valid string.  The string
00442 /// may be empty if there is no address at the specified index.
00443 ///
00444 const std::string& Contact::GetEmail(unsigned int index) const
00445 {
00446         static const std::string blank;
00447         if( index < EmailAddresses.size() )
00448                 return EmailAddresses[index];
00449         return blank;
00450 }
00451 
00452 void Contact::Dump(std::ostream &os) const
00453 {
00454         ios::fmtflags oldflags = os.setf(ios::left);
00455         char fill = os.fill(' ');
00456 
00457         os << "Contact: 0x" << setbase(16) << GetID()
00458                 << " (" << (unsigned int)RecType << ")\n";
00459 
00460         // special fields not in type table
00461         os << "    " << setw(20) << "FirstName";
00462         os << ": " << FirstName << "\n";
00463         os << "    " << setw(20) << "LastName";
00464         os << ": " << LastName << "\n";
00465 
00466         // cycle through email addresses
00467         EmailList::const_iterator eai = EmailAddresses.begin();
00468         for( ; eai != EmailAddresses.end(); ++eai ) {
00469                 if( eai->size() ) {
00470                         os << "    Email               : " << *eai << "\n";
00471                 }
00472         }
00473 
00474         // cycle through the type table
00475         for(    FieldLink<Contact> *b = ContactFieldLinks;
00476                 b->type != CFC_INVALID_FIELD;
00477                 b++ )
00478         {
00479                 const std::string *pField = 0;
00480                 if( b->strMember ) {
00481                         pField = &(this->*(b->strMember));
00482                 }
00483                 else if( b->postMember && b->postField ) {
00484                         pField = &((this->*(b->postMember)).*(b->postField));
00485                 }
00486 
00487                 // print only fields with data
00488                 if( pField && pField->size() ) {
00489                         os << "    " << setw(20) << b->name;
00490                         os << ": " << *pField << "\n";
00491                 }
00492         }
00493 
00494         if( Categories.size() ) {
00495                 string display;
00496                 CategoryList2Str(Categories, display);
00497                 os << "    Categories          : " << display << "\n";
00498         }
00499 
00500         // print Birthday and Anniversary
00501         if( Birthday.HasData() ) {
00502                 os << "    Birthday            : " << Birthday << "\n";
00503         }
00504         if( Anniversary.HasData() ) {
00505                 os << "    Anniversary         : " << Anniversary << "\n";
00506         }
00507 
00508         // print any group links
00509         GroupLinksType::const_iterator
00510                 gb = GroupLinks.begin(), ge = GroupLinks.end();
00511         if( gb != ge )
00512                 os << "    GroupLinks:\n";
00513         for( ; gb != ge; gb++ ) {
00514                 os << "        ID: 0x" << setbase(16) << gb->Link << "\n";
00515         }
00516 
00517         // and finally print unknowns
00518         os << Unknowns;
00519 
00520         // cleanup the stream
00521         os.flags(oldflags);
00522         os.fill(fill);
00523 }
00524 
00525 void Contact::SplitName(const std::string &full, std::string &first, std::string &last)
00526 {
00527         first.clear();
00528         last.clear();
00529 
00530         string::size_type pos = full.find_last_of(' ');
00531         if( pos != string::npos ) {
00532                 // has space, assume last word is last name
00533                 last = full.c_str() + pos + 1;
00534                 first = full.substr(0, pos);
00535         }
00536         else {
00537                 // no space, assume only first name
00538                 first = full.substr(0);
00539         }
00540 }
00541 
00542 void Contact::CategoryStr2List(const std::string &str,
00543                                Barry::CategoryList &list)
00544 {
00545         // start fresh
00546         list.clear();
00547 
00548         if( !str.size() )
00549                 return;
00550 
00551         // parse the comma-delimited string to a list, stripping away
00552         // any white space around each category name
00553         string::size_type start = 0, end = 0, delim = str.find(',', start);
00554         while( start != string::npos ) {
00555                 if( delim == string::npos )
00556                         end = str.size() - 1;
00557                 else
00558                         end = delim - 1;
00559 
00560                 // strip surrounding whitespace
00561                 while( str[start] == ' ' )
00562                         start++;
00563                 while( end && str[end] == ' ' )
00564                         end--;
00565 
00566                 if( start <= end ) {
00567                         string token = str.substr(start, end-start+1);
00568                         list.push_back(token);
00569                 }
00570 
00571                 // next
00572                 start = delim;
00573                 if( start != string::npos )
00574                         start++;
00575                 delim = str.find(',', start);
00576         }
00577 }
00578 
00579 void Contact::CategoryList2Str(const Barry::CategoryList &list,
00580                                std::string &str)
00581 {
00582         str.clear();
00583 
00584         Barry::CategoryList::const_iterator i = list.begin();
00585         for( ; i != list.end(); ++i ) {
00586                 if( str.size() )
00587                         str += ", ";
00588                 str += *i;
00589         }
00590 }
00591 
00592 } // namespace Barry
00593 

Generated on Mon Jan 12 10:51:13 2009 for Barry by  doxygen 1.5.7.1