object $root; var $root child = 0; var $root child_index = 0; var $root inited = 1; public method .children(): nooverride { return children(); }; public method .debug(): nooverride { arg [what]; dblog("DEBUG: " + what.join(" ")); }; public method .del_objname(): nooverride { arg name; (> del_objname(name) <); }; public method .descendants(): nooverride { var kids, i, c; kids = children(); while ((| (c = kids[(i = i + 1)]) |)) kids = union(kids, c.children()); return kids; }; public method .destroy(): nooverride { if (!child) throw(~perm, "Attempt to destroy a defining parent."); (| .uninitialize() |); destroy(); }; public method .find_method() { arg method; return (> find_method(method) <); }; private method .initialize(): nooverride { var ancestors, ancestor, pos, len, method; if (inited) throw(~perm, "Already initialized."); ancestors = ancestors(); len = ancestors.length(); for pos in [0 .. len - 1] { ancestor = ancestors[len - pos]; method = tosym("init_" + tostr(ancestor.objname('symbol))); catch ~methodnf { if ((.find_method(method)) != ancestor) throw(~perm, ((("Initialization method for " + (ancestor.objname())) + " in wrong place(") + (find_method(method).objname())) + ")"); .(method)(); } } inited = 1; child = 1; }; public method .log(): nooverride { arg what; var line; if (type(what) == 'string) { dblog(what); } else if (type(what) == 'list) { for line in (what) .log(line); } else { throw(~invarg, "Log must be called with a string or a list of strings"); } }; public method .objname(): nooverride { arg [args]; var name; name = (| objname() |); if (args) return name || 0; if (name) return "$" + tostr(name); return toliteral(this()); }; public method .parents(): nooverride { return parents(); }; public method .set_objname(): nooverride { arg objname; if (type(objname) != 'symbol) throw(~perm, "Name is not a symbol."); // Make sure everything is lowercase. objname = tosym(lowercase(tostr(objname))); // Do nothing if objname isn't different. if (objname == (| objname() |)) return; (> set_objname(objname) <); }; public method .spawn(): nooverride { arg [other_parents]; var base, obj, name; // if (child) // throw(~perm, "Attempt to spawn a non-defining object."); name = .objname('symbol); base = tostr(name); while ((| lookup(name) |)) { child_index = child_index + 1; name = tosym((base + "_") + tostr(child_index)); } obj = (> create([this()] + other_parents) <); catch any { (> obj.initialize() <); } with { .log($parse_lib.traceback(traceback())); if (!(| obj.destroy() |)) throw(~ack, "Unable to destroy aborted attempt", traceback()); rethrow(error()); } (> obj.set_objname(name) <); return obj; }; private method .uninitialize(): nooverride { var ancestor, p; for ancestor in (ancestors()) { method = tosym("uninit_" + tostr(ancestor.objname('symbol))); catch ~methodnf { if ((.find_method(method)) != ancestor) throw(~perm, ((("UnInitialization method for " + (ancestor.objname())) + " in wrong place (") + ((.find_method(method)).objname())) + ")"); .(method)(); } } for p in (.variables()) .del_var('p); }; public method .add_method() { arg code, meth; return (> add_method(code, meth) <); }; public method .list_method() { arg method, @args; args = [(| args[1] |) || 2, (| args[2] |) || 2]; return (> list_method(method, @args) <); }; public method .del_method() { arg name; return (> del_method(name) <); }; public method .method_info() { arg @args; return (> method_info(@args) <); };