convert line ends
[canaan.git] / prj / cam / src / object / trait.cpp
blob94b04bb326a2563c686b6e9eb635b63e1b395797
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/object/trait.cpp,v 1.16 1999/05/19 16:05:39 mahk Exp $
7 #include <trait_.h>
8 #include <objquery.h>
9 #include <traisrch.h>
10 #include <trcache.h>
11 #include <osysbase.h>
12 #include <config.h>
13 #include <cfgdbg.h>
14 #include <mprintf.h>
15 #include <dlistsim.h>
16 #include <dlisttem.h>
17 #include <traitln_.h>
18 #include <relation.h>
19 #include <linkman.h>
20 #include <linkbase.h>
22 // Must be last header
23 #include <dbmem.h>
25 IMPLEMENT_UNAGGREGATABLE_SELF_DELETE(cBaseTrait,ITrait);
27 ////////////////////////////////////////////////////////////
29 // cUninheritedTrait
32 STDMETHODIMP_(BOOL) cUninheritedTrait::PossessedBy(ObjID obj)
34 return IntrinsicTo(obj);
37 ////////////////////////////////////////
39 STDMETHODIMP_(ObjID) cUninheritedTrait::GetDonor(ObjID )
41 return OBJ_NULL;
44 ////////////////////////////////////////
46 STDMETHODIMP_(IObjectQuery*) cUninheritedTrait::GetAllDonors(ObjID obj)
48 if (IntrinsicTo(obj))
49 return CreateSingleObjectQuery(obj);
50 else
51 return CreateEmptyObjectQuery();
54 ////////////////////////////////////////
56 STDMETHODIMP_(IObjectQuery*) cUninheritedTrait::GetAllHeirs(ObjID , eObjConcreteness)
58 return CreateEmptyObjectQuery();
61 //------------------------------------------------------------
62 // FILTER QUERIES
65 class cTraitFilterQuery : public cFilterObjectQuery
67 ITrait* Trait;
68 protected:
69 BOOL Filter(ObjID obj) { return Trait->IntrinsicTo(obj);};
71 public:
72 cTraitFilterQuery(IObjectQuery* q, ITrait* trait)
73 : cFilterObjectQuery(q),Trait(trait)
75 Trait->AddRef();
76 Skip();
79 ~cTraitFilterQuery() { SafeRelease(Trait);};
82 ////////////////////////////////////////////////////////////
84 // cInheritedTrait
87 STDMETHODIMP_(BOOL) cInheritedTrait::PossessedBy(ObjID obj)
89 if (IntrinsicTo(obj))
90 return TRUE;
91 else
92 return GetDonor(obj) != OBJ_NULL;
95 ////////////////////////////////////////
97 STDMETHODIMP_(ObjID) cInheritedTrait::GetDonor(ObjID obj)
99 ObjID result = OBJ_NULL;
100 IObjectQuery* query = GetAllDonors(obj);
101 if (!query->Done()) result = query->Object();
102 if (result == obj)
104 query->Next();
105 result = (query->Done()) ? OBJ_NULL :query->Object();
107 SafeRelease(query);
108 return result;
111 ////////////////////////////////////////
113 STDMETHODIMP_(IObjectQuery*) cInheritedTrait::GetAllDonors(ObjID obj)
115 IObjectQuery* inner = TraitMan->Query(obj,kTraitQueryAllDonors);
116 IObjectQuery* outer = new cTraitFilterQuery(inner,this);
117 SafeRelease(inner);
118 return outer;
121 ////////////////////////////////////////
123 STDMETHODIMP_(IObjectQuery*) cInheritedTrait::GetAllHeirs(ObjID obj , eObjConcreteness which)
125 // @TODO: make this actually stop when another donor overshadows the property
126 IObjectQuery* inner = TraitMan->Query(obj,kTraitQueryAllDescendents);
127 IObjectQuery* outer = new cConcretenessFilterQuery(inner,which);
128 SafeRelease(inner);
129 return outer;
133 ////////////////////////////////////////////////////////////
135 // Cached trait query
137 // A total rework of the depth first query
141 // Helper class for iterating across metaprop links
144 class cChildren : public MetaPropLinks::cIter
146 public:
147 typedef MetaPropLinks::cIter cParent;
148 cChildren(const cParent& p)
149 : cParent(p)
153 cChildren() {};
155 cChildren(ObjID obj)
157 MetaPropLinks* links = GetObjectMetaPropLinks(obj);
158 if (links)
159 *this = links->Iter();
162 ObjID Object()
164 return Value().dest;
171 // Search stack frame
176 struct sStackFrame
178 ObjID node;
179 BOOL intrinsic;
180 cChildren children;
181 ObjID donor; // current opinion of donor
182 ObjID through; // where we get donor through
184 sStackFrame(ObjID n = OBJ_NULL,
185 BOOL i = FALSE,
186 IObjectQuery* c = NULL,
187 ObjID d = OBJ_NULL,
188 ObjID t = OBJ_NULL)
189 : node(n),
190 donor(d),
191 through(t),
192 intrinsic(i)
200 // Search stack
203 class cSearchStack : public cSimpleStack<sStackFrame>
211 class cCachedTraitQuery : public cBaseObjectQuery
213 TraitID ID;
214 cAutoIPtr<ITrait> Trait;
215 cSearchStack Stack;
216 BOOL do_spew;
217 ObjID YieldMe;
218 ObjID SkipMe;
220 #ifndef SHIP
221 #define trSpew(x) if (do_spew) mprintf x
222 #else
223 #define trSpew(x)
224 #endif
226 static IDonorCache* Cache;
227 static ITraitManager* TraitMan;
229 typedef cBaseObjectQuery cParent;
231 protected:
234 // Given an object, build a query of successors, using cache where possible.
236 BOOL Expand(sStackFrame* frame)
238 ObjID obj = frame->node;
239 ObjID donor;
240 ObjID through;
242 if (obj == SkipMe)
244 SkipMe = OBJ_NULL;
245 trSpew(("Expanding past %d\n",obj));
248 YieldMe = OBJ_NULL;
250 // Look for us in the cache
251 if (Cache->GetDonor(obj,ID,&donor,&through))
253 trSpew(("Cache: %d has %d through %d\n",obj,donor,through));
254 if (donor == OBJ_NULL)
256 frame->children = cChildren();
257 return FALSE;
260 frame->donor = donor;
261 frame->through = through;
263 if (frame->donor != SkipMe)
265 YieldMe = donor;
266 SkipMe = donor;
269 cChildren rest(obj);
270 // fast forward the donor query past our "through"
272 for (; !rest.Done(); rest.Next())
273 if (rest.Object() == through)
274 break;
276 frame->children = rest;
278 return TRUE;
280 else
282 frame->children = cChildren(obj);
284 return !frame->children.Done();
291 // Open a stack frame for an object
293 void Open(ObjID obj)
295 sStackFrame frame(obj);
296 frame.intrinsic = Trait->IntrinsicTo(obj);
297 trSpew(("Opening %d intrinsic %d\n",obj,frame.intrinsic));
299 Stack.Push(frame);
302 void PropagateDonor(const sStackFrame& finished)
304 // Now, set the donor of the current top, based on what we already knew.
305 if (Stack.Top().donor == OBJ_NULL) // if we don't already know
307 ObjID donor = finished.donor;
308 if (finished.intrinsic)
309 donor = finished.node;
310 Stack.Top().donor = donor;
311 Stack.Top().through = finished.node;
316 // Unwind the stack to the next upward branch
319 BOOL Contract()
321 if (Stack.Top().node == OBJ_NULL)
322 return FALSE;
324 while (Stack.Top().children.Done())
326 sStackFrame& finished = Stack.Top();
327 // This node is finished. Cache it.
331 trSpew(("Closing %d Donor: %d through %d Intrinsic: %d\n",
332 finished.node,
333 finished.donor,
334 finished.through,
335 finished.intrinsic));
338 Cache->SetDonor(finished.node,ID,finished.donor,finished.through);
340 Stack.Pop();
342 // If we hit bottom, we're done.
343 if (Stack.Top().node == OBJ_NULL)
344 return FALSE;
346 // propagate the donor backward
347 PropagateDonor(finished);
348 Stack.Top().children.Next();
350 return TRUE;
354 // Go to the next object in the search, before filtering out non-donors
356 void NextObj()
358 if (!Expand(&Stack.Top()))
360 if (!Contract())
361 return;
364 Open(Stack.Top().children.Object());
367 void Skip()
369 while(!Done()
370 && YieldMe == OBJ_NULL
371 && !(Stack.Top().intrinsic && Stack.Top().node != SkipMe))
373 trSpew(("Skipping %d\n",Stack.Top().node));
375 NextObj();
379 // @TODO: Release these interfaces one day
380 void GrabIFaces()
382 if (Cache == NULL)
383 Cache = AppGetObj(IDonorCache);
385 if (TraitMan == NULL)
386 TraitMan = AppGetObj(ITraitManager);
391 public:
392 cCachedTraitQuery(ObjID obj, ITrait* trait, TraitID id)
393 : ID(id),
394 Trait(trait),
395 do_spew(FALSE),
396 YieldMe(OBJ_NULL),
397 SkipMe(OBJ_NULL)
399 #ifndef SHIP
400 do_spew = config_is_defined("trait_cache_spew");
401 trSpew(("Start %s search on %d\n",Trait->Describe()->name,obj));
402 #endif
403 Trait->AddRef();
405 GrabIFaces();
407 Open(obj);
408 // NextObj();
409 Skip();
412 ~cCachedTraitQuery()
414 while (Stack.Top().node != OBJ_NULL)
416 sStackFrame frame = Stack.Pop();
418 // if we got valid cache data, store it.
419 trSpew(("Unwinding %d. ",frame.node));
420 if (frame.donor != OBJ_NULL) // valid donor data
422 trSpew(("Caching donor %d through %d",frame.donor,frame.through));
423 Cache->SetDonor(frame.node,ID,frame.donor,frame.through);
425 PropagateDonor(frame);
426 trSpew(("\n"));
429 trSpew(("End search.\n"));
433 STDMETHOD(Next)()
435 NextObj();
436 Skip();
437 return S_OK;
440 STDMETHOD_(BOOL,Done)() { return Stack.Top().node == OBJ_NULL;};
441 STDMETHOD_(ObjID,Object)()
443 if (YieldMe != OBJ_NULL)
444 return YieldMe;
445 return Stack.Top().node;
449 #undef trSpew
451 ITraitManager* cCachedTraitQuery::TraitMan = NULL;
452 IDonorCache* cCachedTraitQuery::Cache = NULL;
456 ////////////////////////////////////////////////////////////
458 // cCachedTrait
463 cCachedTrait::cCachedTrait(const sTraitDesc& desc, const sTraitPredicate& pred)
464 : cInheritedTrait(desc,pred),
465 pCache(AppGetObj(IDonorCache))
467 ID = pCache->NewTrait(&Desc);
469 ConfigSpew("trait_id_spew",("Trait %s has id %d\n",desc.name,ID));
474 cCachedTrait::~cCachedTrait()
476 SafeRelease(pCache);
480 // Go ahead and do as much of the first level of the search as you
481 // can, using the cache. This is particularly useful in the case
482 // of a concrete that is not in the cache
484 // TRUE means the out param is the donor you're looking for
486 // FALSE means donor is the first donor for search
489 BOOL cCachedTrait::get_cached_donor(ObjID obj, ObjID* donor)
491 BOOL retval = FALSE;
493 MetaPropLinks* links = GetObjectMetaPropLinks(obj);
494 if (!links)
495 return FALSE;
497 MetaPropLinks::cIter iter;
498 for (iter = links->Iter(); !iter.Done(); iter.Next())
500 ObjID qobj = iter.Value().dest;
502 ObjID cachedonor;
503 ObjID through;
505 if (IntrinsicTo(qobj))
507 pCache->SetDonor(obj,ID,qobj,qobj);
508 *donor = qobj;
509 return TRUE;
512 if (pCache->GetDonor(qobj,ID,&cachedonor,&through))
514 if (cachedonor == OBJ_NULL)
515 continue;
516 else
518 *donor = cachedonor;
519 pCache->SetDonor(obj,ID,*donor,qobj);
520 return TRUE;
524 *donor = qobj;
525 return FALSE;
527 // we got no donors
528 *donor = OBJ_NULL;
529 pCache->SetDonor(obj,ID,OBJ_NULL,OBJ_NULL);
530 return TRUE;
533 STDMETHODIMP_(ObjID) cCachedTrait::GetDonor(ObjID obj)
535 // check the cache first
536 ObjID cachedonor,through;
537 if (pCache->GetDonor(obj,ID,&cachedonor,&through))
538 return cachedonor;
540 // Now check the first level
541 ObjID donor;
542 if (get_cached_donor(obj,&donor))
543 return donor;
545 // If that fails, do the search
546 ObjID result = OBJ_NULL;
548 IObjectQuery* query = GetAllDonors(obj);
549 if (!query->Done()) result = query->Object();
550 if (result == obj)
552 query->Next();
553 result = (query->Done()) ? OBJ_NULL :query->Object();
555 SafeRelease(query);
556 return result;
559 STDMETHODIMP_(IObjectQuery*) cCachedTrait::GetAllDonors(ObjID obj)
561 return new cCachedTraitQuery(obj,this,ID);
564 STDMETHODIMP cCachedTrait::Touch(ObjID obj)
566 if (obj == OBJ_NULL)
567 pCache->Flush(FLUSH_ALL_OBJS,ID);
568 else
570 IObjectQuery* objs = TraitMan->Query(obj,kTraitQueryAllDescendents);
571 pCache->FlushObjSet(objs,ID);
572 SafeRelease(objs);
575 return S_OK;