datahash_text.cpp

Go to the documentation of this file.
00001 /*
00002  * datahash_text.cpp
00003  *
00004  * Copyright (C) 2003, 2008 Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Methods for reading/write hashes in/out of text streams.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "datahash_text.h"              // always include our own header first
00036 
00037 #include <fstream>
00038 #include <sstream>
00039 
00040 #include "common/wave_ex.h"
00041 
00042 #include "perf/perf.h"
00043 
00044 #include "util/file.h"
00045 #include "util/parsing.h"
00046 
00047 
00048 
00049 ////////////////////////////////////////////////////////////////////////////////
00050 //
00051 //      static helper methods
00052 //
00053 ////////////////////////////////////////////////////////////////////////////////
00054 
00055 static smart_ptr<Datahash>
00056 readHashFromStreamInternal
00057 (
00058 IO std::istream& stream
00059 )
00060 {
00061         smart_ptr<Datahash> root = new Datahash;
00062         ASSERT(root, "failed to create empty datahash");
00063 
00064         eParseBehavior behave = eParse_StripComments;
00065 
00066         // keep reading lines
00067         while (!stream.eof()) {
00068                 std::string line = getNextLineFromStream(stream, behave);
00069 
00070                 // get key name
00071                 std::string key_name;
00072                 const char * remain =
00073                     getNextTokenFromString(line.c_str(), key_name, behave);
00074 
00075                 // empty?
00076                 if ("" == key_name) {
00077                         continue;       // skip this line
00078                 }
00079 
00080                 // end of hash?
00081                 if ("}" == key_name)
00082                         break;          // end of hash
00083 
00084                 // skip whitespace
00085                 while (*remain && isspace(*remain)) {
00086                         ++remain;
00087                 }
00088 
00089                 // truncate trailing whitespace
00090                 long N = strlen(remain);
00091                 while (N > 0 && isspace(remain[N - 1])) {
00092                         --N;
00093                 }
00094 
00095                 std::string value;
00096                 for (long l = 0; l < N; ++l) {
00097                         value += remain[l];
00098                 }
00099 
00100         //      DPRINTF("key:'%s'   value:'%s'", key_name.c_str(),
00101         //          value.c_str());
00102 
00103                 if (!strcmp("{", value.c_str())) {
00104                         // complex value
00105                         smart_ptr<Datahash> hash =
00106                             readHashFromStream(key_name.c_str(), stream);
00107                         ASSERT(hash, "null value after reading subhash");
00108 
00109                         // add value
00110                         root->insert(key_name, hash);
00111                 } else {
00112                         // simple value
00113                         root->insert(key_name, value);
00114                 }
00115         }
00116 
00117         // success!
00118         return root;
00119 }
00120 
00121 
00122 
00123 static void
00124 newlineAndIndent
00125 (
00126 IO std::ostream& stream,
00127 IN int indent
00128 )
00129 throw()
00130 {
00131         ASSERT(indent >= 0, "bad indent value");
00132 
00133         stream << "\n";
00134         for (int i = 0; i < indent; i++) {
00135                 stream << "\t";
00136         }
00137 }
00138 
00139 
00140 
00141 void
00142 writeHashToStreamInternal
00143 (
00144 IN const Datahash * hash,
00145 IO std::ostream& stream,
00146 IN int indent
00147 )
00148 {
00149         ASSERT(hash, "NULL hash");
00150         ASSERT(indent >= 0, "Bad indent value");
00151 
00152         // iterate over all entries
00153         Datahash::iterator_t i;
00154         hash->getIterator(i);
00155         std::string k;
00156         while (const hash_value_t * phv = hash->getNextElementUnsafe(i, k)) {
00157                 ASSERT(phv, "null?");
00158                 const char * key = k.c_str();
00159                 ASSERT(key, "null");
00160 
00161                 // what sort of data element is this?
00162                 if (eHashDataType_String == phv->type) {
00163                         newlineAndIndent(stream, indent);
00164                         stream << key << "\t" << phv->text;
00165                 } else {
00166                         // subhash
00167                         newlineAndIndent(stream, indent);
00168                         stream << key << " {";
00169                         writeHashToStreamInternal(phv->hash, stream, indent + 1);
00170                         newlineAndIndent(stream, indent);
00171                         stream << "}\n";
00172                 }
00173         }
00174 }
00175 
00176 
00177 
00178 ////////////////////////////////////////////////////////////////////////////////
00179 //
00180 //      public API
00181 //
00182 ////////////////////////////////////////////////////////////////////////////////
00183 
00184 smart_ptr<Datahash>
00185 readHashFromStream
00186 (
00187 IN const char * key_name,
00188 IO std::istream& stream
00189 )
00190 {
00191         perf::Timer timer("readHashFromStream");
00192         ASSERT(key_name, "null");
00193 
00194         try {
00195                 return readHashFromStreamInternal(stream);
00196         } catch (std::exception& e) {
00197                 WAVE_EX(wex);
00198                 wex << "Failed to read hash with key name: " << key_name;
00199                 wex << "\n";
00200                 wex << e.what();
00201         }
00202 
00203         ASSERT(false, "should never get here");
00204         return NULL;
00205 }
00206 
00207 
00208 
00209 smart_ptr<Datahash>
00210 readHashFromTextFile
00211 (
00212 IN const char * filename
00213 )
00214 {
00215         perf::Timer timer("readHashFromTextFile");
00216         ASSERT(filename, "NULL filename");
00217 
00218         // open file
00219         std::ifstream stream(filename);
00220         if (!stream.good()) {
00221                 WAVE_EX(wex);
00222                 wex << "Failed to open file for reading: " << filename;
00223         }
00224 
00225         try {
00226                 return readHashFromStream("(root element)", stream);
00227         } catch (std::exception& e) {
00228                 WAVE_EX(wex);
00229                 wex << "Failure while reading from file: " << filename;
00230                 wex << "\n";
00231                 wex << e.what();
00232         }
00233 
00234         ASSERT(false, "should never get here");
00235         return NULL;
00236 }
00237 
00238 
00239 
00240 smart_ptr<Datahash>
00241 readHashFromString
00242 (
00243 IN const char * val
00244 )
00245 {
00246         ASSERT(val, "null");
00247 
00248         std::istringstream iss(val);
00249         return readHashFromStream("root", iss);
00250 }
00251 
00252 
00253 
00254 void
00255 writeHashToStream
00256 (
00257 IN const Datahash * hash,
00258 IO std::ostream& stream
00259 )
00260 {
00261         perf::Timer timer("writeHashToStream");
00262         ASSERT(hash, "null");
00263 
00264         writeHashToStreamInternal(hash, stream, 0);
00265 }
00266 
00267 
00268 
00269 void
00270 writeHashToTextFile
00271 (
00272 IN const Datahash * hash,
00273 IN const char * filename,
00274 IN const char * comment_at_top_of_file
00275 )
00276 {
00277         perf::Timer timer("writeHashToTextFile");
00278         ASSERT(hash, "NULL hash");
00279         ASSERT(filename, "NULL filename");
00280         // ASSERT(comment_at_top_of_file) -- this can be null
00281 
00282         // write to a temporary file
00283         std::string tmpfile = filename;
00284         tmpfile += ".data.tmp";
00285 
00286         {
00287                 // open the file
00288                 std::ofstream stream(tmpfile.c_str());
00289                 if (!stream.good()) {
00290                         WAVE_EX(wex);
00291                         wex << "Failed to open file for writing: " << tmpfile;
00292                 }
00293 
00294                 // write a comment at the top if requested
00295                 if (comment_at_top_of_file) {
00296                         stream << comment_at_top_of_file << "\n";
00297                 }
00298 
00299                 // recursively write the hash
00300                 writeHashToStreamInternal(hash, stream, 0);
00301 
00302                 // close with a final newline
00303                 stream << "\n";
00304 
00305                 stream.flush();
00306         }
00307 
00308         // rename
00309         if (rename(tmpfile.c_str(), filename)) {
00310                 WAVE_EX(wex);
00311                 wex << "Failed to rename from " << tmpfile << " to ";
00312                 wex << filename;
00313         }
00314 }
00315 
00316 
00317 
00318 void
00319 dumpHash
00320 (
00321 IN const char * title,
00322 IN const Datahash * hash
00323 )
00324 {
00325         ASSERT(title, "null");
00326         ASSERT(hash, "null");
00327 
00328         std::ostringstream oss;
00329         writeHashToStream(hash, oss);
00330 
00331         DPRINTF("HASH DUMP %s:\n%s", title, oss.str().c_str());
00332 }
00333