objtree.cpp

Go to the documentation of this file.
00001 /*
00002  * objtree.cpp
00003  *
00004  * Copyright (C) 2006,2010  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  * Implementation of the objtree API.  See objtree.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "objtree.h"            // always include our own header first!
00036 
00037 #include <sstream>
00038 #include <stdexcept>
00039 
00040 #include "datahash/datahash_util.h"
00041 #include "util/token_stream.h"
00042 
00043 
00044 namespace objtree {
00045 
00046 // List interface destructor implementation
00047 List::~List(void) throw() { }
00048 
00049 Tree::~Tree(void) throw() { }
00050 
00051 
00052 ////////////////////////////////////////////////////////////////////////////////
00053 //
00054 //      static helper methods
00055 //
00056 ////////////////////////////////////////////////////////////////////////////////
00057 
00058 static void
00059 verifyLowerCase
00060 (
00061 IN const char * p
00062 )
00063 {
00064         // actually, this isn't interesting...  should just be valid...
00065         if ('*' == *p)
00066                 return;
00067         ASSERT(isValidPropertyName(p), "Invalid id/query: '%s'", p);
00068 }
00069 
00070 
00071 
00072 static bool
00073 idMatchesQuery
00074 (
00075 IN const char * id,
00076 IN const char * query
00077 )
00078 {
00079         ASSERT(id, "null id");
00080         ASSERT(query, "null query");
00081 
00082         // basic matching: completely matches, or is wildcard
00083         if (!strcmp("*", query))
00084                 return true;    // wildcard query
00085         if (!strcmp(id, query))
00086                 return true;    // query + id exactly match
00087         return false;
00088 }
00089 
00090 
00091 
00092 ////////////////////////////////////////////////////////////////////////////////
00093 //
00094 //      property_t
00095 //
00096 ////////////////////////////////////////////////////////////////////////////////
00097 
00098 void
00099 property_t::dump
00100 (
00101 IN const char * title
00102 )
00103 const
00104 throw()
00105 {
00106         ASSERT(title, "null title for property_t dump");
00107 
00108         DPRINTF("  %s", title);
00109         for (Dictionary::const_iterator i = this->begin(); i != this->end();
00110              ++i) {
00111                 const char * field = i->first.c_str();
00112                 const char * value = i->second.c_str();
00113 
00114                 DPRINTF("    field '%s' --> value '%s'", field, value);
00115         }
00116 }
00117 
00118 
00119 
00120 ////////////////////////////////////////////////////////////////////////////////
00121 //
00122 //      property_set_t
00123 //
00124 ////////////////////////////////////////////////////////////////////////////////
00125 
00126 void
00127 property_set_t::dump
00128 (
00129 IN const char * title
00130 )
00131 const
00132 throw()
00133 {
00134         DPRINTF("property_set_t %s:", title);
00135         for (property_set_t::const_iterator i = this->begin(); i != this->end();
00136              ++i) {
00137                 const char * name = i->first.c_str();
00138                 const property_t& prop = i->second;
00139                 prop.dump(name);
00140         }
00141 }
00142 
00143 
00144 
00145 ////////////////////////////////////////////////////////////////////////////////
00146 //
00147 //      ListImpl - implementation of the objtree::List API
00148 //
00149 ////////////////////////////////////////////////////////////////////////////////
00150 
00151 class ListImpl : public List {
00152 public:
00153         ~ListImpl(void) throw() { }
00154 
00155         void initialize(void) throw() { }
00156 
00157         // objtree::List class interface methods -------------------------------
00158         virtual bool addObject(IN const char * id);
00159         virtual bool removeObject(IN const char * id);
00160         virtual void getObjects(IN const char * id_query,
00161                                 OUT VecString& ids);
00162         virtual void getProperties(IN const char * id_query,
00163                                 OUT property_set_t& pset);
00164         virtual void addProperties(IN const char * id_query,
00165                                 IN const property_set_t& pset);
00166         virtual void setProperties(IN const char * id_query,
00167                                 IN const property_set_t& pset);
00168 
00169 private:
00170         // private typedefs ----------------------------------------------------
00171         typedef std::map<std::string, smart_ptr<property_set_t> > obj_map_t;
00172 
00173         // private helper methods ----------------------------------------------
00174         void getPaths(IN const char * query, OUT VecString& ids);
00175 
00176         // private member data -------------------------------------------------
00177         obj_map_t               m_objects;
00178 };
00179 
00180 
00181 ////////////////////////////////////////////////////////////////////////////////
00182 //
00183 //      ListImpl -- objtree::List class interface methods
00184 //
00185 ////////////////////////////////////////////////////////////////////////////////
00186 
00187 bool
00188 ListImpl::addObject
00189 (
00190 IN const char * id
00191 )
00192 {
00193         ASSERT(id, "null");
00194         verifyLowerCase(id);
00195 
00196         obj_map_t::iterator i = m_objects.find(id);
00197         if (m_objects.end() != i)
00198                 return false;   // can't add, already have an object here
00199 
00200         smart_ptr<property_set_t> props = new property_set_t;
00201         m_objects[id] = props;
00202 
00203         return true;
00204 }
00205 
00206 
00207 
00208 bool
00209 ListImpl::removeObject
00210 (
00211 IN const char * id
00212 )
00213 {
00214         ASSERT(id, "null");
00215         verifyLowerCase(id);
00216 
00217         obj_map_t::iterator i = m_objects.find(id);
00218         if (m_objects.end() == i) {
00219                 DPRINTF("Can't remove object, not in tree: '%s'", id);
00220                 return false;
00221         }
00222         m_objects.erase(i);
00223 
00224         return true;
00225 }
00226 
00227 
00228 
00229 void
00230 ListImpl::getObjects
00231 (
00232 IN const char * id_query,
00233 OUT VecString& ids
00234 )
00235 {
00236         verifyLowerCase(id_query);
00237 
00238         this->getPaths(id_query, ids);
00239 }
00240 
00241 
00242 
00243 void
00244 ListImpl::getProperties
00245 (
00246 IN const char * id_query,
00247 OUT property_set_t& pset
00248 )
00249 {
00250         verifyLowerCase(id_query);
00251 
00252         VecString ids;
00253         this->getPaths(id_query, ids);
00254 
00255         // accumulate common properties
00256         SetString invalid;
00257         pset.clear();
00258 
00259         // loop through all objects found for this id query
00260         for (VecString::iterator i = ids.begin(); i != ids.end(); ++i) {
00261 
00262                 // retrieve object (object == property set)
00263                 const property_set_t * ps = m_objects[*i];
00264                 ASSERT(ps, "null property set");
00265 
00266                 // loop through all properties for this object
00267                 for (property_set_t::const_iterator k = ps->begin();
00268                      k != ps->end(); ++k) {
00269                         const char * name = k->first.c_str();
00270 
00271                         const property_t * p = &k->second;
00272                         ASSERT(p, "null property");
00273 
00274                         // is this property already in the output set?
00275                         property_t * q = pset.getProperty(name);
00276                         if (!q) {
00277                                 // haven't seen this property before--add it
00278                                 pset[name] = *p;
00279                         } else {
00280                                 // already in output set--does value match?
00281                                 if (!strcmp(q->getValue(), p->getValue()))
00282                                         q->setValue(NULL);      // mismatch, so clear
00283                         }
00284                 }
00285         }
00286 }
00287 
00288 
00289 void
00290 ListImpl::addProperties
00291 (
00292 IN const char * id_query,
00293 IN const property_set_t& pset
00294 )
00295 {
00296         verifyLowerCase(id_query);
00297 
00298         // get all objects that match query
00299         VecString ids;
00300         this->getPaths(id_query, ids);
00301 
00302         // loop through all objects
00303         for (VecString::iterator i = ids.begin(); i != ids.end(); ++i) {
00304 
00305                 obj_map_t::iterator j = m_objects.find(*i);
00306                 ASSERT(m_objects.end() != j, "object not in map?");
00307                 property_set_t& ps = *j->second;
00308 
00309                 // loop through input properties, set on this object
00310                 for (property_set_t::const_iterator k = pset.begin();
00311                      k != pset.end(); ++k) {
00312                         const char * name = k->first.c_str();
00313                         const property_t * p = &k->second;
00314 
00315                         // set property on object
00316                         ps[name] = *p;
00317                 }
00318         }
00319 }
00320 
00321 
00322 
00323 void
00324 ListImpl::setProperties
00325 (
00326 IN const char * id_query,
00327 IN const property_set_t& pset
00328 )
00329 {
00330         verifyLowerCase(id_query);
00331 
00332         // get all objects that match query
00333         VecString ids;
00334         this->getPaths(id_query, ids);
00335 
00336         // loop through all objects, set properties
00337         for (VecString::iterator i = ids.begin(); i != ids.end(); ++i) {
00338 
00339                 obj_map_t::iterator k = m_objects.find(*i);
00340                 ASSERT(m_objects.end() != k, "Object not in map?");
00341                 property_set_t * ps = k->second;
00342                 ASSERT(ps, "null");
00343 
00344                 // loop through all input properties, set
00345                 for (property_set_t::const_iterator j = pset.begin();
00346                      j != pset.end(); ++j) {
00347 
00348                         const char * name = j->first.c_str();
00349                         const property_t * p = &j->second;
00350 
00351                         // see if we have a property of this name, type
00352                         property_t * q = ps->getProperty(name);
00353                         if (!q)
00354                                 break;  // no match for name
00355 
00356                         // names match--copy value!
00357                         q->setValue(p->getValue());
00358                 }
00359         }
00360 }
00361 
00362 
00363 
00364 ////////////////////////////////////////////////////////////////////////////////
00365 //
00366 //      ListImpl -- private helper methods
00367 //
00368 ////////////////////////////////////////////////////////////////////////////////
00369 
00370 void
00371 ListImpl::getPaths
00372 (
00373 IN const char * query,
00374 OUT VecString& ids
00375 )
00376 {
00377         ASSERT(query, "null");
00378         verifyLowerCase(query);
00379 
00380         ids.clear();
00381 
00382         for (obj_map_t::iterator i = m_objects.begin(); i != m_objects.end();
00383              ++i) {
00384                 if (idMatchesQuery(i->first.c_str(), query)) {
00385                         ids.push_back(i->first);
00386                 }
00387         }
00388 }
00389 
00390 
00391 
00392 ////////////////////////////////////////////////////////////////////////////////
00393 //
00394 //      TreeImpl - class that implements the Tree interface
00395 //
00396 ////////////////////////////////////////////////////////////////////////////////
00397 
00398 class TreeImpl : public Tree {
00399 public:
00400         ~TreeImpl(void) throw() { }
00401 
00402         void initialize(void) throw() { }
00403 
00404         // objtree::Tree class interface methods -------------------------------
00405         List * getList(IN const char * name);
00406         void getListNames(OUT VecString& names);
00407 
00408 private:
00409         // private typedefs ----------------------------------------------------
00410         typedef std::map<std::string, smart_ptr<List> > map_list_t;
00411 
00412         // private member data -------------------------------------------------
00413         map_list_t              m_lists;
00414 };
00415 
00416 
00417 
00418 List *
00419 TreeImpl::getList
00420 (
00421 IN const char * name
00422 )
00423 {
00424         ASSERT(name, "null list name");
00425 
00426         map_list_t::iterator i = m_lists.find(name);
00427         if (m_lists.end() == i) {
00428                 // DPRINTF("Creating list '%s' on demand", name);
00429                 // create new list
00430                 smart_ptr<List> list = List::create();
00431                 ASSERT(list, "failed to create new list for '%s'", name);
00432                 m_lists[name] = list;
00433                 return list;
00434         }
00435 
00436         return i->second;
00437 }
00438 
00439 
00440 
00441 void
00442 TreeImpl::getListNames
00443 (
00444 OUT VecString& names
00445 )
00446 {
00447         names.clear();
00448 
00449         for (map_list_t::iterator i = m_lists.begin(); i != m_lists.end(); ++i)
00450         {
00451                 names.push_back(i->first);
00452         }
00453 }
00454 
00455 
00456 
00457 ////////////////////////////////////////////////////////////////////////////////
00458 //
00459 //      helper methods
00460 //
00461 ////////////////////////////////////////////////////////////////////////////////
00462 
00463 bool
00464 isValidPropertyName
00465 (
00466 IN const char * name
00467 )
00468 throw()
00469 {
00470         // we have the same requirements as a datahash (see Datahash)
00471         return isValidHashKeyName(name);
00472 }
00473 
00474 
00475 
00476 bool
00477 isTrue
00478 (
00479 IN const char * text
00480 )
00481 throw()
00482 {
00483         ASSERT(text, "null");
00484 
00485         // case-insensitive
00486         char buffer[6];
00487         for (int i = 0; i < 5; ++i) {
00488                 buffer[i] = tolower(text[i]);
00489         }
00490         buffer[5] = 0;
00491 
00492         // true unless we recognize the text as false
00493         if (!strcmp("0", buffer))
00494                 return false;
00495         if (!strcmp("f", buffer))
00496                 return false;
00497         if (!strcmp("false", buffer))
00498                 return false;
00499 
00500         return true;
00501 }
00502 
00503 
00504 
00505 void
00506 persist
00507 (
00508 IN const property_t& p,
00509 OUT std::string& val
00510 )
00511 {
00512         ASSERT(false, "Who is calling persist()?");
00513 //      std::ostringstream out;
00514 
00515 //      out << p.type << ":" << p.value;
00516 
00517 //      val = out.str();
00518 }
00519 
00520 
00521 
00522 void
00523 depersist
00524 (
00525 IN const char * text,
00526 OUT property_t& prop
00527 )
00528 {
00529         ASSERT(text, "null");
00530         ASSERT(false, "Who is calling depersist()?");
00531 
00532 /*
00533         const char * p = text;
00534         while (*p && *p != ':') { ++p; }
00535 
00536         if (!*p || p == text) {
00537                 std::ostringstream out;
00538                 out << "Bad property value: " << text;
00539                 throw std::runtime_error(out.str().c_str());
00540         }
00541         ++p;
00542 
00543         prop.type = text;
00544         prop.value = p;
00545 */
00546 }
00547 
00548 
00549 
00550 void
00551 save
00552 (
00553 IN std::ostream& stream,
00554 IN const property_set_t& pset
00555 )
00556 {
00557         stream << "{\n";
00558 
00559         for (property_set_t::const_iterator i = pset.begin(); i != pset.end();
00560              ++i) {
00561                 std::string val;
00562                 ASSERT(isValidPropertyName(i->first.c_str()),
00563                     "Persisting invalid property name! '%s'", i->first.c_str());
00564                 persist(i->second, val);
00565                 stream << "\t" << i->first << "\t\"" << val << "\"\n";
00566         }
00567 
00568         stream << "}\n";
00569 }
00570 
00571 
00572 
00573 void
00574 load
00575 (
00576 IN std::istream& stream,
00577 OUT property_set_t& pset
00578 )
00579 {
00580         pset.clear();
00581 
00582         std::string token;
00583         expectToken(stream, "{");
00584         while (true) {
00585                 getNextToken(stream, token);
00586                 if ("}" == token)
00587                         break;  // end of properties
00588 
00589                 std::string name = token;
00590                 if (!isValidPropertyName(name.c_str())) {
00591                         std::ostringstream out;
00592                         out << "Invalid property name: " << name;
00593                         throw std::runtime_error(out.str().c_str());
00594                 }
00595 
00596                 property_t p;
00597                 getNextToken(stream, token);
00598                 depersist(token.c_str(), p);
00599 
00600                 pset[name] = p;
00601         }
00602 }
00603 
00604 
00605 
00606 smart_ptr<Datahash>
00607 getDatahash
00608 (
00609 IN const property_set_t& pset
00610 )
00611 {
00612         smart_ptr<Datahash> hash = Datahash::create();
00613         ASSERT(hash, "failed to create datahash");
00614 
00615         for (property_set_t::const_iterator i = pset.begin(); i != pset.end();
00616              ++i) {
00617                 const char * name = i->first.c_str();
00618                 const property_t& p = i->second;
00619 
00620                 smart_ptr<Datahash> sub = Datahash::create();
00621                 ASSERT(sub, "failed to create subhash");
00622 
00623                 // add name and all sub-fields
00624                 sub->insert("name", name);
00625                 for (Dictionary::const_iterator j = p.begin();
00626                      j != p.end(); ++j) {
00627                         const char * field = j->first.c_str();
00628                         const char * value = j->second.c_str();
00629                         sub->insert(field, value);
00630                 }
00631                 hash->insert("property", sub);
00632         }
00633 
00634         return hash;
00635 }
00636 
00637 
00638 
00639 void
00640 getPropertySet
00641 (
00642 IN const Datahash * hash,
00643 OUT property_set_t& pset
00644 )
00645 {
00646         ASSERT(hash, "null");
00647         pset.clear();
00648 
00649         Datahash::iterator_t i;
00650         hash->getIterator("property", i);
00651         while (const hash_value_t * phv = hash->getNextElementUnsafe(i)) {
00652                 if (eHashDataType_Hash != phv->type)
00653                         continue;               // skip non-subhash values
00654                 const Datahash * sub = phv->hash;               // subhash
00655 
00656                 property_t prop;
00657                 Datahash::iterator_t j;
00658                 sub->getIterator(j);    // all elements
00659                 std::string key;
00660                 while (const hash_value_t * phv = sub->getNextElementUnsafe(i, key)) {
00661                         const char * field = key.c_str();
00662                         if (!strcmp(field, "name"))
00663                                 continue;       // skip the name field
00664                         prop.setFieldValue(field, phv->text.c_str());
00665                 }
00666                 pset[getString(sub, "name")] = prop;
00667         }
00668 }
00669 
00670 
00671 
00672 ////////////////////////////////////////////////////////////////////////////////
00673 //
00674 //      factories
00675 //
00676 ////////////////////////////////////////////////////////////////////////////////
00677 
00678 smart_ptr<List>
00679 List::create
00680 (
00681 void
00682 )
00683 {
00684         smart_ptr<ListImpl> local = new ListImpl;
00685         ASSERT(local, "out of memory");
00686 
00687         local->initialize();
00688 
00689         return local;
00690 }
00691 
00692 
00693 
00694 smart_ptr<Tree>
00695 Tree::create
00696 (
00697 void
00698 )
00699 {
00700         smart_ptr<TreeImpl> local = new TreeImpl;
00701         ASSERT(local, "out of memory");
00702 
00703         local->initialize();
00704 
00705         return local;
00706 }
00707 
00708 
00709 
00710 };      // objtree namespace
00711