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();
};