new object $command_cache: $has_commands; var $command_cache interfaces = 0; var $command_cache local_cache = 0; var $command_cache remote_cache = 0; var $command_cache shortcut_cache = 0; var $has_commands shortcuts = #[]; var $root created_on = 796605573; var $root fertile = 1; var $root flags = ['methods, 'code, 'fertile, 'variables, 'core]; var $root inited = 1; var $root managed = [$command_cache]; var $root manager = $command_cache; public method .add_command_interface() { arg interface; if (!(interface.has_flag('command_cache))) throw(~perm, ("Command interface " + interface) + " is not setup as a cache."); interfaces = setadd(interfaces || [], interface); interface.interface_link(); }; private method .add_object_to_local_cache() { arg obj; var info, thing, element, part; info = (| obj.all_local_commands() |); if (info) { for element in (info) { for part in (element[2]) .add_to_local_cache(part[1]); } } }; public method .add_object_to_remote_cache() { arg obj; var info, thing, element, part; info = (| obj.all_remote_commands() |); if (info) { for element in (info) { for part in (element[2]) .add_to_remote_cache(part[1], element[1]); } } }; protected method .add_to_local_cache() { arg command; var part, cmd; if (type(local_cache) != 'dictionary) local_cache = #[]; // if this dies, it will also fail on explode_template_word cmd = (| (command.explode())[1] |); for part in (command.explode_template_word()) local_cache = local_cache.setadd_elem(part, cmd); }; protected method .add_to_remote_cache() { arg command, definer; var part, cmd, value, cmds, defs, refs; if (type(remote_cache) != 'dictionary) remote_cache = #[]; // if this dies, it will also fail on explode_template_word cmd = (| (command.explode())[1] |); for part in (command.explode_template_word()) { if ((value = (| remote_cache[part] |))) { cmds = (value[1]).setadd(cmd); refs = ((| (value[2])[definer] |) || 0) + 1; defs = (value[2]).add(definer, refs); remote_cache = remote_cache.add(part, [cmds, defs]); } else { remote_cache = remote_cache.add(part, [[cmd], #[[definer, 1]]]); } } }; public method .cache_init() { if (!(sender().has_ancestor(definer()))) throw(~nochild, ((sender() + " is not a descendant of ") + definer()) + "."); if (.is_command_cache()) { if ((!local_cache) || (!remote_cache)) .rehash_caches(); } }; public method .cache_uninit() { var user; // if (!.is($user)) // throw(~notuser, this() + " is not a user object."); if (!(sender().has_ancestor(this()))) throw(~nochild, ((sender() + " is not a descendant of ") + this) + ".\n"); if (.is_command_cache()) { // see if anybody still needs us, otherwise purge the caches for user in ($user_db.connected()) { if (user.has_ancestor(this())) return; } .purge_caches(); } }; root method .core_command_cache() { if (this() == definer()) { interfaces = (local_cache = (remote_cache = (shortcut_cache = 0))); } else { (| clear_var('interfaces) |); (| clear_var('local_cache) |); (| clear_var('remote_cache) |); (| clear_var('shortcut_cache) |); } }; public method .del_command_interface() { arg interface; if (!(interface.has_flag('command_cache))) throw(~perm, ("Command interface " + interface) + " is not setup as a cache."); interfaces = setremove(interfaces || [], interface); interface.interface_unlink(); }; protected method .del_from_remote_cache() { arg command, definer; var part, cmd, value, cmds, defs, refs; if (type(remote_cache) != 'dictionary) return; // if this dies, it will also fail on explode_template_word cmd = (| (command.explode())[1] |); for part in (command.explode_template_word()) { if ((value = (| remote_cache[part] |))) { refs = ((| (value[2])[definer] |) || 1) - 1; if (!refs) { remote_cache = remote_cache.del(part); if (!remote_cache) (| clear_var('remote_cache) |); } else { [cmds, defs] = value; defs = defs.add(definer, refs); remote_cache = remote_cache.add(part, [cmds, defs]); } } } }; public method .del_object_from_remote_cache() { arg obj; var info, thing, element, part; info = (| obj.all_remote_commands() |); if (info) { for element in (info) { for part in (element[2]) .del_from_remote_cache(part[1], element[1]); } } }; protected method .find_in_command_cache() { arg cmd_word, cache; var matches, match, obj, objs; matches = #[]; for obj in ([this()] + parents()) { match = (| obj.(cache)()[cmd_word] |); if (match) matches = matches.union(match); } return matches; }; public method .find_in_local_cache() { arg cmd; return (> local_cache[cmd] <); }; protected method .find_in_local_caches() { arg cmd_word; var matches, match, obj, objs; matches = []; for obj in (([this()] + parents()) + (interfaces || [])) { if ((match = (| obj.find_in_local_cache(cmd_word) |))) matches = matches.union(match); } return matches; }; public method .find_in_remote_cache() { arg cmd; return (> remote_cache[cmd] <); }; public method .find_in_remote_caches() { arg cmd_word; var matches, match, obj, objs; matches = []; for obj in ([this()] + parents()) { if ((match = (| obj.find_in_remote_cache(cmd_word) |))) matches = matches.union(match); } return matches; }; public method .interfaces() { return interfaces || []; }; public method .is_command_cache() { return 'command_cache in (.flags()); }; public method .local_cache() { return local_cache; }; public method .match_in_local_cache() { arg str, cmd, args; var command, match, matched, templates, info, cdef, def; templates = (matched = []); for command in (.find_in_local_caches(cmd)) { info = .get_command_info('local, command); if (!info) continue; for cdef in (info) { match = match_template(args, cdef[2]); if (match != 0) matched += [[match.length(), [str, cmd, @match], @cdef.subrange(3)]]; } templates = templates.union(info.slice(3)); } if (matched) { info = [matched[1]]; matched = matched.delete(1); for match in (matched) { if ((match[1]) > ((info[1])[1])) info = [match]; else if ((match[1]) == ((info[1])[1])) info += [match]; } return ['local, info]; } if (!templates) return 0; return ['partial, templates]; }; public method .match_in_remote_cache() { arg str, cmd, args; var cache, definer, command, info, cdef, match, matched, templates; if (!(cache = (| .find_in_remote_caches(cmd) |))) return 0; templates = []; matched = []; for command in (cache[1]) { for definer in ((cache[2]).keys()) { info = definer.get_command_info('remote, command); if (!info) continue; for cdef in (info) { match = args.match_template(cdef[2]); if (match != 0) matched += [[match.length(), definer, [str, cmd, @match], @cdef.subrange(3)]]; } templates = templates.union(info.slice(3)); } } if (matched) { info = [matched[1]]; matched = matched.delete(1); for match in (matched) { if ((match[1]) > ((info[1])[1])) info = [match]; else if ((match[1]) == ((info[1])[1])) info += [match]; } return ['remote, info]; } return ['partial, templates]; }; public method .match_in_shortcut_cache() { arg str, cmd, args; var shortcut, match, obj, shorts; for obj in ([this()] + parents()) { if ((shorts = obj.shortcut_cache())) { for shortcut in (shorts) { match = match_pattern(str, shortcut[1]); if (match != 0) return ['shortcut, [(shortcut[2])[1], [str, @$command_lib.handle_shortcut_fields((shortcut[2])[2], match)]]]; } } } return 0; }; public method .purge_caches() { (> .perms(sender()) <); (| clear_var('shortcut_cache) |); (| clear_var('remote_cache) |); (| clear_var('local_cache) |); }; public method .rehash_caches() { var cmd, obj, part, element; (> .perms(sender()) <); (| .purge_caches() |); if (!(.is_command_cache())) { // if we are not an official 'cache', just cache commands defined on us for cmd in (.local_commands()) { for part in (cmd[2]) .add_to_local_cache(part[1]); } shortcut_cache = (.shortcuts()).to_list(); } else { // otherwise cache all defined commands (> .add_object_to_local_cache(this()) <); shortcut_cache = .all_shortcuts(); } // remote caches are different, and HAVE to be specific to the user if (.is($location)) { for obj in ([this()] + (.contents())) (> .add_object_to_remote_cache(obj) <); } }; public method .remote_cache() { return remote_cache; }; public method .set_as_command_cache() { arg makecache; (> .perms(sender(), 'manager) <); if (makecache) (> .add_flag('command_cache) <); else (> .del_flag('command_cache) <); }; public method .shortcut_cache() { return shortcut_cache; }; root method .uninit_command_cache() { var i; (| .purge_caches() |); for i in (interfaces || []) i.interface_unlink(); };