1 /* NAME:
2 E3ClassTree.c
3
4 DESCRIPTION:
5 Quesa class tree.
6
7 Quesa maintains a class tree, which identifies the various classes
8 which are registered with the system.
9
10 A class contains all the non-instance specific data for a Quesa
11 object. A Quesa object contains a pointer to the relevent node in the
12 class tree, along with the instance-specific data for that object.
13
14 The class tree is populated with the default Quesa objects when Quesa
15 initialises itself, and applications can register additional classes
16 such as renderers/etc.
17
18 The class tree is stored as a hash table for efficiency, but nodes
19 within the tree maintain pointers to their children/parent in order
20 to record their relationship to the rest of the tree.
21
22 COPYRIGHT:
23 Copyright (c) 1999-2005, Quesa Developers. All rights reserved.
24
25 For the current release of Quesa, please see:
26
27 <http://www.quesa.org/>
28
29 Redistribution and use in source and binary forms, with or without
30 modification, are permitted provided that the following conditions
31 are met:
32
33 o Redistributions of source code must retain the above copyright
34 notice, this list of conditions and the following disclaimer.
35
36 o Redistributions in binary form must reproduce the above
37 copyright notice, this list of conditions and the following
38 disclaimer in the documentation and/or other materials provided
39 with the distribution.
40
41 o Neither the name of Quesa nor the names of its contributors
42 may be used to endorse or promote products derived from this
43 software without specific prior written permission.
44
45 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
49 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
51 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 ___________________________________________________________________________
57 */
58 //=============================================================================
59 // Include files
60 //-----------------------------------------------------------------------------
61 #include "E3Prefix.h"
62 #include "E3ClassTree.h"
63 #include "E3HashTable.h"
64 #include "E3Set.h"
65
66 #include <time.h>
67 #include <stdio.h>
68 #include <new>
69
70
71
72
73
74 //=============================================================================
75 // Internal constants
76 //-----------------------------------------------------------------------------
77 #define kClassHashTableSize 64
78 #define kMethodHashTableSize 32
79
80
81
82
83
84 //=============================================================================
85 // Internal types
86 //-----------------------------------------------------------------------------
87
88
89
90
91
92
93
94 //=============================================================================
95 // Internal macros
96 //-----------------------------------------------------------------------------
97 // Called through macro to avoid superfluous function call in release builds
98 #if Q3_DEBUG
99 #define Q3_CLASS_VERIFY(_o) _o->Verify()
100 #else
101 #define Q3_CLASS_VERIFY(_o)
102 #endif
103
104
105
106
107
108 //=============================================================================
109 // Internal functions
110 //-----------------------------------------------------------------------------
111 // e3class_verify : Verify the instance data hasn't been corrupted.
112 //-----------------------------------------------------------------------------
113 // Note : Used for debug builds, to verify object instance data doesn't
114 // write beyond the space allocated for it.
115 //-----------------------------------------------------------------------------
116 #if Q3_DEBUG
117
118 void
Verify()119 OpaqueTQ3Object::Verify ()
120 {
121 // Verify the object
122 Q3_ASSERT ( quesaTag == kQ3ObjectTypeQuesa ) ;
123 Q3_ASSERT_VALID_PTR ( theClass ) ;
124
125 TQ3ObjectType* instanceTrailer = (TQ3ObjectType*) ( ( (TQ3Uns8 *) this ) + theClass->instanceSize ) ;
126
127 Q3_ASSERT ( *instanceTrailer == kQ3ObjectTypeQuesa ) ;
128 }
129
130 #endif // Q3_DEBUG
131
132
133
134
135
136 //=============================================================================
137 // E3ClassInfo::E3ClassInfo : Constructor for class info of root class.
138 //-----------------------------------------------------------------------------
139 #pragma mark -
140
E3ClassInfo(TQ3XMetaHandler newClassMetaHandler,E3ClassInfo * newParent)141 E3ClassInfo::E3ClassInfo (
142 TQ3XMetaHandler newClassMetaHandler,
143 E3ClassInfo* newParent // nil for root class of course
144 )
145 {
146 classType = 0 ;
147 className = NULL ;
148 methodTable = NULL ;
149 abstract = kQ3False ;
150 numInstances = 0 ;
151 instanceSize = 0 ;
152 numChildren = 0 ;
153 theChildren = NULL ;
154 for ( TQ3Int32 i = kQ3MaxBuiltInClassHierarchyDepth - 1 ; i >= 0 ; --i )
155 ownAndParentTypes [ i ] = 0 ;
156
157 classMetaHandler = newClassMetaHandler ;
158 theParent = newParent ;
159 // The above two must be done before any sub class constructors are called as these
160 // two fields are used in Find_Method which is called in the sub class constructors.
161
162 // Also before this:
163 registerMethod = (TQ3XObjectRegisterMethod) Find_Method ( kQ3XMethodTypeNewObjectClass , kQ3True ) ;
164 } ;
165
166
167 //=============================================================================
168 // e3class_attach : Attach a child node to its parent.
169 //-----------------------------------------------------------------------------
170 // Note : We assume the child currently has no parent.
171 //-----------------------------------------------------------------------------
172 TQ3Status
Attach(E3ClassInfoPtr theChild,E3ClassInfoPtr theParent)173 E3ClassInfo::Attach ( E3ClassInfoPtr theChild, E3ClassInfoPtr theParent )
174 {
175 // Validate our parameters
176 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(theChild), kQ3Failure);
177 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(theParent), kQ3Failure);
178
179
180
181 // Grow the list of child pointers
182 TQ3Status qd3dStatus = Q3Memory_Reallocate(&theParent->theChildren,
183 sizeof(E3ClassInfoPtr) * (theParent->numChildren+1));
184 if ( qd3dStatus == kQ3Failure )
185 return kQ3Failure ;
186
187
188
189 // Connect the child to the parent and the parent to the child
190 theParent->theChildren [ theParent->numChildren ] = theChild ;
191 theParent->numChildren++ ;
192
193 return kQ3Success ;
194 }
195
196
197
198
199
200 //=============================================================================
201 // e3class_detach : Detach a node from its parent.
202 //-----------------------------------------------------------------------------
203 void
Detach(void)204 E3ClassInfo::Detach ( void )
205 {
206 // Validate our parameters
207 Q3_REQUIRE(Q3_VALID_PTR(this));
208 Q3_REQUIRE(Q3_VALID_PTR(theParent));
209
210
211
212 // and shift everything above that slot down by one (overwriting
213 // thepointer to theChild, and leaving a bogus entry at the end).
214 for ( TQ3Uns32 n = 0 ; n < theParent->numChildren ; ++n )
215 {
216 // If this child matches, shift everything else down
217 if ( theParent->theChildren [ n ] == this )
218 {
219 // If there's anything above us, copy it down
220 if (n != (theParent->numChildren-1))
221 Q3Memory_Copy(&theParent->theChildren[n+1],
222 &theParent->theChildren[n],
223 sizeof(E3ClassInfoPtr) * (theParent->numChildren-n-1));
224
225
226 // We're done
227 break;
228 }
229 }
230
231
232
233 // Shrink the parent's list of children
234 --theParent->numChildren ;
235 TQ3Status qd3dStatus = Q3Memory_Reallocate(&theParent->theChildren,
236 sizeof(E3ClassInfoPtr) * theParent->numChildren);
237 Q3_ASSERT(qd3dStatus == kQ3Success);
238
239
240
241 // Remove the parent from the child
242 theParent = NULL ;
243 }
244
245
246
247
248
249 //=============================================================================
250 // e3class_find_by_name : Recursive search for a class given a name.
251 //-----------------------------------------------------------------------------
252 E3ClassInfoPtr
Find(const char * theClassName)253 E3ClassInfo::Find ( const char *theClassName )
254 {
255 // Validate our parameters
256 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
257 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(theClassName), NULL);
258 Q3_REQUIRE_OR_RESULT(strlen(theClassName) < kQ3StringMaximumLength, NULL);
259
260
261
262 // Check this node
263 if ( E3CString_IsEqual ( className, theClassName ) )
264 return this ;
265
266
267
268 // Check the children of the class
269 for ( TQ3Uns32 n = 0 ; n < numChildren ; ++n )
270 {
271 // Get the child
272 E3ClassInfoPtr theChild = theChildren [ n ] ;
273 Q3_ASSERT_VALID_PTR(theChild);
274
275
276 // Check it
277 E3ClassInfoPtr theResult = theChild->Find ( theClassName ) ;
278 if ( theResult != NULL )
279 return theResult ;
280 }
281
282 return NULL ;
283 }
284
285
286
287
288
289 //=============================================================================
290 // e3class_find_method : Find a method for a class.
291 //-----------------------------------------------------------------------------
292 TQ3XFunctionPointer
Find_Method(TQ3XMethodType methodType,TQ3Boolean canInherit)293 E3ClassInfo::Find_Method ( TQ3XMethodType methodType, TQ3Boolean canInherit )
294 {
295 // Validate our parameters
296 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
297
298
299
300 // Walk up the class tree until we find the method
301
302 if ( classMetaHandler != NULL )
303 if ( TQ3XFunctionPointer leafMethod = classMetaHandler ( methodType ) )
304 return leafMethod ;
305
306 // If this class doesn't implement it, and we can inherit, try each of the parents in turn
307 if ( canInherit )
308 for ( E3ClassInfoPtr theClass = theParent ; theClass != NULL ; theClass = theClass->theParent )
309 if ( theClass->classMetaHandler != NULL ) // Check the current class
310 if ( TQ3XFunctionPointer theMethod = theClass->classMetaHandler ( methodType ) )
311 return theMethod ;
312
313
314
315 // Did not find a suitable method, return NULL
316 return NULL ;
317 }
318
319
320
321
322
323 //=============================================================================
324 // e3class_dump_class : Dump some stats on a class.
325 //-----------------------------------------------------------------------------
326 // Note : Debug only - dumps some stats on a class to a file, and calls
327 // itself recursively to dump the children of the class.
328 //-----------------------------------------------------------------------------
329 void
Dump_Class(FILE * theFile,TQ3Uns32 indent)330 E3ClassInfo::Dump_Class ( FILE *theFile, TQ3Uns32 indent )
331 {
332 char thePad[100];
333 TQ3Uns32 n;
334
335 E3GlobalsPtr theGlobals = E3Globals_Get () ;
336
337
338
339 // Validate our parameters
340 Q3_ASSERT_VALID_PTR(theFile);
341 Q3_ASSERT_VALID_PTR(this);
342 Q3_ASSERT(indent < (sizeof(thePad)-1));
343
344
345
346 // Prepare the pad string
347 for (n = 0; n < indent; ++n )
348 thePad[n] = ' ';
349
350 thePad[n] = 0x00;
351
352
353
354 // Dump the class
355 fprintf(theFile, "\n%s%s%s\n",
356 thePad,
357 className,
358 numInstances == 0 ? "" : " *** MEMORY LEAK ***");
359
360 if ( ( classType < 0) && ( classType >= theGlobals->classNextType) )
361 fprintf(theFile, "%s-> classType = 0x%lx\n", thePad, classType);
362 else
363 fprintf(theFile, "%s-> classType = %c%c%c%c\n", thePad,
364 ((char *) &classType)[0],
365 ((char *) &classType)[1],
366 ((char *) &classType)[2],
367 ((char *) &classType)[3]);
368
369 fprintf(theFile, "%s-> numInstances = %lu\n", thePad, numInstances);
370
371 fprintf(theFile, "%s-> instanceSize = %lu\n", thePad, instanceSize);
372
373 fprintf(theFile, "%s-> numChildren = %lu\n", thePad, numChildren);
374
375 if (E3HashTable_GetNumItems( methodTable) == 0)
376 fprintf(theFile, "%s-> method cache is empty\n", thePad);
377 else
378 {
379 fprintf(theFile, "%s-> method cache, collision max = %lu\n", thePad,
380 E3HashTable_GetCollisionMax( methodTable));
381
382 fprintf(theFile, "%s-> method cache, collision avg = %.2f\n", thePad,
383 E3HashTable_GetCollisionAverage( methodTable));
384
385 fprintf(theFile, "%s-> method cache, num items = %lu\n", thePad,
386 E3HashTable_GetNumItems( methodTable));
387
388 fprintf(theFile, "%s-> method cache, table size = %lu\n", thePad,
389 E3HashTable_GetTableSize( methodTable));
390 }
391
392
393
394 // Dump the children of the class
395 for (n = 0; n < numChildren; ++n )
396 {
397 // Get the child
398 E3ClassInfoPtr theChild = theChildren[n];
399 Q3_ASSERT_VALID_PTR(theChild);
400
401
402 // Dump it
403 theChild->Dump_Class ( theFile, indent + 2 ) ;
404 }
405 }
406
407
408
409
410
411 //=============================================================================
412 // Public functions
413 //-----------------------------------------------------------------------------
414 // E3ClassTree_Destroy : Destroy the class tree.
415 //-----------------------------------------------------------------------------
416 #pragma mark -
417
418 void
Destroy(void)419 E3ClassTree::Destroy ( void )
420 {
421 E3GlobalsPtr theGlobals = E3Globals_Get () ;
422
423 // If we have a class tree, destroy it
424 if (theGlobals->classTree != NULL)
425 {
426 E3HashTable_Destroy(&theGlobals->classTree);
427 theGlobals->classTreeRoot = NULL;
428 }
429 }
430
431
432
433
434
435
436
437 //=============================================================================
438 // E3ClassTree_GetNextClassType : Get the next available class type.
439 //-----------------------------------------------------------------------------
440 TQ3ObjectType
GetNextClassType(void)441 E3ClassTree::GetNextClassType ( void )
442 {
443 E3GlobalsPtr theGlobals = E3Globals_Get () ;
444
445 // Decrement the class type, and return the next available type
446 theGlobals->classNextType--;
447
448 return theGlobals->classNextType ;
449 }
450
451
452
453
454
455
456
457 //=============================================================================
458 // E3ClassTree_RegisterClass : Register a new class.
459 //-----------------------------------------------------------------------------
460 // Note : Attempts to register a new class within the class tree.
461 //
462 // If parentClass is kQ3ObjectTypeInvalid, the new node is created
463 // at the root of the class tree. This can only be performed once,
464 // and it is an error to attempt to create two nodes at the root.
465 //
466 //-----------------------------------------------------------------------------
467 TQ3Status
RegisterExternalClass(TQ3ObjectType parentClassType,TQ3ObjectType classType,const char * className,TQ3XMetaHandler classMetaHandler,TQ3Uns32 leafInstanceSize)468 E3ClassTree::RegisterExternalClass (
469 TQ3ObjectType parentClassType,
470 TQ3ObjectType classType,
471 const char *className,
472 TQ3XMetaHandler classMetaHandler,
473 TQ3Uns32 leafInstanceSize )
474 {
475 if ( E3ClassInfo* theParent = E3ClassTree::GetClass ( parentClassType ) )
476 return E3ClassTree::RegisterClass ( parentClassType,
477 classType,
478 className,
479 classMetaHandler,
480 leafInstanceSize + theParent->instanceSize ) ;
481
482 return kQ3Failure ;
483 }
484
485
486 TQ3Status
RegisterClass(TQ3ObjectType parentClassType,TQ3ObjectType classType,const char * className,TQ3XMetaHandler classMetaHandler,TQ3Uns32 instanceSize)487 E3ClassTree::RegisterClass ( TQ3ObjectType parentClassType,
488 TQ3ObjectType classType,
489 const char *className,
490 TQ3XMetaHandler classMetaHandler,
491 TQ3Uns32 instanceSize )
492 {
493 E3GlobalsPtr theGlobals = E3Globals_Get () ;
494 TQ3Status qd3dStatus = kQ3Success ;
495
496 // Validate our parameters
497 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(className), kQ3Failure);
498 Q3_REQUIRE_OR_RESULT(strlen(className) < kQ3StringMaximumLength, kQ3Failure);
499
500 E3ClassInfo* theParent = E3ClassTree::GetClass ( parentClassType ) ;
501 if ( theParent == NULL )
502 Q3_REQUIRE_OR_RESULT(theGlobals->classTree == NULL, kQ3Failure);
503
504 // Make sure the class isn't registered yet
505 if ( E3ClassTree::GetClass ( classType ) != NULL )
506 return kQ3Failure ;
507
508
509
510 // Find the parent class
511 if ( theParent != NULL )
512 {
513 Q3_ASSERT ( instanceSize >= theParent->instanceSize ) ;
514 }
515
516 TQ3XObjectRegisterMethod registerMethod = NULL ;
517 if ( classMetaHandler != NULL )
518 registerMethod = (TQ3XObjectRegisterMethod) classMetaHandler ( kQ3XMethodTypeNewObjectClass ) ;
519
520 if ( registerMethod == NULL )
521 for ( E3ClassInfoPtr theClass = theParent ; theClass != NULL ; theClass = theClass->theParent )
522 if ( theClass->classMetaHandler != NULL ) // Check the current class
523 if ( ( registerMethod = (TQ3XObjectRegisterMethod) theClass->classMetaHandler ( kQ3XMethodTypeNewObjectClass ) ) != NULL )
524 break ;
525
526 if ( registerMethod == NULL )
527 return kQ3Failure ;
528
529 E3ClassInfo* newClass = registerMethod ( classMetaHandler, theParent ) ; // performs the new ( std::nothrow ) of the most appropriate C++ class
530
531 if ( newClass == NULL )
532 return kQ3Failure ;
533
534 newClass->className = (char *) Q3Memory_Allocate ( strlen ( className ) + 1 ) ;
535 newClass->methodTable = E3HashTable_Create ( kMethodHashTableSize) ;
536
537 if ( newClass->className == NULL || newClass->methodTable == NULL )
538 {
539 if ( newClass->className != NULL )
540 Q3Memory_Free ( & newClass->className ) ;
541
542 if ( newClass->methodTable != NULL )
543 E3HashTable_Destroy ( &newClass->methodTable ) ;
544
545 delete newClass ;
546 return kQ3Failure ;
547 }
548
549
550
551 // Initialise the class
552 newClass->classType = classType ;
553 newClass->instanceSize = instanceSize ;
554
555 strcpy ( newClass->className, className ) ;
556
557
558
559 // If we don't have a hash table yet, create it
560 if ( theGlobals->classTree == NULL )
561 {
562 theGlobals->classTreeRoot = newClass ;
563 theGlobals->classTree = E3HashTable_Create ( kClassHashTableSize ) ;
564 if ( theGlobals->classTree == NULL )
565 qd3dStatus = kQ3Failure ;
566 }
567
568
569
570 // Store the class in the hash table
571 if ( qd3dStatus != kQ3Failure )
572 qd3dStatus = E3HashTable_Add ( theGlobals->classTree, classType, newClass ) ;
573
574 if ( qd3dStatus != kQ3Failure && newClass->theParent != NULL )
575 qd3dStatus = E3ClassInfo::Attach ( newClass, newClass->theParent ) ;
576
577
578 // Fill out the ownAndParentTypes array
579 TQ3Int32 ourClassDepth = 0 ;
580 E3ClassInfoPtr aClass ;
581 for ( aClass = newClass->theParent ; aClass ; aClass = aClass->theParent )
582 ++ourClassDepth ;
583
584 for ( aClass = newClass ; aClass ; aClass = aClass->theParent )
585 {
586 if ( ourClassDepth < kQ3MaxBuiltInClassHierarchyDepth )
587 newClass->ownAndParentTypes [ ourClassDepth ] = aClass->classType ;
588 --ourClassDepth ;
589 }
590
591
592
593 // Handle failure
594 if ( qd3dStatus == kQ3Failure )
595 {
596 // Clean up the class tree
597 if ( theGlobals->classTree != NULL )
598 {
599 if ( E3HashTable_Find ( theGlobals->classTree, classType) != NULL )
600 E3HashTable_Remove ( theGlobals->classTree, classType ) ;
601 }
602
603 if ( theGlobals->classTreeRoot == newClass )
604 theGlobals->classTreeRoot = NULL ;
605
606
607 // Clean up the class
608 Q3Memory_Free ( & newClass->className ) ;
609 E3HashTable_Destroy ( & newClass->methodTable ) ;
610 delete newClass ;
611 }
612
613 return qd3dStatus ;
614 }
615
616
617
618
619 //=============================================================================
620 // E3ClassTree_UnregisterClass : Unregister a class.
621 //-----------------------------------------------------------------------------
622 // Note : Any child classes derived from this class will also be removed
623 // from the class tree.
624 //
625 // If isRequired is true, we always unregister the class.
626 // Otherwise we check to see if this class has any instances left.
627 // If it does, those instances will have pointers back to the
628 // class and so it ca't be unregistered.
629 //-----------------------------------------------------------------------------
630 TQ3Status
UnregisterClass(TQ3ObjectType classType,TQ3Boolean isRequired)631 E3ClassTree::UnregisterClass ( TQ3ObjectType classType, TQ3Boolean isRequired )
632 {
633 E3GlobalsPtr theGlobals = E3Globals_Get () ;
634
635
636
637 // Find the class to unregister
638 E3ClassInfoPtr theClass = E3ClassTree::GetClass ( classType ) ;
639 if ( theClass == NULL )
640 return kQ3Failure ;
641
642
643
644 // If we're not required to unregister the class, check the instance count
645 if (!isRequired)
646 {
647 // Make sure the class doesn't have any instances left. Instances have
648 // a pointer back to the class tree, so we can't remove the class from
649 // the tree while those pointers still exist.
650 if (theClass->numInstances != 0)
651 {
652 E3ErrorManager_PostError(kQ3ErrorObjectClassInUse, kQ3False);
653 return kQ3Failure ;
654 }
655 }
656
657
658
659 // Unregister any children of the class
660 while (theClass->numChildren != 0)
661 {
662 // Get the first child
663 E3ClassInfoPtr theChild = theClass->theChildren[0];
664 Q3_ASSERT_VALID_PTR(theChild);
665
666
667 // Try and unregister it
668 TQ3Status qd3dStatus = E3ClassTree::UnregisterClass ( theChild->classType, isRequired ) ;
669 if ( qd3dStatus == kQ3Failure )
670 return qd3dStatus ;
671 }
672
673
674
675 // Remove the class from the tree
676 if ( theClass->theParent != NULL )
677 theClass->E3ClassInfo::Detach () ;
678
679 if ( theGlobals->classTreeRoot == theClass )
680 theGlobals->classTreeRoot = NULL ;
681
682 E3HashTable_Remove(theGlobals->classTree, classType);
683
684
685
686 // Dispose of the class itself
687 Q3_ASSERT(theClass->numChildren == 0);
688 Q3_ASSERT(theClass->theChildren == NULL);
689
690 Q3Memory_Free(&theClass->className);
691 E3HashTable_Destroy(&theClass->methodTable);
692
693 delete theClass ;
694
695 return kQ3Success ;
696 }
697
698
699
700
701
702 //=============================================================================
703 // E3ClassTree_InitialiseInstanceDataOfClass : Initialise the instance data of a class.
704 //-----------------------------------------------------------------------------
705 // Note : Initialises instances of the specified class and its parent
706 // classes, passing paramData to the new method.
707 //
708 // If sharedParams is true, the paramData parameter is passed to
709 // the specified class and its parent classes.
710 //
711 // Otherwise, NULL is passed as the paramData for any classes
712 // above the parent.
713 //
714 // The sharedParams behaviour is necessary to support the QD3D
715 // call Q3XObjectHierarchy_NewObject.
716 //-----------------------------------------------------------------------------
717 TQ3Status
InitialiseInstanceData(E3ClassInfoPtr inClass,TQ3Boolean sharedParams,const void * paramData)718 OpaqueTQ3Object::InitialiseInstanceData ( E3ClassInfoPtr inClass,
719 TQ3Boolean sharedParams,
720 const void *paramData )
721 {
722 TQ3Status qd3dStatus = kQ3Success ;
723 TQ3Uns32 parentInstanceSize = 0 ;
724 E3ClassInfoPtr parentClass = inClass->theParent ;
725
726 // If this class has a parent, initialise the parent object
727 if ( parentClass != NULL )
728 {
729 parentInstanceSize = parentClass->instanceSize ;
730 if (sharedParams)
731 qd3dStatus = InitialiseInstanceData ( parentClass, sharedParams, paramData ) ;
732 else
733 qd3dStatus = InitialiseInstanceData ( parentClass, sharedParams, NULL ) ;
734 }
735
736 // If this class has any private data, initialise it
737 if ( inClass->instanceSize != parentInstanceSize )
738 {
739 // If the object has a new method, call it to initialise the object
740 if ( ( (E3Root*) inClass )->newMethod != NULL )
741 return ( (E3Root*) inClass )->newMethod ( (TQ3Object) this,
742 (void*) ( (TQ3Uns8*) this + parentInstanceSize ),
743 (void *) paramData ) ;
744
745 // If the object is an element, it might have a copy add method
746 // which we call to initialise the object.
747 TQ3XElementCopyAddMethod elementCopyAddMethod = NULL ;
748 if ( Q3_CLASS_INFO_IS_CLASS ( inClass , E3Element ) )
749 elementCopyAddMethod = ( (E3ElementInfo*) inClass )->elementCopyAddMethod ;
750
751 if ( elementCopyAddMethod != NULL )
752 return elementCopyAddMethod ( paramData, ( (TQ3Uns8*) this + parentInstanceSize ) ) ;
753
754
755 // Otherwise if there was no new method, but there was parameter data, do a
756 // bitwise copy. Classes which require more advanced initialisation must supply
757 // a new method, and classes which don't have any parameter data will be left
758 // with instance data that's initialised to 0s.
759
760 if ( paramData != NULL )
761 Q3Memory_Copy ( paramData, ( (TQ3Uns8*) this ) + parentInstanceSize, inClass->instanceSize - parentInstanceSize ) ;
762
763 }
764
765 return qd3dStatus ;
766 }
767
768
769
770
771
772 //=============================================================================
773 // E3ClassTree_CreateInstance : Create an instance of a class.
774 //-----------------------------------------------------------------------------
775 // Note : Creates instances of the specified class and its parent
776 // classes, passing paramData to the new method.
777 //
778 // If sharedParams is true, the paramData parameter is passed to
779 // the specified class and its parent classes.
780 //
781 // Otherwise, NULL is passed as the paramData for any classes
782 // above the parent.
783 //
784 // The sharedParams behaviour is necessary to support the QD3D
785 // call Q3XObjectHierarchy_NewObject.
786 //-----------------------------------------------------------------------------
787 TQ3Object
CreateInstance(TQ3ObjectType classType,TQ3Boolean sharedParams,const void * paramData)788 E3ClassTree::CreateInstance ( TQ3ObjectType classType,
789 TQ3Boolean sharedParams,
790 const void *paramData )
791 {
792 // Find the class to instantiate
793 //
794 // Instantiating objects is often the first thing to fail if the library
795 // has not been initialised yet, so we also check for that case here.
796 E3ClassInfoPtr theClass = E3ClassTree::GetClass ( classType ) ;
797 if ( theClass == NULL )
798 {
799 E3ErrorManager_PostWarning ( kQ3WarningTypeHasNotBeenRegistered ) ;
800
801 if ( ! Q3IsInitialized () )
802 E3ErrorManager_PostError ( kQ3ErrorNotInitialized, kQ3False ) ;
803
804 return NULL ;
805 }
806
807
808 return theClass->CreateInstance ( sharedParams , paramData ) ;
809 }
810
811
812
813 TQ3Object
CreateInstance(TQ3Boolean sharedParams,const void * paramData)814 E3ClassInfo::CreateInstance ( TQ3Boolean sharedParams,
815 const void *paramData )
816 {
817 Q3_ASSERT ( ! abstract ) ;
818
819 if ( abstract )
820 return NULL ; // Cannot create an object of an abstract class, the required methods are missing (pure virtual)
821
822 // Allocate and initialise the object
823 TQ3Object theObject = (TQ3Object) Q3Memory_AllocateClear ( instanceSize + sizeof ( TQ3ObjectType ) ) ;
824 if ( theObject == NULL )
825 return NULL ;
826
827 theObject->quesaTag = kQ3ObjectTypeQuesa ;
828 theObject->theClass = this ;
829
830 // Initialise the trailer
831 TQ3ObjectType* instanceTrailer = (TQ3ObjectType *) (((TQ3Uns8 *) theObject) + instanceSize ) ;
832 *instanceTrailer = kQ3ObjectTypeQuesa ;
833
834 TQ3Status qd3dStatus = theObject->InitialiseInstanceData ( this, sharedParams, paramData ) ;
835
836 if ( qd3dStatus == kQ3Failure )
837 {
838 Q3Memory_Free ( &theObject ) ;
839 return NULL ;
840 }
841
842 // Increment the instance count of the class (watch for overflow)
843 ++numInstances ;
844 Q3_ASSERT ( numInstances > 0 ) ;
845
846
847
848 // Verify the object and return it
849 Q3_CLASS_VERIFY ( theObject ) ;
850
851 return theObject ;
852 }
853
854
855
856
857
858 //=============================================================================
859 // e3ClassTree_DeleteInstanceDataOfClass : Call delete methods on the instance data of a class and its parent classes.
860 //-----------------------------------------------------------------------------
861 void
DeleteInstanceData(E3ClassInfoPtr inClass)862 OpaqueTQ3Object::DeleteInstanceData ( E3ClassInfoPtr inClass )
863 {
864 TQ3Uns32 parentInstanceSize = 0 ;
865
866 if ( inClass->theParent != NULL )
867 parentInstanceSize = inClass->theParent->instanceSize ;
868
869 TQ3XElementDeleteMethod elementDeleteMethod = NULL ;
870 if ( Q3_CLASS_INFO_IS_CLASS ( inClass , E3Element ) )
871 elementDeleteMethod = ( (E3ElementInfo*) inClass )->elementDeleteMethod ;
872
873 // Call the object's delete method
874 if ( elementDeleteMethod != NULL )
875 {
876 OpaqueTQ3Object* thisPtr = this ; // So can be set to null in delete method
877 elementDeleteMethod ( ( (TQ3Uns8*) this + parentInstanceSize ) ) ;
878 }
879 else
880 {
881 if ( ( (E3Root*) inClass )->deleteMethod != NULL )
882 ( (E3Root*) inClass )->deleteMethod ( (TQ3Object) this , (void*) ( (TQ3Uns8*) this + parentInstanceSize ) ) ;
883 }
884
885
886
887 // Dispose of the parent object, if any
888 if ( inClass->theParent != NULL )
889 DeleteInstanceData ( inClass->theParent ) ;
890
891 }
892
893
894
895
896
897 //=============================================================================
898 // E3ClassTree_DestroyInstance : Destroy an instance of a class.
899 //-----------------------------------------------------------------------------
900 void
DestroyInstance(void)901 OpaqueTQ3Object::DestroyInstance ( void )
902 {
903 // Validate our parameters
904 Q3_REQUIRE(Q3_VALID_PTR(this));
905 Q3_CLASS_VERIFY(this);
906
907
908
909 // Call the object's delete method and all its parent classes' delete methods
910 DeleteInstanceData ( theClass ) ;
911
912
913
914 // Decrement the instance count of the class
915 Q3_ASSERT(theClass->numInstances > 0);
916 theClass->numInstances-- ;
917
918
919
920 // Dispose of the object
921 TQ3Object theObject = (TQ3Object) this ;
922 Q3Memory_Free ( & theObject ) ;
923 }
924
925
926
927
928
929 //=============================================================================
930 // E3ClassTree_DuplicateInstanceDataOfClass : Duplicate the instance data of a class and its parent classes.
931 //-----------------------------------------------------------------------------
932 TQ3Status
DuplicateInstanceData(TQ3Object newObject,E3ClassInfoPtr inClass)933 OpaqueTQ3Object::DuplicateInstanceData ( TQ3Object newObject,
934 E3ClassInfoPtr inClass )
935 {
936 TQ3Uns32 parentInstanceSize = 0 ;
937
938 // If the object has a parent, duplicate the parent object
939 if ( inClass->theParent != NULL )
940 {
941 parentInstanceSize = inClass->theParent->instanceSize ;
942 if ( DuplicateInstanceData ( newObject , inClass->theParent ) == kQ3Failure )
943 return kQ3Failure ;
944 }
945
946
947
948 // If the object has any private data, allocate and duplicate it
949 if ( inClass->instanceSize != parentInstanceSize )
950 {
951 // Call the object's duplicate method to initialise it. If the object
952 // does not have duplicate method, we do a bitwise copy.
953
954 if ( ( (E3Root*) inClass )->duplicateMethod != NULL)
955 {
956 if ( ( (E3Root*) inClass )->duplicateMethod (
957 (TQ3Object) this,
958 (void*) ( (TQ3Uns8*) this + parentInstanceSize ),
959 newObject,
960 (void*) ( (TQ3Uns8*) newObject + parentInstanceSize ) ) == kQ3Failure )
961 {
962 if ( inClass->theParent != NULL )
963 newObject->DeleteInstanceData ( inClass->theParent ) ;
964 return kQ3Failure ;
965 }
966 }
967 else
968 {
969 TQ3XElementCopyDuplicateMethod elementDuplicateMethod = NULL ;
970 if ( Q3_CLASS_INFO_IS_CLASS ( inClass , E3Element ) )
971 elementDuplicateMethod = ( (E3ElementInfo*) inClass )->elementCopyDuplicateMethod ;
972
973 if ( elementDuplicateMethod != NULL )
974 {
975 TQ3Status qd3dStatus = elementDuplicateMethod ( (void*) ( (TQ3Uns8*) this + parentInstanceSize ) , (void*) ( (TQ3Uns8*) newObject + parentInstanceSize ) ) ;
976 if ( qd3dStatus == kQ3Failure )
977 {
978 if ( inClass->theParent != NULL )
979 newObject->DeleteInstanceData ( inClass->theParent ) ;
980 return kQ3Failure ;
981 }
982 }
983 else
984 Q3Memory_Copy ( (void*) ( (TQ3Uns8*) this + parentInstanceSize ) ,
985 (void*) ( (TQ3Uns8*) newObject + parentInstanceSize ) , inClass->instanceSize - parentInstanceSize ) ;
986 }
987 }
988 return kQ3Success ;
989 }
990
991
992
993
994 //=============================================================================
995 // E3ClassTree_DuplicateInstance : Duplicate an instance of a class.
996 //-----------------------------------------------------------------------------
997 TQ3Object
DuplicateInstance(void)998 OpaqueTQ3Object::DuplicateInstance ( void )
999 {
1000 // Verify our parameters
1001 Q3_CLASS_VERIFY(this);
1002 Q3_ASSERT_VALID_PTR(theClass);
1003
1004
1005
1006 // Allocate and initialise the object
1007 TQ3Object newObject = (TQ3Object) Q3Memory_AllocateClear ( theClass->instanceSize + sizeof ( TQ3ObjectType ) ) ;
1008 if ( newObject == NULL )
1009 return NULL ;
1010
1011 newObject->quesaTag = kQ3ObjectTypeQuesa ;
1012 newObject->theClass = theClass;
1013
1014 // Initialise the trailer
1015 TQ3ObjectType* instanceTrailer = (TQ3ObjectType *) (((TQ3Uns8 *) newObject) + theClass->instanceSize ) ;
1016 *instanceTrailer = kQ3ObjectTypeQuesa ;
1017
1018
1019 TQ3Status qd3dStatus = DuplicateInstanceData ( newObject , theClass ) ;
1020 if ( qd3dStatus == kQ3Failure )
1021 {
1022 Q3Memory_Free ( &newObject ) ;
1023 return NULL ;
1024 }
1025
1026
1027 // Increment the instance count of the object's class
1028 theClass->numInstances++ ;
1029 Q3_ASSERT ( theClass->numInstances > 0 ) ;
1030
1031
1032
1033 // Verify the object and return it
1034 Q3_CLASS_VERIFY ( newObject ) ;
1035
1036 return newObject ;
1037 }
1038
1039
1040
1041 //=============================================================================
1042 // E3ClassTree_FindInstanceData : Find the instance data of an object.
1043 //-----------------------------------------------------------------------------
1044 // Note : Returns the instance data of an object or one of its parents.
1045 //
1046 // If passed kQ3ObjectTypeLeaf, returns the instance data of the
1047 // leaf object.
1048 //-----------------------------------------------------------------------------
1049
1050 void *
FindLeafInstanceData(void)1051 OpaqueTQ3Object::FindLeafInstanceData ( void ) // Same as the old FindInstanceData ( kQ3ObjectTypeLeaf ) but simpler
1052 {
1053 // Validate our parameters
1054 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1055 Q3_CLASS_VERIFY(this);
1056
1057 if ( theClass->theParent != NULL )
1058 return (void*) ( (TQ3Uns8*) this + theClass->theParent->instanceSize ) ;
1059
1060 return (void*) this ;
1061 }
1062
1063
1064
1065
1066
1067 //=============================================================================
1068 // E3ClassTree_GetObjectType : Get a method for a class.
1069 //-----------------------------------------------------------------------------
1070 // Note : Gets the type of the first sub-class of baseType.
1071 //-----------------------------------------------------------------------------
1072 TQ3ObjectType
GetObjectType(TQ3ObjectType baseType)1073 OpaqueTQ3Object::GetObjectType ( TQ3ObjectType baseType )
1074 {
1075 // Verify our parameters
1076 Q3_CLASS_VERIFY(this);
1077
1078 E3ClassInfoPtr aClass = theClass ;
1079
1080
1081 // Walk up to the level immediately below the base class
1082 while ( ( aClass != NULL )
1083 && ( aClass->theParent != NULL )
1084 && ( aClass->theParent->classType != baseType ) )
1085 {
1086 Q3_ASSERT_VALID_PTR(aClass);
1087 aClass = aClass->theParent ;
1088 }
1089
1090 if ( ( aClass == NULL ) || ( aClass->theParent == NULL ) )
1091 return kQ3ObjectTypeInvalid ;
1092
1093 return aClass->classType ;
1094 }
1095
1096
1097
1098
1099
1100 //=============================================================================
1101 // E3ClassTree_GetLeafObject : Get the leaf object for an object.
1102 //-----------------------------------------------------------------------------
1103 // Note : Not available in release builds.
1104 //-----------------------------------------------------------------------------
1105 #if Q3_DEBUG
1106 TQ3Object
GetLeafObject(void)1107 OpaqueTQ3Object::GetLeafObject ( void )
1108 {
1109 return (TQ3Object) this ;
1110 }
1111 #endif
1112
1113
1114
1115
1116
1117 //=============================================================================
1118 // E3ClassTree_GetClassByType : Find a node in the tree by type.
1119 //-----------------------------------------------------------------------------
1120 E3ClassInfoPtr
GetClass(TQ3ObjectType classType)1121 E3ClassTree::GetClass ( TQ3ObjectType classType )
1122 {
1123 E3GlobalsPtr theGlobals = E3Globals_Get () ;
1124
1125
1126
1127 // Validate our parameters
1128 Q3_REQUIRE_OR_RESULT(classType != kQ3ObjectTypeInvalid, NULL);
1129
1130
1131
1132 // We can't find anything if we don't have a tree
1133 if ( theGlobals->classTree == NULL )
1134 return NULL ;
1135
1136
1137
1138 // Find the class
1139 return (E3ClassInfoPtr) E3HashTable_Find ( theGlobals->classTree, classType ) ;
1140 }
1141
1142
1143
1144
1145
1146 //=============================================================================
1147 // E3ClassTree_GetClassByName : Find a node in the tree by name.
1148 //-----------------------------------------------------------------------------
1149 // Note : NB - the class tree is implemented as a hash table, keyed off
1150 // the class type. This means that searches by name are very much
1151 // sub-optimal, and are implemented as a linear search.
1152 //-----------------------------------------------------------------------------
1153 E3ClassInfoPtr
GetClass(const char * className)1154 E3ClassTree::GetClass ( const char *className )
1155 {
1156 E3GlobalsPtr theGlobals = E3Globals_Get () ;
1157
1158
1159
1160 // Validate our parameters
1161 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(className), NULL);
1162 Q3_REQUIRE_OR_RESULT(strlen(className) < kQ3StringMaximumLength, NULL);
1163
1164
1165
1166 // Make sure we have a root
1167 if ( theGlobals->classTreeRoot == NULL )
1168 return NULL ;
1169
1170
1171
1172 // Find the class, starting at the root
1173 E3ClassInfoPtr theClass = theGlobals->classTreeRoot->Find ( className ) ;
1174
1175 return theClass ;
1176 }
1177
1178
1179
1180
1181
1182 //=============================================================================
1183 // E3ClassTree_GetClassByObject : Find a node in the tree from an object.
1184 //-----------------------------------------------------------------------------
1185 E3ClassInfoPtr
GetClass(TQ3Object theObject)1186 E3ClassTree::GetClass ( TQ3Object theObject )
1187 {
1188 // Validate our parameters
1189 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(theObject), NULL);
1190
1191
1192
1193 // Get the class
1194 return theObject->theClass ;
1195 }
1196
1197
1198
1199
1200
1201 E3ClassInfoPtr
GetClass(void)1202 OpaqueTQ3Object::GetClass ( void )
1203 {
1204 // Validate our parameters
1205 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1206
1207
1208
1209 // Get the class
1210 return theClass ;
1211 }
1212
1213
1214
1215
1216
1217 //=============================================================================
1218 // E3ClassTree_GetParent : Get the parent of a class, if any.
1219 //-----------------------------------------------------------------------------
1220 // Note : The root node of the class tree has no parent.
1221 //-----------------------------------------------------------------------------
1222 E3ClassInfoPtr
GetParent(void)1223 E3ClassInfo::GetParent ( void )
1224 {
1225 // Validate our parameters
1226 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1227
1228
1229
1230 // Return the parent of the class
1231 return theParent ;
1232 }
1233
1234
1235
1236
1237
1238 //=============================================================================
1239 // E3ClassTree_GetNumChildren : Get the number of children of a class.
1240 //-----------------------------------------------------------------------------
1241 TQ3Uns32
GetNumChildren(void)1242 E3ClassInfo::GetNumChildren ( void )
1243 {
1244 // Validate our parameters
1245 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), 0);
1246
1247
1248
1249 // Return the number of children of the class
1250 return numChildren ;
1251 }
1252
1253
1254
1255
1256
1257 //=============================================================================
1258 // E3ClassTree_GetChild : Get a child of a class.
1259 //-----------------------------------------------------------------------------
1260 // Note : We assume the index of theChild is valid.
1261 //-----------------------------------------------------------------------------
1262 E3ClassInfoPtr
GetChild(TQ3Uns32 childIndex)1263 E3ClassInfo::GetChild ( TQ3Uns32 childIndex )
1264 {
1265 // Validate our parameters
1266 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1267 Q3_REQUIRE_OR_RESULT(childIndex >= 0 && childIndex < numChildren, NULL);
1268
1269
1270
1271 // Return the child of the class
1272 E3ClassInfoPtr theChild = theChildren [ childIndex ] ;
1273 Q3_ASSERT_VALID_PTR(theChild);
1274
1275 return theChild ;
1276 }
1277
1278
1279
1280
1281
1282 //=============================================================================
1283 // E3ClassTree_GetType : Get the type of a class.
1284 //-----------------------------------------------------------------------------
1285 TQ3ObjectType
GetType(void)1286 E3ClassInfo::GetType ( void )
1287 {
1288 // Validate our parameters
1289 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), kQ3ObjectTypeInvalid);
1290
1291
1292
1293 // Return the type of the class
1294 return classType ;
1295 }
1296
1297
1298
1299
1300
1301 //=============================================================================
1302 // E3ClassTree_IsType : Is a class an instance of a type?
1303 //-----------------------------------------------------------------------------
1304 TQ3Boolean
IsType(TQ3ObjectType theType)1305 E3ClassInfo::IsType ( TQ3ObjectType theType)
1306 {
1307 E3ClassInfoPtr theClass = this ;
1308
1309 // Walk up through the class and its parents, checking the type
1310 while ( theClass != NULL)
1311 {
1312 // Check this object
1313 if (theClass->classType == theType)
1314 return kQ3True ;
1315
1316
1317
1318 // Move up a level
1319 theClass = theClass->theParent;
1320 }
1321
1322
1323
1324 // If we're still here, we didn't find the type
1325 return kQ3False ;
1326 }
1327
1328
1329
1330
1331
1332 //=============================================================================
1333 // E3ClassTree_GetName : Get the name of a class.
1334 //-----------------------------------------------------------------------------
1335 // Note : The pointer returned is read-only, and owned by the class tree.
1336 //-----------------------------------------------------------------------------
1337 const char *
GetName(void)1338 E3ClassInfo::GetName ( void )
1339 {
1340 // Validate our parameters
1341 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1342
1343
1344
1345 // Return the name of the class
1346 return className ;
1347 }
1348
1349
1350
1351
1352
1353 //=============================================================================
1354 // E3ClassTree_GetMetaHandler : Get the metahandler of a class.
1355 //-----------------------------------------------------------------------------
1356 TQ3XMetaHandler
GetMetaHandler(void)1357 E3ClassInfo::GetMetaHandler ( void )
1358 {
1359 // Validate our parameters
1360 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1361
1362
1363
1364 // Return the metahandler of the class
1365 return classMetaHandler ;
1366 }
1367
1368
1369
1370
1371
1372 //=============================================================================
1373 // E3ClassTree_GetInstanceSize : Get the size of a class's instance data.
1374 //-----------------------------------------------------------------------------
1375 TQ3Uns32
GetInstanceSize(void)1376 E3ClassInfo::GetInstanceSize ( void )
1377 {
1378 // Validate our parameters
1379 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), 0);
1380
1381
1382
1383 // Return the size of the instance data for the class
1384 if ( theParent )
1385 return instanceSize - theParent->instanceSize ;
1386
1387 return instanceSize ;
1388 }
1389
1390
1391
1392
1393
1394 //=============================================================================
1395 // E3ClassTree_GetNumInstances : Get number of instances of a class.
1396 //-----------------------------------------------------------------------------
1397 TQ3Uns32
GetNumInstances(void)1398 E3ClassInfo::GetNumInstances ( void )
1399 {
1400 // Validate our parameters
1401 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), 0);
1402
1403
1404
1405 // Return the number of instances of the class
1406 return numInstances ;
1407 }
1408
1409
1410
1411
1412
1413 //=============================================================================
1414 // E3ClassTree_GetMethod : Get a method for a class.
1415 //-----------------------------------------------------------------------------
1416 // Note : When looking for methods, we first check the method table for
1417 // the class. If this fails, we call the class metahandler.
1418 //
1419 // When calling the metahandler, we inherit methods that the class
1420 // does't implement from the parent of the class.
1421 //
1422 // If we find the method, we store it in the method table to cache
1423 // it for the next time.
1424 //-----------------------------------------------------------------------------
1425 TQ3XFunctionPointer
GetMethod(TQ3XMethodType methodType)1426 E3ClassInfo::GetMethod ( TQ3XMethodType methodType )
1427 {
1428 // Validate our parameters
1429 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1430
1431
1432
1433 // Find the method
1434 //
1435 // We first check the hash table for the class. If this fails, we invoke the
1436 // metahandler for the class to obtain the method and store it away in the
1437 // hash table for future use.
1438 //
1439 // When invoking the metahandler, we inherit methods that this class doesn't
1440 // implement from the parent - ensuring that the hash table is eventually
1441 // populated with all of the (invoked) methods of the class.
1442 TQ3XFunctionPointer theMethod = (TQ3XFunctionPointer) E3HashTable_Find ( methodTable, methodType ) ;
1443 if ( theMethod == NULL )
1444 {
1445 theMethod = Find_Method ( methodType, kQ3True ) ;
1446 if (theMethod != NULL)
1447 E3HashTable_Add ( methodTable, methodType, (void *)theMethod ) ;
1448 }
1449
1450 return theMethod ;
1451 }
1452
1453
1454
1455
1456
1457 //=============================================================================
1458 // E3ClassTree_GetMethodByObject : Get a method for an object's class.
1459 //-----------------------------------------------------------------------------
1460 TQ3XFunctionPointer
GetMethod(TQ3XMethodType methodType)1461 OpaqueTQ3Object::GetMethod ( TQ3XMethodType methodType )
1462 {
1463 // Validate our parameters
1464 Q3_REQUIRE_OR_RESULT(Q3_VALID_PTR(this), NULL);
1465
1466
1467
1468 // Get the method
1469 return theClass->GetMethod ( methodType ) ;
1470 }
1471
1472
1473
1474
1475
1476 //=============================================================================
1477 // E3ClassTree_AddMethod : Add a method for a class.
1478 //-----------------------------------------------------------------------------
1479 // Note : In order to support renderers, we need to be able to add
1480 // methods to the class tree's cache - this is because these
1481 // objects go through a secondary metahandler, which needs to be
1482 // queried by the renderer class itself.
1483 //-----------------------------------------------------------------------------
1484 void
AddMethod(TQ3XMethodType methodType,TQ3XFunctionPointer theMethod)1485 E3ClassInfo::AddMethod ( TQ3XMethodType methodType, TQ3XFunctionPointer theMethod )
1486 {
1487 // Validate our parameters
1488 Q3_REQUIRE(Q3_VALID_PTR(this));
1489 Q3_REQUIRE(Q3_VALID_PTR(theMethod));
1490
1491
1492
1493 // Add the method to the hash table for the class
1494 E3HashTable_Add ( methodTable, methodType, (void*)theMethod ) ;
1495 }
1496
1497
1498
1499
1500
1501 //=============================================================================
1502 // E3ClassTree_AddMethodByType : Add a method for a class given its type.
1503 //-----------------------------------------------------------------------------
1504 void
AddMethod(TQ3ObjectType classType,TQ3XMethodType methodType,TQ3XFunctionPointer theMethod)1505 E3ClassTree::AddMethod ( TQ3ObjectType classType, TQ3XMethodType methodType, TQ3XFunctionPointer theMethod )
1506 {
1507 // Validate our parameters
1508 Q3_REQUIRE(Q3_VALID_PTR(theMethod));
1509
1510
1511
1512 // Find the class, then add the method
1513 E3ClassInfoPtr theClass = E3ClassTree::GetClass ( classType ) ;
1514 Q3_ASSERT( theClass != NULL );
1515 if ( theClass != NULL )
1516 theClass->AddMethod ( methodType, theMethod ) ;
1517 }
1518
1519
1520
1521
1522
1523 //=============================================================================
1524 // E3ClassTree_Dump : Dump some stats on the class tree.
1525 //-----------------------------------------------------------------------------
1526 // Note : Dumps some stats on the class tree to a file, for testing hash
1527 // table sizes/memory leaks/etc.
1528 //-----------------------------------------------------------------------------
1529 void
Dump(void)1530 E3ClassTree::Dump ( void )
1531 {
1532 E3GlobalsPtr theGlobals = E3Globals_Get () ;
1533
1534
1535
1536 // Open our file
1537 FILE* theFile = fopen("Quesa class tree.dump", "wt");
1538 if (theFile == NULL)
1539 return;
1540
1541
1542
1543 // Write out a header
1544 time_t theTime = time(NULL);
1545 fprintf(theFile, "Dumping Quesa class tree - %s", ctime(&theTime));
1546
1547
1548
1549 // Print some stats on the class tree
1550 fprintf(theFile, "class tree, collision max = %lu\n",
1551 E3HashTable_GetCollisionMax(theGlobals->classTree));
1552
1553 fprintf(theFile, "class tree, collision avg = %.2f\n",
1554 E3HashTable_GetCollisionAverage(theGlobals->classTree));
1555
1556 fprintf(theFile, "class tree, num items = %lu\n",
1557 E3HashTable_GetNumItems(theGlobals->classTree));
1558
1559 fprintf(theFile, "class tree, table size = %lu\n",
1560 E3HashTable_GetTableSize(theGlobals->classTree));
1561
1562 fprintf(theFile, "class tree, stat counters = %d/%d/%d/%d/%d\n",
1563 theGlobals->classStats1,
1564 theGlobals->classStats2,
1565 theGlobals->classStats3,
1566 theGlobals->classStats4,
1567 theGlobals->classStats5);
1568
1569 fprintf(theFile, "class tree, table size = %lu\n",
1570 E3HashTable_GetTableSize(theGlobals->classTree));
1571
1572
1573
1574 // Dump the class tree, starting at the root
1575 theGlobals->classTreeRoot->Dump_Class ( theFile, 1 ) ;
1576
1577
1578
1579 // Clean up
1580 fclose(theFile);
1581 }
1582