]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/referencecache.cpp
Merge commit '4f80165b29510dac0d86a230993a04717143e542' into master-merge
[xonotic/netradiant.git] / radiant / referencecache.cpp
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 #include "globaldefs.h"
24
25 #include "debugging/debugging.h"
26
27 #include "iscenegraph.h"
28 #include "iselection.h"
29 #include "iundo.h"
30 #include "imap.h"
31
32 MapModules& ReferenceAPI_getMapModules();
33
34 #include "imodel.h"
35
36 ModelModules& ReferenceAPI_getModelModules();
37
38 #include "ifilesystem.h"
39 #include "iarchive.h"
40 #include "ifiletypes.h"
41 #include "ireference.h"
42 #include "ientity.h"
43 #include "qerplugin.h"
44
45 #include <list>
46
47 #include "container/cache.h"
48 #include "container/hashfunc.h"
49 #include "os/path.h"
50 #include "stream/textfilestream.h"
51 #include "nullmodel.h"
52 #include "maplib.h"
53 #include "stream/stringstream.h"
54 #include "os/file.h"
55 #include "moduleobserver.h"
56 #include "moduleobservers.h"
57
58 #include "mainframe.h"
59 #include "map.h"
60 #include "filetypes.h"
61
62 extern bool g_writeMapComments;
63
64 bool References_Saved();
65
66 void MapChanged(){
67         Map_SetModified( g_map, !References_Saved() );
68 }
69
70
71 EntityCreator* g_entityCreator = 0;
72
73 bool MapResource_loadFile( const MapFormat& format, scene::Node& root, const char* filename ){
74         globalOutputStream() << "Open file " << filename << " for read...";
75         TextFileInputStream file( filename );
76         if ( !file.failed() ) {
77                 globalOutputStream() << "success\n";
78                 ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( filename ), "Loading Map" );
79                 ASSERT_NOTNULL( g_entityCreator );
80                 format.readGraph( root, file, *g_entityCreator );
81                 return true;
82         }
83         else
84         {
85                 globalErrorStream() << "failure\n";
86                 return false;
87         }
88 }
89
90 NodeSmartReference MapResource_load( const MapFormat& format, const char* path, const char* name ){
91         NodeSmartReference root( NewMapRoot( name ) );
92
93         StringOutputStream fullpath( 256 );
94         fullpath << path << name;
95
96         if ( path_is_absolute( fullpath.c_str() ) ) {
97                 MapResource_loadFile( format, root, fullpath.c_str() );
98         }
99         else
100         {
101                 globalErrorStream() << "map path is not fully qualified: " << makeQuoted( fullpath.c_str() ) << "\n";
102         }
103
104         return root;
105 }
106
107 bool MapResource_saveFile( const MapFormat& format, scene::Node& root, GraphTraversalFunc traverse, const char* filename ){
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                 globalOutputStream() << "success\n";
113                 ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( filename ), "Saving Map" );
114                 format.writeGraph( root, traverse, file, g_writeMapComments );
115                 return true;
116         }
117
118         globalErrorStream() << "failure\n";
119         return false;
120 }
121
122 bool file_saveBackup( const char* path ){
123         if ( file_writeable( path ) ) {
124                 StringOutputStream backup( 256 );
125                 backup << StringRange( path, path_get_extension( path ) ) << "bak";
126
127 #if GDEF_OS_WINDOWS
128                 // NT symlinks are not supported yet.
129                 return ( !file_exists( backup.c_str() ) || file_remove( backup.c_str() ) ) // remove backup
130                            && file_move( path, backup.c_str() ); // rename current to backup
131 #else
132                 // POSIX symlinks are supported.
133                 return file_move( path, backup.c_str() ); // rename current to backup
134 #endif
135         }
136
137         globalErrorStream() << "map path is not writeable: " << makeQuoted( path ) << "\n";
138         return false;
139 }
140
141 bool MapResource_save( const MapFormat& format, scene::Node& root, const char* path, const char* name ){
142         StringOutputStream fullpath( 256 );
143         fullpath << path << name;
144
145         if ( path_is_absolute( fullpath.c_str() ) ) {
146 #if GDEF_OS_WINDOWS
147                 // NT symlinks are not supported yet.
148                 if ( !file_exists( fullpath.c_str() ) || file_saveBackup( fullpath.c_str() ) ) {
149 #else
150                 // POSIX symlinks are supported.
151                 /* We don't want a backup + rename operation if the .map file is
152                  * a symlink. Otherwise we'll break the user's careful symlink setup.
153                  * Just overwrite the original file. Assume the user has versioning. */
154                 bool make_backup;
155                 struct stat st;
156                 if ( lstat(fullpath.c_str(), &st) == 0 ) {
157                         make_backup = true;             // file exists
158                         if ( (st.st_mode & S_IFMT) == S_IFLNK ) {
159                                 make_backup = false;    // .. but it is a symlink
160                         }
161                 } else {
162                         make_backup = false;            // nothing to move
163                 }
164
165                 if ( !make_backup || file_saveBackup( fullpath.c_str() ) ) {
166 #endif
167                         return MapResource_saveFile( format, root, Map_Traverse, fullpath.c_str() );
168                 }
169
170                 globalErrorStream() << "failed to save a backup map file: " << makeQuoted( fullpath.c_str() ) << "\n";
171                 return false;
172         }
173
174         globalErrorStream() << "map path is not fully qualified: " << makeQuoted( fullpath.c_str() ) << "\n";
175         return false;
176 }
177
178 namespace
179 {
180 NodeSmartReference g_nullNode( NewNullNode() );
181 NodeSmartReference g_nullModel( g_nullNode );
182 }
183
184 class NullModelLoader : public ModelLoader
185 {
186 public:
187 scene::Node& loadModel( ArchiveFile& file ){
188         return g_nullModel;
189 }
190 };
191
192 namespace
193 {
194 NullModelLoader g_NullModelLoader;
195 }
196
197
198 /// \brief Returns the model loader for the model \p type or 0 if the model \p type has no loader module
199 ModelLoader* ModelLoader_forType( const char* type ){
200         const char* moduleName = findModuleName( &GlobalFiletypes(), ModelLoader::Name(), type );
201         if ( string_not_empty( moduleName ) ) {
202                 ModelLoader* table = ReferenceAPI_getModelModules().findModule( moduleName );
203                 if ( table != 0 ) {
204                         return table;
205                 }
206                 else
207                 {
208                         globalErrorStream() << "ERROR: Model type incorrectly registered: \"" << moduleName << "\"\n";
209                         return &g_NullModelLoader;
210                 }
211         }
212         return 0;
213 }
214
215 NodeSmartReference ModelResource_load( ModelLoader* loader, const char* name ){
216         ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( name ), "Loading Model" );
217
218         NodeSmartReference model( g_nullModel );
219
220         {
221                 ArchiveFile* file = GlobalFileSystem().openFile( name );
222
223                 if ( file != 0 ) {
224                         globalOutputStream() << "Loaded Model: \"" << name << "\"\n";
225                         model = loader->loadModel( *file );
226                         file->release();
227                 }
228                 else
229                 {
230                         globalErrorStream() << "Model load failed: \"" << name << "\"\n";
231                 }
232         }
233
234         model.get().m_isRoot = true;
235
236         return model;
237 }
238
239
240 inline hash_t path_hash( const char* path, hash_t previous = 0 ){
241 #if GDEF_OS_WINDOWS
242         return string_hash_nocase( path, previous );
243 #else // UNIX
244         return string_hash( path, previous );
245 #endif
246 }
247
248 struct PathEqual
249 {
250         bool operator()( const CopiedString& path, const CopiedString& other ) const {
251                 return path_equal( path.c_str(), other.c_str() );
252         }
253 };
254
255 struct PathHash
256 {
257         typedef hash_t hash_type;
258
259         hash_type operator()( const CopiedString& path ) const {
260                 return path_hash( path.c_str() );
261         }
262 };
263
264 typedef std::pair<CopiedString, CopiedString> ModelKey;
265
266 struct ModelKeyEqual
267 {
268         bool operator()( const ModelKey& key, const ModelKey& other ) const {
269                 return path_equal( key.first.c_str(), other.first.c_str() ) && path_equal( key.second.c_str(), other.second.c_str() );
270         }
271 };
272
273 struct ModelKeyHash
274 {
275         typedef hash_t hash_type;
276
277         hash_type operator()( const ModelKey& key ) const {
278                 return hash_combine( path_hash( key.first.c_str() ), path_hash( key.second.c_str() ) );
279         }
280 };
281
282 typedef HashTable<ModelKey, NodeSmartReference, ModelKeyHash, ModelKeyEqual> ModelCache;
283 ModelCache g_modelCache;
284 bool g_modelCache_enabled = true;
285
286 ModelCache::iterator ModelCache_find( const char* path, const char* name ){
287         if ( g_modelCache_enabled ) {
288                 return g_modelCache.find( ModelKey( path, name ) );
289         }
290         return g_modelCache.end();
291 }
292
293 ModelCache::iterator ModelCache_insert( const char* path, const char* name, scene::Node& node ){
294         if ( g_modelCache_enabled ) {
295                 return g_modelCache.insert( ModelKey( path, name ), NodeSmartReference( node ) );
296         }
297         return g_modelCache.insert( ModelKey( "", "" ), g_nullModel );
298 }
299
300 void ModelCache_flush( const char* path, const char* name ){
301         ModelCache::iterator i = g_modelCache.find( ModelKey( path, name ) );
302         if ( i != g_modelCache.end() ) {
303                 //ASSERT_MESSAGE((*i).value.getCount() == 0, "resource flushed while still in use: " << (*i).key.first.c_str() << (*i).key.second.c_str());
304                 g_modelCache.erase( i );
305         }
306 }
307
308 void ModelCache_clear(){
309         g_modelCache_enabled = false;
310         g_modelCache.clear();
311         g_modelCache_enabled = true;
312 }
313
314 NodeSmartReference Model_load( ModelLoader* loader, const char* path, const char* name, const char* type ){
315         if ( loader != 0 ) {
316                 return ModelResource_load( loader, name );
317         }
318         else
319         {
320                 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), type );
321                 if ( string_not_empty( moduleName ) ) {
322                         const MapFormat* format = ReferenceAPI_getMapModules().findModule( moduleName );
323                         if ( format != 0 ) {
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                                 globalErrorStream() << "Model type not supported: \"" << name << "\"\n";
336                         }
337                         return g_nullModel;
338                 }
339         }
340 }
341
342 namespace
343 {
344 bool g_realised = false;
345
346 // name may be absolute or relative
347 const char* rootPath( const char* name ){
348         return GlobalFileSystem().findRoot(
349                            path_is_absolute( name )
350                            ? name
351                            : GlobalFileSystem().findFile( name )
352                            );
353 }
354 }
355
356 struct ModelResource : public Resource
357 {
358         NodeSmartReference m_model;
359         const CopiedString m_originalName;
360         CopiedString m_path;
361         CopiedString m_name;
362         CopiedString m_type;
363         ModelLoader* m_loader;
364         ModuleObservers m_observers;
365         std::time_t m_modified;
366         std::size_t m_unrealised;
367
368         ModelResource( const CopiedString& name ) :
369                 m_model( g_nullModel ),
370                 m_originalName( name ),
371                 m_type( path_get_extension( name.c_str() ) ),
372                 m_loader( 0 ),
373                 m_modified( 0 ),
374                 m_unrealised( 1 ){
375                 m_loader = ModelLoader_forType( m_type.c_str() );
376
377                 if ( g_realised ) {
378                         realise();
379                 }
380         }
381
382         ~ModelResource(){
383                 if ( realised() ) {
384                         unrealise();
385                 }
386                 ASSERT_MESSAGE( !realised(), "ModelResource::~ModelResource: resource reference still realised: " << makeQuoted( m_name.c_str() ) );
387         }
388
389         // NOT COPYABLE
390         ModelResource( const ModelResource& );
391
392         // NOT ASSIGNABLE
393         ModelResource& operator=( const ModelResource& );
394
395         void setModel( const NodeSmartReference& model ){
396                 m_model = model;
397         }
398
399         void clearModel(){
400                 m_model = g_nullModel;
401         }
402
403         void loadCached(){
404                 if ( g_modelCache_enabled ) {
405                         // cache lookup
406                         ModelCache::iterator i = ModelCache_find( m_path.c_str(), m_name.c_str() );
407                         if ( i == g_modelCache.end() ) {
408                                 i = ModelCache_insert(
409                                         m_path.c_str(),
410                                         m_name.c_str(),
411                                         Model_load( m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str() )
412                                         );
413                         }
414
415                         setModel( ( *i ).value );
416                 }
417                 else
418                 {
419                         setModel( Model_load( m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str() ) );
420                 }
421         }
422
423         void loadModel(){
424                 loadCached();
425                 connectMap();
426                 mapSave();
427         }
428
429         bool load(){
430                 ASSERT_MESSAGE( realised(), "resource not realised" );
431                 if ( m_model == g_nullModel ) {
432                         loadModel();
433                 }
434
435                 return m_model != g_nullModel;
436         }
437
438         bool save(){
439                 if ( !mapSaved() ) {
440                         const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), m_type.c_str() );
441                         if ( string_not_empty( moduleName ) ) {
442                                 const MapFormat* format = ReferenceAPI_getMapModules().findModule( moduleName );
443                                 if ( format != 0 && MapResource_save( *format, m_model.get(), m_path.c_str(), m_name.c_str() ) ) {
444                                         mapSave();
445                                         return true;
446                                 }
447                         }
448                 }
449                 return false;
450         }
451
452         void flush(){
453                 if ( realised() ) {
454                         ModelCache_flush( m_path.c_str(), m_name.c_str() );
455                 }
456         }
457
458         scene::Node* getNode(){
459                 //if(m_model != g_nullModel)
460                 {
461                         return m_model.get_pointer();
462                 }
463                 //return 0;
464         }
465
466         void setNode( scene::Node* node ){
467                 ModelCache::iterator i = ModelCache_find( m_path.c_str(), m_name.c_str() );
468                 if ( i != g_modelCache.end() ) {
469                         ( *i ).value = NodeSmartReference( *node );
470                 }
471                 setModel( NodeSmartReference( *node ) );
472
473                 connectMap();
474         }
475
476         void attach( ModuleObserver& observer ){
477                 if ( realised() ) {
478                         observer.realise();
479                 }
480                 m_observers.attach( observer );
481         }
482
483         void detach( ModuleObserver& observer ){
484                 if ( realised() ) {
485                         observer.unrealise();
486                 }
487                 m_observers.detach( observer );
488         }
489
490         bool realised(){
491                 return m_unrealised == 0;
492         }
493
494         void realise(){
495                 ASSERT_MESSAGE( m_unrealised != 0, "ModelResource::realise: already realised" );
496                 if ( --m_unrealised == 0 ) {
497                         m_path = rootPath( m_originalName.c_str() );
498                         m_name = path_make_relative( m_originalName.c_str(), m_path.c_str() );
499
500                         //globalOutputStream() << "ModelResource::realise: " << m_path.c_str() << m_name.c_str() << "\n";
501
502                         m_observers.realise();
503                 }
504         }
505
506         void unrealise(){
507                 if ( ++m_unrealised == 1 ) {
508                         m_observers.unrealise();
509
510                         //globalOutputStream() << "ModelResource::unrealise: " << m_path.c_str() << m_name.c_str() << "\n";
511                         clearModel();
512                 }
513         }
514
515         bool isMap() const {
516                 return Node_getMapFile( m_model ) != 0;
517         }
518
519         void connectMap(){
520                 MapFile* map = Node_getMapFile( m_model );
521                 if ( map != 0 ) {
522                         map->setChangedCallback( makeCallbackF(MapChanged) );
523                 }
524         }
525
526         std::time_t modified() const {
527                 StringOutputStream fullpath( 256 );
528                 fullpath << m_path.c_str() << m_name.c_str();
529                 return file_modified( fullpath.c_str() );
530         }
531
532         void mapSave(){
533                 m_modified = modified();
534                 MapFile* map = Node_getMapFile( m_model );
535                 if ( map != 0 ) {
536                         map->save();
537                 }
538         }
539
540         bool mapSaved() const {
541                 MapFile* map = Node_getMapFile( m_model );
542                 if ( map != 0 ) {
543                         return m_modified == modified() && map->saved();
544                 }
545                 return true;
546         }
547
548         bool isModified() const {
549                 return ( ( !string_empty( m_path.c_str() ) // had or has an absolute path
550                                    && m_modified != modified() ) // AND disk timestamp changed
551                                  || !path_equal( rootPath( m_originalName.c_str() ), m_path.c_str() ) ); // OR absolute vfs-root changed
552         }
553
554         void refresh(){
555                 if ( isModified() ) {
556                         flush();
557                         unrealise();
558                         realise();
559                 }
560         }
561 };
562
563 class HashtableReferenceCache : public ReferenceCache, public ModuleObserver
564 {
565 typedef HashedCache<CopiedString, ModelResource, PathHash, PathEqual> ModelReferences;
566 ModelReferences m_references;
567 std::size_t m_unrealised;
568
569 class ModelReferencesSnapshot
570 {
571 ModelReferences& m_references;
572 typedef std::list<ModelReferences::iterator> Iterators;
573 Iterators m_iterators;
574 public:
575 typedef Iterators::iterator iterator;
576
577 ModelReferencesSnapshot( ModelReferences& references ) : m_references( references ){
578         for ( ModelReferences::iterator i = m_references.begin(); i != m_references.end(); ++i )
579         {
580                 m_references.capture( i );
581                 m_iterators.push_back( i );
582         }
583 }
584
585 ~ModelReferencesSnapshot(){
586         for ( Iterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i )
587         {
588                 m_references.release( *i );
589         }
590 }
591
592 iterator begin(){
593         return m_iterators.begin();
594 }
595
596 iterator end(){
597         return m_iterators.end();
598 }
599 };
600
601 public:
602
603 typedef ModelReferences::iterator iterator;
604
605 HashtableReferenceCache() : m_unrealised( 1 ){
606 }
607
608 iterator begin(){
609         return m_references.begin();
610 }
611
612 iterator end(){
613         return m_references.end();
614 }
615
616 void clear(){
617         m_references.clear();
618 }
619
620 Resource* capture( const char* path ){
621         //globalOutputStream() << "capture: \"" << path << "\"\n";
622         return m_references.capture( CopiedString( path ) ).get();
623 }
624
625 void release( const char* path ){
626         m_references.release( CopiedString( path ) );
627         //globalOutputStream() << "release: \"" << path << "\"\n";
628 }
629
630 void setEntityCreator( EntityCreator& entityCreator ){
631         g_entityCreator = &entityCreator;
632 }
633
634 bool realised() const {
635         return m_unrealised == 0;
636 }
637
638 void realise(){
639         ASSERT_MESSAGE( m_unrealised != 0, "HashtableReferenceCache::realise: already realised" );
640         if ( --m_unrealised == 0 ) {
641                 g_realised = true;
642
643                 {
644                         ModelReferencesSnapshot snapshot( m_references );
645                         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
646                         {
647                                 ModelReferences::value_type& value = *( *i );
648                                 if ( value.value.count() != 1 ) {
649                                         value.value.get()->realise();
650                                 }
651                         }
652                 }
653         }
654 }
655
656 void unrealise(){
657         if ( ++m_unrealised == 1 ) {
658                 g_realised = false;
659
660                 {
661                         ModelReferencesSnapshot snapshot( m_references );
662                         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
663                         {
664                                 ModelReferences::value_type& value = *( *i );
665                                 if ( value.value.count() != 1 ) {
666                                         value.value.get()->unrealise();
667                                 }
668                         }
669                 }
670
671                 ModelCache_clear();
672         }
673 }
674
675 void refresh(){
676         ModelReferencesSnapshot snapshot( m_references );
677         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
678         {
679                 ModelResource* resource = ( *( *i ) ).value.get();
680                 if ( !resource->isMap() ) {
681                         resource->refresh();
682                 }
683         }
684 }
685 };
686
687 namespace
688 {
689 HashtableReferenceCache g_referenceCache;
690 }
691
692 #if 0
693 class ResourceVisitor
694 {
695 public:
696 virtual void visit( const char* name, const char* path, const
697                                         };
698 #endif
699
700 void SaveReferences(){
701         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
702         for ( HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i )
703         {
704                 ( *i ).value->save();
705         }
706         MapChanged();
707 }
708
709 bool References_Saved(){
710         for ( HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i )
711         {
712                 scene::Node* node = ( *i ).value->getNode();
713                 if ( node != 0 ) {
714                         MapFile* map = Node_getMapFile( *node );
715                         if ( map != 0 && !map->saved() ) {
716                                 return false;
717                         }
718                 }
719         }
720         return true;
721 }
722
723 void RefreshReferences(){
724         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Refreshing Models" );
725         g_referenceCache.refresh();
726 }
727
728
729 void FlushReferences(){
730         ModelCache_clear();
731
732         g_referenceCache.clear();
733 }
734
735 ReferenceCache& GetReferenceCache(){
736         return g_referenceCache;
737 }
738
739
740 #include "modulesystem/modulesmap.h"
741 #include "modulesystem/singletonmodule.h"
742 #include "modulesystem/moduleregistry.h"
743
744 class ReferenceDependencies :
745         public GlobalRadiantModuleRef,
746         public GlobalFileSystemModuleRef,
747         public GlobalFiletypesModuleRef
748 {
749 ModelModulesRef m_model_modules;
750 MapModulesRef m_map_modules;
751 public:
752 ReferenceDependencies() :
753         m_model_modules( GlobalRadiant().getRequiredGameDescriptionKeyValue( "modeltypes" ) ),
754         m_map_modules( GlobalRadiant().getRequiredGameDescriptionKeyValue( "maptypes" ) )
755 {
756 }
757
758 ModelModules& getModelModules(){
759         return m_model_modules.get();
760 }
761
762 MapModules& getMapModules(){
763         return m_map_modules.get();
764 }
765 };
766
767 class ReferenceAPI : public TypeSystemRef
768 {
769 ReferenceCache* m_reference;
770 public:
771 typedef ReferenceCache Type;
772
773 STRING_CONSTANT( Name, "*" );
774
775 ReferenceAPI(){
776         g_nullModel = NewNullModel();
777
778         GlobalFileSystem().attach( g_referenceCache );
779
780         m_reference = &GetReferenceCache();
781 }
782
783 ~ReferenceAPI(){
784         GlobalFileSystem().detach( g_referenceCache );
785
786         g_nullModel = g_nullNode;
787 }
788
789 ReferenceCache* getTable(){
790         return m_reference;
791 }
792 };
793
794 typedef SingletonModule<ReferenceAPI, ReferenceDependencies> ReferenceModule;
795 typedef Static<ReferenceModule> StaticReferenceModule;
796 StaticRegisterModule staticRegisterReference( StaticReferenceModule::instance() );
797
798 ModelModules& ReferenceAPI_getModelModules(){
799         return StaticReferenceModule::instance().getDependencies().getModelModules();
800 }
801
802 MapModules& ReferenceAPI_getMapModules(){
803         return StaticReferenceModule::instance().getDependencies().getMapModules( );
804 }