00001 /***************************************************************************
00002 * This file is part of the MagiC++ library. *
00003 * *
00004 * Copyright (C) 1998-2001 Marko Grönroos <magi@iki.fi> *
00005 * *
00006 ***************************************************************************
00007 * *
00008 * This library is free software; you can redistribute it and/or *
00009 * modify it under the terms of the GNU Library General Public *
00010 * License as published by the Free Software Foundation; either *
00011 * version 2 of the License, or (at your option) any later version. *
00012 * *
00013 * This library is distributed in the hope that it will be useful, *
00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
00016 * Library General Public License for more details. *
00017 * *
00018 * You should have received a copy of the GNU Library General Public *
00019 * License along with this library; see the file COPYING.LIB. If *
00020 * not, write to the Free Software Foundation, Inc., 59 Temple Place *
00021 * - Suite 330, Boston, MA 02111-1307, USA. *
00022 * *
00023 ***************************************************************************/
00024
00025 #include "magic/mobject.h"
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <fstream.h> // Needed by readStringMap, etc.
00029 #include "magic/mmap.h"
00030 #include "magic/mstream.h"
00031 #include "magic/mclass.h"
00032
00033 decl_module (map);
00034
00035 // impl_dynamic (GenHash, {Object});
00036
00037
00038
00040 // //
00041 // | | ---- o //
00042 // |\ /| ___ -- | ) ___ | _ //
00043 // | V | ___| | ) |--- ___| | |/ //
00044 // | | | | | |-- | | | | | //
00045 // | | \__| | | \__| | | //
00046 // //
00048
00049 impl_dynamic (MapPair, {Object});
00050
00051 MapPair::~MapPair () {
00052 delete key;
00053 key = NULL;
00054 if (!isref)
00055 delete value;
00056 value = NULL;
00057 }
00058
00059 DumpContext& MapPair::operator>> (DumpContext& out) const {
00060 if (key)
00061 out.name("key") << *key;
00062 if (value)
00063 out.name("value") << *value;
00064 return out;
00065 }
00066
00067 void MapPair::check () const {
00068 ASSERT (value!=(void*)0x1);
00069 }
00070
00071
00072
00074 // //
00075 // | | | ---- | //
00076 // | | ___ ____ | _ | ) ___ | ___ | //
00077 // |---| ___| ( |/ | |--- | | | \ | / / ) -+- //
00078 // | | | | \__ | | | ) | | | |/ |--- | //
00079 // | | \__| ____) | | |___ \__! \___/ | \ \__ \ //
00080 // //
00082
00083 impl_dynamic (HashBucket, {Object});
00084
00085 HashBucket::HashBucket () {
00086 next=NULL;
00087 }
00088
00089 HashBucket::~HashBucket () {
00090 delete next;
00091 next = NULL;
00092 }
00093
00094 DumpContext& HashBucket::operator>> (DumpContext& out) const {
00095 out << pair;
00096 return out;
00097 }
00098
00099 void HashBucket::check () const {
00100 pair.check ();
00101 if (next)
00102 next->check ();
00103 }
00104
00105
00106
00108 // //
00109 // ---- | | | //
00110 // | ___ __ | | ___ ____ | _ //
00111 // | --- / ) |/ | |---| ___| ( |/ | //
00112 // | \ |--- | | | | | | \__ | | //
00113 // |___/ \__ | | | | \__| ____) | | //
00114 // //
00116
00117 impl_dynamic (GenHash, {Object});
00118
00119 void GenHash::make (HashFunc* hfunc, int hsize, int flags) {
00120 hashsize = hsize;
00121 hash.resize (hashsize);
00122 hashfunc = hfunc;
00123 isref = flags;
00124 }
00125
00126 void GenHash::set (const Comparable* key, Object* value) {
00127 // Calculate the hash value of the key. This is a feature of
00128 // Comparable-inherited objects.
00129 int hval = key->hashfunc (hashsize);
00130
00131 // Find the bucket according to the hash value
00132 HashBucket* bucket = hash.getp (hval);
00133
00134 // If there is a bucket in that hash slot, add into it
00135 if (bucket)
00136 // As long as there is something in the bucket
00137 for (;bucket; bucket=bucket->next) {
00138 // Check if this is the matching bucket
00139 if (bucket->pair.match (*key)) {
00140 // It existed. Wow.
00141 delete bucket->pair.value; // Replace the old value
00142 delete key; // Dispose the excess key
00143 break;
00144 }
00145
00146 // That wasn't the right bucket. If there are no more
00147 // buckets, create a new one and exit the search
00148 if (!bucket->next) {
00149 // Add the item to the new bucket
00150 bucket->next = new HashBucket; // Create new bucket
00151 bucket = bucket->next; // Move on to the new bucket
00152 bucket->pair.key = key; // Set the key
00153 bucket->pair.isref = isref; // Clone the global isref setting
00154 break;
00155 }
00156 }
00157 else {
00158 // Add a new bucket in into the hash array
00159 hash.put (bucket = new HashBucket, hval);
00160 bucket->pair.key = key;
00161 }
00162
00163 // Finally set the value
00164 bucket->pair.value = value;
00165 }
00166
00167 ostream& GenHash::operator>> (ostream& out) const {
00168 for (GenHashIter iter (this); !iter.exhausted(); iter.next())
00169 out << iter.getkey() << "=" << iter.getvalue() << "\n";
00170 return out;
00171 }
00172
00173 const Object* GenHash::get (const Comparable& key) const throw (not_found) {
00174 for (const HashBucket* sanko = hash.getp (key.hashfunc(hashsize)); sanko; sanko=sanko->next) {
00175 ASSERT (sanko->pair.key != (void*)0x1);
00176 ASSERT (sanko->pair.value != (void*)0x1);
00177 if (sanko->pair.match (key))
00178 return sanko->pair.value;
00179 }
00180
00181 return NULL;
00182
00183 /*
00184 if (0) {
00185 // String-avaimet kerrotaan...
00186 if (key.is_a ("String")) {
00187 const String& skey = static_cast<const String&> (key);
00188 throw not_found (format ("CMap object not found with key '%s'", (CONSTR) skey));
00189 } else
00190 throw not_found ("CMap object not found with a key");
00191 } else {
00192 // Ilmoitetaan virheestä palauttamalla NULL
00193 return NULL;
00194 }
00195 */
00196 }
00197
00198 void GenHash::empty () {
00199 hash.empty ();
00200 }
00201
00202 void GenHash::remove (const Comparable& key) {
00203 HashBucket* prev = NULL;
00204 int bucket = key.hashfunc (hashsize);
00205 for (HashBucket* sanko = hash.getp (bucket); sanko; sanko=sanko->next) {
00206 if (sanko->pair.match (key)) {
00207 if (prev) {
00208 // Ei olla ensimmäinen
00209 prev->next = sanko->next;
00210 sanko->next = NULL;
00211 delete sanko;
00212 } else {
00213 // Ollaan ensimmäinen
00214
00215 if (sanko->next) {
00216 // Perässä on muita
00217 HashBucket* next = sanko->next;
00218 sanko->next = NULL;
00219 hash.put (next, bucket);
00220 delete sanko;
00221 } else {
00222 // Ollaan ainoa
00223 hash.remove (bucket);
00224 // hash.empty ();
00225 }
00226 }
00227 break;
00228 }
00229 prev = sanko;
00230 }
00231 }
00232
00233 void GenHash::operator+= (const GenHash& other) {
00234 for (GenHashIter i (&other); !i.exhausted(); i.next())
00235 set (static_cast<Comparable*> (i.getkey().clone ()), i.getvalue().clone ());
00236 }
00237
00238 DumpContext& GenHash::operator>> (DumpContext& out) const {
00239 out.name ("hashsize") << hashsize;
00240 out.name ("isref") << (int)isref;
00241 out.name ("hash") << hash;
00242 return out;
00243 }
00244
00245 void GenHash::check () const {
00246 for (int i=0; i<hash.size; i++)
00247 if (hash.getp(i)) // Check only non-null buckets
00248 hash[i].check (); // Check the bucket
00249 }
00250
00251
00252
00254 // //
00255 // ---- | | | --- //
00256 // | ___ _ | | ___ ____ | _ | | ___ | _ //
00257 // | --- / ) |/ | |---| ___| ( |/ | | -+- / ) |/ //
00258 // | \ |--- | | | | | | \__ | | | | |--- | //
00259 // |___/ \__ | | | | \__| ____) | | _|_ \ \__ | //
00260 // //
00262
00263 GenHashIter::GenHashIter (const GenHash* ghash) {
00264 hash = ghash;
00265 first ();
00266 }
00267
00268 void GenHashIter::first () {
00269 bucket = 0;
00270 currbucket = NULL;
00271 exh = 0;
00272 next ();
00273 }
00274
00275 void GenHashIter::next () {
00276 if (currbucket) {
00277 if ((currbucket = currbucket->next))
00278 return;
00279 else
00280 bucket++;
00281 }
00282
00283 while (bucket<hash->hash.size && (currbucket=(HashBucket*)(hash->hash.getp (bucket))) == NULL)
00284 bucket++;
00285
00286 if (bucket>=hash->hash.size)
00287 exh = 1;
00288 }
00289
00290 Comparable& GenHashIter::getkeyv () {
00291 if (currbucket)
00292 return const_cast<Comparable&> (*(currbucket->pair.key));
00293 else
00294 return *(Comparable*)NULL;
00295 }
00296
00297 Object& GenHashIter::getvaluev () {
00298 if (currbucket)
00299 return *(currbucket->pair.value);
00300 else
00301 return *(Object*) NULL;
00302 }
00303
00304 const Comparable& GenHashIter::getkey () const {
00305 if (currbucket)
00306 return *(currbucket->pair.key);
00307 else
00308 return *(const Comparable*) NULL;
00309 }
00310
00311 const Object& GenHashIter::getvalue () const {
00312 if (currbucket)
00313 return *(currbucket->pair.value);
00314 else
00315 return *(const Object*) NULL;
00316 }
00317
00318
00319
00321 // //
00322 // //
00323 // //
00324 // //
00325 // //
00326 // //
00327 // //
00329
00330 void splitpairs (CMap<String,String>& trg, const String& source,
00331 char psep='=', char rsep='&') {
00332
00333 // Tyhjennetään kakat pois
00334 trg.empty ();
00335
00336 // Halkaistaan ensin kentät erilleen ja laitetaan väliaikaiseen vektoriin
00337 Array<String> tmp;
00338 source.split (tmp, rsep);
00339
00340 // Käydään parit sisältävät merkkijonot läpi
00341 for (int i=0; i<tmp.size; i++) {
00342 // Halkaistaan pari kahtia
00343 int pos = tmp[i].find (psep);
00344 String left = tmp[i].left (pos);
00345 String right = tmp[i].mid (pos+1);
00346 trg.set (left, right);
00347 }
00348 }
00349
00350 String joinpairs (const CMap<String,String>& src, char psep='=', char rsep='&') {
00351 Array<String> arr;
00352 forStringMap (src, i)
00353 arr.add (new String (format ("%s%c%s", (CONSTR)i.key(), psep, (CONSTR)i.value())));
00354
00355 String result;
00356 result.join (arr, rsep);
00357 return result;
00358 }
00359
00360 // Print operation to string
00361 String toString (const StringMap& map) {
00362 String result = "{";
00363 bool first=true;
00364 forStringMap (map, i) {
00365 if (first)
00366 first = false;
00367 else
00368 result += ", ";
00369 result += i.key() + "=\042" + i.value() + "\042";
00370 }
00371 result += "}";
00372
00373 return result;
00374 }
00375
00376
00377
00379 // ---- o | | //
00380 // ( | _ |\ /| ___ -- //
00381 // --- -+- |/\ | |/ \ ___ | V | ___| | ) //
00382 // ) | | | | | ( \ | | | ( | |-- //
00383 // ___/ \ | | | | ---/ | | \__| | //
00384 // __/ //
00386
00387 StringMap readStringMap (const String& filename) {
00388 ifstream in ((CONSTR) filename);
00389 if (!in)
00390 throw file_not_found (strformat ("Could not open file '%s' for reading StringMap", (CONSTR) filename));
00391
00392 // Determine current path
00393 String path = "./";
00394 if (filename.findRev("/")!=-1)
00395 path = filename.mid(0, filename.findRev("/")+1);
00396
00397 // Call stream reader function
00398 return readStringMap (in, path);
00399 }
00400
00401 StringMap readStringMap (istream& in, const char* path=NULL) {
00402 StringMap result;
00403 String paramStr;
00404 String section="";
00405 String buffer;
00406 Array<String> regpar;
00407 Array<String> pair; // Key-value pair
00408
00409 while (getS (in, buffer, '\n')) {
00410 buffer.chop(); // Cleanup trailing whitespaces
00411
00412 // Map definition can be terminated with this special
00413 // row. Case sensitive.
00414 if (buffer == "# end-of-map")
00415 break;
00416
00417 if (buffer.regmatch ("^INCLUDE\\:? *(.+)", regpar)) {
00418 // Include file directive. Determine path.
00419 String curPath="./";
00420 if (path)
00421 curPath = path;
00422
00423 // Read the include file recursively
00424 result += readStringMap (curPath+regpar[1]);
00425
00426 } else if (buffer.regmatch ("^\\[([a-zA-Z0-9]*)\\]", regpar)) {
00427 // Section name line
00428 section = (regpar[1]=="]")? String():regpar[1];
00429
00430 } else if (buffer.regmatch ("^[ \t]*\\#")) {
00431 // Comment line, bypass
00432 continue;
00433 } else {
00434 // Check if key=value pair
00435 buffer.split (pair, '=');
00436 if (pair.size==2) {
00437 // Cleanup key and value
00438 pair[0].chop();
00439 pair[1].chop();
00440
00441 // If neither is empty, add to map
00442 if (!isempty(pair[0]) && !isempty(pair[1])) {
00443 // Add section prefix to key
00444 if (!isempty(section))
00445 pair[0] = section+"."+pair[0];
00446 result.set (pair[0], pair[1]); // Add to map
00447 }
00448 }
00449 }
00450 } // while not EOF
00451
00452 return result;
00453 }
00454
00455 void writeStringMap (const StringMap& map, ostream& out) {
00456 // TODO: Handle sectioning correctly
00457
00458 // Write everything under the default section
00459 out << "[]\n";
00460
00461 forStringMap (map, mapi)
00462 out << mapi.key() << "=" << mapi.value() << "\n";
00463 }
00464
00465
1.2.6 written by Dimitri van Heesch,
© 1997-2001