author | mickeyl <mickeyl> | 2004-11-16 19:14:18 (UTC) |
---|---|---|
committer | mickeyl <mickeyl> | 2004-11-16 19:14:18 (UTC) |
commit | ea3945a9bd8f9830f70b1efa133f9df13b19362f (patch) (unidiff) | |
tree | f2ea22cc50e9aa8aa73ee7dea148f41c563c9666 /noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp | |
parent | 1c6f490e8541626f68422e0a3a7c7281d7f5b7d3 (diff) | |
download | opie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.zip opie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.tar.gz opie-ea3945a9bd8f9830f70b1efa133f9df13b19362f.tar.bz2 |
libopie1 goes into unsupported
Diffstat (limited to 'noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp b/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp new file mode 100644 index 0000000..7ceaf5b --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp | |||
@@ -0,0 +1,824 @@ | |||
1 | /* | ||
2 | * XML Backend for the OPIE-Contact Database. | ||
3 | * | ||
4 | * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) | ||
5 | * | ||
6 | * ===================================================================== | ||
7 | *This program is free software; you can redistribute it and/or | ||
8 | *modify it under the terms of the GNU Library General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2 of the License, or (at your option) any later version. | ||
11 | * ===================================================================== | ||
12 | * | ||
13 | * ===================================================================== | ||
14 | * Version: $Id$ | ||
15 | * ===================================================================== | ||
16 | * History: | ||
17 | * $Log$ | ||
18 | * Revision 1.1 2004/11/16 21:46:07 mickeyl | ||
19 | * libopie1 goes into unsupported | ||
20 | * | ||
21 | * Revision 1.10 2004/03/01 15:44:36 chicken | ||
22 | * fix includes | ||
23 | * | ||
24 | * Revision 1.9 2003/09/22 14:31:16 eilers | ||
25 | * Added first experimental incarnation of sql-backend for addressbook. | ||
26 | * Some modifications to be able to compile the todo sql-backend. | ||
27 | * A lot of changes fill follow... | ||
28 | * | ||
29 | * Revision 1.8 2003/08/30 15:28:26 eilers | ||
30 | * Removed some unimportant debug output which causes slow down.. | ||
31 | * | ||
32 | * Revision 1.7 2003/08/01 12:30:16 eilers | ||
33 | * Merging changes from BRANCH_1_0 to HEAD | ||
34 | * | ||
35 | * Revision 1.6 2003/07/07 16:19:47 eilers | ||
36 | * Fixing serious bug in hasQuerySettings() | ||
37 | * | ||
38 | * Revision 1.5 2003/04/13 18:07:10 zecke | ||
39 | * More API doc | ||
40 | * QString -> const QString& | ||
41 | * QString = 0l -> QString::null | ||
42 | * | ||
43 | * Revision 1.4 2003/03/21 14:32:54 mickeyl | ||
44 | * g++ compliance fix: default arguments belong into the declaration, but not the definition | ||
45 | * | ||
46 | * Revision 1.3 2003/03/21 12:26:28 eilers | ||
47 | * Fixing small bug: If we search a birthday from today to today, it returned | ||
48 | * every contact .. | ||
49 | * | ||
50 | * Revision 1.2 2003/03/21 10:33:09 eilers | ||
51 | * Merged speed optimized xml backend for contacts to main. | ||
52 | * Added QDateTime to querybyexample. For instance, it is now possible to get | ||
53 | * all Birthdays/Anniversaries between two dates. This should be used | ||
54 | * to show all birthdays in the datebook.. | ||
55 | * This change is sourcecode backward compatible but you have to upgrade | ||
56 | * the binaries for today-addressbook. | ||
57 | * | ||
58 | * Revision 1.1.2.2 2003/02/11 12:17:28 eilers | ||
59 | * Speed optimization. Removed the sequential search loops. | ||
60 | * | ||
61 | * Revision 1.1.2.1 2003/02/10 15:31:38 eilers | ||
62 | * Writing offsets to debug output.. | ||
63 | * | ||
64 | * Revision 1.1 2003/02/09 15:05:01 eilers | ||
65 | * Nothing happened.. Just some cleanup before I will start.. | ||
66 | * | ||
67 | * Revision 1.12 2003/01/03 16:58:03 eilers | ||
68 | * Reenable debug output | ||
69 | * | ||
70 | * Revision 1.11 2003/01/03 12:31:28 eilers | ||
71 | * Bugfix for calculating data diffs.. | ||
72 | * | ||
73 | * Revision 1.10 2003/01/02 14:27:12 eilers | ||
74 | * Improved query by example: Search by date is possible.. First step | ||
75 | * for a today plugin for birthdays.. | ||
76 | * | ||
77 | * Revision 1.9 2002/12/08 12:48:57 eilers | ||
78 | * Moved journal-enum from ocontact into i the xml-backend.. | ||
79 | * | ||
80 | * Revision 1.8 2002/11/14 17:04:24 eilers | ||
81 | * Sorting will now work if fullname is identical on some entries | ||
82 | * | ||
83 | * Revision 1.7 2002/11/13 15:02:46 eilers | ||
84 | * Small Bug in sorted fixed | ||
85 | * | ||
86 | * Revision 1.6 2002/11/13 14:14:51 eilers | ||
87 | * Added sorted for Contacts.. | ||
88 | * | ||
89 | * Revision 1.5 2002/11/01 15:10:42 eilers | ||
90 | * Added regExp-search in database for all fields in a contact. | ||
91 | * | ||
92 | * Revision 1.4 2002/10/16 10:52:40 eilers | ||
93 | * Added some docu to the interface and now using the cache infrastucture by zecke.. :) | ||
94 | * | ||
95 | * Revision 1.3 2002/10/14 16:21:54 eilers | ||
96 | * Some minor interface updates | ||
97 | * | ||
98 | * Revision 1.2 2002/10/07 17:34:24 eilers | ||
99 | * added OBackendFactory for advanced backend access | ||
100 | * | ||
101 | * Revision 1.1 2002/09/27 17:11:44 eilers | ||
102 | * Added API for accessing the Contact-Database ! It is compiling, but | ||
103 | * please do not expect that anything is working ! | ||
104 | * I will debug that stuff in the next time .. | ||
105 | * Please read README_COMPILE for compiling ! | ||
106 | * | ||
107 | * | ||
108 | */ | ||
109 | |||
110 | #include "ocontactaccessbackend_xml.h" | ||
111 | |||
112 | #include <qasciidict.h> | ||
113 | #include <qfile.h> | ||
114 | #include <qfileinfo.h> | ||
115 | #include <qregexp.h> | ||
116 | #include <qarray.h> | ||
117 | #include <qmap.h> | ||
118 | |||
119 | #include <qpe/global.h> | ||
120 | |||
121 | #include <opie/xmltree.h> | ||
122 | #include "ocontactaccessbackend.h" | ||
123 | #include "ocontactaccess.h" | ||
124 | |||
125 | #include <stdlib.h> | ||
126 | #include <errno.h> | ||
127 | |||
128 | using namespace Opie; | ||
129 | |||
130 | |||
131 | OContactAccessBackend_XML::OContactAccessBackend_XML ( const QString& appname, const QString& filename ): | ||
132 | m_changed( false ) | ||
133 | { | ||
134 | // Just m_contactlist should call delete if an entry | ||
135 | // is removed. | ||
136 | m_contactList.setAutoDelete( true ); | ||
137 | m_uidToContact.setAutoDelete( false ); | ||
138 | |||
139 | m_appName = appname; | ||
140 | |||
141 | /* Set journalfile name ... */ | ||
142 | m_journalName = getenv("HOME"); | ||
143 | m_journalName +="/.abjournal" + appname; | ||
144 | |||
145 | /* Expecting to access the default filename if nothing else is set */ | ||
146 | if ( filename.isEmpty() ){ | ||
147 | m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" ); | ||
148 | } else | ||
149 | m_fileName = filename; | ||
150 | |||
151 | /* Load Database now */ | ||
152 | load (); | ||
153 | } | ||
154 | |||
155 | bool OContactAccessBackend_XML::save() | ||
156 | { | ||
157 | |||
158 | if ( !m_changed ) | ||
159 | return true; | ||
160 | |||
161 | QString strNewFile = m_fileName + ".new"; | ||
162 | QFile f( strNewFile ); | ||
163 | if ( !f.open( IO_WriteOnly|IO_Raw ) ) | ||
164 | return false; | ||
165 | |||
166 | int total_written; | ||
167 | int idx_offset = 0; | ||
168 | QString out; | ||
169 | |||
170 | // Write Header | ||
171 | out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n" | ||
172 | " <Groups>\n" | ||
173 | " </Groups>\n" | ||
174 | " <Contacts>\n"; | ||
175 | QCString cstr = out.utf8(); | ||
176 | f.writeBlock( cstr.data(), cstr.length() ); | ||
177 | idx_offset += cstr.length(); | ||
178 | out = ""; | ||
179 | |||
180 | // Write all contacts | ||
181 | QListIterator<OContact> it( m_contactList ); | ||
182 | for ( ; it.current(); ++it ) { | ||
183 | // qWarning(" Uid %d at Offset: %x", (*it)->uid(), idx_offset ); | ||
184 | out += "<Contact "; | ||
185 | (*it)->save( out ); | ||
186 | out += "/>\n"; | ||
187 | cstr = out.utf8(); | ||
188 | total_written = f.writeBlock( cstr.data(), cstr.length() ); | ||
189 | idx_offset += cstr.length(); | ||
190 | if ( total_written != int(cstr.length()) ) { | ||
191 | f.close(); | ||
192 | QFile::remove( strNewFile ); | ||
193 | return false; | ||
194 | } | ||
195 | out = ""; | ||
196 | } | ||
197 | out += " </Contacts>\n</AddressBook>\n"; | ||
198 | |||
199 | // Write Footer | ||
200 | cstr = out.utf8(); | ||
201 | total_written = f.writeBlock( cstr.data(), cstr.length() ); | ||
202 | if ( total_written != int( cstr.length() ) ) { | ||
203 | f.close(); | ||
204 | QFile::remove( strNewFile ); | ||
205 | return false; | ||
206 | } | ||
207 | f.close(); | ||
208 | |||
209 | // move the file over, I'm just going to use the system call | ||
210 | // because, I don't feel like using QDir. | ||
211 | if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) { | ||
212 | qWarning( "problem renaming file %s to %s, errno: %d", | ||
213 | strNewFile.latin1(), m_journalName.latin1(), errno ); | ||
214 | // remove the tmp file... | ||
215 | QFile::remove( strNewFile ); | ||
216 | } | ||
217 | |||
218 | /* The journalfile should be removed now... */ | ||
219 | removeJournal(); | ||
220 | |||
221 | m_changed = false; | ||
222 | return true; | ||
223 | } | ||
224 | |||
225 | bool OContactAccessBackend_XML::load () | ||
226 | { | ||
227 | m_contactList.clear(); | ||
228 | m_uidToContact.clear(); | ||
229 | |||
230 | /* Load XML-File and journal if it exists */ | ||
231 | if ( !load ( m_fileName, false ) ) | ||
232 | return false; | ||
233 | /* The returncode of the journalfile is ignored due to the | ||
234 | * fact that it does not exist when this class is instantiated ! | ||
235 | * But there may such a file exist, if the application crashed. | ||
236 | * Therefore we try to load it to get the changes before the # | ||
237 | * crash happened... | ||
238 | */ | ||
239 | load (m_journalName, true); | ||
240 | |||
241 | return true; | ||
242 | } | ||
243 | |||
244 | void OContactAccessBackend_XML::clear () | ||
245 | { | ||
246 | m_contactList.clear(); | ||
247 | m_uidToContact.clear(); | ||
248 | |||
249 | m_changed = false; | ||
250 | } | ||
251 | |||
252 | bool OContactAccessBackend_XML::wasChangedExternally() | ||
253 | { | ||
254 | QFileInfo fi( m_fileName ); | ||
255 | |||
256 | QDateTime lastmod = fi.lastModified (); | ||
257 | |||
258 | return (lastmod != m_readtime); | ||
259 | } | ||
260 | |||
261 | QArray<int> OContactAccessBackend_XML::allRecords() const | ||
262 | { | ||
263 | QArray<int> uid_list( m_contactList.count() ); | ||
264 | |||
265 | uint counter = 0; | ||
266 | QListIterator<OContact> it( m_contactList ); | ||
267 | for( ; it.current(); ++it ){ | ||
268 | uid_list[counter++] = (*it)->uid(); | ||
269 | } | ||
270 | |||
271 | return ( uid_list ); | ||
272 | } | ||
273 | |||
274 | OContact OContactAccessBackend_XML::find ( int uid ) const | ||
275 | { | ||
276 | OContact foundContact; //Create empty contact | ||
277 | |||
278 | OContact* found = m_uidToContact.find( QString().setNum( uid ) ); | ||
279 | |||
280 | if ( found ){ | ||
281 | foundContact = *found; | ||
282 | } | ||
283 | |||
284 | return ( foundContact ); | ||
285 | } | ||
286 | |||
287 | QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings, | ||
288 | const QDateTime& d ) | ||
289 | { | ||
290 | |||
291 | QArray<int> m_currentQuery( m_contactList.count() ); | ||
292 | QListIterator<OContact> it( m_contactList ); | ||
293 | uint arraycounter = 0; | ||
294 | |||
295 | for( ; it.current(); ++it ){ | ||
296 | /* Search all fields and compare them with query object. Store them into list | ||
297 | * if all fields matches. | ||
298 | */ | ||
299 | QDate* queryDate = 0l; | ||
300 | QDate* checkDate = 0l; | ||
301 | bool allcorrect = true; | ||
302 | for ( int i = 0; i < Qtopia::Groups; i++ ) { | ||
303 | // Birthday and anniversary are special nonstring fields and should | ||
304 | // be handled specially | ||
305 | switch ( i ){ | ||
306 | case Qtopia::Birthday: | ||
307 | queryDate = new QDate( query.birthday() ); | ||
308 | checkDate = new QDate( (*it)->birthday() ); | ||
309 | case Qtopia::Anniversary: | ||
310 | if ( queryDate == 0l ){ | ||
311 | queryDate = new QDate( query.anniversary() ); | ||
312 | checkDate = new QDate( (*it)->anniversary() ); | ||
313 | } | ||
314 | |||
315 | if ( queryDate->isValid() ){ | ||
316 | if( checkDate->isValid() ){ | ||
317 | if ( settings & OContactAccess::DateYear ){ | ||
318 | if ( queryDate->year() != checkDate->year() ) | ||
319 | allcorrect = false; | ||
320 | } | ||
321 | if ( settings & OContactAccess::DateMonth ){ | ||
322 | if ( queryDate->month() != checkDate->month() ) | ||
323 | allcorrect = false; | ||
324 | } | ||
325 | if ( settings & OContactAccess::DateDay ){ | ||
326 | if ( queryDate->day() != checkDate->day() ) | ||
327 | allcorrect = false; | ||
328 | } | ||
329 | if ( settings & OContactAccess::DateDiff ) { | ||
330 | QDate current; | ||
331 | // If we get an additional date, we | ||
332 | // will take this date instead of | ||
333 | // the current one.. | ||
334 | if ( !d.date().isValid() ) | ||
335 | current = QDate::currentDate(); | ||
336 | else | ||
337 | current = d.date(); | ||
338 | |||
339 | // We have to equalize the year, otherwise | ||
340 | // the search will fail.. | ||
341 | checkDate->setYMD( current.year(), | ||
342 | checkDate->month(), | ||
343 | checkDate->day() ); | ||
344 | if ( *checkDate < current ) | ||
345 | checkDate->setYMD( current.year()+1, | ||
346 | checkDate->month(), | ||
347 | checkDate->day() ); | ||
348 | |||
349 | // Check whether the birthday/anniversary date is between | ||
350 | // the current/given date and the maximum date | ||
351 | // ( maximum time range ) ! | ||
352 | qWarning("Checking if %s is between %s and %s ! ", | ||
353 | checkDate->toString().latin1(), | ||
354 | current.toString().latin1(), | ||
355 | queryDate->toString().latin1() ); | ||
356 | if ( current.daysTo( *queryDate ) >= 0 ){ | ||
357 | if ( !( ( *checkDate >= current ) && | ||
358 | ( *checkDate <= *queryDate ) ) ){ | ||
359 | allcorrect = false; | ||
360 | qWarning (" Nope!.."); | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | } else{ | ||
365 | // checkDate is invalid. Therefore this entry is always rejected | ||
366 | allcorrect = false; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | delete queryDate; | ||
371 | queryDate = 0l; | ||
372 | delete checkDate; | ||
373 | checkDate = 0l; | ||
374 | break; | ||
375 | default: | ||
376 | /* Just compare fields which are not empty in the query object */ | ||
377 | if ( !query.field(i).isEmpty() ){ | ||
378 | switch ( settings & ~( OContactAccess::IgnoreCase | ||
379 | | OContactAccess::DateDiff | ||
380 | | OContactAccess::DateYear | ||
381 | | OContactAccess::DateMonth | ||
382 | | OContactAccess::DateDay | ||
383 | | OContactAccess::MatchOne | ||
384 | ) ){ | ||
385 | |||
386 | case OContactAccess::RegExp:{ | ||
387 | QRegExp expr ( query.field(i), | ||
388 | !(settings & OContactAccess::IgnoreCase), | ||
389 | false ); | ||
390 | if ( expr.find ( (*it)->field(i), 0 ) == -1 ) | ||
391 | allcorrect = false; | ||
392 | } | ||
393 | break; | ||
394 | case OContactAccess::WildCards:{ | ||
395 | QRegExp expr ( query.field(i), | ||
396 | !(settings & OContactAccess::IgnoreCase), | ||
397 | true ); | ||
398 | if ( expr.find ( (*it)->field(i), 0 ) == -1 ) | ||
399 | allcorrect = false; | ||
400 | } | ||
401 | break; | ||
402 | case OContactAccess::ExactMatch:{ | ||
403 | if (settings & OContactAccess::IgnoreCase){ | ||
404 | if ( query.field(i).upper() != | ||
405 | (*it)->field(i).upper() ) | ||
406 | allcorrect = false; | ||
407 | }else{ | ||
408 | if ( query.field(i) != (*it)->field(i) ) | ||
409 | allcorrect = false; | ||
410 | } | ||
411 | } | ||
412 | break; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | if ( allcorrect ){ | ||
418 | m_currentQuery[arraycounter++] = (*it)->uid(); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Shrink to fit.. | ||
423 | m_currentQuery.resize(arraycounter); | ||
424 | |||
425 | return m_currentQuery; | ||
426 | } | ||
427 | |||
428 | QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const | ||
429 | { | ||
430 | QArray<int> m_currentQuery( m_contactList.count() ); | ||
431 | QListIterator<OContact> it( m_contactList ); | ||
432 | uint arraycounter = 0; | ||
433 | |||
434 | for( ; it.current(); ++it ){ | ||
435 | if ( (*it)->match( r ) ){ | ||
436 | m_currentQuery[arraycounter++] = (*it)->uid(); | ||
437 | } | ||
438 | |||
439 | } | ||
440 | // Shrink to fit.. | ||
441 | m_currentQuery.resize(arraycounter); | ||
442 | |||
443 | return m_currentQuery; | ||
444 | } | ||
445 | |||
446 | const uint OContactAccessBackend_XML::querySettings() | ||
447 | { | ||
448 | return ( OContactAccess::WildCards | ||
449 | | OContactAccess::IgnoreCase | ||
450 | | OContactAccess::RegExp | ||
451 | | OContactAccess::ExactMatch | ||
452 | | OContactAccess::DateDiff | ||
453 | | OContactAccess::DateYear | ||
454 | | OContactAccess::DateMonth | ||
455 | | OContactAccess::DateDay | ||
456 | ); | ||
457 | } | ||
458 | |||
459 | bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const | ||
460 | { | ||
461 | /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay | ||
462 | * may be added with any of the other settings. IgnoreCase should never used alone. | ||
463 | * Wildcards, RegExp, ExactMatch should never used at the same time... | ||
464 | */ | ||
465 | |||
466 | // Step 1: Check whether the given settings are supported by this backend | ||
467 | if ( ( querySettings & ( | ||
468 | OContactAccess::IgnoreCase | ||
469 | | OContactAccess::WildCards | ||
470 | | OContactAccess::DateDiff | ||
471 | | OContactAccess::DateYear | ||
472 | | OContactAccess::DateMonth | ||
473 | | OContactAccess::DateDay | ||
474 | | OContactAccess::RegExp | ||
475 | | OContactAccess::ExactMatch | ||
476 | ) ) != querySettings ) | ||
477 | return false; | ||
478 | |||
479 | // Step 2: Check whether the given combinations are ok.. | ||
480 | |||
481 | // IngoreCase alone is invalid | ||
482 | if ( querySettings == OContactAccess::IgnoreCase ) | ||
483 | return false; | ||
484 | |||
485 | // WildCards, RegExp and ExactMatch should never used at the same time | ||
486 | switch ( querySettings & ~( OContactAccess::IgnoreCase | ||
487 | | OContactAccess::DateDiff | ||
488 | | OContactAccess::DateYear | ||
489 | | OContactAccess::DateMonth | ||
490 | | OContactAccess::DateDay | ||
491 | ) | ||
492 | ){ | ||
493 | case OContactAccess::RegExp: | ||
494 | return ( true ); | ||
495 | case OContactAccess::WildCards: | ||
496 | return ( true ); | ||
497 | case OContactAccess::ExactMatch: | ||
498 | return ( true ); | ||
499 | case 0: // one of the upper removed bits were set.. | ||
500 | return ( true ); | ||
501 | default: | ||
502 | return ( false ); | ||
503 | } | ||
504 | } | ||
505 | |||
506 | // Currently only asc implemented.. | ||
507 | QArray<int> OContactAccessBackend_XML::sorted( bool asc, int , int , int ) | ||
508 | { | ||
509 | QMap<QString, int> nameToUid; | ||
510 | QStringList names; | ||
511 | QArray<int> m_currentQuery( m_contactList.count() ); | ||
512 | |||
513 | // First fill map and StringList with all Names | ||
514 | // Afterwards sort namelist and use map to fill array to return.. | ||
515 | QListIterator<OContact> it( m_contactList ); | ||
516 | for( ; it.current(); ++it ){ | ||
517 | names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) ); | ||
518 | nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() ); | ||
519 | } | ||
520 | names.sort(); | ||
521 | |||
522 | int i = 0; | ||
523 | if ( asc ){ | ||
524 | for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it ) | ||
525 | m_currentQuery[i++] = nameToUid[ (*it) ]; | ||
526 | }else{ | ||
527 | for ( QStringList::Iterator it = names.end(); it != names.begin(); --it ) | ||
528 | m_currentQuery[i++] = nameToUid[ (*it) ]; | ||
529 | } | ||
530 | |||
531 | return m_currentQuery; | ||
532 | |||
533 | } | ||
534 | |||
535 | bool OContactAccessBackend_XML::add ( const OContact &newcontact ) | ||
536 | { | ||
537 | //qWarning("odefaultbackend: ACTION::ADD"); | ||
538 | updateJournal (newcontact, ACTION_ADD); | ||
539 | addContact_p( newcontact ); | ||
540 | |||
541 | m_changed = true; | ||
542 | |||
543 | return true; | ||
544 | } | ||
545 | |||
546 | bool OContactAccessBackend_XML::replace ( const OContact &contact ) | ||
547 | { | ||
548 | m_changed = true; | ||
549 | |||
550 | OContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) ); | ||
551 | |||
552 | if ( found ) { | ||
553 | OContact* newCont = new OContact( contact ); | ||
554 | |||
555 | updateJournal ( *newCont, ACTION_REPLACE); | ||
556 | m_contactList.removeRef ( found ); | ||
557 | m_contactList.append ( newCont ); | ||
558 | m_uidToContact.remove( QString().setNum( contact.uid() ) ); | ||
559 | m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont ); | ||
560 | |||
561 | qWarning("Nur zur Sicherheit: %d == %d ?",contact.uid(), newCont->uid()); | ||
562 | |||
563 | return true; | ||
564 | } else | ||
565 | return false; | ||
566 | } | ||
567 | |||
568 | bool OContactAccessBackend_XML::remove ( int uid ) | ||
569 | { | ||
570 | m_changed = true; | ||
571 | |||
572 | OContact* found = m_uidToContact.find ( QString().setNum( uid ) ); | ||
573 | |||
574 | if ( found ) { | ||
575 | updateJournal ( *found, ACTION_REMOVE); | ||
576 | m_contactList.removeRef ( found ); | ||
577 | m_uidToContact.remove( QString().setNum( uid ) ); | ||
578 | |||
579 | return true; | ||
580 | } else | ||
581 | return false; | ||
582 | } | ||
583 | |||
584 | bool OContactAccessBackend_XML::reload(){ | ||
585 | /* Reload is the same as load in this implementation */ | ||
586 | return ( load() ); | ||
587 | } | ||
588 | |||
589 | void OContactAccessBackend_XML::addContact_p( const OContact &newcontact ) | ||
590 | { | ||
591 | OContact* contRef = new OContact( newcontact ); | ||
592 | |||
593 | m_contactList.append ( contRef ); | ||
594 | m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef ); | ||
595 | } | ||
596 | |||
597 | /* This function loads the xml-database and the journalfile */ | ||
598 | bool OContactAccessBackend_XML::load( const QString filename, bool isJournal ) | ||
599 | { | ||
600 | |||
601 | /* We use the time of the last read to check if the file was | ||
602 | * changed externally. | ||
603 | */ | ||
604 | if ( !isJournal ){ | ||
605 | QFileInfo fi( filename ); | ||
606 | m_readtime = fi.lastModified (); | ||
607 | } | ||
608 | |||
609 | const int JOURNALACTION = Qtopia::Notes + 1; | ||
610 | const int JOURNALROW = JOURNALACTION + 1; | ||
611 | |||
612 | bool foundAction = false; | ||
613 | journal_action action = ACTION_ADD; | ||
614 | int journalKey = 0; | ||
615 | QMap<int, QString> contactMap; | ||
616 | QMap<QString, QString> customMap; | ||
617 | QMap<QString, QString>::Iterator customIt; | ||
618 | QAsciiDict<int> dict( 47 ); | ||
619 | |||
620 | dict.setAutoDelete( TRUE ); | ||
621 | dict.insert( "Uid", new int(Qtopia::AddressUid) ); | ||
622 | dict.insert( "Title", new int(Qtopia::Title) ); | ||
623 | dict.insert( "FirstName", new int(Qtopia::FirstName) ); | ||
624 | dict.insert( "MiddleName", new int(Qtopia::MiddleName) ); | ||
625 | dict.insert( "LastName", new int(Qtopia::LastName) ); | ||
626 | dict.insert( "Suffix", new int(Qtopia::Suffix) ); | ||
627 | dict.insert( "FileAs", new int(Qtopia::FileAs) ); | ||
628 | dict.insert( "Categories", new int(Qtopia::AddressCategory) ); | ||
629 | dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) ); | ||
630 | dict.insert( "Emails", new int(Qtopia::Emails) ); | ||
631 | dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) ); | ||
632 | dict.insert( "HomeCity", new int(Qtopia::HomeCity) ); | ||
633 | dict.insert( "HomeState", new int(Qtopia::HomeState) ); | ||
634 | dict.insert( "HomeZip", new int(Qtopia::HomeZip) ); | ||
635 | dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) ); | ||
636 | dict.insert( "HomePhone", new int(Qtopia::HomePhone) ); | ||
637 | dict.insert( "HomeFax", new int(Qtopia::HomeFax) ); | ||
638 | dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) ); | ||
639 | dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) ); | ||
640 | dict.insert( "Company", new int(Qtopia::Company) ); | ||
641 | dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) ); | ||
642 | dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) ); | ||
643 | dict.insert( "BusinessState", new int(Qtopia::BusinessState) ); | ||
644 | dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) ); | ||
645 | dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) ); | ||
646 | dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) ); | ||
647 | dict.insert( "JobTitle", new int(Qtopia::JobTitle) ); | ||
648 | dict.insert( "Department", new int(Qtopia::Department) ); | ||
649 | dict.insert( "Office", new int(Qtopia::Office) ); | ||
650 | dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) ); | ||
651 | dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) ); | ||
652 | dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) ); | ||
653 | dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) ); | ||
654 | dict.insert( "Profession", new int(Qtopia::Profession) ); | ||
655 | dict.insert( "Assistant", new int(Qtopia::Assistant) ); | ||
656 | dict.insert( "Manager", new int(Qtopia::Manager) ); | ||
657 | dict.insert( "Spouse", new int(Qtopia::Spouse) ); | ||
658 | dict.insert( "Children", new int(Qtopia::Children) ); | ||
659 | dict.insert( "Gender", new int(Qtopia::Gender) ); | ||
660 | dict.insert( "Birthday", new int(Qtopia::Birthday) ); | ||
661 | dict.insert( "Anniversary", new int(Qtopia::Anniversary) ); | ||
662 | dict.insert( "Nickname", new int(Qtopia::Nickname) ); | ||
663 | dict.insert( "Notes", new int(Qtopia::Notes) ); | ||
664 | dict.insert( "action", new int(JOURNALACTION) ); | ||
665 | dict.insert( "actionrow", new int(JOURNALROW) ); | ||
666 | |||
667 | //qWarning( "OContactDefaultBackEnd::loading %s", filename.latin1() ); | ||
668 | |||
669 | XMLElement *root = XMLElement::load( filename ); | ||
670 | if(root != 0l ){ // start parsing | ||
671 | /* Parse all XML-Elements and put the data into the | ||
672 | * Contact-Class | ||
673 | */ | ||
674 | XMLElement *element = root->firstChild(); | ||
675 | //qWarning("OContactAccess::load tagName(): %s", root->tagName().latin1() ); | ||
676 | element = element->firstChild(); | ||
677 | |||
678 | /* Search Tag "Contacts" which is the parent of all Contacts */ | ||
679 | while( element && !isJournal ){ | ||
680 | if( element->tagName() != QString::fromLatin1("Contacts") ){ | ||
681 | //qWarning ("OContactDefBack::Searching for Tag \"Contacts\"! Found: %s", | ||
682 | // element->tagName().latin1()); | ||
683 | element = element->nextChild(); | ||
684 | } else { | ||
685 | element = element->firstChild(); | ||
686 | break; | ||
687 | } | ||
688 | } | ||
689 | /* Parse all Contacts and ignore unknown tags */ | ||
690 | while( element ){ | ||
691 | if( element->tagName() != QString::fromLatin1("Contact") ){ | ||
692 | //qWarning ("OContactDefBack::Searching for Tag \"Contact\"! Found: %s", | ||
693 | // element->tagName().latin1()); | ||
694 | element = element->nextChild(); | ||
695 | continue; | ||
696 | } | ||
697 | /* Found alement with tagname "contact", now parse and store all | ||
698 | * attributes contained | ||
699 | */ | ||
700 | //qWarning("OContactDefBack::load element tagName() : %s", | ||
701 | // element->tagName().latin1() ); | ||
702 | QString dummy; | ||
703 | foundAction = false; | ||
704 | |||
705 | XMLElement::AttributeMap aMap = element->attributes(); | ||
706 | XMLElement::AttributeMap::Iterator it; | ||
707 | contactMap.clear(); | ||
708 | customMap.clear(); | ||
709 | for( it = aMap.begin(); it != aMap.end(); ++it ){ | ||
710 | // qWarning ("Read Attribute: %s=%s", it.key().latin1(),it.data().latin1()); | ||
711 | |||
712 | int *find = dict[ it.key() ]; | ||
713 | /* Unknown attributes will be stored as "Custom" elements */ | ||
714 | if ( !find ) { | ||
715 | // qWarning("Attribute %s not known.", it.key().latin1()); | ||
716 | //contact.setCustomField(it.key(), it.data()); | ||
717 | customMap.insert( it.key(), it.data() ); | ||
718 | continue; | ||
719 | } | ||
720 | |||
721 | /* Check if special conversion is needed and add attribute | ||
722 | * into Contact class | ||
723 | */ | ||
724 | switch( *find ) { | ||
725 | /* | ||
726 | case Qtopia::AddressUid: | ||
727 | contact.setUid( it.data().toInt() ); | ||
728 | break; | ||
729 | case Qtopia::AddressCategory: | ||
730 | contact.setCategories( Qtopia::Record::idsFromString( it.data( ))); | ||
731 | break; | ||
732 | */ | ||
733 | case JOURNALACTION: | ||
734 | action = journal_action(it.data().toInt()); | ||
735 | foundAction = true; | ||
736 | qWarning ("ODefBack(journal)::ACTION found: %d", action); | ||
737 | break; | ||
738 | case JOURNALROW: | ||
739 | journalKey = it.data().toInt(); | ||
740 | break; | ||
741 | default: // no conversion needed add them to the map | ||
742 | contactMap.insert( *find, it.data() ); | ||
743 | break; | ||
744 | } | ||
745 | } | ||
746 | /* now generate the Contact contact */ | ||
747 | OContact contact( contactMap ); | ||
748 | |||
749 | for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) { | ||
750 | contact.setCustomField( customIt.key(), customIt.data() ); | ||
751 | } | ||
752 | |||
753 | if (foundAction){ | ||
754 | foundAction = false; | ||
755 | switch ( action ) { | ||
756 | case ACTION_ADD: | ||
757 | addContact_p (contact); | ||
758 | break; | ||
759 | case ACTION_REMOVE: | ||
760 | if ( !remove (contact.uid()) ) | ||
761 | qWarning ("ODefBack(journal)::Unable to remove uid: %d", | ||
762 | contact.uid() ); | ||
763 | break; | ||
764 | case ACTION_REPLACE: | ||
765 | if ( !replace ( contact ) ) | ||
766 | qWarning ("ODefBack(journal)::Unable to replace uid: %d", | ||
767 | contact.uid() ); | ||
768 | break; | ||
769 | default: | ||
770 | qWarning ("Unknown action: ignored !"); | ||
771 | break; | ||
772 | } | ||
773 | }else{ | ||
774 | /* Add contact to list */ | ||
775 | addContact_p (contact); | ||
776 | } | ||
777 | |||
778 | /* Move to next element */ | ||
779 | element = element->nextChild(); | ||
780 | } | ||
781 | }else { | ||
782 | qWarning("ODefBack::could not load"); | ||
783 | } | ||
784 | delete root; | ||
785 | qWarning("returning from loading" ); | ||
786 | return true; | ||
787 | } | ||
788 | |||
789 | |||
790 | void OContactAccessBackend_XML::updateJournal( const OContact& cnt, | ||
791 | journal_action action ) | ||
792 | { | ||
793 | QFile f( m_journalName ); | ||
794 | bool created = !f.exists(); | ||
795 | if ( !f.open(IO_WriteOnly|IO_Append) ) | ||
796 | return; | ||
797 | |||
798 | QString buf; | ||
799 | QCString str; | ||
800 | |||
801 | // if the file was created, we have to set the Tag "<CONTACTS>" to | ||
802 | // get a XML-File which is readable by our parser. | ||
803 | // This is just a cheat, but better than rewrite the parser. | ||
804 | if ( created ){ | ||
805 | buf = "<Contacts>"; | ||
806 | QCString cstr = buf.utf8(); | ||
807 | f.writeBlock( cstr.data(), cstr.length() ); | ||
808 | } | ||
809 | |||
810 | buf = "<Contact "; | ||
811 | cnt.save( buf ); | ||
812 | buf += " action=\"" + QString::number( (int)action ) + "\" "; | ||
813 | buf += "/>\n"; | ||
814 | QCString cstr = buf.utf8(); | ||
815 | f.writeBlock( cstr.data(), cstr.length() ); | ||
816 | } | ||
817 | |||
818 | void OContactAccessBackend_XML::removeJournal() | ||
819 | { | ||
820 | QFile f ( m_journalName ); | ||
821 | if ( f.exists() ) | ||
822 | f.remove(); | ||
823 | } | ||
824 | |||