source: gsdl/trunk/lib/gdbmclass.cpp@ 15557

Last change on this file since 15557 was 15557, checked in by mdewsnip, 16 years ago

(Adding new DB support) Set up dbclass as an abstract class that gdbmclass (and others in the future) inherit from.

File size: 13.4 KB
Line 
1/**********************************************************************
2 *
3 * gdbmclass.cpp --
4 * Copyright (C) 1999-2008 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 "gdbmclass.h"
27#include "unitool.h"
28#include "gsdlunicode.h"
29#include "OIDtools.h"
30
31
32gdbmclass::~gdbmclass()
33{
34 closedatabase();
35}
36
37// returns true if opened
38bool gdbmclass::opendatabase (const text_t &filename, int mode, int num_retrys,
39#ifdef __WIN32__
40 bool need_filelock
41#else
42 bool
43#endif
44 )
45{
46 text_t data_location;
47 int block_size = 512;
48
49 if (gdbmfile != NULL) {
50 if (openfile == filename) return true;
51 else closedatabase ();
52 }
53
54 openfile = filename;
55
56 // Map the DB mode values into GDBM mode values
57 int gdbm_mode = GDBM_READER;
58 if (mode == DB_WRITER)
59 {
60 gdbm_mode = GDBM_WRITER;
61 }
62 else if (mode == DB_WRITER_CREATE)
63 {
64 gdbm_mode = GDBM_WRCREAT;
65 }
66
67 char *namebuffer = filename.getcstr();
68 do {
69#ifdef __WIN32__
70 gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL, (need_filelock) ? 1 : 0);
71#else
72 gdbmfile = gdbm_open (namebuffer, block_size, gdbm_mode, 00664, NULL);
73#endif
74 --num_retrys;
75 } while (num_retrys>0 && gdbmfile==NULL &&
76 (gdbm_errno==GDBM_CANT_BE_READER || gdbm_errno==GDBM_CANT_BE_WRITER));
77 delete []namebuffer;
78
79 if (gdbmfile == NULL && logout != NULL) {
80 outconvertclass text_t2ascii;
81 (*logout) << text_t2ascii << "database open failed on: " << filename << "\n";
82 }
83
84 return (gdbmfile != NULL);
85}
86
87
88void gdbmclass::closedatabase ()
89{
90 if (gdbmfile == NULL) return;
91
92 gdbm_close (gdbmfile);
93 gdbmfile = NULL;
94 openfile.clear();
95}
96
97
98// returns true on success
99bool gdbmclass::setinfo (const text_t &key, const infodbclass &info)
100{
101 if (gdbmfile == NULL) return false;
102
103 text_t subkey;
104 text_t data;
105
106 // get all the keys and values
107 infodbclass::const_iterator info_here = info.begin();
108 infodbclass::const_iterator info_end = info.end();
109 while (info_here != info_end) {
110 // add the key
111 subkey.clear();
112 subkey.push_back('<');
113 text_t::const_iterator subkey_here = (*info_here).first.begin();
114 text_t::const_iterator subkey_end = (*info_here).first.end();
115 while (subkey_here != subkey_end) {
116 if (*subkey_here == '>') {
117 subkey.push_back('\\'); subkey.push_back('>');
118 } else if (*subkey_here == '\n') {
119 subkey.push_back('\\'); subkey.push_back('n');
120 } else if (*subkey_here == '\r') {
121 subkey.push_back('\\'); subkey.push_back('r');
122 } else if (*subkey_here == '\\') {
123 subkey.push_back('\\'); subkey.push_back('\\');
124 } else {
125 subkey.push_back (*subkey_here);
126 }
127 ++subkey_here;
128 }
129 subkey.push_back('>');
130
131 // add the values
132 text_tarray::const_iterator subvalue_here = (*info_here).second.begin();
133 text_tarray::const_iterator subvalue_end = (*info_here).second.end();
134 while (subvalue_here != subvalue_end) {
135 data += subkey;
136
137 text_t::const_iterator thissubvalue_here = (*subvalue_here).begin();
138 text_t::const_iterator thissubvalue_end = (*subvalue_here).end();
139 while (thissubvalue_here != thissubvalue_end) {
140 if (*thissubvalue_here == '>') {
141 data.push_back('\\'); data.push_back('>');
142 } else if (*thissubvalue_here == '\n') {
143 data.push_back('\\'); data.push_back('n');
144 } else if (*thissubvalue_here == '\r') {
145 data.push_back('\\'); data.push_back('r');
146 } else if (*thissubvalue_here == '\\') {
147 data.push_back('\\'); data.push_back('\\');
148 } else {
149 data.push_back (*thissubvalue_here);
150 }
151
152 ++thissubvalue_here;
153 }
154
155 data.push_back('\n');
156 ++subvalue_here;
157 }
158
159 ++info_here;
160 }
161
162 // store the value
163 datum key_data;
164 datum data_data;
165
166 // get a utf-8 encoded c string of the unicode key
167 key_data.dptr = (to_utf8(key)).getcstr();
168 if (key_data.dptr == NULL) {
169 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
170 return false;
171 }
172 key_data.dsize = strlen (key_data.dptr);
173
174 data_data.dptr = (to_utf8(data)).getcstr();
175 if (data_data.dptr == NULL) {
176 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
177 delete []key_data.dptr;
178 return false;
179 }
180 data_data.dsize = strlen (data_data.dptr);
181
182 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
183 delete []key_data.dptr;
184 delete []data_data.dptr;
185
186 return (ret == 0);
187}
188
189
190//returns true on success
191bool gdbmclass::setinfo (const text_t &key, const text_t &data)
192{
193 if (gdbmfile == NULL) return false;
194
195 // store the value
196 datum key_data;
197 datum data_data;
198
199 // get a utf-8 encoded c string of the unicode key
200 key_data.dptr = (to_utf8(key)).getcstr();
201 if (key_data.dptr == NULL) {
202 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
203 return false;
204 }
205 key_data.dsize = strlen (key_data.dptr);
206
207 data_data.dptr = (to_utf8(data)).getcstr();
208 if (data_data.dptr == NULL) {
209 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
210 delete []key_data.dptr;
211 return false;
212 }
213 data_data.dsize = strlen (data_data.dptr);
214
215 int ret = gdbm_store (gdbmfile, key_data, data_data, GDBM_REPLACE);
216 delete []key_data.dptr;
217 delete []data_data.dptr;
218
219 return (ret == 0);
220}
221
222
223void gdbmclass::deletekey (const text_t &key)
224{
225 if (gdbmfile == NULL) return;
226
227 // get a utf-8 encoded c string of the unicode key
228 datum key_data;
229 key_data.dptr = (to_utf8(key)).getcstr();
230 if (key_data.dptr == NULL) return;
231 key_data.dsize = strlen (key_data.dptr);
232
233 // delete the key
234 gdbm_delete (gdbmfile, key_data);
235
236 // free up the key memory
237 delete []key_data.dptr;
238}
239
240
241// getfirstkey and getnextkey are used for traversing the database
242// no insertions or deletions should be carried out while traversing
243// the database. when there are no keys left to visit in the database
244// an empty string is returned.
245text_t gdbmclass::getfirstkey ()
246{
247 if (gdbmfile == NULL) return g_EmptyText;
248
249 // get the first key
250 datum firstkey_data = gdbm_firstkey (gdbmfile);
251 if (firstkey_data.dptr == NULL) return g_EmptyText;
252
253 // convert it to text_t
254 text_t firstkey;
255 firstkey.setcarr (firstkey_data.dptr, firstkey_data.dsize);
256 free (firstkey_data.dptr);
257 return to_uni(firstkey); // convert to unicode
258}
259
260
261text_t gdbmclass::getnextkey (const text_t &key)
262{
263 if (gdbmfile == NULL || key.empty()) return g_EmptyText;
264
265 // get a utf-8 encoded c string of the unicode key
266 datum key_data;
267 key_data.dptr = (to_utf8(key)).getcstr();
268 if (key_data.dptr == NULL) return g_EmptyText;
269 key_data.dsize = strlen (key_data.dptr);
270
271 // get the next key
272 datum nextkey_data = gdbm_nextkey (gdbmfile, key_data);
273 if (nextkey_data.dptr == NULL) {
274 delete []key_data.dptr;
275 return g_EmptyText;
276 }
277
278 // convert it to text_t
279 text_t nextkey;
280 nextkey.setcarr (nextkey_data.dptr, nextkey_data.dsize);
281 free (nextkey_data.dptr);
282 delete []key_data.dptr;
283 return to_uni(nextkey); // convert to unicode
284}
285
286
287// replaces the .fc, .lc, .pr, .rt, .ns and .ps syntax (first child,
288// last child, parent, root, next sibling, previous sibling)
289// it expects child, parent, etc. to exist if syntax has been used
290// so you should test before using
291text_t gdbmclass::translate_OID (const text_t &inOID, infodbclass &info)
292{
293 if (inOID.size() < 4) return inOID;
294 if (findchar (inOID.begin(), inOID.end(), '.') == inOID.end()) return inOID;
295
296 text_t OID = inOID;
297 text_tarray tailarray;
298 text_t tail = substr (OID.end()-3, OID.end());
299 if (tail == ".rt") {
300 get_top (inOID, OID);
301 return OID;
302 }
303 while (tail == ".fc" || tail == ".lc" || tail == ".pr" ||
304 tail == ".ns" || tail == ".ps") {
305 tailarray.push_back(tail);
306 OID.erase (OID.end()-3, OID.end());
307 tail = substr (OID.end()-3, OID.end());
308 if (tail == ".rt") {
309 get_top (inOID, OID);
310 return OID;
311 }
312 }
313
314 if (tailarray.empty()) return inOID;
315 text_tarray::const_iterator begin = tailarray.begin();
316 text_tarray::const_iterator here = tailarray.end() - 1;
317
318 while (here >= begin) {
319
320 if (*here == ".fc")
321 get_first_child (OID, info);
322 else if (*here == ".lc")
323 get_last_child (OID, info);
324 else if (*here == ".pr")
325 OID = get_parent (OID);
326 else if (*here == ".ns")
327 get_next_sibling (OID, info);
328 else if (*here == ".ps")
329 get_previous_sibling (OID, info);
330
331 if (here == begin)
332 break;
333 --here;
334 }
335
336 return OID;
337}
338
339
340void gdbmclass::get_first_child (text_t &OID, infodbclass &info)
341{
342 text_t firstchild;
343 if (getinfo (OID, info)) {
344 text_t &contains = info["contains"];
345 if (!contains.empty()) {
346 text_t parent = OID;
347 getdelimitstr (contains.begin(), contains.end(), ';', firstchild);
348 if (firstchild.empty()) OID = contains;
349 else OID = firstchild;
350 if (*(OID.begin()) == '"') translate_parent (OID, parent);
351 }
352 }
353}
354
355
356void gdbmclass::get_last_child (text_t &OID, infodbclass &info)
357{
358 text_tarray children;
359 if (getinfo (OID, info)) {
360 text_t &contains = info["contains"];
361 if (!contains.empty()) {
362 text_t parent = OID;
363 splitchar (contains.begin(), contains.end(), ';', children);
364 OID = children.back();
365 if (*(OID.begin()) == '"') translate_parent (OID, parent);
366 }
367 }
368}
369
370
371void gdbmclass::get_next_sibling (text_t &OID, infodbclass &info)
372{
373 text_tarray siblings;
374 text_t parent = get_parent (OID);
375
376 if (getinfo (parent, info)) {
377 text_t &contains = info["contains"];
378 if (!contains.empty()) {
379 splitchar (contains.begin(), contains.end(), ';', siblings);
380 text_tarray::const_iterator here = siblings.begin();
381 text_tarray::const_iterator end = siblings.end();
382 text_t shrunk_OID = OID;
383 shrink_parent (shrunk_OID);
384 while (here != end) {
385 if (*here == shrunk_OID && (here+1 != end)) {
386 OID = *(here+1);
387 if (*(OID.begin()) == '"') translate_parent (OID, parent);
388 break;
389 }
390 ++here;
391 }
392 }
393 }
394}
395
396
397void gdbmclass::get_previous_sibling (text_t &OID, infodbclass &info)
398{
399 text_tarray siblings;
400 text_t parent = get_parent (OID);
401
402 if (getinfo (parent, info)) {
403 text_t &contains = info["contains"];
404 if (!contains.empty()) {
405 splitchar (contains.begin(), contains.end(), ';', siblings);
406 text_tarray::const_iterator here = siblings.begin();
407 text_tarray::const_iterator end = siblings.end();
408 text_t shrunk_OID = OID;
409 shrink_parent (shrunk_OID);
410 while (here != end) {
411 if (*here == shrunk_OID && (here != siblings.begin())) {
412 OID = *(here-1);
413 if (*(OID.begin()) == '"') translate_parent (OID, parent);
414 break;
415 }
416 ++here;
417 }
418 }
419 }
420}
421
422
423// returns true on success
424bool gdbmclass::getinfo (const text_t& key, infodbclass &info)
425{
426 text_t data;
427
428 if (!getkeydata (key, data)) return false;
429 text_t::iterator here = data.begin ();
430 text_t::iterator end = data.end ();
431
432 text_t ikey, ivalue;
433 info.clear (); // reset info
434
435 while (getinfoline (here, end, ikey, ivalue)) {
436 info.addinfo (ikey, ivalue);
437 }
438
439 return true;
440}
441
442
443// returns true if exists
444bool gdbmclass::exists (const text_t& key)
445{
446 text_t data;
447 return getkeydata (key, data);
448}
449
450
451// returns true on success
452bool gdbmclass::getkeydata (const text_t& key, text_t &data)
453{
454 datum key_data;
455 datum return_data;
456
457 if (gdbmfile == NULL) return false;
458
459 // get a utf-8 encoded c string of the unicode key
460 key_data.dptr = (to_utf8(key)).getcstr();
461 if (key_data.dptr == NULL) {
462 if (logout != NULL) (*logout) << "gdbmclass: out of memory\n";
463 return false;
464 }
465 key_data.dsize = strlen (key_data.dptr);
466
467 // fetch the result
468 return_data = gdbm_fetch (gdbmfile, key_data);
469 delete []key_data.dptr;
470
471 if (return_data.dptr == NULL) return false;
472
473 data.setcarr (return_data.dptr, return_data.dsize);
474 free (return_data.dptr);
475 data = to_uni(data); // convert to unicode
476
477 return true;
478}
479
480
481// returns true on success
482bool gdbmclass::getinfoline (text_t::iterator &here, text_t::iterator end,
483 text_t &key, text_t &value)
484{
485 key.clear();
486 value.clear();
487
488 // ignore white space
489 while (here != end && is_unicode_space (*here)) ++here;
490
491 // get the '<'
492 if (here == end || *here != '<') return false;
493 ++here;
494
495 // get the key
496 while (here != end && *here != '>') {
497 key.push_back(*here);
498 ++here;
499 }
500
501 // get the '>'
502 if (here == end || *here != '>') return false;
503 ++here;
504
505 // get the value
506 while (here != end && *here != '\n') {
507 if (*here == '\\') {
508 // found escape character
509 ++here;
510 if (here != end) {
511 if (*here == 'n') value.push_back ('\n');
512 else if (*here == 'r') value.push_back ('\r');
513 else value.push_back(*here);
514 }
515
516 } else {
517 // a normal character
518 value.push_back(*here);
519 }
520
521 ++here;
522 }
523
524 return true;
525}
Note: See TracBrowser for help on using the repository browser.