source: main/tags/2.80/gsdl/src/recpt/infodbclass.cpp@ 24528

Last change on this file since 24528 was 13116, checked in by kjdon, 18 years ago

added .rt to the list of OID modifiers - this is the root id of the document

  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/**********************************************************************
2 *
3 * infodbclass.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "infodbclass.h"
27#include "unitool.h"
28#include "gsdlunicode.h"
29#include "fileutil.h"
30#include "OIDtools.h"
31#include <stdlib.h>
32
33
34
35
36// constructors
37infodbclass::infodbclass () {
38}
39
40void infodbclass::setinfo (const text_t &key, const text_t &value) {
41 text_tarray &tarr = info[key];
42 tarr.erase(tarr.begin(), tarr.end());
43 tarr.push_back(value);
44}
45
46void infodbclass::setintinfo (const text_t &key, int value) {
47 setinfo (key, value);
48}
49
50void infodbclass::setcinfo (const text_t &key, unsigned short c) {
51 text_t t;
52 t.push_back (c);
53 setinfo (key, t);
54}
55
56text_t *infodbclass::getinfo (const text_t &key) {
57 iterator here = info.find (key);
58 if (here == info.end()) return NULL;
59
60 if ((*here).second.empty()) return NULL;
61
62 return &((*here).second[0]);
63}
64
65text_t *infodbclass::getinfo (const text_t &key, const text_t& slang) {
66 text_tarray *t = getmultinfo(key, slang);
67 if (t == NULL || t->empty()) return NULL;
68 return &((*t)[0]);
69}
70
71int infodbclass::getintinfo (const text_t &key) {
72 text_t *t = getinfo (key);
73 if (t == NULL) return 0;
74 return t->getint();
75}
76
77int infodbclass::getintinfo (const text_t &key, const text_t& slang) {
78 text_tarray *t = getmultinfo(key, slang);
79 if (t == NULL || t->empty()) return 0;
80 return (*t)[0].getint();
81}
82
83text_t &infodbclass::operator[] (const text_t &key) {
84 text_tarray &tarr = info[key];
85 if (tarr.empty()) {
86 text_t e;
87 tarr.push_back(e);
88 }
89 return tarr[0];
90}
91
92
93void infodbclass::addinfo (const text_t &key, const text_t &value) {
94 text_tarray &tarr = info[key];
95 tarr.push_back (value);
96}
97
98void infodbclass::addintinfo (const text_t &key, int value) {
99 addinfo (key, value);
100}
101
102void infodbclass::addcinfo (const text_t &key, unsigned short c) {
103 text_t t;
104 t.push_back(c);
105 addinfo (key, t);
106}
107
108text_tarray *infodbclass::getmultinfo (const text_t &key) {
109 iterator here = info.find (key);
110 if (here == info.end()) return NULL;
111
112 return &((*here).second);
113}
114
115text_tarray *infodbclass::getmultinfo (const text_t &key, const text_t& slang) {
116 iterator here = info.end();
117 if (slang.empty()) {
118 here = info.find (key);
119 }
120 else {
121 here = info.find (key+":"+slang);
122 if (here == info.end()) {
123 here = info.find (key);
124 }
125 }
126 if (here == info.end()) return NULL;
127
128 return &((*here).second);
129}
130
131
132gdbmclass::~gdbmclass() {
133 closedatabase();
134}
135
136// returns true if opened
137bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys,
138#ifdef __WIN32__
139 bool need_filelock
140#else
141 bool
142#endif
143 ) {
144 text_t data_location;
145 int block_size = 512;
146
147 if (gdbmfile != NULL) {
148 if (openfile == filename) return true;
149 else closedatabase ();
150 }
151
152 openfile = filename;
153
154 char *namebuffer = filename.getcstr();
155 do {
156#ifdef __WIN32__
157 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL, (need_filelock) ? 1 : 0);
158#else
159 gdbmfile = gdbm_open (namebuffer, block_size, mode, 00664, NULL);
160#endif
161 --num_retrys;
162 } while (num_retrys>0 && gdbmfile==NULL &&
163 (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER));
164 delete []namebuffer;
165
166 if (gdbmfile == NULL && logout != NULL) {
167 outconvertclass text_t2ascii;
168 (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
169 }
170
171 return (gdbmfile != NULL);
172}
173
174void gdbmclass::closedatabase () {
175 if (gdbmfile == NULL) return;
176
177 gdbm_close (gdbmfile);
178 gdbmfile = NULL;
179 openfile.clear();
180}
181
182
183// returns true on success
184bool gdbmclass::setinfo (const text_t &key, const infodbclass &info) {
185 if (gdbmfile == NULL) return false;
186
187 text_t subkey;
188 text_t data;
189
190 // get all the keys and values
191 infodbclass::const_iterator info_here = info.begin();
192 infodbclass::const_iterator info_end = info.end();
193 while (info_here != info_end) {
194 // add the key
195 subkey.clear();
196 subkey.push_back('<');
197 text_t::const_iterator subkey_here = (*info_here).first.begin();
198 text_t::const_iterator subkey_end = (*info_here).first.end();
199 while (subkey_here != subkey_end) {
200 if (*subkey_here == '>') {
201 subkey.push_back('\\'); subkey.push_back('>');
202 } else if (*subkey_here == '\n') {
203 subkey.push_back('\\'); subkey.push_back('n');
204 } else if (*subkey_here == '\r') {
205 subkey.push_back('\\'); subkey.push_back('r');
206 } else if (*subkey_here == '\\') {
207 subkey.push_back('\\'); subkey.push_back('\\');
208 } else {
209 subkey.push_back (*subkey_here);
210 }
211 ++subkey_here;
212 }
213 subkey.push_back('>');
214
215 // add the values
216 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
217 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
218 while (subvalue_here != subvalue_end) {
219 data += subkey;
220
221 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
222 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
223 while (thissubvalue_here != thissubvalue_end) {
224 if (*thissubvalue_here == '>') {
225 data.push_back('\\'); data.push_back('>');
226 } else if (*thissubvalue_here == '\n') {
227 data.push_back('\\'); data.push_back('n');
228 } else if (*thissubvalue_here == '\r') {
229 data.push_back('\\'); data.push_back('r');
230 } else if (*thissubvalue_here == '\\') {
231 data.push_back('\\'); data.push_back('\\');
232 } else {
233 data.push_back (*thissubvalue_here);
234 }
235
236 ++thissubvalue_here;
237 }
238
239 data.push_back('\n');
240 ++subvalue_here;
241 }
242
243 ++info_here;
244 }
245
246 // store the value
247 datum key_data;
248 datum data_data;
249
250 // get a utf-8 encoded c string of the unicode key
251 key_data.dptr = (to_utf8(key)).getcstr();
252 if (key_data.dptr == NULL) {
253 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
254 return false;
255 }
256 key_data.dsize = strlen (key_data.dptr);
257
258 data_data.dptr = (to_utf8(data)).getcstr();
259 if (data_data.dptr == NULL) {
260 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
261 delete []key_data.dptr;
262 return false;
263 }
264 data_data.dsize = strlen (data_data.dptr);
265
266 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
267 delete []key_data.dptr;
268 delete []data_data.dptr;
269
270 return (ret == 0);
271
272}
273
274//returns true on success
275bool gdbmclass::setinfo (const text_t &key, const text_t &data) {
276 if (gdbmfile == NULL) return false;
277
278 // store the value
279 datum key_data;
280 datum data_data;
281
282 // get a utf-8 encoded c string of the unicode key
283 key_data.dptr = (to_utf8(key)).getcstr();
284 if (key_data.dptr == NULL) {
285 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
286 return false;
287 }
288 key_data.dsize = strlen (key_data.dptr);
289
290 data_data.dptr = (to_utf8(data)).getcstr();
291 if (data_data.dptr == NULL) {
292 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
293 delete []key_data.dptr;
294 return false;
295 }
296 data_data.dsize = strlen (data_data.dptr);
297
298 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
299 delete []key_data.dptr;
300 delete []data_data.dptr;
301
302 return (ret == 0);
303
304}
305
306
307void gdbmclass::deletekey (const text_t &key) {
308 if (gdbmfile == NULL) return;
309
310 // get a utf-8 encoded c string of the unicode key
311 datum key_data;
312 key_data.dptr = (to_utf8(key)).getcstr();
313 if (key_data.dptr == NULL) return;
314 key_data.dsize = strlen (key_data.dptr);
315
316 // delete the key
317 gdbm_delete (gdbmfile, key_data);
318
319 // free up the key memory
320 delete []key_data.dptr;
321}
322
323
324// getfirstkey and getnextkey are used for traversing the database
325// no insertions or deletions should be carried out while traversing
326// the database. when there are no keys left to visit in the database
327// an empty string is returned.
328text_t gdbmclass::getfirstkey () {
329 if (gdbmfile == NULL) return g_EmptyText;
330
331 // get the first key
332 datum firstkey_data = gdbm_firstkey (gdbmfile);
333 if (firstkey_data.dptr == NULL) return g_EmptyText;
334
335 // convert it to text_t
336 text_t firstkey;
337 firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize);
338 free (firstkey_data.dptr);
339 return to_uni(firstkey); // convert to unicode
340}
341
342text_t gdbmclass::getnextkey (const text_t &key) {
343 if (gdbmfile == NULL || key.empty()) return g_EmptyText;
344
345 // get a utf-8 encoded c string of the unicode key
346 datum key_data;
347 key_data.dptr = (to_utf8(key)).getcstr();
348 if (key_data.dptr == NULL) return g_EmptyText;
349 key_data.dsize = strlen (key_data.dptr);
350
351 // get the next key
352 datum nextkey_data = gdbm_nextkey (gdbmfile, key_data);
353 if (nextkey_data.dptr == NULL) {
354 delete []key_data.dptr;
355 return g_EmptyText;
356 }
357
358 // convert it to text_t
359 text_t nextkey;
360 nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize);
361 free (nextkey_data.dptr);
362 delete []key_data.dptr;
363 return to_uni(nextkey); // convert to unicode
364}
365
366
367// replaces the .fc, .lc, .pr, .rt, .ns and .ps syntax (first child,
368// last child, parent, root, next sibling, previous sibling)
369// it expects child, parent, etc. to exist if syntax has been used
370// so you should test before using
371text_t gdbmclass::translate_OID (const text_t &inOID, infodbclass &info) {
372
373 if (inOID.size() < 4) return inOID;
374 if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID;
375
376 text_t OID = inOID;
377 text_tarray tailarray;
378 text_t tail = substr (OID.end()-3, OID.end());
379 if (tail == ".rt") {
380 get_top (inOID, OID);
381 return OID;
382 }
383 while (tail == ".fc" || tail == ".lc" || tail == ".pr" ||
384 tail == ".ns" || tail == ".ps") {
385 tailarray.push_back(tail);
386 OID.erase (OID.end()-3, OID.end());
387 tail = substr (OID.end()-3, OID.end());
388 if (tail == ".rt") {
389 get_top (inOID, OID);
390 return OID;
391 }
392 }
393
394 if (tailarray.empty()) return inOID;
395 text_tarray::const_iterator begin = tailarray.begin();
396 text_tarray::const_iterator here = tailarray.end() - 1;
397
398 while (here >= begin) {
399
400 if (*here == ".fc")
401 get_first_child (OID, info);
402 else if (*here == ".lc")
403 get_last_child (OID, info);
404 else if (*here == ".pr")
405 OID = get_parent (OID);
406 else if (*here == ".ns")
407 get_next_sibling (OID, info);
408 else if (*here == ".ps")
409 get_previous_sibling (OID, info);
410
411 if (here == begin)
412 break;
413 --here;
414 }
415 return OID;
416}
417
418void gdbmclass::get_first_child (text_t &OID, infodbclass &info) {
419
420 text_t firstchild;
421 if (getinfo (OID, info)) {
422 text_t &contains = info["contains"];
423 if (!contains.empty()) {
424 text_t parent = OID;
425 getdelimitstr (contains.begin(), contains.end(), ';', firstchild);
426 if (firstchild.empty()) OID = contains;
427 else OID = firstchild;
428 if (*(OID.begin()) == '"') translate_parent (OID, parent);
429 }
430 }
431}
432
433void gdbmclass::get_last_child (text_t &OID, infodbclass &info) {
434
435 text_tarray children;
436 if (getinfo (OID, info)) {
437 text_t &contains = info["contains"];
438 if (!contains.empty()) {
439 text_t parent = OID;
440 splitchar (contains.begin(), contains.end(), ';', children);
441 OID = children.back();
442 if (*(OID.begin()) == '"') translate_parent (OID, parent);
443 }
444 }
445}
446
447void gdbmclass::get_next_sibling (text_t &OID, infodbclass &info) {
448
449 text_tarray siblings;
450 text_t parent = get_parent (OID);
451
452 if (getinfo (parent, info)) {
453 text_t &contains = info["contains"];
454 if (!contains.empty()) {
455 splitchar (contains.begin(), contains.end(), ';', siblings);
456 text_tarray::const_iterator here = siblings.begin();
457 text_tarray::const_iterator end = siblings.end();
458 text_t shrunk_OID = OID;
459 shrink_parent (shrunk_OID);
460 while (here != end) {
461 if (*here == shrunk_OID && (here+1 != end)) {
462 OID = *(here+1);
463 if (*(OID.begin()) == '"') translate_parent (OID, parent);
464 break;
465 }
466 ++here;
467 }
468 }
469 }
470}
471
472void gdbmclass::get_previous_sibling (text_t &OID, infodbclass &info) {
473
474 text_tarray siblings;
475 text_t parent = get_parent (OID);
476
477 if (getinfo (parent, info)) {
478 text_t &contains = info["contains"];
479 if (!contains.empty()) {
480 splitchar (contains.begin(), contains.end(), ';', siblings);
481 text_tarray::const_iterator here = siblings.begin();
482 text_tarray::const_iterator end = siblings.end();
483 text_t shrunk_OID = OID;
484 shrink_parent (shrunk_OID);
485 while (here != end) {
486 if (*here == shrunk_OID && (here != siblings.begin())) {
487 OID = *(here-1);
488 if (*(OID.begin()) == '"') translate_parent (OID, parent);
489 break;
490 }
491 ++here;
492 }
493 }
494 }
495}
496
497// returns true on success
498bool gdbmclass::getinfo (const text_t& key, infodbclass &info) {
499 text_t data;
500
501 if (!getkeydata (key, data)) return false;
502 text_t::iterator here = data.begin ();
503 text_t::iterator end = data.end ();
504
505 text_t ikey, ivalue;
506 info.clear (); // reset info
507
508 while (getinfoline (here, end, ikey, ivalue)) {
509 info.addinfo (ikey, ivalue);
510 }
511
512 return true;
513}
514
515// returns true if exists
516bool gdbmclass::exists (const text_t& key) {
517 text_t data;
518 return getkeydata (key, data);
519}
520
521// returns true on success
522bool gdbmclass::getkeydata (const text_t& key, text_t &data) {
523 datum key_data;
524 datum return_data;
525
526 if (gdbmfile == NULL) return false;
527
528 // get a utf-8 encoded c string of the unicode key
529 key_data.dptr = (to_utf8(key)).getcstr();
530 if (key_data.dptr == NULL) {
531 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
532 return false;
533 }
534 key_data.dsize = strlen (key_data.dptr);
535
536 // fetch the result
537 return_data = gdbm_fetch (gdbmfile, key_data);
538 delete []key_data.dptr;
539
540 if (return_data.dptr == NULL) return false;
541
542 data.setcarr (return_data.dptr, return_data.dsize);
543 free (return_data.dptr);
544 data = to_uni(data); // convert to unicode
545
546 return true;
547}
548
549// returns true on success
550bool gdbmclass::getinfoline (text_t::iterator &here, text_t::iterator end,
551 text_t &key, text_t &value) {
552 key.clear();
553 value.clear();
554
555 // ignore white space
556 while (here != end && is_unicode_space (*here)) ++here;
557
558 // get the '<'
559 if (here == end || *here != '<') return false;
560 ++here;
561
562 // get the key
563 while (here != end && *here != '>') {
564 key.push_back(*here);
565 ++here;
566 }
567
568 // get the '>'
569 if (here == end || *here != '>') return false;
570 ++here;
571
572 // get the value
573 while (here != end && *here != '\n') {
574 if (*here == '\\') {
575 // found escape character
576 ++here;
577 if (here != end) {
578 if (*here == 'n') value.push_back ('\n');
579 else if (*here == 'r') value.push_back ('\r');
580 else value.push_back(*here);
581 }
582
583 } else {
584 // a normal character
585 value.push_back(*here);
586 }
587
588 ++here;
589 }
590
591 return true;
592}
Note: See TracBrowser for help on using the repository browser.