typeinst.cpp

Go to the documentation of this file.
00001 /*
00002  * typeinst.cpp
00003  *
00004  * Copyright (C) 2008,2009,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  * Implementation of basic type/instance routines.
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "typeinst.h"                   // always include our own header first!
00035 
00036 #include <fstream>
00037 
00038 #include "common/wave_ex.h"
00039 #include "datahash/datahash_text.h"
00040 #include "datahash/datahash_util.h"
00041 #include "geometry/geometry_3d.h"
00042 #include "perf/perf.h"
00043 #include "story/story.h"
00044 #include "threadsafe/threadsafe_map.h"
00045 #include "threadsafe/threadsafe_queue.h"
00046 #include "util/parsing.h"
00047 #include "util/token_stream.h"
00048 
00049 
00050 namespace aesop {
00051 
00052 
00053 // interface destructor implementation
00054 ComponentData::~ComponentData(void) throw() { /*DPRINTF("GOING AWAY");*/ }
00055 Instance::~Instance(void) throw() { }
00056 TypeComponentLoader::~TypeComponentLoader(void) throw() { }
00057 
00058 
00059 typedef threadsafe_queue<smart_ptr<TypeComponentLoader> > vec_loader_t;
00060 
00061 typedef threadsafe_map<std::string, smart_ptr<vec_loader_t> > map_loaders_t;
00062 
00063 
00064 static map_loaders_t s_loaders;
00065 
00066 
00068 //
00069 //      static helper methods
00070 //
00072 
00073 static void
00074 readIdFromStream
00075 (
00076 IO std::istream& stream,
00077 OUT AESOPIdBuffer& buffer,
00078 IO std::string& token
00079 )
00080 {
00081         if (!stream.good()) {
00082                 WAVE_EX(wex);
00083                 wex << "bad stream while parsing instances?";
00084         }
00085 
00086         getNextToken(stream, token);
00087         buffer.set(token.c_str());
00088         DPRINTF("  read Id: %s", (const char *) buffer);
00089 }
00090 
00091 
00092 
00093 static bool
00094 isReservedComponent
00095 (
00096 IN const char * name
00097 )
00098 {
00099         ASSERT(name, "null");
00100 
00101         // reserve empty or anything beginning with underscore
00102         return (!*name || '_' == *name);
00103 }
00104 
00105 
00106 
00108 //
00109 //      ComponentData -- base class default implementations
00110 //
00112 
00113 void
00114 ComponentData::dump
00115 (
00116 IN const char * msg
00117 )
00118 const
00119 throw()
00120 {
00121         // ASSERT(msg) -- can be null!
00122         if (msg) {
00123                 DPRINTF("%s", msg);
00124         }
00125         DPRINTF("    (data dump not overridden from base class)");
00126 }
00127 
00128 
00129 
00131 //
00132 //      Registry -- local object to maintain type loaders
00133 //
00135 
00136 class Registry {
00137 public:
00138         // constructor, destructor ---------------------------------------------
00139         Registry(void) throw() { }
00140         ~Registry(void) throw() { }
00141 
00142         // public class methods ------------------------------------------------
00143         void initialize(IN smart_ptr<story::Story>& story);
00144         void loadInstanceComponentData(IN smart_ptr<Instance>& instance);
00145         const Datahash * getComponentData(IN const char * typeName,
00146                                         IN const char * componentName);
00147 
00148 private:
00149         // private typedefs ----------------------------------------------------
00150         typedef std::map<std::string, smart_ptr<TypeComponentLoader> >
00151             loader_map_t;
00152 
00153         struct type_rec_t {
00154                 smart_ptr<Datahash>             typeData;
00155                 loader_map_t                    loaders;
00156         };
00157 
00158         typedef threadsafe_map<std::string, smart_ptr<type_rec_t> > type_map_t;
00159 
00160         // private helper methods ----------------------------------------------
00161         bool isTypeLoaded(IN const char * typeId);
00162         smart_ptr<type_rec_t> loadType(IN const char * typeId);
00163         smart_ptr<type_rec_t> getType(IN const char * typeId);
00164 
00165         // private member data -------------------------------------------------
00166         smart_ptr<story::Story> m_story;
00167         type_map_t              m_types;
00168 };
00169 
00170 
00171 static smart_ptr<Registry> s_registry;          // static instance
00172 
00173 
00174 void
00175 Registry::initialize
00176 (
00177 IN smart_ptr<story::Story>& story
00178 )
00179 {
00180         ASSERT(story, "null");
00181 
00182         m_story = story;
00183 }
00184 
00185 
00186 
00188 //
00189 //      Registry -- class methods
00190 //
00191 //      NOTE: all methods on this interface MUST be threadsafe!
00192 //
00194 
00195 void
00196 Registry::loadInstanceComponentData
00197 (
00198 IN smart_ptr<Instance>& instance
00199 )
00200 {
00201         ASSERT(instance, "null");
00202 
00203         const char * typeId = instance->getTypeId();
00204         DPRINTF("Loading instance component data for type %s", typeId);
00205 
00206         smart_ptr<type_rec_t> tr = this->getType(typeId);
00207         if (!tr) {
00208                 DPRINTF("  No such type?  %s", typeId);
00209                 ASSERT(false, "stop");
00210                 return;
00211         }
00212         DPRINTF("Got a type: %s", typeId);
00213 
00214         // loop through all type component loaders and have them load data
00215         for (loader_map_t::iterator i = tr->loaders.begin();
00216              i != tr->loaders.end(); ++i) {
00217                 const char * name = i->first.c_str();
00218                 smart_ptr<TypeComponentLoader> tcl = i->second;
00219                 ASSERT(tcl, "null");
00220 
00221                 DPRINTF("  Loading '%s' component...", name);
00222 
00223                 // get the hash data for this component stored with the type
00224                 smart_ptr<Datahash> hash = getSubhash(tr->typeData, name);
00225                 const char * id = getString(hash, "id");
00226 
00227                 // get full path to component file
00228                 std::string full_path = m_story->getObjectPath(name, id);
00229                 DPRINTF("    path: %s", full_path.c_str());
00230 
00231                 // invoke component loader
00232                 smart_ptr<ComponentData> cdata =
00233                     tcl->loadTS(instance, hash, full_path.c_str());
00234 
00235                 // update instance
00236                 instance->setComponentData(name, cdata);
00237         }
00238 }
00239 
00240 
00241 
00242 const Datahash *
00243 Registry::getComponentData
00244 (
00245 IN const char * typeName,
00246 IN const char * componentName
00247 )
00248 {
00249         ASSERT(typeName, "null");
00250         ASSERT(componentName, "null");
00251 
00252         smart_ptr<type_rec_t> type = this->getType(typeName);
00253         if (!type) {
00254                 DPRINTF("No such type: '%s'", typeName);
00255                 return NULL;
00256         }
00257 
00258         if (!type->typeData) {
00259                 DPRINTF("No type data?  type='%s'", typeName);
00260                 return NULL;
00261         }
00262 
00263         int nElements = type->typeData->count(componentName);
00264         if (!nElements) {
00265                 DPRINTF("type '%s' contains no component data: '%s'",
00266                     typeName, componentName);
00267                 return NULL;
00268         }
00269         ASSERT_THROW(1 == nElements, "Type '" << typeName << "' contains "
00270             "multiple '" << componentName << "' component entries?");
00271 
00272         // type contains a single entry for this component name
00273         Datahash::iterator_t i;
00274         type->typeData->getIterator(componentName, i);
00275         const hash_value_t * hv = type->typeData->getNextElementUnsafe(i);
00276         ASSERT_THROW(hv, "component disappeared");
00277         ASSERT_THROW(hv->hash, "component is not a hash");
00278         return hv->hash;
00279 }
00280 
00281 
00282 
00284 //
00285 //      Registry -- private helper methods
00286 //
00288 
00289 smart_ptr<Registry::type_rec_t>
00290 Registry::loadType
00291 (
00292 IN const char * typeId
00293 )
00294 {
00295         perf::Timer timer("TypeRegistry::loadType");
00296         ASSERT(typeId, "null");
00297         ASSERT(m_story, "null");
00298 
00299         // threadsafe!
00300         // we rely on story and types map being threadsafe
00301         DPRINTF("Reading type '%s'", typeId);
00302 
00303         // create type record
00304         smart_ptr<type_rec_t> type_rec = new type_rec_t;
00305         ASSERT(type_rec, "out of memory");
00306 
00307         // get full path
00308         std::string path = m_story->getObjectPath("type", typeId);
00309         path += ".type";        // types are special--get extension always
00310 
00311         // load as datahash
00312         type_rec->typeData = readHashFromTextFile(path.c_str());
00313         ASSERT(type_rec->typeData, "null");
00314 
00315         // read keys (components)
00316         VecString badKeys;
00317         Datahash * hash = type_rec->typeData;
00318         Datahash::iterator_t i;
00319         hash->getIterator(i);
00320         std::string keystr;
00321         hash_value_t hv;
00322         while (hash->getNextElement(i, keystr, hv)) {
00323                 const char * key = keystr.c_str();
00324                 DPRINTF("  key: %s", key);
00325                 smart_ptr<Datahash> data = hv.hash;
00326                 if (!data)
00327                         continue;       // skip this element
00328 
00329                 // get the ID
00330                 const char * id = getString(data, "id");
00331                 DPRINTF("  id: %s", id);
00332 
00333                 // what is full path to this object?
00334                 std::string full_path = m_story->getObjectPath(key, id);
00335                 DPRINTF("  path: %s", full_path.c_str());
00336 
00337                 // do we have any loaders for this?
00338                 smart_ptr<TypeComponentLoader> tcl;
00339                 smart_ptr<vec_loader_t> pv;
00340                 s_loaders.lookup(key, pv);
00341                 if (pv) {
00342                         vec_loader_t::iterator_t i;
00343                         pv->getIterator(i);
00344                         smart_ptr<TypeComponentLoader> p;
00345                         while (pv->getNextElement(i, p)) {
00346                                 ASSERT(p, "null loader in queue");
00347                                 DPRINTF("  Checking loader...");
00348                                 if (p->isMyFormat(data, full_path.c_str())) {
00349                                         tcl = p;
00350                                         break;
00351                                 }
00352                         }
00353                 }
00354 
00355                 if (!tcl) {
00356                         // no registered loader for this!  Skip it...
00357                         DPRINTF("No loader for component %s=%s in type %s (%s)",
00358                             key, id, typeId, path.c_str());
00359 
00360                         // don't need datahash data either
00361                         badKeys.push_back(key);
00362                 } else {
00363                         // have a loader!  Save it for this type
00364                         DPRINTF("Found a loader for %s=%s in type %s (%s)",
00365                             key, id, typeId, path.c_str());
00366                         DPRINTF("  Have %d loaders so far",
00367                             (int) type_rec->loaders.size());
00368                         type_rec->loaders[key] = tcl;
00369                 }
00370         }
00371 
00372         // nuke any bad keys
00373         for (VecString::const_iterator i = badKeys.begin(); i != badKeys.end();
00374              ++i) {
00375                 const char * key = i->c_str();
00376                 type_rec->typeData->remove(key);
00377         }
00378 
00379         // okay, have datahash with all loaded components
00380         DPRINTF("Have a type, about to insert...");
00381         m_types.insert(typeId, type_rec);
00382         DPRINTF("Returning type record now...");
00383         return type_rec;
00384 }
00385 
00386 
00387 
00388 smart_ptr<Registry::type_rec_t>
00389 Registry::getType
00390 (
00391 IN const char * typeId
00392 )
00393 {
00394         ASSERT(typeId, "null");
00395 
00396         // threadsafe!  map is threadsafe
00397         smart_ptr<type_rec_t> result;
00398         m_types.lookup(typeId, result);
00399 
00400         // if we need to load it, do so now
00401         return (result) ? result : this->loadType(typeId);
00402 }
00403 
00404 
00405 
00407 //
00408 //      InstImpl -- class that implements the aesop::Instance interface
00409 //
00411 
00412 class InstImpl : public Instance {
00413 public:
00414         // constructor, destructor ---------------------------------------------
00415         ~InstImpl(void) throw() { }
00416 
00417         // public class methods ------------------------------------------------
00418         void initialize(IO std::istream& stream);
00419         void initialize(IN const char * instanceId,
00420                                 IN const char * typeId)
00421                 {
00422                         if (instanceId) {
00423                                 m_instanceId.set(instanceId);
00424                         }
00425                         m_typeId.set(typeId);
00426                 }
00427 
00428         // aesop::Instance class interface methods -----------------------------
00429 
00430         // REMEMBER: must be threadsafe!
00431         const char * getInstanceId(void) const throw() { return m_instanceId; }
00432         const char * getTypeId(void) const throw() { return m_typeId; }
00433         placement_t& getPlacement(void) throw() { return m_placement; }
00434         void setComponentData(IN const char * name,
00435                                 IN smart_ptr<ComponentData>& data);
00436         smart_ptr<ComponentData> getComponentData(IN const char * name);
00437         const Datahash * getTypeComponentData(IN const char * name);
00438         void dump(IN const char * title) throw();
00439         void setInstanceData(IN const char * name,
00440                                 IN smart_ptr<Datahash>& data);
00441         smart_ptr<Datahash> getInstanceData(IN const char * name);
00442 
00443 private:
00444         // private typedefs ----------------------------------------------------
00445         typedef threadsafe_map<std::string, smart_ptr<ComponentData> >
00446             data_map_t;
00447 
00448         typedef threadsafe_map<std::string, smart_ptr<Datahash> >
00449             instData_map_t;
00450 
00451         // private helper methods ----------------------------------------------
00452 
00453         // private member data -------------------------------------------------
00454         AESOPIdBuffer           m_instanceId;   // ID of this instance
00455         AESOPIdBuffer           m_typeId;       // ID of the type
00456         placement_t             m_placement;
00457         data_map_t              m_data;         // map of component data objects
00458         instData_map_t          m_instData;     // per-component raw inst data
00459 };
00460 
00461 
00462 
00464 //
00465 //      InstImpl -- public class methods (only accessible in this file)
00466 //
00468 
00469 void
00470 InstImpl::initialize
00471 (
00472 IN std::istream& stream
00473 )
00474 {
00475         ASSERT(stream.good(), "bad?");
00476 
00477         // require these
00478         bool readTypeId = false;
00479         bool readPlacement = false;
00480 
00481         // switch based on key
00482         std::string token;
00483         for (;;) {
00484                 getNextToken(stream, token);
00485                 if ("}" == token)
00486                         break;          // all done parsing instance
00487 
00488                 // is this a keyword we understand?
00489                 if ("typeId" == token) {
00490                         readIdFromStream(stream, m_typeId, token);
00491                         readTypeId = true;
00492                 } else if ("placement" == token) {
00493                         expectToken(stream, "{");
00494                         m_placement = parsePlacement(stream);
00495                         readPlacement = true;
00496                 } else if ("instanceId" == token) {
00497                         readIdFromStream(stream, m_instanceId, token);
00498                 } else if ("instanceData" == token) {
00499                         expectToken(stream, "{");
00500                         smart_ptr<Datahash> hash =
00501                             readHashFromStream("instanceData", stream);
00502 
00503                         // add subhashes as per-component instance data
00504                         Datahash::iterator_t i;
00505                         hash->getIterator(i);
00506                         std::string keystr;
00507                         hash_value_t hv;
00508                         while (hash->getNextElement(i, keystr, hv)) {
00509                                 if (!hv.hash)
00510                                         continue;       // skip this
00511                                 const char * name = keystr.c_str();
00512                                 DPRINTF("  Setting instance data: %s", name);
00513                                 this->setInstanceData(name, hv.hash);
00514                         }
00515                 } else {
00516                         WAVE_EX(wex);
00517                         wex << "Unexpected keyword while parsing instance: ";
00518                         wex << token;
00519                 }
00520         }
00521 
00522         // get all the data we needed?
00523         if (!readTypeId || !readPlacement) {
00524                 WAVE_EX(wex);
00525                 wex << "Instance is missing type and/or placement.";
00526         }
00527 }
00528 
00529 
00530 
00532 //
00533 //      InstImpl -- aesop::Instance class interface methods
00534 //
00536 
00537 void
00538 InstImpl::setComponentData
00539 (
00540 IN const char * name,
00541 IN smart_ptr<ComponentData>& data
00542 )
00543 {
00544         ASSERT(name, "null");
00545         // ASSERT(data) -- can be null
00546 
00547         // threadsafe!  We use a threadsafe collection
00548         if (data) {
00549                 // caller has provided component data.  update our map
00550                 m_data.insert(name, data);
00551                 return;
00552         }
00553 
00554         // caller has requested that we clear this component data
00555         m_data.remove(name);
00556 }
00557 
00558 
00559 
00560 smart_ptr<ComponentData>
00561 InstImpl::getComponentData
00562 (
00563 IN const char * name
00564 )
00565 {
00566         ASSERT(name, "null");
00567 
00568         smart_ptr<ComponentData> cdata;
00569         m_data.lookup(name, cdata);
00570         return cdata;           // will be null if not found
00571 }
00572 
00573 
00574 
00575 const Datahash *
00576 InstImpl::getTypeComponentData
00577 (
00578 IN const char * name
00579 )
00580 {
00581         ASSERT(name, "null");
00582         ASSERT(s_registry, "null");
00583 
00584         return s_registry->getComponentData(m_typeId, name);
00585 }
00586 
00587 
00588 
00589 void
00590 InstImpl::dump
00591 (
00592 IN const char * title
00593 )
00594 throw()
00595 {
00596         DPRINTF("Instance (%s)", title);
00597         DPRINTF("  instanceId=%s", this->getInstanceId());
00598         DPRINTF("  typeId=%s", this->getTypeId());
00599         m_placement.dump("instance");
00600 
00601         // iterate over all components in map
00602         data_map_t::iterator_t i;
00603         m_data.getIterator(i);
00604         std::string name;
00605         smart_ptr<ComponentData> data;
00606         while (m_data.getNextElement(i, name, data)) {
00607                 ASSERT(data, "null");
00608 
00609                 DPRINTF("  Contains component data: %s", name.c_str());
00610                 std::string msg = "    '";
00611                 msg += name;
00612                 msg += "' component data";
00613                 data->dump(msg.c_str());
00614         }
00615 }
00616 
00617 
00618 
00619 void
00620 InstImpl::setInstanceData
00621 (
00622 IN const char * name,
00623 IN smart_ptr<Datahash>& data
00624 )
00625 {
00626         ASSERT(name, "null");
00627         // ASSERT(data) -- can be null!
00628 
00629         if (data) {
00630                 m_instData.insert(name, data);
00631         } else {
00632                 m_instData.remove(name);
00633         }
00634 }
00635 
00636 
00637 
00638 smart_ptr<Datahash>
00639 InstImpl::getInstanceData
00640 (
00641 IN const char * name
00642 )
00643 {
00644         smart_ptr<Datahash> data;
00645         m_instData.lookup(name, data);
00646         return data;            // will be null if not found
00647 }
00648 
00649 
00650 
00652 //
00653 //      Instance -- static factory methods
00654 //
00656 
00657 smart_ptr<Instance>
00658 Instance::parse
00659 (
00660 IO std::istream& stream
00661 )
00662 {
00663         perf::Timer timer("parseInstance");
00664         ASSERT(stream.good(), "bad?");
00665         ASSERT(s_registry, "need to call initializeTypeInstanceLibrary()");
00666 
00667         DPRINTF("Parsing instance...");
00668         smart_ptr<InstImpl> local = new InstImpl;
00669         ASSERT(local, "out of memory");
00670 
00671         local->initialize(stream);
00672 
00673         smart_ptr<Instance> i = local;
00674         s_registry->loadInstanceComponentData(i);
00675 
00676         i->dump("Just parsed");
00677 
00678         // all done!
00679         return i;
00680 }
00681 
00682 
00683 
00684 smart_ptr<Instance>
00685 Instance::create
00686 (
00687 IN const char * instanceId,
00688 IN const char * typeId
00689 )
00690 {
00691         // ASSERT(instanceId) -- can be null!
00692         ASSERT(typeId, "null");
00693         ASSERT(s_registry, "need to call initializeTypeInstanceLibrary()");
00694 
00695         smart_ptr<InstImpl> local = new InstImpl;
00696         ASSERT(local, "out of memory");
00697 
00698         local->initialize(instanceId, typeId);
00699 
00700         smart_ptr<Instance> i = local;
00701         s_registry->loadInstanceComponentData(i);
00702 
00703         i->dump("Just created");
00704 
00705         return i;
00706 }
00707 
00708 
00709 
00711 //
00712 //      public API
00713 //
00715 
00716 void
00717 registerTypeComponentLoader
00718 (
00719 IN smart_ptr<TypeComponentLoader>& tcl
00720 )
00721 {
00722         ASSERT(tcl, "null");
00723 
00724         // what type of component does this loader load?
00725         const char * cName = tcl->getComponentName();
00726         DPRINTF("Registering component loader: '%s'", cName);
00727         ASSERT(cName, "null");
00728 
00729         // verify this isn't a reserved keyword
00730         ASSERT_THROW(!isReservedComponent(cName),
00731             "Cannot register a type component loader for this type: '" <<
00732             cName << "'.  That is a reserved type component name.");
00733 
00734         // find vector of loaders for this type
00735         smart_ptr<vec_loader_t> pv;
00736         s_loaders.lookup(cName, pv);
00737         if (!pv) {
00738                 // need to create new map entry!
00739                 smart_ptr<vec_loader_t> pv1 = new vec_loader_t;
00740                 ASSERT(pv1, "out of memory");
00741                 s_loaders.insert(cName, pv1);
00742                 pv = pv1;
00743         }
00744 
00745         // append to vector
00746         ASSERT(pv, "should have a vector of component loaders now");
00747         pv->push_back(tcl);
00748 }
00749 
00750 
00751 
00752 void
00753 initializeTypeInstanceLibrary
00754 (
00755 IN smart_ptr<story::Story>& story
00756 )
00757 {
00758         ASSERT(story, "null");
00759 
00760         // already have instance?
00761         if (s_registry) {
00762                 DPRINTF("already created registry!");
00763                 DPRINTF("ignoring redundant initializeTypeInstanceLibrary() call");
00764                 return;
00765         }
00766 
00767         smart_ptr<Registry> local = new Registry;
00768         ASSERT(local, "out of memory");
00769         local->initialize(story);
00770 
00771         // successfully initialized!
00772         s_registry = local;
00773 }
00774 
00775 
00776 
00777 void
00778 loadInstances
00779 (
00780 IO std::istream& stream,
00781 OUT vec_instance_t& instances
00782 )
00783 {
00784         perf::Timer timer("loadInstances");
00785         ASSERT(stream.good(), "bad?");
00786         instances.clear();
00787 
00788         // keep parsing until end of stream or closing bracket
00789         std::string token;
00790         while (true) {
00791                 bool eof = false;
00792                 getNextToken(stream, token, true, &eof);
00793                 if (eof || "}" == token) {
00794                         break;          // end of list!
00795                 }
00796 
00797                 if ("instance" != token) {
00798                         WAVE_EX(wex);
00799                         wex << "Invalid token!  Expected 'instance' in ";
00800                         wex << "instance list, read '" << token << "' instead.";
00801                 }
00802 
00803                 // open instance bracket
00804                 expectToken(stream, "{");
00805 
00806                 // parse instance
00807                 instances.push_back(Instance::parse(stream));
00808         }
00809 
00810         // all done!
00811         DPRINTF("Read %d instances from stream", (int) instances.size());
00812 }
00813 
00814 
00815 
00816 const char *
00817 getKeyWithOverrides
00818 (
00819 IN const char * keyName,
00820 IN const Datahash * instanceData,
00821 IN const Datahash * typeData,
00822 IN const Datahash * modelData,
00823 IN eDatahash_Flag flag
00824 )
00825 {
00826         ASSERT(keyName, "null");
00827         // ASSERT(instanceData) -- can be null
00828         // ASSERT(typeData) -- can be null
00829         // ASSERT(modelData) -- also can be null!
00830 
00831         const char * val = NULL;        // the value to return
00832 
00833         // check instance data first--it overrides everything else
00834         if (instanceData) {
00835                 val = getString(instanceData, keyName, eDatahash_Optional);
00836         }
00837         if (val) {
00838                 DPRINTF("    Getting value from instance: %s = %s", keyName,
00839                     val);
00840                 return val;
00841         }
00842 
00843         // check type data next--it overrides the model data
00844         if (typeData) {
00845                 val = getString(typeData, keyName, eDatahash_Optional);
00846         }
00847         if (val) {
00848                 DPRINTF("    Getting value from type: %s = %s", keyName, val);
00849                 return val;
00850         }
00851 
00852         // not overridden at all--use base value in model
00853         if (modelData) {
00854                 val = getString(modelData, keyName, flag);
00855         }
00856         if (val) {
00857                 DPRINTF("    Getting value from model: %s = %s", keyName, val);
00858         }
00859         return val;
00860 }
00861 
00862 
00863 
00864 };      // aesop namespace