1 /* Copyright (C) 2000-2007 Thomas Bopp, Thorsten Hampel, Ludger Merkens
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.
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.
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
17 * $Id: Object.pike,v 1.9 2010/10/08 14:45:56 nicke Exp $
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.
26 #include <attributes.h>
31 #include <functions.h>
34 //! Each Object in sTeam is derived from this class
35 class Object : public access,events,annotateable,decorateable,references{
45 private mapping mAttributes; /* attribute mapping of object */
46 private mapping mAttributesAcquire;
47 private mapping mAttributesLocked;
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 */
61 return indices(this_object());
64 mixed set_attribute(string index, mixed data);
67 * create_object() is the real constructor, not called when object is loaded
79 * Called after the object was created. Then calls create_object() which
80 * actually is the function to be overwritten.
87 object caller = MCALLER;
88 if ( caller != _Database &&
90 caller != _Persistence &&
91 !(get_object_class() & CLASS_FACTORY) &&
92 !_Server->is_factory(caller) &&
93 !_SECURITY->access_create_object(0, caller) )
95 FATAL("Calling object is not a factory !");
96 THROW("Security violation while creating object", E_ACCESS);
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.
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);
134 * Called after the Database has loaded the object data.
147 * Database calls this function after loading an object.
154 if ( CALLER != _Database && CALLER != _Server && CALLER != _Persistence )
155 THROW("Illegal Call to loaded() !", E_ACCESS);
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 ) );
163 werror( "Could not register decoration %s while loading %d : %s\n%O\n",
164 decoration_path, get_object_id(), err[0], err[1] );
170 private bool register_decoration ( string path )
172 object deco = load_decoration( path );
173 if ( !objectp(deco) ) return false;
174 deco->register_attribute_functions( do_set_attribute, do_query_attribute );
182 private void unregister_decoration ( string path )
189 final bool is_loaded() { return __loaded; }
193 * See if this object can be dropped (swapping)
195 * @return can this object be swapped out or not.
197 bool check_swap() { return true; }
198 bool check_upgrade() { return true; }
202 * Master calls this function in each instance when the class is upgraded.
211 * This is the constructor of the object.
213 * @param string|object id - the name of the object if just created,
218 create(string|object id, void|mapping attributes)
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) )
225 FATAL("-- Calling object is not a factory ! - aborting creation!");
226 THROW("Security violation while creating object", E_ACCESS);
229 if ( mappingp(attributes) )
230 mAttributes = copy_value(attributes);
233 mAttributesAcquire = ([ ]);
234 mAttributesLocked = ([ ]);
238 if ( objectp(id) ) { // object is newly loaded
240 iObjectID = oProxy->get_object_id();
241 sIdentifier = "object";
245 [ iObjectID, oProxy ] = _Persistence->new_object(id);
248 database_registration(id);
255 * Save the object. This call is delegated to the Database singleton.
259 void require_save(void|string ident, void|string index, void|int action, void|array args)
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);
270 object duplicate(void|mapping vars)
272 mapping duplicates = do_duplicate(vars);
273 foreach(values(duplicates), object dup) {
274 dup->fix_references(duplicates);
279 mapping do_fix_references(mapping duplications, mixed val)
281 mapping result = ([ "val": 0, "fixed": 0, ]);
284 foreach ( val, mixed v ) {
285 mapping res = do_fix_references(duplications, v);
286 result->val += ({ res->val });
287 result->fixed |= res->fixed;
290 else if ( mappingp(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;
298 else if ( objectp(val) ) {
299 if ( objectp(duplications[val]) ) {
301 result->val = duplications[val];
309 void fix_references(mapping duplications)
311 THROW("Security Violation", E_ACCESS);
313 mapping attr = copy_value(mAttributes);
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);
324 * Duplicate an object - that is create a copy, the permisions are
327 * @return the copy of this object
330 mapping do_duplicate(void|mapping vars)
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 )
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
345 if ( mappingp(vars) && !zero_type(vars["version_of"]) )
346 attr[OBJ_VERSIONOF] = vars["version_of"];
349 ([ "name": do_query_attribute(OBJ_NAME),
351 "attributesAcquired": mAttributesAcquire,
352 "attributesLocked": mAttributesLocked, ]);
353 if ( mappingp(vars) )
355 if ( !stringp(exec_vars->name) || exec_vars->name == "" )
356 exec_vars->name = "Copy of " + get_object_id();
359 object dup_obj = factory->execute( exec_vars );
361 mapping dup_depending_objects = ([ ]);
362 foreach ( get_depending_objects(), object dep ) {
363 dup_depending_objects |= dep->do_duplicate( vars );
365 foreach ( values(dup_depending_objects), object dep ) {
366 dup_obj->add_depending_object( dep );
368 duplicates |= dup_depending_objects;
370 run_event(EVENT_DUPLICATE, CALLER);
374 mapping copy(object obj, mapping vars)
376 mapping copies = ([ ]);
377 foreach ( obj->get_annotations(), object ann ) {
379 catch(copied = ann->do_duplicate());
380 object dup_ann = copied[ann];
381 if ( objectp(dup_ann) ) {
382 __annotateable::add_annotation(dup_ann);
391 * currently no idea what this function is good for ... maybe comment
392 * directly when writing the code ?
396 void database_registration(string name)
403 * This is the destructor of the object - the object will be swapped out.
413 final void do_lock_attribute(int|string key)
415 mAttributesLocked[key] = true;
416 require_save(STORE_DATA);
422 * Set the event for a specific attribute.
424 * @param key - the key of the attribute
428 lock_attribute(int|string key)
430 try_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, true);
431 do_lock_attribute(key);
432 run_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, true);
436 * Unlock all attributes.
438 * @see lock_attribute
440 final void unlock_attributes()
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);
450 void do_unlock_attribute(string key)
452 m_delete(mAttributesLocked, key);
453 require_save(STORE_DATA);
459 * Set the event for a specific attribute.
461 * @param key - the key of the attribute
465 unlock_attribute(string key)
467 try_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, false);
468 do_unlock_attribute(key);
469 run_event(EVENT_ATTRIBUTES_LOCK, CALLER, key, false);
473 * Returns whether an attribute is locked or not. Attributes can be locked
474 * to keep people from moving objects around (for example coordinates)
476 * @param mixed key - the attribute key to check
477 * @return locked or not
479 bool is_locked(mixed key)
481 return mAttributesLocked[key];
485 * Returns an array containing all locked attributes
487 * @return locked attributes as array
489 array get_locked_attributes()
491 array locked_attributes = ({});
492 array locked_keys = indices(mAttributesLocked);
493 foreach (locked_keys, string key) {
494 if (mAttributesLocked[key]) {
495 locked_attributes += ({key});
498 return locked_attributes;
503 * Each attribute might cause a different event to be fired, get
504 * the one for changing the attribute.
506 * @param int|string key - the attribute key
507 * @return the corresponding event
508 * @see get_attributes_read_event
510 int get_attributes_change_event(int|string key)
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;
519 * Each attribute might cause a different event to be fired, get
520 * the one for reading the attribute.
522 * @param int|string key - the attribute key
523 * @return the corresponding event
524 * @see get_attributes_read_event
526 int get_attributes_read_event(int|string key)
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);
537 return fGetEvent(key);
541 * Get the mapping of all registered attributes. That is only the
542 * descriptions, permissions, type registration of the attributes.
544 * @return mapping of all registered attributes
545 * @see describe_attribute
547 mapping describe_attributes()
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,
559 "eventWrite": EVENT_ATTRIBUTES_CHANGE,
560 "control": CONTROL_ATTR_USER
566 mapping get_attributes()
568 return describe_attributes();
572 * Get the mapping of acquired Attributes.
574 * @return Mapping of acquired attributes (copy of the mapping of course)
576 mapping get_acquired_attributes()
578 return copy_value(mAttributesAcquire);
582 * Get the names of all attributes used in the object. Regardless if
583 * they are registered or not.
586 * @return list of names
587 * @see get_attributes
589 array get_attribute_names()
591 return indices(mAttributes);
594 * Describe an attribute - call the factory of this class for it.
595 * It will return an array of registration data.
597 * @param mixed key - the attribute to describe
598 * @return array of registration data - check attributes.h for it.
600 array describe_attribute(mixed key)
602 object factory = _Server->get_factory(this_object());
603 return factory->describe_attribute(key);
607 * Check before setting an attribute. This include security checks
608 * and finding out if the type of data matches the registered type.
610 * @param mixed key - the attribute key
611 * @param mixed data - the new value for the attribute
612 * @return true|throws exception
616 bool check_set_attribute(string key, mixed data)
618 if ( intp(key) || arrayp(key) || mappingp(key) )
619 steam_error("Wrong key for Attribute '"+key+"'");
621 if ( mappingp(mAttributesLocked) && mAttributesLocked[key] )
622 THROW("Trying to set locked attribute '"+key+" ' in '"+
623 get_identifier()+ "' !", E_ACCESS);
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 )
629 if ( objectp(factory) )
630 return factory->check_attribute(key, data);
637 * This function is called when an attribute is changed in the object,
638 * that acquires an attribute from this object.
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
645 bool keep_acquire(object o, mixed key, mixed val)
647 return false; // nothing is acquired from the object anymore
651 * Sets a single attribute of an object. This function checks for acquiring
652 * and possible sets the attribute in the object acquired from.
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
660 bool do_set_attribute(string index, mixed|void data)
662 object|function acquire;
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 */
671 acquire->set_attribute(index, data);
672 if ( index == OBJ_NAME )
673 set_identifier(data);
677 // set acquire to zero
678 mAttributesAcquire[index] = 0;
679 require_save(STORE_DATA);
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);
688 if ( zero_type(data) )
689 m_delete(mAttributes, index);
691 mAttributes[index] = copy_value(data);
693 /* Database needs to save changes sometimes */
694 require_save(STORE_ATTRIB, index);
701 * Set an attribute <u>key</u> to new value <u>data</u>.
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
708 mixed set_attribute(string key, void|mixed data)
710 check_set_attribute(key, data);
711 mixed oldval = do_query_attribute(key);
713 try_event(get_attributes_change_event(key), CALLER, ([ key:data ]),
716 do_set_attribute(key, data);
717 run_event(get_attributes_change_event(key), CALLER, ([ key:data ]),
722 int arrange(float x, float y)
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, ]) );
733 mixed do_append_attribute(string key, mixed data)
735 array val = do_query_attribute(key);
736 if ( mappingp(data) ) {
738 return do_set_attribute(key, data + val);
740 if ( zero_type(val) || val == 0 )
742 if ( !arrayp(data) ) {
743 if ( search(val, data) >= 0 )
747 return do_set_attribute(key, data + val);
748 THROW("Can only append arrays on attributes !", E_ERROR);
754 mixed remove_from_attribute(string key, mixed data)
756 mixed val = do_query_attribute(key);
758 if ( search(val, data) >= 0 ) {
759 return do_set_attribute(key, val - ({ data }));
762 else if ( mappingp(val) ) {
764 return do_set_attribute(key, val);
773 * Sets a number of attributes. The format is
774 * attr = ([ key1:val1, key2:val2,...]) and the function calls set_attribute
777 * @param mapping attr - the attribute mapping.
778 * @return true | throws exception
781 bool set_attributes(mapping attr)
784 mapping eventAttr = ([ ]);
785 mapping oldAttr = ([ ]);
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);
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]);
803 // now the attributes are really set
804 foreach(indices(attr), mixed key) {
805 do_set_attribute(key, attr[key]);
808 // notification about the change, again for each package individually
809 foreach( indices(eventAttr), event )
810 run_event(event, CALLER, eventAttr[event], oldAttr[event]);
816 void do_set_acquire_attribute(mixed index, void|object|function|string acquire)
820 // quick and dirty hack, because protocoll cannot send functions
821 if ( stringp(acquire) && acquire == REG_ACQ_ENVIRONMENT )
822 acquire = get_environment;
824 if ( functionp(acquire) )
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);
837 mAttributesAcquire[index] = acquire;
838 require_save(STORE_DATA);
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.
849 * @param index - the attribute to set acquiring
850 * @param acquire - object or function(object) for acquiring
854 set_acquire_attribute(mixed index, void|object|function|int acquire)
856 try_event(EVENT_ATTRIBUTES_ACQUIRE, CALLER, index, acquire);
857 // check for possible endless loops
859 do_set_acquire_attribute(index, acquire);
861 run_event(EVENT_ATTRIBUTES_ACQUIRE, CALLER, index, acquire);
865 * Retrieve the acquiring status for an attribute.
867 * @param mixed key - the key to get acquiring status for
868 * @return function|object of acquiring or 0.
869 * @see set_acquire_attribute
871 object|function get_acquire_attribute(mixed key)
873 return mAttributesAcquire[key];
877 * Get the value of one attribute.
879 * @param mixed key - what attribute to query.
880 * @return the value of the queried attribute
884 query_attribute(mixed key)
888 int event = get_attributes_read_event(key);
889 if ( event > 0 ) try_event(event, CALLER, key);
891 val = do_query_attribute(key);
893 if ( event > 0 ) run_event(event, CALLER, key );
895 return copy_value(val);
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.
903 * @param mixed key - the attribute to query.
904 * @return value of the queried attribute
905 * @see query_attribute
908 mixed do_query_attribute(mixed key)
910 object|function acquire;
912 if ( mappingp(mAttributesAcquire) )
913 acquire = mAttributesAcquire[key];
914 if ( functionp(acquire) ) acquire = acquire();
916 // if the attribute is acquired from another object query the attribute
918 if ( objectp(acquire) )
919 return acquire->query_attribute(key);
920 return mAttributes[key];
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
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
937 query_attributes(void|array|mapping keys)
942 function qa = query_attribute;
944 if ( !arrayp(keys) ) {
945 if ( !mappingp(keys) )
946 keys = mkmapping(indices(mAttributes), values(mAttributes)) |
947 mkmapping(indices(mAttributesAcquire), values(mAttributesAcquire));
949 if ( mappingp(keys) ) {
950 foreach(indices(keys), mixed key) {
955 FATAL( "Could not query attribute: %O\n", key );
960 result = allocate(sizeof(keys));;
963 for ( i = sizeof(keys)-1; i >= 0; i-- )
964 result[i] = qa(keys[i]);
970 * Set new permission for an object in the acl. Old permissions
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
979 int sanction_object(object grp, int permission)
981 ASSERTINFO(_SECURITY->valid_proxy(grp), "Sanction on non-proxy!");
982 if ( query_sanction(grp) == permission )
983 return permission; // if permissions are already fine
985 try_event(EVENT_SANCTION, CALLER, grp, permission);
986 set_sanction(grp, permission);
988 run_event(EVENT_SANCTION, CALLER, grp, permission);
993 * Sets the new meta permissions for an object. These are permissions
994 * that are used for giving away permissions on this object.
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
1002 sanction_object_meta(object grp, int permission)
1004 try_event(EVENT_SANCTION_META, CALLER, grp, permission);
1005 set_meta_sanction(grp, permission);
1006 run_event(EVENT_SANCTION_META, CALLER, grp, permission);
1011 * Add an annotation to this object. Each object in steam
1014 * @param object ann - the annotation to add
1015 * @return successfull or not.
1017 bool add_annotation(object ann)
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);
1026 * Remove an annotation from this object. This only removes
1027 * it from the list of annotations, but doesnt delete it.
1029 * @param object ann - the annotation to remove
1030 * @return true or false
1031 * @see add_annotation
1033 bool remove_annotation(object ann)
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);
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.
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
1051 bool add_decoration ( string path )
1053 try_event( EVENT_DECORATE, CALLER, path );
1054 if ( ! register_decoration( path ) )
1056 __decorateable::add_decoration( path );
1057 run_event( EVENT_DECORATE, CALLER, path );
1062 * Removes a decoration from the object. This function just removes
1063 * the decoration path from the list of decorations.
1065 * @param string path the decoration path to remove
1066 * @return true if the decoration was successfully removed, false otherwise
1067 * @see add_decoration
1069 bool remove_decoration ( string path )
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 );
1079 * The persistent id of this object.
1081 * @return the ID of the object
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];
1098 * Is this an object ? yes!
1102 final bool is_object() { return true; }
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.
1109 * @param id - the new id
1112 set_object_id(int id)
1114 if ( CALLER == _Database && CALLER != _Persistence )
1120 * @return the class of the object
1122 int get_object_class()
1124 return CLASS_OBJECT;
1129 object factory = _Server->get_factory(get_object_class());
1130 return factory->get_class_name();
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.
1138 * @see get_identifier
1142 set_identifier(string name)
1146 if ( !stringp(name) )
1147 name = "* unknown *";
1149 identifier = replace(name, "/", "_");
1151 if ( identifier == sIdentifier )
1154 object env = get_environment();
1155 if ( objectp(env) ) {
1156 identifier = get_object_id() + "__" + identifier;
1159 sIdentifier = identifier;
1160 require_save(STORE_DATA);
1168 void update_identifier()
1170 object env = get_environment();
1171 if ( ! objectp(env) ) return;
1172 if ( objectp(env) && objectp(oo) ) {
1173 set_identifier(get_object_id()+"__"+sIdentifier);
1179 foreach(get_annotations(), object ann) {
1181 catch(ann->update_path());
1188 * Moves the object to a destination, which requires move permission.
1190 * @param dest - the destination of the move operation
1191 * @return move successfull or throws an exception
1193 bool move(object dest)
1197 if ( !objectp(dest) ) {
1198 try_event(EVENT_MOVE, CALLER, oEnvironment, dest);
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);
1204 // finally set objects new environment
1207 run_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1212 ASSERTINFO(IS_PROXY(dest), "Destination is not a proxy object !");
1214 /* Moving into an exit takes the exits location as destination */
1215 if ( dest->get_object_class() & CLASS_EXIT )
1216 dest = dest->get_exit();
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);
1224 try_event(EVENT_MOVE, CALLER, oEnvironment, dest);
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);
1230 /* then insert object into new environment */
1232 if ( objectp(oEnvironment) ) /* prevent object from being in void */
1235 // finally set objects new environment
1236 run_event(EVENT_MOVE, CALLER, oEnvironment, dest);
1237 oEnvironment = dest;
1240 require_save(STORE_DATA);
1242 // now check for name and rename other object
1243 update_identifier();
1249 * Get the environment of this object.
1251 * @see get_root_environment
1253 * @return environment of the object
1256 public object get_environment()
1258 return oEnvironment;
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
1270 * @see get_environment
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
1279 public object get_root_environment( int|void pass_through_users )
1282 while ( objectp(tmp_env) ) {
1283 if ( !pass_through_users &&
1284 tmp_env->get_object_class() & CLASS_USER ) return tmp_env;
1286 tmp_env = env->get_environment();
1292 * Unserialize data of the object. Called when Database loads the object
1294 * @param str - the serialized object data
1295 * @see unserialize_access
1297 * @see retrieve_data
1301 restore_data(mixed data, string|void index)
1303 if ( CALLER != _Database )
1304 THROW("Invalid call to restore_data()", E_ACCESS);
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;
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"];
1327 oEnvironment = data["Environment"];
1328 sIdentifier = data["identifier"];
1335 * Unserialize data of the object. Called when Database loads the object
1337 * @param str - the serialized object data
1339 * @see retrieve_attr_data
1343 restore_attr_data(mixed data, string|void index)
1345 if ( CALLER != _Database )
1346 THROW("Invalid call to restore_attr_data()", E_ACCESS);
1347 if (!zero_type(index))
1349 if (index==OBJ_NAME) {
1350 if ( !stringp(data) )
1353 mAttributes[index] = data;
1356 else if ( mappingp(data) ) {
1360 FATAL("Failed to restore Attribute data in %d (data=%O)",
1369 * serialize data of the object. Called by the Database object to save
1370 * the objects varibales into the Database.
1372 * @return the Variables of the object to be stored into database.
1378 retrieve_data(string|void index)
1380 if ( CALLER != _Database )
1381 THROW("Invalid call to retrieve_data()", E_ACCESS);
1383 if (zero_type(index))
1386 "identifier": sIdentifier,
1387 //"Attributes":mAttributes,
1388 "AttributesLocked":mAttributesLocked,
1389 "AttributesAcquire":mAttributesAcquire,
1390 "Environment":oEnvironment,
1391 "DependingObjects":aDependingObjects,
1392 "DependingOn":aDependingOn,
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;
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
1414 * @return mixed - single attribute if index is given
1415 * full attribute data otherwise
1422 retrieve_attr_data(string|void index)
1424 if ( CALLER != _Database )
1425 THROW("Invalid call to retrieve_data()", E_ACCESS);
1426 if (zero_type(index))
1429 return mAttributes[index];
1435 * returns the proxy object for this object, the proxy is set
1436 * when the object is created.
1438 * @return the proxy object of this object
1447 * trusted object mechanism - checks if an object is trusted by this object
1449 * @param object obj - is the object trusted
1450 * @return if the object is trustedd or not.
1452 bool trust(object obj)
1454 if ( obj == oProxy )
1460 * This function is called by delete to delete this object.
1468 if ( objectp(oEnvironment) ) {
1469 FATAL("Failed to remove object from environment when deleting %O",
1475 remove_all_annotations();
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;
1484 if ( !dep->delete() )
1487 FATAL("Failed to delete %O (which depends on %O): %O\n%O", dep,
1490 // remove this object from the depending objects list of the objects it
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 "
1504 * Call this function to delete this object. Of course this requires
1505 * write permissions.
1508 * @see delete_object
1513 // delete this object:
1514 try_event(EVENT_DELETE, CALLER);
1516 run_event(EVENT_DELETE, CALLER);
1518 object temp = get_module("temp_objects");
1519 if ( objectp(temp) )
1520 temp->queued_destruct();
1522 destruct(this_object());
1527 * non-documents have no content, see pike:file_stat() for sizes of
1530 * @return the content size of an object
1540 * Returns the id of the content inside the Database.
1542 * @return the content-id inside database
1544 final int get_content_id()
1551 * get the identifier of an object, this is the unique name inside
1552 * the current environment of the object
1554 * @return the unique name
1555 * @see update_identifier
1560 if ( !stringp(sIdentifier) )
1561 return do_query_attribute(OBJ_NAME) || "";
1566 * file stat information about the object
1568 * @return the information like in file_stat()
1572 return ({ 33261, get_content_size(),
1573 mAttributes[OBJ_CREATION_TIME], time(), time(),
1574 (objectp(get_creator()) ?
1575 get_creator()->get_object_id():0),
1577 "application/x-unknown-content-type", });
1581 * Database is allowed to get any function pointer (for restoring object data)
1582 * and is the only object allowed to call this function.
1584 * @param string func - the function to get the pointer to.
1585 * @return the functionp to func
1588 function get_function(string func)
1590 object caller = CALLER;
1592 if ( caller != _Database && !_Server->is_a_factory(caller) )
1593 throw(sprintf("Only database is allowed to get function pointer. NOT %O", caller));
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;
1610 return this_object()[func];
1616 return query_attribute(OBJ_ICON);
1620 * Find out if a given function is present inside this object.
1622 * @param string func - the function to find out about.
1623 * @return is the function present ?
1626 bool is_function(string func)
1628 return functionp(this_object()[func]);
1632 object factory = get_factory(CLASS_DOCUMENT);
1634 // attribute testing
1635 set_attribute("objtest", "hello");
1636 Test.test( "setting attribute",
1637 do_query_attribute("objtest") == "hello" );
1639 Test.test( "acquire to self throws",
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 );
1660 Test.test( "deleting depending object", search(obj1->get_depending_objects(), obj2)<0 );
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();
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) );
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 ) );
1684 object ref = get_factory(CLASS_CONTAINER)->execute((["name": "reference",]));
1686 do_set_attribute("ref", ref);
1687 do_set_attribute("refmap", ([ "ref": ref, ]));
1688 do_set_attribute("refarr", ({ ref }));
1690 // recursive duplication
1691 object dup = ref->duplicate(true);
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);
1699 remove_annotation(ann);
1700 Test.test( "removing annotation",
1701 ( search(get_annotations(), ann) == -1 ) );
1702 Test.test( "removed annotation reference",
1705 object listener = add_event(ann, EVENT_ANNOTATE, PHASE_NOTIFY,
1706 lambda (mixed args) { });
1707 Test.skipped( "event listener",
1708 "listener="+listener->describe() );
1710 // todo: much more event testing ...
1712 object obj = factory->execute( (["name":"object", ]) );
1713 ann->add_annotation(obj);
1718 delete(); // we are just a test object...
1724 return get_identifier()+"(#"+get_object_id()+","+
1725 master()->describe_program(object_program(this_object()))+","+
1726 get_object_class()+")";
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";
1748 string get_typeof(mixed val)
1758 if ( mappingp(val) )
1768 void lowSerializeXML(object parent, mixed val)
1771 parent->add_prop("Type", "Int");
1772 parent->add_data((string)val);
1774 else if ( floatp(val) ) {
1775 parent->add_prop("Type", "Float");
1776 parent->add_data((string)val);
1778 else if ( stringp(val) ) {
1779 parent->add_prop("Type", "String");
1780 parent->add_data((string)val);
1782 else if ( objectp(val) ) {
1783 parent->add_prop("Type", "Object");
1784 parent->add_data("ID#"+val->get_object_id());
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);
1804 void lowAppendXML(object rootNode, void|int depth)
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 ) {
1814 mixed val = mAttributes[key];
1815 if ( arrayp(val) || mappingp(val) ) {
1816 attrNode = xslt.Node("ComplexAttribute",([ "Key": key, ]));
1817 lowSerializeXML(attrNode, val);
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());
1825 attrNode = xslt.Node("Attribute",([ "Key": key, ]));
1826 attrNode->add_prop("Type", get_typeof(val));
1827 attrNode->add_data((string)val);
1829 attributesNode->add_child(attrNode);
1833 string getXML(void|int depth)
1835 object doc = xslt.DOM("Object");
1836 object rootNode = doc->get_root();
1837 lowAppendXML(rootNode, depth);
1838 return doc->render_xml();
1842 void route_call(function f, void|array args)
1844 if ( CALLER != master() )
1845 steam_error("Invalid call to route_call !");
1850 array get_depending_objects () {
1851 if ( !arrayp(aDependingObjects) ) return ({ });
1852 else return aDependingObjects;
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 );
1864 void remove_depending_object ( object obj ) {
1865 if ( arrayp(aDependingObjects) ) {
1866 if ( search( aDependingObjects, obj ) >= 0 )
1867 aDependingObjects -= ({ obj });
1869 require_save( STORE_DATA );
1873 array get_depending_on () {
1874 if ( !arrayp(aDependingOn) ) return ({ });
1875 else return aDependingOn;
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 );
1887 void remove_depending_on ( object obj ) {
1888 if ( arrayp(aDependingOn) && search( aDependingOn, obj ) >= 0 ) {
1889 aDependingOn -= ({ obj });
1890 require_save( STORE_DATA );