1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "referencecache.h"
23
24 #include "debugging/debugging.h"
25
26 #include "iscenegraph.h"
27 #include "iselection.h"
28 #include "iundo.h"
29 #include "imap.h"
30 MapModules& ReferenceAPI_getMapModules();
31 #include "imodel.h"
32 ModelModules& ReferenceAPI_getModelModules();
33 #include "ifilesystem.h"
34 #include "iarchive.h"
35 #include "ifiletypes.h"
36 #include "ireference.h"
37 #include "ientity.h"
38 #include "qerplugin.h"
39
40 #include <list>
41
42 #include "container/cache.h"
43 #include "container/hashfunc.h"
44 #include "os/path.h"
45 #include "stream/textfilestream.h"
46 #include "nullmodel.h"
47 #include "maplib.h"
48 #include "stream/stringstream.h"
49 #include "os/file.h"
50 #include "moduleobserver.h"
51 #include "moduleobservers.h"
52
53 #include "mainframe.h"
54 #include "map.h"
55 #include "filetypes.h"
56
57
58 bool References_Saved();
59
MapChanged()60 void MapChanged()
61 {
62 Map_SetModified(g_map, !References_Saved());
63 }
64
65
66 EntityCreator* g_entityCreator = 0;
67
MapResource_loadFile(const MapFormat & format,scene::Node & root,const char * filename)68 bool MapResource_loadFile(const MapFormat& format, scene::Node& root, const char* filename)
69 {
70 globalOutputStream() << "Open file " << filename << " for read...";
71 TextFileInputStream file(filename);
72 if(!file.failed())
73 {
74 globalOutputStream() << "success\n";
75 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(filename));
76 ASSERT_NOTNULL(g_entityCreator);
77 format.readGraph(root, file, *g_entityCreator);
78 return true;
79 }
80 else
81 {
82 globalErrorStream() << "failure\n";
83 return false;
84 }
85 }
86
MapResource_load(const MapFormat & format,const char * path,const char * name)87 NodeSmartReference MapResource_load(const MapFormat& format, const char* path, const char* name)
88 {
89 NodeSmartReference root(NewMapRoot(name));
90
91 StringOutputStream fullpath(256);
92 fullpath << path << name;
93
94 if(path_is_absolute(fullpath.c_str()))
95 {
96 MapResource_loadFile(format, root, fullpath.c_str());
97 }
98 else
99 {
100 globalErrorStream() << "map path is not fully qualified: " << makeQuoted(fullpath.c_str()) << "\n";
101 }
102
103 return root;
104 }
105
MapResource_saveFile(const MapFormat & format,scene::Node & root,GraphTraversalFunc traverse,const char * filename)106 bool MapResource_saveFile(const MapFormat& format, scene::Node& root, GraphTraversalFunc traverse, const char* filename)
107 {
108 //ASSERT_MESSAGE(path_is_absolute(filename), "MapResource_saveFile: path is not absolute: " << makeQuoted(filename));
109 globalOutputStream() << "Open file " << filename << " for write...";
110 TextFileOutputStream file(filename);
111 if(!file.failed())
112 {
113 globalOutputStream() << "success\n";
114 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(filename));
115 format.writeGraph(root, traverse, file);
116 return true;
117 }
118
119 globalErrorStream() << "failure\n";
120 return false;
121 }
122
file_saveBackup(const char * path)123 bool file_saveBackup(const char* path)
124 {
125 if(file_writeable(path))
126 {
127 StringOutputStream backup(256);
128 backup << StringRange(path, path_get_extension(path)) << "bak";
129
130 return (!file_exists(backup.c_str()) || file_remove(backup.c_str())) // remove backup
131 && file_move(path, backup.c_str()); // rename current to backup
132 }
133
134 globalErrorStream() << "map path is not writeable: " << makeQuoted(path) << "\n";
135 return false;
136 }
137
MapResource_save(const MapFormat & format,scene::Node & root,const char * path,const char * name)138 bool MapResource_save(const MapFormat& format, scene::Node& root, const char* path, const char* name)
139 {
140 StringOutputStream fullpath(256);
141 fullpath << path << name;
142
143 if(path_is_absolute(fullpath.c_str()))
144 {
145 if(!file_exists(fullpath.c_str()) || file_saveBackup(fullpath.c_str()))
146 {
147 return MapResource_saveFile(format, root, Map_Traverse, fullpath.c_str());
148 }
149
150 globalErrorStream() << "failed to save a backup map file: " << makeQuoted(fullpath.c_str()) << "\n";
151 return false;
152 }
153
154 globalErrorStream() << "map path is not fully qualified: " << makeQuoted(fullpath.c_str()) << "\n";
155 return false;
156 }
157
158 namespace
159 {
160 NodeSmartReference g_nullNode(NewNullNode());
161 NodeSmartReference g_nullModel(g_nullNode);
162 }
163
164 class NullModelLoader : public ModelLoader
165 {
166 public:
loadModel(ArchiveFile & file)167 scene::Node& loadModel(ArchiveFile& file)
168 {
169 return g_nullModel;
170 }
171 };
172
173 namespace
174 {
175 NullModelLoader g_NullModelLoader;
176 }
177
178
179 /// \brief Returns the model loader for the model \p type or 0 if the model \p type has no loader module
ModelLoader_forType(const char * type)180 ModelLoader* ModelLoader_forType(const char* type)
181 {
182 const char* moduleName = findModuleName(&GlobalFiletypes(), ModelLoader::Name(), type);
183 if(string_not_empty(moduleName))
184 {
185 ModelLoader* table = ReferenceAPI_getModelModules().findModule(moduleName);
186 if(table != 0)
187 {
188 return table;
189 }
190 else
191 {
192 globalErrorStream() << "ERROR: Model type incorrectly registered: \"" << moduleName << "\"\n";
193 return &g_NullModelLoader;
194 }
195 }
196 return 0;
197 }
198
ModelResource_load(ModelLoader * loader,const char * name)199 NodeSmartReference ModelResource_load(ModelLoader* loader, const char* name)
200 {
201 ScopeDisableScreenUpdates disableScreenUpdates(path_get_filename_start(name));
202
203 NodeSmartReference model(g_nullModel);
204
205 {
206 ArchiveFile* file = GlobalFileSystem().openFile(name);
207
208 if(file != 0)
209 {
210 globalOutputStream() << "Loaded Model: \"" << name << "\"\n";
211 model = loader->loadModel(*file);
212 file->release();
213 }
214 else
215 {
216 globalErrorStream() << "Model load failed: \"" << name << "\"\n";
217 }
218 }
219
220 model.get().m_isRoot = true;
221
222 return model;
223 }
224
225
path_hash(const char * path,hash_t previous=0)226 inline hash_t path_hash(const char* path, hash_t previous = 0)
227 {
228 #if defined(WIN32)
229 return string_hash_nocase(path, previous);
230 #else // UNIX
231 return string_hash(path, previous);
232 #endif
233 }
234
235 struct PathEqual
236 {
operator ()PathEqual237 bool operator()(const CopiedString& path, const CopiedString& other) const
238 {
239 return path_equal(path.c_str(), other.c_str());
240 }
241 };
242
243 struct PathHash
244 {
245 typedef hash_t hash_type;
operator ()PathHash246 hash_type operator()(const CopiedString& path) const
247 {
248 return path_hash(path.c_str());
249 }
250 };
251
252 typedef std::pair<CopiedString, CopiedString> ModelKey;
253
254 struct ModelKeyEqual
255 {
operator ()ModelKeyEqual256 bool operator()(const ModelKey& key, const ModelKey& other) const
257 {
258 return path_equal(key.first.c_str(), other.first.c_str()) && path_equal(key.second.c_str(), other.second.c_str());
259 }
260 };
261
262 struct ModelKeyHash
263 {
264 typedef hash_t hash_type;
operator ()ModelKeyHash265 hash_type operator()(const ModelKey& key) const
266 {
267 return hash_combine(path_hash(key.first.c_str()), path_hash(key.second.c_str()));
268 }
269 };
270
271 typedef HashTable<ModelKey, NodeSmartReference, ModelKeyHash, ModelKeyEqual> ModelCache;
272 ModelCache g_modelCache;
273 bool g_modelCache_enabled = true;
274
ModelCache_find(const char * path,const char * name)275 ModelCache::iterator ModelCache_find(const char* path, const char* name)
276 {
277 if(g_modelCache_enabled)
278 {
279 return g_modelCache.find(ModelKey(path, name));
280 }
281 return g_modelCache.end();
282 }
283
ModelCache_insert(const char * path,const char * name,scene::Node & node)284 ModelCache::iterator ModelCache_insert(const char* path, const char* name, scene::Node& node)
285 {
286 if(g_modelCache_enabled)
287 {
288 return g_modelCache.insert(ModelKey(path, name), NodeSmartReference(node));
289 }
290 return g_modelCache.insert(ModelKey("", ""), g_nullModel);
291 }
292
ModelCache_flush(const char * path,const char * name)293 void ModelCache_flush(const char* path, const char* name)
294 {
295 ModelCache::iterator i = g_modelCache.find(ModelKey(path, name));
296 if(i != g_modelCache.end())
297 {
298 //ASSERT_MESSAGE((*i).value.getCount() == 0, "resource flushed while still in use: " << (*i).key.first.c_str() << (*i).key.second.c_str());
299 g_modelCache.erase(i);
300 }
301 }
302
ModelCache_clear()303 void ModelCache_clear()
304 {
305 g_modelCache_enabled = false;
306 g_modelCache.clear();
307 g_modelCache_enabled = true;
308 }
309
Model_load(ModelLoader * loader,const char * path,const char * name,const char * type)310 NodeSmartReference Model_load(ModelLoader* loader, const char* path, const char* name, const char* type)
311 {
312 if(loader != 0)
313 {
314 return ModelResource_load(loader, name);
315 }
316 else
317 {
318 const char* moduleName = findModuleName(&GlobalFiletypes(), MapFormat::Name(), type);
319 if(string_not_empty(moduleName))
320 {
321 const MapFormat* format = ReferenceAPI_getMapModules().findModule(moduleName);
322 if(format != 0)
323 {
324 return MapResource_load(*format, path, name);
325 }
326 else
327 {
328 globalErrorStream() << "ERROR: Map type incorrectly registered: \"" << moduleName << "\"\n";
329 return g_nullModel;
330 }
331 }
332 else
333 {
334 if(string_not_empty(type))
335 {
336 globalErrorStream() << "Model type not supported: \"" << name << "\"\n";
337 }
338 return g_nullModel;
339 }
340 }
341 }
342
343 namespace
344 {
345 bool g_realised = false;
346
347 // name may be absolute or relative
rootPath(const char * name)348 const char* rootPath(const char* name)
349 {
350 return GlobalFileSystem().findRoot(
351 path_is_absolute(name)
352 ? name
353 : GlobalFileSystem().findFile(name)
354 );
355 }
356 }
357
358 struct ModelResource : public Resource
359 {
360 NodeSmartReference m_model;
361 const CopiedString m_originalName;
362 CopiedString m_path;
363 CopiedString m_name;
364 CopiedString m_type;
365 ModelLoader* m_loader;
366 ModuleObservers m_observers;
367 std::time_t m_modified;
368 std::size_t m_unrealised;
369
ModelResourceModelResource370 ModelResource(const CopiedString& name) :
371 m_model(g_nullModel),
372 m_originalName(name),
373 m_type(path_get_extension(name.c_str())),
374 m_loader(0),
375 m_modified(0),
376 m_unrealised(1)
377 {
378 m_loader = ModelLoader_forType(m_type.c_str());
379
380 if(g_realised)
381 {
382 realise();
383 }
384 }
~ModelResourceModelResource385 ~ModelResource()
386 {
387 if(realised())
388 {
389 unrealise();
390 }
391 ASSERT_MESSAGE(!realised(), "ModelResource::~ModelResource: resource reference still realised: " << makeQuoted(m_name.c_str()));
392 }
393 // NOT COPYABLE
394 ModelResource(const ModelResource&);
395 // NOT ASSIGNABLE
396 ModelResource& operator=(const ModelResource&);
397
setModelModelResource398 void setModel(const NodeSmartReference& model)
399 {
400 m_model = model;
401 }
clearModelModelResource402 void clearModel()
403 {
404 m_model = g_nullModel;
405 }
406
loadCachedModelResource407 void loadCached()
408 {
409 if(g_modelCache_enabled)
410 {
411 // cache lookup
412 ModelCache::iterator i = ModelCache_find(m_path.c_str(), m_name.c_str());
413 if(i == g_modelCache.end())
414 {
415 i = ModelCache_insert(
416 m_path.c_str(),
417 m_name.c_str(),
418 Model_load(m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str())
419 );
420 }
421
422 setModel((*i).value);
423 }
424 else
425 {
426 setModel(Model_load(m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str()));
427 }
428 }
429
loadModelModelResource430 void loadModel()
431 {
432 loadCached();
433 connectMap();
434 mapSave();
435 }
436
loadModelResource437 bool load()
438 {
439 ASSERT_MESSAGE(realised(), "resource not realised");
440 if(m_model == g_nullModel)
441 {
442 loadModel();
443 }
444
445 return m_model != g_nullModel;
446 }
saveModelResource447 bool save()
448 {
449 if(!mapSaved())
450 {
451 const char* moduleName = findModuleName(GetFileTypeRegistry(), MapFormat::Name(), m_type.c_str());
452 if(string_not_empty(moduleName))
453 {
454 const MapFormat* format = ReferenceAPI_getMapModules().findModule(moduleName);
455 if(format != 0 && MapResource_save(*format, m_model.get(), m_path.c_str(), m_name.c_str()))
456 {
457 mapSave();
458 return true;
459 }
460 }
461 }
462 return false;
463 }
flushModelResource464 void flush()
465 {
466 if(realised())
467 {
468 ModelCache_flush(m_path.c_str(), m_name.c_str());
469 }
470 }
getNodeModelResource471 scene::Node* getNode()
472 {
473 //if(m_model != g_nullModel)
474 {
475 return m_model.get_pointer();
476 }
477 //return 0;
478 }
setNodeModelResource479 void setNode(scene::Node* node)
480 {
481 ModelCache::iterator i = ModelCache_find(m_path.c_str(), m_name.c_str());
482 if(i != g_modelCache.end())
483 {
484 (*i).value = NodeSmartReference(*node);
485 }
486 setModel(NodeSmartReference(*node));
487
488 connectMap();
489 }
attachModelResource490 void attach(ModuleObserver& observer)
491 {
492 if(realised())
493 {
494 observer.realise();
495 }
496 m_observers.attach(observer);
497 }
detachModelResource498 void detach(ModuleObserver& observer)
499 {
500 if(realised())
501 {
502 observer.unrealise();
503 }
504 m_observers.detach(observer);
505 }
realisedModelResource506 bool realised()
507 {
508 return m_unrealised == 0;
509 }
realiseModelResource510 void realise()
511 {
512 ASSERT_MESSAGE(m_unrealised != 0, "ModelResource::realise: already realised");
513 if(--m_unrealised == 0)
514 {
515 m_path = rootPath(m_originalName.c_str());
516 m_name = path_make_relative(m_originalName.c_str(), m_path.c_str());
517
518 //globalOutputStream() << "ModelResource::realise: " << m_path.c_str() << m_name.c_str() << "\n";
519
520 m_observers.realise();
521 }
522 }
unrealiseModelResource523 void unrealise()
524 {
525 if(++m_unrealised == 1)
526 {
527 m_observers.unrealise();
528
529 //globalOutputStream() << "ModelResource::unrealise: " << m_path.c_str() << m_name.c_str() << "\n";
530 clearModel();
531 }
532 }
isMapModelResource533 bool isMap() const
534 {
535 return Node_getMapFile(m_model) != 0;
536 }
connectMapModelResource537 void connectMap()
538 {
539 MapFile* map = Node_getMapFile(m_model);
540 if(map != 0)
541 {
542 map->setChangedCallback(FreeCaller<MapChanged>());
543 }
544 }
modifiedModelResource545 std::time_t modified() const
546 {
547 StringOutputStream fullpath(256);
548 fullpath << m_path.c_str() << m_name.c_str();
549 return file_modified(fullpath.c_str());
550 }
mapSaveModelResource551 void mapSave()
552 {
553 m_modified = modified();
554 MapFile* map = Node_getMapFile(m_model);
555 if(map != 0)
556 {
557 map->save();
558 }
559 }
mapSavedModelResource560 bool mapSaved() const
561 {
562 MapFile* map = Node_getMapFile(m_model);
563 if(map != 0)
564 {
565 return m_modified == modified() && map->saved();
566 }
567 return true;
568 }
isModifiedModelResource569 bool isModified() const
570 {
571 return ((!string_empty(m_path.c_str()) // had or has an absolute path
572 && m_modified != modified()) // AND disk timestamp changed
573 || !path_equal(rootPath(m_originalName.c_str()), m_path.c_str())); // OR absolute vfs-root changed
574 }
refreshModelResource575 void refresh()
576 {
577 if(isModified())
578 {
579 flush();
580 unrealise();
581 realise();
582 }
583 }
584 };
585
586 class HashtableReferenceCache : public ReferenceCache, public ModuleObserver
587 {
588 typedef HashedCache<CopiedString, ModelResource, PathHash, PathEqual> ModelReferences;
589 ModelReferences m_references;
590 std::size_t m_unrealised;
591
592 class ModelReferencesSnapshot
593 {
594 ModelReferences& m_references;
595 typedef std::list<ModelReferences::iterator> Iterators;
596 Iterators m_iterators;
597 public:
598 typedef Iterators::iterator iterator;
ModelReferencesSnapshot(ModelReferences & references)599 ModelReferencesSnapshot(ModelReferences& references) : m_references(references)
600 {
601 for(ModelReferences::iterator i = m_references.begin(); i != m_references.end(); ++i)
602 {
603 m_references.capture(i);
604 m_iterators.push_back(i);
605 }
606 }
~ModelReferencesSnapshot()607 ~ModelReferencesSnapshot()
608 {
609 for(Iterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i)
610 {
611 m_references.release(*i);
612 }
613 }
begin()614 iterator begin()
615 {
616 return m_iterators.begin();
617 }
end()618 iterator end()
619 {
620 return m_iterators.end();
621 }
622 };
623
624 public:
625
626 typedef ModelReferences::iterator iterator;
627
HashtableReferenceCache()628 HashtableReferenceCache() : m_unrealised(1)
629 {
630 }
631
begin()632 iterator begin()
633 {
634 return m_references.begin();
635 }
end()636 iterator end()
637 {
638 return m_references.end();
639 }
640
clear()641 void clear()
642 {
643 m_references.clear();
644 }
645
capture(const char * path)646 Resource* capture(const char* path)
647 {
648 //globalOutputStream() << "capture: \"" << path << "\"\n";
649 return m_references.capture(CopiedString(path)).get();
650 }
release(const char * path)651 void release(const char* path)
652 {
653 m_references.release(CopiedString(path));
654 //globalOutputStream() << "release: \"" << path << "\"\n";
655 }
656
setEntityCreator(EntityCreator & entityCreator)657 void setEntityCreator(EntityCreator& entityCreator)
658 {
659 g_entityCreator = &entityCreator;
660 }
661
realised() const662 bool realised() const
663 {
664 return m_unrealised == 0;
665 }
realise()666 void realise()
667 {
668 ASSERT_MESSAGE(m_unrealised != 0, "HashtableReferenceCache::realise: already realised");
669 if(--m_unrealised == 0)
670 {
671 g_realised = true;
672
673 {
674 ModelReferencesSnapshot snapshot(m_references);
675 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
676 {
677 ModelReferences::value_type& value = *(*i);
678 if(value.value.count() != 1)
679 {
680 value.value.get()->realise();
681 }
682 }
683 }
684 }
685 }
unrealise()686 void unrealise()
687 {
688 if(++m_unrealised == 1)
689 {
690 g_realised = false;
691
692 {
693 ModelReferencesSnapshot snapshot(m_references);
694 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
695 {
696 ModelReferences::value_type& value = *(*i);
697 if(value.value.count() != 1)
698 {
699 value.value.get()->unrealise();
700 }
701 }
702 }
703
704 ModelCache_clear();
705 }
706 }
refresh()707 void refresh()
708 {
709 ModelReferencesSnapshot snapshot(m_references);
710 for(ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i)
711 {
712 ModelResource* resource = (*(*i)).value.get();
713 if(!resource->isMap())
714 {
715 resource->refresh();
716 }
717 }
718 }
719 };
720
721 namespace
722 {
723 HashtableReferenceCache g_referenceCache;
724 }
725
726 #if 0
727 class ResourceVisitor
728 {
729 public:
730 virtual void visit(const char* name, const char* path, const
731 };
732 #endif
733
SaveReferences()734 void SaveReferences()
735 {
736 ScopeDisableScreenUpdates disableScreenUpdates("Processing...", "Saving Map");
737 for(HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i)
738 {
739 (*i).value->save();
740 }
741 MapChanged();
742 }
743
References_Saved()744 bool References_Saved()
745 {
746 for(HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i)
747 {
748 scene::Node* node = (*i).value->getNode();
749 if(node != 0)
750 {
751 MapFile* map = Node_getMapFile(*node);
752 if(map != 0 && !map->saved())
753 {
754 return false;
755 }
756 }
757 }
758 return true;
759 }
760
RefreshReferences()761 void RefreshReferences()
762 {
763 ScopeDisableScreenUpdates disableScreenUpdates("Refreshing models");
764 g_referenceCache.refresh();
765 }
766
767
FlushReferences()768 void FlushReferences()
769 {
770 ModelCache_clear();
771
772 g_referenceCache.clear();
773 }
774
GetReferenceCache()775 ReferenceCache& GetReferenceCache()
776 {
777 return g_referenceCache;
778 }
779
780
781 #include "modulesystem/modulesmap.h"
782 #include "modulesystem/singletonmodule.h"
783 #include "modulesystem/moduleregistry.h"
784
785 class ReferenceDependencies :
786 public GlobalRadiantModuleRef,
787 public GlobalFileSystemModuleRef,
788 public GlobalFiletypesModuleRef
789 {
790 ModelModulesRef m_model_modules;
791 MapModulesRef m_map_modules;
792 public:
ReferenceDependencies()793 ReferenceDependencies() :
794 m_model_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("modeltypes")),
795 m_map_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("maptypes"))
796 {
797 }
getModelModules()798 ModelModules& getModelModules()
799 {
800 return m_model_modules.get();
801 }
getMapModules()802 MapModules& getMapModules()
803 {
804 return m_map_modules.get();
805 }
806 };
807
808 class ReferenceAPI : public TypeSystemRef
809 {
810 ReferenceCache* m_reference;
811 public:
812 typedef ReferenceCache Type;
813 STRING_CONSTANT(Name, "*");
814
ReferenceAPI()815 ReferenceAPI()
816 {
817 g_nullModel = NewNullModel();
818
819 GlobalFileSystem().attach(g_referenceCache);
820
821 m_reference = &GetReferenceCache();
822 }
~ReferenceAPI()823 ~ReferenceAPI()
824 {
825 GlobalFileSystem().detach(g_referenceCache);
826
827 g_nullModel = g_nullNode;
828 }
getTable()829 ReferenceCache* getTable()
830 {
831 return m_reference;
832 }
833 };
834
835 typedef SingletonModule<ReferenceAPI, ReferenceDependencies> ReferenceModule;
836 typedef Static<ReferenceModule> StaticReferenceModule;
837 StaticRegisterModule staticRegisterReference(StaticReferenceModule::instance());
838
ReferenceAPI_getModelModules()839 ModelModules& ReferenceAPI_getModelModules()
840 {
841 return StaticReferenceModule::instance().getDependencies().getModelModules();
842 }
ReferenceAPI_getMapModules()843 MapModules& ReferenceAPI_getMapModules()
844 {
845 return StaticReferenceModule::instance().getDependencies().getMapModules();
846 }
847