Object._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2007 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: Object.pike,v 1.9 2010/10/08 14:45:56 nicke Exp $
18  */
19 inherit "/base/access" : __access;
20 inherit "/base/events" : __events;
21 inherit "/base/annotateable" : __annotateable;
22 inherit "/base/decorateable" : __decorateable;
23 inherit "/base/references" : __references;
24  * Returns a bit array of classes and represent the inherit structure.
25 #include <macros.h>
26 #include <attributes.h>
27 #include <classes.h>
28 #include <access.h>
29 #include <events.h>
30 #include <assert.h>
31 #include <functions.h>
32 #include <database.h>
33 #include <types.h>
34 //! Each Object in sTeam is derived from this class
35 class Object : public access,events,annotateable,decorateable,references{
36 public:
37 
38 
39 
40 
41 
42 
43 
44 
45 private mapping mAttributes; /* attribute mapping of object */
46 private mapping mAttributesAcquire;
47 private mapping mAttributesLocked;
48 
49  int iObjectID; /* Database ID of object */
50  object oEnvironment; /* the environment of the object */
51 private object oProxy; /* the corresponding pointer object */
52  string sIdentifier; /* the identifier of the object */
53 private function fGetEvent; /* cache function to get event for attr */
54 private bool __loaded; /* true if object is loaded */
55  array aDependingObjects; /* objects that depend on this object */
56  array aDependingOn; /* objects that this object depends on */
57 
58 
59 array __indices()
60 {
61  return indices(this_object());
62 }
63 
64 mixed set_attribute(string index, mixed data);
65 
66 /**
67  * create_object() is the real constructor, not called when object is loaded
68  *
69  * @see create
70  */
71 protected:
72  void create_object()
73 {
74 }
75 
76 public:
77 
78 /**
79  * Called after the object was created. Then calls create_object() which
80  * actually is the function to be overwritten.
81  *
82  * @see create
83  * @see create_object
84  */
85 final void created()
86 {
87  object caller = MCALLER;
88  if ( caller != _Database &&
89  caller != _Server &&
90  caller != _Persistence &&
91  !(get_object_class() & CLASS_FACTORY) &&
92  !_Server->is_factory(caller) &&
93  !_SECURITY->access_create_object(0, caller) )
94  {
95  FATAL("Calling object is not a factory !");
96  THROW("Security violation while creating object", E_ACCESS);
97  }
98  create_object();
99  load_object();
100 }
101 
102 
103 /**
104  * init the object, called when object is constructed _and_ loaded
105  * Notice during this function, the object ID of the object is not yet
106  * valid. See load_object for a function that will be called after the
107  * object id is valid.
108  *
109  * @see create
110  * @see load_object
111  */
112 protected:
113  void
114 init()
115 {
116  init_events();
117  init_access();
118  init_annotations();
119  init_decorations();
120  init_references();
121 
122  add_data_storage(STORE_DECORATIONS,retrieve_decorations, restore_decorations);
123  add_data_storage(STORE_ACCESS,retrieve_access_data, restore_access_data, 1);
124  add_data_storage(STORE_ATTRIB,retrieve_attr_data, restore_attr_data, 1);
125  add_data_storage(STORE_DATA, retrieve_data, restore_data, 1);
126  add_data_storage(STORE_EVENTS,retrieve_events, restore_events);
127  add_data_storage(STORE_ANNOTS,retrieve_annotations, restore_annotations);
128  add_data_storage(STORE_REFS,store_references, restore_references);
129 }
130 
131 public:
132 
133 /**
134  * Called after the Database has loaded the object data.
135  *
136  * @see loaded
137  */
138 protected:
139  void load_object()
140 {
141 }
142 
143 public:
144 
145 
146 /**
147  * Database calls this function after loading an object.
148  *
149  * @see upgrade
150  * @see init
151  */
152 final void loaded()
153 {
154  if ( CALLER != _Database && CALLER != _Server && CALLER != _Persistence )
155  THROW("Illegal Call to loaded() !", E_ACCESS);
156  __loaded = true;
157  load_object();
158  object factory = _Server->get_factory(get_object_class());
159  if ( objectp(factory) )
160  foreach ( aDecorations, string decoration_path ) {
161  mixed err = catch( register_decoration( decoration_path ) );
162  if ( err )
163  werror( "Could not register decoration %s while loading %d : %s\n%O\n",
164  decoration_path, get_object_id(), err[0], err[1] );
165  }
166 }
167 
168 
169 private:
170 private bool register_decoration ( string path )
171 {
172  object deco = load_decoration( path );
173  if ( !objectp(deco) ) return false;
174  deco->register_attribute_functions( do_set_attribute, do_query_attribute );
175  return true;
176 }
177 
178 public:
179 
180 
181 private:
182 private void unregister_decoration ( string path )
183 {
184 }
185 
186 public:
187 
188 
189 final bool is_loaded() { return __loaded; }
190 
191 
192 /**
193  * See if this object can be dropped (swapping)
194  *
195  * @return can this object be swapped out or not.
196  */
197 bool check_swap() { return true; }
198 bool check_upgrade() { return true; }
199 
200 
201 /**
202  * Master calls this function in each instance when the class is upgraded.
203  *
204  */
205 void upgrade()
206 {
207 }
208 
209 
210 /**
211  * This is the constructor of the object.
212  *
213  * @param string|object id - the name of the object if just created,
214  * or the proxy
215  */
216 protected:
217 final void
218 create(string|object id, void|mapping attributes)
219 {
220  object caller = MCALLER;
221  if ( caller != _Database && caller != _Server && caller != _Persistence &&
222  !(get_object_class() & CLASS_FACTORY) &&!_Server->is_factory(caller) &&
223  !_SECURITY->access_create_object(0, caller) )
224  {
225  FATAL("-- Calling object is not a factory ! - aborting creation!");
226  THROW("Security violation while creating object", E_ACCESS);
227  }
228 
229  if ( mappingp(attributes) )
230  mAttributes = copy_value(attributes);
231  else
232  mAttributes = ([ ]);
233  mAttributesAcquire = ([ ]);
234  mAttributesLocked = ([ ]);
235 
236  init();
237 
238  if ( objectp(id) ) { // object is newly loaded
239  oProxy = id;
240  iObjectID = oProxy->get_object_id();
241  sIdentifier = "object";
242  }
243  else
244  {
245  [ iObjectID, oProxy ] = _Persistence->new_object(id);
246  __loaded = true;
247  sIdentifier = id;
248  database_registration(id);
249  }
250 }
251 
252 public:
253 
254 /**
255  * Save the object. This call is delegated to the Database singleton.
256  *
257  */
258 protected:
259  void require_save(void|string ident, void|string index, void|int action, void|array args)
260 {
261  _Persistence->require_save(ident, index);
262  if (stringp(index) && index != DOC_LAST_ACCESSED) {
263  mAttributes[OBJ_LAST_CHANGED] = time();
264  _Persistence->require_save(STORE_ATTRIB, OBJ_LAST_CHANGED);
265  }
266 }
267 
268 public:
269 
270 object duplicate(void|mapping vars)
271 {
272  mapping duplicates = do_duplicate(vars);
273  foreach(values(duplicates), object dup) {
274  dup->fix_references(duplicates);
275  }
276 }
277 
278 protected:
279  mapping do_fix_references(mapping duplications, mixed val)
280 {
281  mapping result = ([ "val": 0, "fixed": 0, ]);
282  if ( arrayp(val) ) {
283  result->val = ({ });
284  foreach ( val, mixed v ) {
285  mapping res = do_fix_references(duplications, v);
286  result->val += ({ res->val });
287  result->fixed |= res->fixed;
288  }
289  }
290  else if ( mappingp(val) ) {
291  result->val = ([ ]);
292  foreach(indices(val), mixed v) {
293  mapping res = do_fix_references(duplications, val[v]);
294  result->val[v] = res->val;
295  result->fixed |= res->fixed;
296  }
297  }
298  else if ( objectp(val) ) {
299  if ( objectp(duplications[val]) ) {
300  result->fixed = 1;
301  result->val = duplications[val];
302  }
303  }
304  return result;
305 }
306 
307 public:
308 
309 void fix_references(mapping duplications)
310 {
311  THROW("Security Violation", E_ACCESS);
312 
313  mapping attr = copy_value(mAttributes);
314 
315  foreach(indices(attr), mixed key) {
316  mapping result = do_fix_references(duplications, attr[key]);
317  if ( result->fixed ) {
318  do_set_attribute(key, result->val);
319  }
320  }
321 }
322 
323 /**
324  * Duplicate an object - that is create a copy, the permisions are
325  * not copied though.
326  *
327  * @return the copy of this object
328  * @see create
329  */
330 mapping do_duplicate(void|mapping vars)
331 {
332  try_event(EVENT_DUPLICATE, CALLER);
333  object factory = _Server->get_factory(get_object_class());
334  mapping attr = copy_value(mAttributes);
335  array keys = indices(attr);
336  foreach(keys, mixed idx) {
337  if ( mAttributesAcquire[idx] != 0 )
338  m_delete(attr, idx);
339  }
340  m_delete(attr, DOC_VERSIONS);
341  m_delete(attr, DOC_VERSION);
342  m_delete(attr, OBJ_VERSIONOF);
343  m_delete(attr, OBJ_LOCK); // get rid of locks in copy
344 
345  if ( mappingp(vars) && !zero_type(vars["version_of"]) )
346  attr[OBJ_VERSIONOF] = vars["version_of"];
347 
348  mapping exec_vars =
349  ([ "name": do_query_attribute(OBJ_NAME),
350  "attributes":attr,
351  "attributesAcquired": mAttributesAcquire,
352  "attributesLocked": mAttributesLocked, ]);
353  if ( mappingp(vars) )
354  exec_vars += vars;
355  if ( !stringp(exec_vars->name) || exec_vars->name == "" )
356  exec_vars->name = "Copy of " + get_object_id();
357 
358 
359  object dup_obj = factory->execute( exec_vars );
360 
361  mapping dup_depending_objects = ([ ]);
362  foreach ( get_depending_objects(), object dep ) {
363  dup_depending_objects |= dep->do_duplicate( vars );
364  }
365  foreach ( values(dup_depending_objects), object dep ) {
366  dup_obj->add_depending_object( dep );
367  }
368  duplicates |= dup_depending_objects;
369 
370  run_event(EVENT_DUPLICATE, CALLER);
371  return duplicates;;
372 }
373 
374 mapping copy(object obj, mapping vars)
375 {
376  mapping copies = ([ ]);
377  foreach ( obj->get_annotations(), object ann ) {
378  mapping copied;
379  catch(copied = ann->do_duplicate());
380  object dup_ann = copied[ann];
381  if ( objectp(dup_ann) ) {
382  __annotateable::add_annotation(dup_ann);
383  }
384  copies |= copied;
385  }
386  return copies;
387 }
388 
389 
390 /**
391  * currently no idea what this function is good for ... maybe comment
392  * directly when writing the code ?
393  *
394  */
395 protected:
396  void database_registration(string name)
397 {
398 }
399 
400 public:
401 
402 /**
403  * This is the destructor of the object - the object will be swapped out.
404  *
405  * @see delete_object
406  */
407 final void
408 destroy()
409 {
410 }
411 
412 protected:
413  final void do_lock_attribute(int|string key)
414 {
415  mAttributesLocked[key] = true;
416  require_save(STORE_DATA);
417 }
418 
419 public:
420 
421 /**
422  * Set the event for a specific attribute.
423  *
424  * @param key - the key of the attribute
425  * @see set_attribute
426  */
427 final void
428 lock_attribute(int|string key)
429 {
430  try_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, true);
431  do_lock_attribute(key);
432  run_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, true);
433 }
434 
435 /**
436  * Unlock all attributes.
437  *
438  * @see lock_attribute
439  */
440 final void unlock_attributes()
441 {
442  try_event(EVENT_ATTRIBUTES_LOCK, CALLER, 0, false);
443  mAttributesLocked = ([ ]);
444  run_event(EVENT_ATTRIBUTES_LOCK, CALLER, 0, false);
445  require_save(STORE_DATA);
446 }
447 
448 
449 protected:
450  void do_unlock_attribute(string key)
451 {
452  m_delete(mAttributesLocked, key);
453  require_save(STORE_DATA);
454 }
455 
456 public:
457 
458 /**
459  * Set the event for a specific attribute.
460  *
461  * @param key - the key of the attribute
462  * @see set_attribute
463  */
464 final void
465 unlock_attribute(string key)
466 {
467  try_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, false);
468  do_unlock_attribute(key);
469  run_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, false);
470 }
471 
472 /**
473  * Returns whether an attribute is locked or not. Attributes can be locked
474  * to keep people from moving objects around (for example coordinates)
475  *
476  * @param mixed key - the attribute key to check
477  * @return locked or not
478  */
479 bool is_locked(mixed key)
480 {
481  return mAttributesLocked[key];
482 }
483 
484 /**
485  * Returns an array containing all locked attributes
486  *
487  * @return locked attributes as array
488  */
489 array get_locked_attributes()
490 {
491  array locked_attributes = ({});
492  array locked_keys = indices(mAttributesLocked);
493  foreach (locked_keys, string key) {
494  if (mAttributesLocked[key]) {
495  locked_attributes += ({key});
496  }
497  }
498  return locked_attributes;
499 }
500 
501 
502 /**
503  * Each attribute might cause a different event to be fired, get
504  * the one for changing the attribute.
505  *
506  * @param int|string key - the attribute key
507  * @return the corresponding event
508  * @see get_attributes_read_event
509  */
510 int get_attributes_change_event(int|string key)
511 {
512  object factory = _Server->get_factory(get_object_class());
513  if ( objectp(factory) )
514  return factory->get_attributes_change_event(key);
515  return EVENT_ATTRIBUTES_CHANGE;
516 }
517 
518 /**
519  * Each attribute might cause a different event to be fired, get
520  * the one for reading the attribute.
521  *
522  * @param int|string key - the attribute key
523  * @return the corresponding event
524  * @see get_attributes_read_event
525  */
526 int get_attributes_read_event(int|string key)
527 {
528  if ( !functionp(fGetEvent) ) {
529  object factory = _Server->get_factory(get_object_class());
530  if ( objectp(factory) ) {
531  fGetEvent = factory->get_attributes_read_event;
532  return fGetEvent(key);
533  }
534  return 0;
535  }
536  else
537  return fGetEvent(key);
538 }
539 
540 /**
541  * Get the mapping of all registered attributes. That is only the
542  * descriptions, permissions, type registration of the attributes.
543  *
544  * @return mapping of all registered attributes
545  * @see describe_attribute
546  */
547 mapping describe_attributes()
548 {
549  object factory = _Server->get_factory(get_object_class());
550  mapping attributes = factory->get_attributes();
551  foreach(indices(mAttributes)+indices(mAttributesAcquire), mixed attr) {
552  if ( !attributes[attr] )
553  attributes[attr] = ([
554  "type": CMD_TYPE_UNKNOWN,
555  "key": (string)attr,
556  "description": "",
557  "eventRead": 0,
558  "acquire": 0,
559  "eventWrite": EVENT_ATTRIBUTES_CHANGE,
560  "control": CONTROL_ATTR_USER
561  ]);
562  }
563  return attributes;
564 }
565 
566 mapping get_attributes()
567 {
568  return describe_attributes();
569 }
570 
571 /**
572  * Get the mapping of acquired Attributes.
573  *
574  * @return Mapping of acquired attributes (copy of the mapping of course)
575  */
576 mapping get_acquired_attributes()
577 {
578  return copy_value(mAttributesAcquire);
579 }
580 
581 /**
582  * Get the names of all attributes used in the object. Regardless if
583  * they are registered or not.
584  *
585  * @param none
586  * @return list of names
587  * @see get_attributes
588  */
589 array get_attribute_names()
590 {
591  return indices(mAttributes);
592 }
593 /**
594  * Describe an attribute - call the factory of this class for it.
595  * It will return an array of registration data.
596  *
597  * @param mixed key - the attribute to describe
598  * @return array of registration data - check attributes.h for it.
599  */
600 array describe_attribute(mixed key)
601 {
602  object factory = _Server->get_factory(this_object());
603  return factory->describe_attribute(key);
604 }
605 
606 /**
607  * Check before setting an attribute. This include security checks
608  * and finding out if the type of data matches the registered type.
609  *
610  * @param mixed key - the attribute key
611  * @param mixed data - the new value for the attribute
612  * @return true|throws exception
613  * @see set_attribute
614  */
615 protected:
616  bool check_set_attribute(string key, mixed data)
617 {
618  if ( intp(key) || arrayp(key) || mappingp(key) )
619  steam_error("Wrong key for Attribute '"+key+"'");
620 
621  if ( mappingp(mAttributesLocked) && mAttributesLocked[key] )
622  THROW("Trying to set locked attribute '"+key+" ' in '"+
623  get_identifier()+ "' !", E_ACCESS);
624 
625  // check only with real objects and no factory involved whatsoever
626  object factory = _Server->get_factory(this_object());
627  if ( factory == CALLER || get_object_class() & CLASS_FACTORY )
628  return true;
629  if ( objectp(factory) )
630  return factory->check_attribute(key, data);
631  return true;
632 }
633 
634 public:
635 
636 /**
637  * This function is called when an attribute is changed in the object,
638  * that acquires an attribute from this object.
639  *
640  * @param object o - the object where an attribute was changed
641  * @param key - the key of the attribute
642  * @param val - the new value of the attribute
643  * @return false will make the acquire set to none in the calling object
644  */
645 bool keep_acquire(object o, mixed key, mixed val)
646 {
647  return false; // nothing is acquired from the object anymore
648 }
649 
650 /**
651  * Sets a single attribute of an object. This function checks for acquiring
652  * and possible sets the attribute in the object acquired from.
653  *
654  * @param index - what attribute
655  * @param data - data of that attribute
656  * @return successfully or not, check attributes.h for possible results
657  * @see query_attribute
658  **/
659 protected:
660  bool do_set_attribute(string index, mixed|void data)
661 {
662  object|function acquire;
663 
664  acquire = mAttributesAcquire[index];
665  /* setting attributes removes acquiring settings */
666  if ( functionp(acquire) ) acquire = acquire();
667  if ( objectp(acquire) ) {
668  /* if the attribute was changed in the acquired object we should
669  * get information about it too */
670  if ( acq ) {
671  acquire->set_attribute(index, data);
672  if ( index == OBJ_NAME )
673  set_identifier(data);
674  return data;
675  }
676  else {
677  // set acquire to zero
678  mAttributesAcquire[index] = 0;
679  require_save(STORE_DATA);
680  }
681  }
682 
683  /* OBJ_NAME requires speccial actions: the identifier (sIdentifier) must
684  * be unique inside the objects current environment */
685  if ( index == OBJ_NAME )
686  set_identifier(data);
687 
688  if ( zero_type(data) )
689  m_delete(mAttributes, index);
690  else
691  mAttributes[index] = copy_value(data);
692 
693  /* Database needs to save changes sometimes */
694  require_save(STORE_ATTRIB, index);
695  return true;
696 }
697 
698 public:
699 
700 /**
701  * Set an attribute <u>key</u> to new value <u>data</u>.
702  *
703  * @param mixed key - the key of the attribute to change.
704  * @param mixed data - the new value for that attribute.
705  * @return the new value of the attribute | throws and exception
706  * @see query_attribute
707  */
708 mixed set_attribute(string key, void|mixed data)
709 {
710  check_set_attribute(key, data);
711  mixed oldval = do_query_attribute(key);
712 
713  try_event(get_attributes_change_event(key), CALLER, ([ key:data ]),
714  ([ key: oldval ]) );
715 
716  do_set_attribute(key, data);
717  run_event(get_attributes_change_event(key), CALLER, ([ key:data ]),
718  ([ key: oldval ]) );
719  return data;
720 }
721 
722 int arrange(float x, float y)
723 {
724  try_event(EVENT_ARRANGE_OBJECT, CALLER,
725  ([ OBJ_POSITION_X: x, OBJ_POSITION_Y: y, ]) );
726  do_set_attribute(OBJ_POSITION_X, x);
727  do_set_attribute(OBJ_POSITION_Y, y);
728  run_event(EVENT_ARRANGE_OBJECT, CALLER,
729  ([ OBJ_POSITION_X: x, OBJ_POSITION_Y: y, ]) );
730 }
731 
732 protected:
733  mixed do_append_attribute(string key, mixed data)
734 {
735  array val = do_query_attribute(key);
736  if ( mappingp(data) ) {
737  if ( mappingp(val) )
738  return do_set_attribute(key, data + val);
739  }
740  if ( zero_type(val) || val == 0 )
741  val = ({ });
742  if ( !arrayp(data) ) {
743  if ( search(val, data) >= 0 )
744  return val;
745  data = ({ data });
746  }
747  return do_set_attribute(key, data + val);
748  THROW("Can only append arrays on attributes !", E_ERROR);
749 }
750 
751 public:
752 
753 protected:
754  mixed remove_from_attribute(string key, mixed data)
755 {
756  mixed val = do_query_attribute(key);
757  if ( arrayp(val) ) {
758  if ( search(val, data) >= 0 ) {
759  return do_set_attribute(key, val - ({ data }));
760  }
761  }
762  else if ( mappingp(val) ) {
763  m_delete(val, data);
764  return do_set_attribute(key, val);
765  }
766  return val;
767 }
768 
769 public:
770 
771 
772 /**
773  * Sets a number of attributes. The format is
774  * attr = ([ key1:val1, key2:val2,...]) and the function calls set_attribute
775  * for each key.
776  *
777  * @param mapping attr - the attribute mapping.
778  * @return true | throws exception
779  * @see set_attribute
780  */
781 bool set_attributes(mapping attr)
782 {
783  int event;
784  mapping eventAttr = ([ ]);
785  mapping oldAttr = ([ ]);
786 
787  foreach(indices(attr), mixed key) {
788  check_set_attribute(key, attr[key]);
789  event = get_attributes_change_event(key);
790  // generate packages for each event that should be fired
791  if ( !mappingp(eventAttr[event]) )
792  eventAttr[event] = ([ ]);
793  if ( !mappingp(oldAttr[event]) )
794  oldAttr[event] = ([ ]);
795  eventAttr[event][key] = attr[key];
796  oldAttr[event][key] = do_query_attribute(key);
797  }
798  // each attribute might run a different event, run each event individually
799  // if security fails one of this the attribute-setting is canceled
800  foreach( indices(eventAttr), event )
801  try_event(event, CALLER, eventAttr[event], oldAttr[event]);
802 
803  // now the attributes are really set
804  foreach(indices(attr), mixed key) {
805  do_set_attribute(key, attr[key]);
806  }
807 
808  // notification about the change, again for each package individually
809  foreach( indices(eventAttr), event )
810  run_event(event, CALLER, eventAttr[event], oldAttr[event]);
811 
812  return true;
813 }
814 
815 protected:
816  void do_set_acquire_attribute(mixed index, void|object|function|string acquire)
817 {
818  object acq;
819 
820  // quick and dirty hack, because protocoll cannot send functions
821  if ( stringp(acquire) && acquire == REG_ACQ_ENVIRONMENT )
822  acquire = get_environment;
823 
824  if ( functionp(acquire) )
825  acq = acquire();
826  else
827  acq = acquire;
828 
829  while ( objectp(acq) && acq->status() >= 0 ) {
830  if ( functionp(acq->get_object) )
831  acq = acq->get_object();
832  if ( acq == this_object() )
833  THROW("Acquire ended up in loop !", E_ERROR);
834  acq = acq->get_acquire_attribute(index);
835  }
836 
837  mAttributesAcquire[index] = acquire;
838  require_save(STORE_DATA);
839 }
840 
841 public:
842 
843 /**
844  * Set the object to acquire an attribute from. When querying the attribute
845  * inside this object the value will actually the one set in the object
846  * acquired from. Furthermore when changing the attributes value it
847  * will be changed in the acquired object.
848  *
849  * @param index - the attribute to set acquiring
850  * @param acquire - object or function(object) for acquiring
851  * @see set_attribute
852  */
853 void
854 set_acquire_attribute(mixed index, void|object|function|int acquire)
855 {
856  try_event(EVENT_ATTRIBUTES_ACQUIRE, CALLER, index, acquire);
857  // check for possible endless loops
858 
859  do_set_acquire_attribute(index, acquire);
860 
861  run_event(EVENT_ATTRIBUTES_ACQUIRE, CALLER, index, acquire);
862 }
863 
864 /**
865  * Retrieve the acquiring status for an attribute.
866  *
867  * @param mixed key - the key to get acquiring status for
868  * @return function|object of acquiring or 0.
869  * @see set_acquire_attribute
870  */
871 object|function get_acquire_attribute(mixed key)
872 {
873  return mAttributesAcquire[key];
874 }
875 
876 /**
877  * Get the value of one attribute.
878  *
879  * @param mixed key - what attribute to query.
880  * @return the value of the queried attribute
881  * @see set_attribute
882  **/
883 mixed
884 query_attribute(mixed key)
885 {
886  mixed val;
887 
888  int event = get_attributes_read_event(key);
889  if ( event > 0 ) try_event(event, CALLER, key);
890 
891  val = do_query_attribute(key);
892 
893  if ( event > 0 ) run_event(event, CALLER, key );
894 
895  return copy_value(val);
896 }
897 
898 /**
899  * Query an attribute locally. This also follows acquired attributes.
900  * No event is run by calling this and local calls wont have security
901  * or any blocking event problem.
902  *
903  * @param mixed key - the attribute to query.
904  * @return value of the queried attribute
905  * @see query_attribute
906  */
907 protected:
908  mixed do_query_attribute(mixed key)
909 {
910  object|function acquire;
911 
912  if ( mappingp(mAttributesAcquire) )
913  acquire = mAttributesAcquire[key];
914  if ( functionp(acquire) ) acquire = acquire();
915 
916  // if the attribute is acquired from another object query the attribute
917  // there.
918  if ( objectp(acquire) )
919  return acquire->query_attribute(key);
920  return mAttributes[key];
921 }
922 
923 public:
924 
925 
926 /**
927  * Query the value of a list of attributes. Subsequently call
928  * <a href="#query_attribute">query_attribute()</a>
929  * and returns the result as an array or, if a mapping with keys was
930  * given, the result is returned as a mapping key:value
931  *
932  * @param array|mapping|void keys - the attributes to query
933  * @return the result of the query as elements of an array.
934  * @see query_attribute
935  */
936 array|mapping
937 query_attributes(void|array|mapping keys)
938 {
939  int i;
940  array result;
941 
942  function qa = query_attribute;
943 
944  if ( !arrayp(keys) ) {
945  if ( !mappingp(keys) )
946  keys = mkmapping(indices(mAttributes), values(mAttributes)) |
947  mkmapping(indices(mAttributesAcquire), values(mAttributesAcquire));
948 
949  if ( mappingp(keys) ) {
950  foreach(indices(keys), mixed key) {
951  mixed err = catch {
952  keys[key] = qa(key);
953  };
954  if ( err != 0 )
955  FATAL( "Could not query attribute: %O\n", key );
956  }
957  return keys;
958  }
959  }
960  result = allocate(sizeof(keys));;
961 
962 
963  for ( i = sizeof(keys)-1; i >= 0; i-- )
964  result[i] = qa(keys[i]);
965  return result;
966 }
967 
968 
969 /**
970  * Set new permission for an object in the acl. Old permissions
971  * are overwritten.
972  *
973  * @param grp - the group or object to change permissions for
974  * @param permission - new permission for this object
975  * @return the new permission
976  * @see sanction_object_meta
977  * @see /base/access.set_sanction
978  */
979 int sanction_object(object grp, int permission)
980 {
981  ASSERTINFO(_SECURITY->valid_proxy(grp), "Sanction on non-proxy!");
982  if ( query_sanction(grp) == permission )
983  return permission; // if permissions are already fine
984 
985  try_event(EVENT_SANCTION, CALLER, grp, permission);
986  set_sanction(grp, permission);
987 
988  run_event(EVENT_SANCTION, CALLER, grp, permission);
989  return permission;
990 }
991 
992 /**
993  * Sets the new meta permissions for an object. These are permissions
994  * that are used for giving away permissions on this object.
995  *
996  * @param grp - group or object to sanction
997  * @param permission - new meta permission for this object
998  * @return the new permission
999  * @see sanction_object
1000  */
1001 int
1002 sanction_object_meta(object grp, int permission)
1003 {
1004  try_event(EVENT_SANCTION_META, CALLER, grp, permission);
1005  set_meta_sanction(grp, permission);
1006  run_event(EVENT_SANCTION_META, CALLER, grp, permission);
1007  return permission;
1008 }
1009 
1010 /**
1011  * Add an annotation to this object. Each object in steam
1012  * can be annotated.
1013  *
1014  * @param object ann - the annotation to add
1015  * @return successfull or not.
1016  */
1017 bool add_annotation(object ann)
1018 {
1019  try_event(EVENT_ANNOTATE, CALLER, ann);
1020  __annotateable::add_annotation(ann);
1021  do_set_attribute(OBJ_ANNOTATIONS_CHANGED, time());
1022  run_event(EVENT_ANNOTATE, CALLER, ann);
1023 }
1024 
1025 /**
1026  * Remove an annotation from this object. This only removes
1027  * it from the list of annotations, but doesnt delete it.
1028  *
1029  * @param object ann - the annotation to remove
1030  * @return true or false
1031  * @see add_annotation
1032  */
1033 bool remove_annotation(object ann)
1034 {
1035  try_event(EVENT_REMOVE_ANNOTATION, CALLER, ann);
1036  __annotateable::remove_annotation(ann);
1037  do_set_attribute(OBJ_ANNOTATIONS_CHANGED, time());
1038  run_event(EVENT_REMOVE_ANNOTATION, CALLER, ann);
1039 }
1040 
1041 /**
1042  * Add a decoration to this object. The decoration is identified by a path of
1043  * the form: "server:/path-in-server-sandbox" or "object:/object-path" or
1044  * "object:#object-id". An instance of the decoration will be provided and
1045  * attached to the proxy of this object.
1046  *
1047  * @param string path the path to the decoration source code
1048  * @return true if the decoration could be successfully added, false if not
1049  * @see remove_decoration
1050  */
1051 bool add_decoration ( string path )
1052 {
1053  try_event( EVENT_DECORATE, CALLER, path );
1054  if ( ! register_decoration( path ) )
1055  return false;
1056  __decorateable::add_decoration( path );
1057  run_event( EVENT_DECORATE, CALLER, path );
1058  return true;
1059 }
1060 
1061 /**
1062  * Removes a decoration from the object. This function just removes
1063  * the decoration path from the list of decorations.
1064  *
1065  * @param string path the decoration path to remove
1066  * @return true if the decoration was successfully removed, false otherwise
1067  * @see add_decoration
1068  */
1069 bool remove_decoration ( string path )
1070 {
1071  try_event( EVENT_REMOVE_DECORATION, CALLER, path );
1072  __decorateable::remove_decoration( path );
1073  unregister_decoration( path );
1074  run_event( EVENT_REMOVE_DECORATION, CALLER, path );
1075  return true;
1076 }
1077 
1078 /**
1079  * The persistent id of this object.
1080  *
1081  * @return the ID of the object
1082  */
1083 final int
1084 get_object_id()
1085 {
1086  return iObjectID;
1087 }
1088 
1089 string
1090 get_etag()
1091 {
1092  string etag = sprintf("%018x",iObjectID);
1093  if ( sizeof(etag) > 18 ) etag = etag[(sizeof(etag)-18)..];
1094  return etag[0..4]+"-"+etag[5..10]+"-"+etag[11..17];
1095 }
1096 
1097 /**
1098  * Is this an object ? yes!
1099  *
1100  * @return true
1101  */
1102 final bool is_object() { return true; }
1103 
1104 /**
1105  * Sets the object id, but requires privileges in order to be successfull.
1106  * This actually means the caller has to be the <u>database</u> so this
1107  * function is not callable for normal use.
1108  *
1109  * @param id - the new id
1110  */
1111 final void
1112 set_object_id(int id)
1113 {
1114  if ( CALLER == _Database && CALLER != _Persistence )
1115  iObjectID = id;
1116 }
1117 
1118 /**
1119  *
1120  * @return the class of the object
1121  */
1122 int get_object_class()
1123 {
1124  return CLASS_OBJECT;
1125 }
1126 
1127 string get_class()
1128 {
1129  object factory = _Server->get_factory(get_object_class());
1130  return factory->get_class_name();
1131 }
1132 
1133 /**
1134  * update the current identifier of the object. This must happen
1135  * on each movement, because there might be an object of the same
1136  * name in the new environment.
1137  *
1138  * @see get_identifier
1139  */
1140 protected:
1141  void
1142 set_identifier(string name)
1143 {
1144  string identifier;
1145 
1146  if ( !stringp(name) )
1147  name = "* unknown *";
1148 
1149  identifier = replace(name, "/", "_");
1150 
1151  if ( identifier == sIdentifier )
1152  return;
1153 
1154  object env = get_environment();
1155  if ( objectp(env) ) {
1156  identifier = get_object_id() + "__" + identifier;
1157  }
1158  }
1159  sIdentifier = identifier;
1160  require_save(STORE_DATA);
1161  update_path();
1162 
1163  return;
1164 }
1165 
1166 public:
1167 
1168 void update_identifier()
1169 {
1170  object env = get_environment();
1171  if ( ! objectp(env) ) return;
1172  if ( objectp(env) && objectp(oo) ) {
1173  set_identifier(get_object_id()+"__"+sIdentifier);
1174  }
1175 }
1176 
1177 void update_path()
1178 {
1179  foreach(get_annotations(), object ann) {
1180  if (objectp(ann))
1181  catch(ann->update_path());
1182  }
1183  }
1184 }
1185 
1186 
1187 /**
1188  * Moves the object to a destination, which requires move permission.
1189  *
1190  * @param dest - the destination of the move operation
1191  * @return move successfull or throws an exception
1192  */
1193 bool move(object dest)
1194 {
1195  mixed err;
1196 
1197  if ( !objectp(dest) ) {
1198  try_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1199 
1200  /* first remove object from its current environment */
1201  if ( objectp(oEnvironment) && oEnvironment->status() >= 0 ) {
1202  THROW("failed to remove object from environment !",E_ERROR|E_MOVE);
1203  }
1204  // finally set objects new environment
1205  oEnvironment = 0;
1206  update_path();
1207  run_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1208 
1209  return true;
1210  }
1211 
1212  ASSERTINFO(IS_PROXY(dest), "Destination is not a proxy object !");
1213 
1214  /* Moving into an exit takes the exits location as destination */
1215  if ( dest->get_object_class() & CLASS_EXIT )
1216  dest = dest->get_exit();
1217 
1218  THROW("Moving object inside itself !", E_ERROR|E_MOVE);
1219  if ( !(dest->get_object_class() & CLASS_CONTAINER) )
1220  THROW("Failed to move object into non-container!", E_ERROR|E_MOVE);
1221 
1222  return true;
1223 
1224  try_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1225 
1226  /* first remove object from its current environment */
1227  if ( objectp(oEnvironment) && oEnvironment->status() >= 0 ) {
1228  THROW("failed to remove object from environment !",E_ERROR|E_MOVE);
1229  }
1230  /* then insert object into new environment */
1231  if ( err != 0 ) {
1232  if ( objectp(oEnvironment) ) /* prevent object from being in void */
1233  throw(err);
1234  }
1235  // finally set objects new environment
1236  run_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1237  oEnvironment = dest;
1238  update_path();
1239 
1240  require_save(STORE_DATA);
1241 
1242  // now check for name and rename other object
1243  update_identifier();
1244 
1245  return true;
1246 }
1247 
1248 /**
1249  * Get the environment of this object.
1250  *
1251  * @see get_root_environment
1252  *
1253  * @return environment of the object
1254  * @see move
1255  */
1256 public object get_environment()
1257 {
1258  return oEnvironment;
1259 }
1260 
1261 /**
1262  * Get the root environment of this object by recursively going through the
1263  * environments until one without environment is reached. If this object has
1264  * no environment, then the object itself will be returned.
1265  * This function will not pass through user objects into the environment of
1266  * a user unless explicitly ordered to do so. Otherwise it will stop if it
1267  * encounters a user object and return it (meaning the root environment is the
1268  * user's rucksack).
1269  *
1270  * @see get_environment
1271  *
1272  * @param pass_through_users if 1 then this function will pass through user
1273  * objects and continue into the room a user is in. If 0 then it will stop
1274  * when it encounters a user, thus returning the user object (rucksack) as
1275  * the root environemnt.
1276  * @return root environment of the object, or the object itself if it has no
1277  * environment
1278  */
1279 public object get_root_environment( int|void pass_through_users )
1280 {
1281  object env;
1282  while ( objectp(tmp_env) ) {
1283  if ( !pass_through_users &&
1284  tmp_env->get_object_class() & CLASS_USER ) return tmp_env;
1285  env = tmp_env;
1286  tmp_env = env->get_environment();
1287  }
1288  return env;
1289 }
1290 
1291 /**
1292  * Unserialize data of the object. Called when Database loads the object
1293  *
1294  * @param str - the serialized object data
1295  * @see unserialize_access
1296 private:
1297  * @see retrieve_data
1298  */
1299 final void
1300 private:
1301 restore_data(mixed data, string|void index)
1302 {
1303  if ( CALLER != _Database )
1304  THROW("Invalid call to restore_data()", E_ACCESS);
1305 
1306  if (index) {
1307  switch(index) {
1308  case "AttributesLocked" : mAttributesLocked = data; break;
1309  case "AttributesAcquire" : mAttributesAcquire = data; break;
1310  case "Environment" : oEnvironment = data; break;
1311  case "identifier" : sIdentifier = data; break;
1312  case "DependingObjects" : aDependingObjects = data; break;
1313  case "DependingOn" : aDependingOn = data; break;
1314  }
1315  }
1316  else
1317  {
1318  if ( mappingp(data->AttributesLocked) )
1319  mAttributesLocked = data["AttributesLocked"];
1320  if ( mappingp(data->AttributesAcquire) )
1321  mAttributesAcquire = data["AttributesAcquire"];
1322  if ( arrayp(data->DependingObjects) )
1323  aDependingObjects = data["DependingObjects"];
1324  if ( arrayp(data->DependingOn) )
1325  aDependingOn = data["DependingOn"];
1326 
1327  oEnvironment = data["Environment"];
1328  sIdentifier = data["identifier"];
1329  }
1330 }
1331 
1332 public:
1333 
1334 /**
1335  * Unserialize data of the object. Called when Database loads the object
1336  *
1337  * @param str - the serialized object data
1338 private:
1339  * @see retrieve_attr_data
1340  */
1341 final void
1342 private:
1343 restore_attr_data(mixed data, string|void index)
1344 {
1345  if ( CALLER != _Database )
1346  THROW("Invalid call to restore_attr_data()", E_ACCESS);
1347  if (!zero_type(index))
1348  {
1349  if (index==OBJ_NAME) {
1350  if ( !stringp(data) )
1351  data = "";
1352  }
1353  mAttributes[index] = data;
1354 
1355  }
1356  else if ( mappingp(data) ) {
1357  mAttributes = data;
1358  }
1359  else {
1360  FATAL("Failed to restore Attribute data in %d (data=%O)",
1361  get_object_id(),
1362  data);
1363  }
1364 }
1365 
1366 public:
1367 
1368 /**
1369  * serialize data of the object. Called by the Database object to save
1370  * the objects varibales into the Database.
1371  *
1372  * @return the Variables of the object to be stored into database.
1373 private:
1374  * @see restore_data
1375  */
1376 final mixed
1377 private:
1378 retrieve_data(string|void index)
1379 {
1380  if ( CALLER != _Database )
1381  THROW("Invalid call to retrieve_data()", E_ACCESS);
1382 
1383  if (zero_type(index))
1384  {
1385  return ([
1386  "identifier": sIdentifier,
1387  //"Attributes":mAttributes,
1388  "AttributesLocked":mAttributesLocked,
1389  "AttributesAcquire":mAttributesAcquire,
1390  "Environment":oEnvironment,
1391  "DependingObjects":aDependingObjects,
1392  "DependingOn":aDependingOn,
1393  ]);
1394  } else {
1395  switch(index) {
1396  case "identifier": return sIdentifier;
1397  case "AttributesLocked": return mAttributesLocked;
1398  case "AttributesAcquire": return mAttributesAcquire;
1399  case "Environment": return oEnvironment;
1400  case "DependingObjects": return aDependingObjects;
1401  case "DependingOn": return aDependingOn;
1402  }
1403  }
1404 }
1405 
1406 public:
1407 
1408 
1409 /**
1410  * serialize data of the object. Called by the Database object to save
1411  * the objects varibales into the Database.
1412  * This callback is registered as indexed data_storage
1413  *
1414  * @return mixed - single attribute if index is given
1415  * full attribute data otherwise
1416  *
1417 private:
1418  * @see restore_data
1419  */
1420 final mixed
1421 private:
1422 retrieve_attr_data(string|void index)
1423 {
1424  if ( CALLER != _Database )
1425  THROW("Invalid call to retrieve_data()", E_ACCESS);
1426  if (zero_type(index))
1427  return mAttributes;
1428  else
1429  return mAttributes[index];
1430 }
1431 
1432 public:
1433 
1434 /**
1435  * returns the proxy object for this object, the proxy is set
1436  * when the object is created.
1437  *
1438  * @return the proxy object of this object
1439  * @see create
1440  */
1441 object
1442 {
1443  return oProxy;
1444 }
1445 
1446 /**
1447  * trusted object mechanism - checks if an object is trusted by this object
1448  *
1449  * @param object obj - is the object trusted
1450  * @return if the object is trustedd or not.
1451  */
1452 bool trust(object obj)
1453 {
1454  if ( obj == oProxy )
1455  return true;
1456  return false;
1457 }
1458 
1459 /**
1460  * This function is called by delete to delete this object.
1461  *
1462  * @see delete
1463  */
1464 protected:
1465  void
1466 delete_object()
1467 {
1468  if ( objectp(oEnvironment) ) {
1469  FATAL("Failed to remove object from environment when deleting %O",
1470  }
1471  }
1472 
1473  mixed err;
1474  err = catch {
1475  remove_all_annotations();
1476  };
1477 
1478  // delete all objects that dpend on this object:
1479  mixed deps = get_depending_objects();
1480  if ( arrayp(deps) ) {
1481  foreach ( deps, mixed dep ) {
1482  if ( !objectp(dep) ) continue;
1483  err = catch {
1484  if ( !dep->delete() )
1485  };
1486  if ( err )
1487  FATAL("Failed to delete %O (which depends on %O): %O\n%O", dep,
1488  }
1489  }
1490  // remove this object from the depending objects list of the objects it
1491  // depends on:
1492  deps = get_depending_on();
1493  if ( arrayp(deps) ) {
1494  foreach ( deps, mixed dep ) {
1495  if ( !objectp(dep) ) continue;
1496  FATAL("Failed to unregister depending object on delete: %O (which "
1497  }
1498  }
1499 }
1500 
1501 public:
1502 
1503 /**
1504  * Call this function to delete this object. Of course this requires
1505  * write permissions.
1506  *
1507  * @see destroy
1508  * @see delete_object
1509  */
1510 final bool
1511 delete()
1512 {
1513  // delete this object:
1514  try_event(EVENT_DELETE, CALLER);
1515  delete_object();
1516  run_event(EVENT_DELETE, CALLER);
1517  oEnvironment = 0;
1518  object temp = get_module("temp_objects");
1519  if ( objectp(temp) )
1520  temp->queued_destruct();
1521  else
1522  destruct(this_object());
1523  return true;
1524 }
1525 
1526 /**
1527  * non-documents have no content, see pike:file_stat() for sizes of
1528  * directories, etc.
1529  *
1530  * @return the content size of an object
1531  * @see stat
1532  */
1533 int
1534 get_content_size()
1535 {
1536  return 0;
1537 }
1538 
1539 /**
1540  * Returns the id of the content inside the Database.
1541  *
1542  * @return the content-id inside database
1543  */
1544 final int get_content_id()
1545 {
1546  return 0;
1547 }
1548 
1549 
1550 /**
1551  * get the identifier of an object, this is the unique name inside
1552  * the current environment of the object
1553  *
1554  * @return the unique name
1555  * @see update_identifier
1556  */
1557 string
1558 get_identifier()
1559 {
1560  if ( !stringp(sIdentifier) )
1561  return do_query_attribute(OBJ_NAME) || "";
1562  return sIdentifier;
1563 }
1564 
1565 /**
1566  * file stat information about the object
1567  *
1568  * @return the information like in file_stat()
1569  */
1570 array stat()
1571 {
1572  return ({ 33261, get_content_size(),
1573  mAttributes[OBJ_CREATION_TIME], time(), time(),
1574  (objectp(get_creator()) ?
1575  get_creator()->get_object_id():0),
1576  0,
1577  "application/x-unknown-content-type", });
1578 }
1579 
1580 /**
1581  * Database is allowed to get any function pointer (for restoring object data)
1582  * and is the only object allowed to call this function.
1583  *
1584  * @param string func - the function to get the pointer to.
1585  * @return the functionp to func
1586  * @see is_function
1587  */
1588 function get_function(string func)
1589 {
1590  object caller = CALLER;
1591 
1592  if ( caller != _Database && !_Server->is_a_factory(caller) )
1593  throw(sprintf("Only database is allowed to get function pointer. NOT %O", caller));
1594 
1595  switch(func) {
1596  case "do_set_acquire_attribute":
1597  return do_set_acquire_attribute;
1598  case "do_set_attribute":
1599  return do_set_attribute;
1600  case "do_sanction_object":
1601  return set_sanction;
1602  case "do_lock_attribute":
1603  return do_lock_attribute;
1604  case "do_unlock_attribute":
1605  return do_unlock_attribute;
1606  case "do_sanction_object_meta":
1607  return set_meta_sanction;
1608  }
1609 
1610  return this_object()[func];
1611 }
1612 
1613 
1614 object get_icon()
1615 {
1616  return query_attribute(OBJ_ICON);
1617 }
1618 
1619 /**
1620  * Find out if a given function is present inside this object.
1621  *
1622  * @param string func - the function to find out about.
1623  * @return is the function present ?
1624  * @see get_function
1625  */
1626 bool is_function(string func)
1627 {
1628  return functionp(this_object()[func]);
1629 }
1630 
1631 {
1632  object factory = get_factory(CLASS_DOCUMENT);
1633 
1634  // attribute testing
1635  set_attribute("objtest", "hello");
1636  Test.test( "setting attribute",
1637  do_query_attribute("objtest") == "hello" );
1638 
1639  Test.test( "acquire to self throws",
1640 
1641  // depending objects test
1642  object obj1 = factory->execute( ([ "name":"obj_for_deps", "mimetype":"text/plain" ]) );
1643  object obj2 = factory->execute( ([ "name":"dep_obj_1", "mimetype":"text/plain" ]) );
1644  object obj3 = factory->execute( ([ "name":"dep_obj_2", "mimetype":"text/plain" ]) );
1645  obj1->add_depending_object( obj2 );
1646  obj1->add_depending_object( obj3 );
1647  Test.test( "adding depending object",
1648  search(obj1->get_depending_objects(), obj2)>=0
1649  && search(obj2->get_depending_on(), obj1)>=0 );
1650  obj1->remove_depending_object( obj2 );
1651  Test.test( "removing depending object",
1652  search(obj1->get_depending_objects(), obj2)<0
1653  && search(obj2->get_depending_on(), obj1)<0 );
1654  obj1->add_depending_object( obj2 );
1655  Test.test( "re-adding depending object",
1656  search(obj1->get_depending_objects(), obj2)>=0
1657  && search(obj2->get_depending_on(), obj1)>=0 );
1658 
1659  obj2->delete();
1660  Test.test( "deleting depending object", search(obj1->get_depending_objects(), obj2)<0 );
1661  obj1->delete();
1662  Test.test( "deleting object that others depended on",
1663  (!objectp(obj1) || obj1->status()==PSTAT_DELETED)
1664  && (!objectp(obj3) || obj3->status()==PSTAT_DELETED) );
1665  if ( objectp(obj3) ) obj3->delete();
1666 
1667  // sanction test, access
1668  object steam = GROUP("steam");
1669  if ( !objectp(steam) )
1670  steam_error("Something seriously wrong - no steam group !");
1671  int val = sanction_object(steam, SANCTION_EXECUTE);
1672  Test.test( "sanction", (val & SANCTION_EXECUTE) );
1673 
1674  // annotations
1675  object ann = factory->execute( ([
1676  "name":"an annotation", "mimetype":"text/html" ]) );
1677  Test.test( "annotation has correct class",
1678  (ann->get_object_class() & CLASS_DOCHTML) );
1679  add_annotation(ann);
1680  Test.test( "adding annotation",
1681  ( search(get_annotations(), ann) != -1 ) );
1682 
1683  // Duplicate tests
1684  object ref = get_factory(CLASS_CONTAINER)->execute((["name": "reference",]));
1685  move(ref);
1686  do_set_attribute("ref", ref);
1687  do_set_attribute("refmap", ([ "ref": ref, ]));
1688  do_set_attribute("refarr", ({ ref }));
1689 
1690  // recursive duplication
1691  object dup = ref->duplicate(true);
1692 
1693  object dupthis = dup->get_inventory()[0];
1694  Test.test("Duplicated Object", dupthis->get_identifier() == get_identifier());
1695  Test.test("Single Ref", dupthis->query_attribute("ref") == dup);
1696  Test.test("Map Ref", dupthis->query_attribute("refmap")["ref"] == dup);
1697  Test.test("Array Ref", dupthis->query_attribute("refarr")[0] == dup);
1698 
1699  remove_annotation(ann);
1700  Test.test( "removing annotation",
1701  ( search(get_annotations(), ann) == -1 ) );
1702  Test.test( "removed annotation reference",
1703 
1704  //test events
1705  object listener = add_event(ann, EVENT_ANNOTATE, PHASE_NOTIFY,
1706  lambda (mixed args) { });
1707  Test.skipped( "event listener",
1708  "listener="+listener->describe() );
1709 
1710  // todo: much more event testing ...
1711 
1712  object obj = factory->execute( (["name":"object", ]) );
1713  ann->add_annotation(obj);
1714 
1715  ann->delete();
1716  ref->delete();
1717 
1718  delete(); // we are just a test object...
1719 }
1720 
1721 
1722 string describe()
1723 {
1724  return get_identifier()+"(#"+get_object_id()+","+
1725  master()->describe_program(object_program(this_object()))+","+
1726  get_object_class()+")";
1727 }
1728 
1729 string _sprintf()
1730 {
1731  return describe();
1732 }
1733 
1734 string get_xml()
1735 {
1736  object serialize = get_module("Converter:XML");
1737  string xml = "<?xml version='1.0' encoding='iso-8859-1'?>";
1738  mapping val = mkmapping(::_indices(1), ::_values(1));
1739  foreach ( indices(val), string idx ) {
1740  if ( !functionp(val[idx]) )
1741  xml += "<"+idx+">\n" +
1742  serialize->compose(val[idx])+"\n</"+idx+">\n";
1743  }
1744  return xml;
1745 }
1746 
1747 protected:
1748  string get_typeof(mixed val)
1749 {
1750  if ( intp(val) )
1751  return "Int";
1752  if ( floatp(val) )
1753  return "Float";
1754  if ( objectp(val) )
1755  return "Object";
1756  if ( arrayp(val) )
1757  return "Array";
1758  if ( mappingp(val) )
1759  return "Map";
1760  if ( stringp(val) )
1761  return "String";
1762  return "unknown";
1763 }
1764 
1765 public:
1766 
1767 protected:
1768  void lowSerializeXML(object parent, mixed val)
1769 {
1770  if ( intp(val) ) {
1771  parent->add_prop("Type", "Int");
1772  parent->add_data((string)val);
1773  }
1774  else if ( floatp(val) ) {
1775  parent->add_prop("Type", "Float");
1776  parent->add_data((string)val);
1777  }
1778  else if ( stringp(val) ) {
1779  parent->add_prop("Type", "String");
1780  parent->add_data((string)val);
1781  }
1782  else if ( objectp(val) ) {
1783  parent->add_prop("Type", "Object");
1784  parent->add_data("ID#"+val->get_object_id());
1785  }
1786  else {
1787  parent->add_prop("Type", (arrayp(val) ?"Array":"Map"));
1788  foreach ( indices(val), mixed idx ) {
1789  mixed vlx = val[idx];
1790  object item = xslt.Node("item", ([ ]) );
1791  object key = xslt.Node("key", ([ ]) );
1792  object value = xslt.Node("value", ([ ]) );
1793  parent->add_child(item);
1794  item->add_child(key);
1795  item->add_child(value);
1796  lowSerializeXML(key, idx);
1797  lowSerializeXML(value, vlx);
1798  }
1799  }
1800 }
1801 
1802 public:
1803 
1804 void lowAppendXML(object rootNode, void|int depth)
1805 {
1806  rootNode->add_prop("ID", (string)get_object_id());
1807  rootNode->add_prop("Type",
1808  get_factory(get_object_class())->get_class_name());
1809  rootNode->add_prop("Name", do_query_attribute(OBJ_NAME));
1810  object attributesNode = xslt.Node("Attributes", ([ ]));
1811  rootNode->add_child(attributesNode);
1812  foreach ( indices(mAttributes), string key ) {
1813  object attrNode;
1814  mixed val = mAttributes[key];
1815  if ( arrayp(val) || mappingp(val) ) {
1816  attrNode = xslt.Node("ComplexAttribute",([ "Key": key, ]));
1817  lowSerializeXML(attrNode, val);
1818  }
1819  else if ( objectp(val) ) {
1820  attrNode = xslt.Node("Attribute",([ "Key": key, ]));
1821  attrNode->add_prop("Type", "Object");
1822  attrNode->add_data("ID#" + val->get_object_id());
1823  }
1824  else {
1825  attrNode = xslt.Node("Attribute",([ "Key": key, ]));
1826  attrNode->add_prop("Type", get_typeof(val));
1827  attrNode->add_data((string)val);
1828  }
1829  attributesNode->add_child(attrNode);
1830  }
1831 }
1832 
1833 string getXML(void|int depth)
1834 {
1835  object doc = xslt.DOM("Object");
1836  object rootNode = doc->get_root();
1837  lowAppendXML(rootNode, depth);
1838  return doc->render_xml();
1839 }
1840 
1841 
1842 void route_call(function f, void|array args)
1843 {
1844  if ( CALLER != master() )
1845  steam_error("Invalid call to route_call !");
1846  f(@args);
1847 }
1848 
1849 
1850 array get_depending_objects () {
1851  if ( !arrayp(aDependingObjects) ) return ({ });
1852  else return aDependingObjects;
1853 }
1854 
1855 
1856 void add_depending_object ( object obj ) {
1857  if ( !arrayp(aDependingObjects) ) aDependingObjects = ({ });
1858  if ( search( aDependingObjects, obj ) < 0 )
1859  aDependingObjects += ({ obj });
1860  require_save( STORE_DATA );
1861 }
1862 
1863 
1864 void remove_depending_object ( object obj ) {
1865  if ( arrayp(aDependingObjects) ) {
1866  if ( search( aDependingObjects, obj ) >= 0 )
1867  aDependingObjects -= ({ obj });
1868  }
1869  require_save( STORE_DATA );
1870 }
1871 
1872 
1873 array get_depending_on () {
1874  if ( !arrayp(aDependingOn) ) return ({ });
1875  else return aDependingOn;
1876 }
1877 
1878 
1879 void add_depending_on ( object obj ) {
1880  if ( !arrayp(aDependingOn) ) aDependingOn = ({ });
1881  if ( search( aDependingOn, obj ) < 0 )
1882  aDependingOn += ({ obj });
1883  require_save( STORE_DATA );
1884 }
1885 
1886 
1887 void remove_depending_on ( object obj ) {
1888  if ( arrayp(aDependingOn) && search( aDependingOn, obj ) >= 0 ) {
1889  aDependingOn -= ({ obj });
1890  require_save( STORE_DATA );
1891  }
1892 }
1893 
1894 
1895 };