/** * PermissionsEx * Copyright (C) zml and PermissionsEx contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ninja.leaping.permissionsex.bukkit; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import ninja.leaping.permissionsex.PermissionsEx; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; import org.bukkit.plugin.PluginManager; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * @author zml2008 */ public class PermissionList extends HashMap<String, Permission> { private static FieldReplacer<PluginManager, Map> INJECTOR; private static final Map<Class<?>, FieldReplacer<Permission, Map>> CHILDREN_MAPS = new HashMap<>(); /** * k = child permission * v.k = parent permission * v.v = value parent gives child */ private final Multimap<String, Map.Entry<String, Boolean>> childParentMapping = Multimaps.synchronizedMultimap(HashMultimap.<String, Map.Entry<String, Boolean>>create()); private final PermissionsExPlugin plugin; public PermissionList(PermissionsExPlugin plugin) { super(); this.plugin = plugin; } public PermissionList(Map<? extends String, ? extends Permission> existing, PermissionsExPlugin plugin) { super(existing); this.plugin = plugin; } private FieldReplacer<Permission, Map> getFieldReplacer(Permission perm) { FieldReplacer<Permission, Map> ret = CHILDREN_MAPS.get(perm.getClass()); if (ret == null) { ret = new FieldReplacer<>(perm.getClass(), "children", Map.class); CHILDREN_MAPS.put(perm.getClass(), ret); } return ret; } private void removeAllChildren(String perm) { for (Iterator<Map.Entry<String, Map.Entry<String, Boolean>>> it = childParentMapping.entries().iterator(); it.hasNext(); ) { if (it.next().getValue().getKey().equals(perm)) { it.remove(); } } } public void uninject() { INJECTOR.set(plugin.getServer().getPluginManager(), new HashMap<>(this)); } private class NotifyingChildrenMap extends LinkedHashMap<String, Boolean> { private final Permission perm; public NotifyingChildrenMap(Permission perm) { super(perm.getChildren()); this.perm = perm; } @Override public Boolean remove(Object perm) { removeFromMapping(String.valueOf(perm)); return super.remove(perm); } private void removeFromMapping(String child) { for (Iterator<Map.Entry<String, Boolean>> it = childParentMapping.get(child).iterator(); it.hasNext(); ) { if (it.next().getKey().equals(perm.getName())) { it.remove(); } } } @Override public Boolean put(String perm, Boolean val) { //removeFromMapping(perm); childParentMapping.put(perm, new SimpleEntry<>(this.perm.getName(), val)); return super.put(perm, val); } @Override public void clear() { removeAllChildren(perm.getName()); super.clear(); } } public static PermissionList inject(PermissionsExPlugin manager) { if (INJECTOR == null) { INJECTOR = new FieldReplacer<>(manager.getServer().getPluginManager().getClass(), "permissions", Map.class); } Map existing = INJECTOR.get(manager.getServer().getPluginManager()); @SuppressWarnings("unchecked") PermissionList list = new PermissionList(existing, manager); INJECTOR.set(manager.getServer().getPluginManager(), list); return list; } @Override public Permission put(String k, final Permission v) { for (Map.Entry<String, Boolean> ent : v.getChildren().entrySet()) { childParentMapping.put(ent.getKey(), new SimpleEntry<>(v.getName(), ent.getValue())); } FieldReplacer<Permission, Map> repl = getFieldReplacer(v); repl.set(v, new NotifyingChildrenMap(v)); if (v.getDefault() == PermissionDefault.TRUE || v.getDefault() == PermissionDefault.FALSE) { plugin.getManager().getSubjects(PermissionsEx.SUBJECTS_DEFAULTS) .transientData() .update(PermissionsEx.SUBJECTS_USER, input -> input.setPermission(PermissionsEx.GLOBAL_CONTEXT, v.getName(), v.getDefault() == PermissionDefault.TRUE ? 1 : -1)); } return super.put(k, v); } @Override public Permission remove(Object k) { final Permission ret = super.remove(k); if (ret != null) { removeAllChildren(k.toString()); getFieldReplacer(ret).set(ret, new LinkedHashMap<>(ret.getChildren())); if (ret.getDefault() == PermissionDefault.TRUE || ret.getDefault() == PermissionDefault.FALSE) { plugin.getManager().getSubjects(PermissionsEx.SUBJECTS_DEFAULTS) .transientData() .update(PermissionsEx.SUBJECTS_USER, input -> input.setPermission(PermissionsEx.GLOBAL_CONTEXT, ret.getName(), 0)); } } return ret; } @Override public void clear() { childParentMapping.clear(); super.clear(); } public Collection<Map.Entry<String, Boolean>> getParents(String permission) { return ImmutableSet.copyOf(childParentMapping.get(permission.toLowerCase())); } }