001 package org.bukkit.permissions;
002
003 import java.util.ArrayList;
004 import java.util.LinkedHashMap;
005 import java.util.List;
006 import java.util.Map;
007 import java.util.Set;
008 import java.util.logging.Level;
009
010 import org.apache.commons.lang.Validate;
011 import org.bukkit.Bukkit;
012 import org.bukkit.plugin.PluginManager;
013
014 /**
015 * Represents a unique permission that may be attached to a {@link Permissible}
016 */
017 public class Permission {
018 public static final PermissionDefault DEFAULT_PERMISSION = PermissionDefault.OP;
019
020 private final String name;
021 private final Map<String, Boolean> children = new LinkedHashMap<String, Boolean>();
022 private PermissionDefault defaultValue = DEFAULT_PERMISSION;
023 private String description;
024
025 public Permission(String name) {
026 this(name, null, null, null);
027 }
028
029 public Permission(String name, String description) {
030 this(name, description, null, null);
031 }
032
033 public Permission(String name, PermissionDefault defaultValue) {
034 this(name, null, defaultValue, null);
035 }
036
037 public Permission(String name, String description, PermissionDefault defaultValue) {
038 this(name, description, defaultValue, null);
039 }
040
041 public Permission(String name, Map<String, Boolean> children) {
042 this(name, null, null, children);
043 }
044
045 public Permission(String name, String description, Map<String, Boolean> children) {
046 this(name, description, null, children);
047 }
048
049 public Permission(String name, PermissionDefault defaultValue, Map<String, Boolean> children) {
050 this(name, null, defaultValue, children);
051 }
052
053 public Permission(String name, String description, PermissionDefault defaultValue, Map<String, Boolean> children) {
054 this.name = name;
055 this.description = (description == null) ? "" : description;
056
057 if (defaultValue != null) {
058 this.defaultValue = defaultValue;
059 }
060
061 if (children != null) {
062 this.children.putAll(children);
063 }
064
065 recalculatePermissibles();
066 }
067
068 /**
069 * Returns the unique fully qualified name of this Permission
070 *
071 * @return Fully qualified name
072 */
073 public String getName() {
074 return name;
075 }
076
077 /**
078 * Gets the children of this permission.
079 *
080 * If you change this map in any form, you must call {@link #recalculatePermissibles()} to recalculate all {@link Permissible}s
081 *
082 * @return Permission children
083 */
084 public Map<String, Boolean> getChildren() {
085 return children;
086 }
087
088 /**
089 * Gets the default value of this permission.
090 *
091 * @return Default value of this permission.
092 */
093 public PermissionDefault getDefault() {
094 return defaultValue;
095 }
096
097 /**
098 * Sets the default value of this permission.
099 * <p />
100 * This will not be saved to disk, and is a temporary operation until the server reloads permissions.
101 * Changing this default will cause all {@link Permissible}s that contain this permission to recalculate their permissions
102 *
103 * @param value The new default to set
104 */
105 public void setDefault(PermissionDefault value) {
106 if (defaultValue == null) {
107 throw new IllegalArgumentException("Default value cannot be null");
108 }
109
110 defaultValue = value;
111 recalculatePermissibles();
112 }
113
114 /**
115 * Gets a brief description of this permission, if set
116 *
117 * @return Brief description of this permission
118 */
119 public String getDescription() {
120 return description;
121 }
122
123 /**
124 * Sets the description of this permission.
125 * <p />
126 * This will not be saved to disk, and is a temporary operation until the server reloads permissions.
127 *
128 * @param value The new description to set
129 */
130 public void setDescription(String value) {
131 if (value == null) {
132 description = "";
133 } else {
134 description = value;
135 }
136 }
137
138 /**
139 * Gets a set containing every {@link Permissible} that has this permission.
140 * <p />
141 * This set cannot be modified.
142 *
143 * @return Set containing permissibles with this permission
144 */
145 public Set<Permissible> getPermissibles() {
146 return Bukkit.getServer().getPluginManager().getPermissionSubscriptions(name);
147 }
148
149 /**
150 * Recalculates all {@link Permissible}s that contain this permission.
151 * <p />
152 * This should be called after modifying the children, and is automatically called after modifying the default value
153 */
154 public void recalculatePermissibles() {
155 Set<Permissible> perms = getPermissibles();
156
157 Bukkit.getServer().getPluginManager().recalculatePermissionDefaults(this);
158
159 for (Permissible p : perms) {
160 p.recalculatePermissions();
161 }
162 }
163
164 /**
165 * Adds this permission to the specified parent permission.
166 * <p />
167 * If the parent permission does not exist, it will be created and registered.
168 *
169 * @param name Name of the parent permission
170 * @param value The value to set this permission to
171 * @return Parent permission it created or loaded
172 */
173 public Permission addParent(String name, boolean value) {
174 PluginManager pm = Bukkit.getServer().getPluginManager();
175 String lname = name.toLowerCase();
176
177 Permission perm = pm.getPermission(lname);
178
179 if (perm == null) {
180 perm = new Permission(lname);
181 pm.addPermission(perm);
182 }
183
184 addParent(perm, value);
185
186 return perm;
187 }
188
189 /**
190 * Adds this permission to the specified parent permission.
191 *
192 * @param perm Parent permission to register with
193 * @param value The value to set this permission to
194 */
195 public void addParent(Permission perm, boolean value) {
196 perm.getChildren().put(getName(), value);
197 perm.recalculatePermissibles();
198 }
199
200 /**
201 * Loads a list of Permissions from a map of data, usually used from retrieval from a yaml file.
202 * <p />
203 * The data may contain a list of name:data, where the data contains the following keys:
204 * default: Boolean true or false. If not specified, false.
205 * children: Map<String, Boolean> of child permissions. If not specified, empty list.
206 * description: Short string containing a very small description of this description. If not specified, empty string.
207 *
208 * @param data Map of permissions
209 * @param error An error message to show if a permission is invalid.
210 * @param def Default permission value to use if missing
211 * @return Permission object
212 */
213 public static List<Permission> loadPermissions(Map<?, ?> data, String error, PermissionDefault def) {
214 List<Permission> result = new ArrayList<Permission>();
215
216 for (Map.Entry<?, ?> entry : data.entrySet()) {
217 try {
218 result.add(Permission.loadPermission(entry.getKey().toString(), (Map<?, ?>) entry.getValue(), def, result));
219 } catch (Throwable ex) {
220 Bukkit.getServer().getLogger().log(Level.SEVERE, String.format(error, entry.getKey()), ex);
221 }
222 }
223
224 return result;
225 }
226
227 /**
228 * Loads a Permission from a map of data, usually used from retrieval from a yaml file.
229 * <p />
230 * The data may contain the following keys:
231 * default: Boolean true or false. If not specified, false.
232 * children: Map<String, Boolean> of child permissions. If not specified, empty list.
233 * description: Short string containing a very small description of this description. If not specified, empty string.
234 *
235 * @param name Name of the permission
236 * @param data Map of keys
237 * @return Permission object
238 */
239 public static Permission loadPermission(String name, Map<String, Object> data) {
240 return loadPermission(name, data, DEFAULT_PERMISSION, null);
241 }
242
243 /**
244 * Loads a Permission from a map of data, usually used from retrieval from a yaml file.
245 * <p />
246 * The data may contain the following keys:
247 * default: Boolean true or false. If not specified, false.
248 * children: Map<String, Boolean> of child permissions. If not specified, empty list.
249 * description: Short string containing a very small description of this description. If not specified, empty string.
250 *
251 * @param name Name of the permission
252 * @param data Map of keys
253 * @param def Default permission value to use if not set
254 * @param output A list to append any created child-Permissions to, may be null
255 * @return Permission object
256 */
257 public static Permission loadPermission(String name, Map<?, ?> data, PermissionDefault def, List<Permission> output) {
258 Validate.notNull(name, "Name cannot be null");
259 Validate.notNull(data, "Data cannot be null");
260
261 String desc = null;
262 Map<String, Boolean> children = null;
263
264 if (data.get("default") != null) {
265 PermissionDefault value = PermissionDefault.getByName(data.get("default").toString());
266 if (value != null) {
267 def = value;
268 } else {
269 throw new IllegalArgumentException("'default' key contained unknown value");
270 }
271 }
272
273 if (data.get("children") != null) {
274 Object childrenNode = data.get("children");
275 if (childrenNode instanceof Iterable) {
276 children = new LinkedHashMap<String, Boolean>();
277 for (Object child : (Iterable<?>) childrenNode) {
278 if (child != null) {
279 children.put(child.toString(), Boolean.TRUE);
280 }
281 }
282 } else if (childrenNode instanceof Map) {
283 children = extractChildren((Map<?,?>) childrenNode, name, def, output);
284 } else {
285 throw new IllegalArgumentException("'children' key is of wrong type");
286 }
287 }
288
289 if (data.get("description") != null) {
290 desc = data.get("description").toString();
291 }
292
293 return new Permission(name, desc, def, children);
294 }
295
296 private static Map<String, Boolean> extractChildren(Map<?, ?> input, String name, PermissionDefault def, List<Permission> output) {
297 Map<String, Boolean> children = new LinkedHashMap<String, Boolean>();
298
299 for (Map.Entry<?, ?> entry : input.entrySet()) {
300 if ((entry.getValue() instanceof Boolean)) {
301 children.put(entry.getKey().toString(), (Boolean) entry.getValue());
302 } else if ((entry.getValue() instanceof Map)) {
303 try {
304 Permission perm = loadPermission(entry.getKey().toString(), (Map<?, ?>) entry.getValue(), def, output);
305 children.put(perm.getName(), Boolean.TRUE);
306
307 if (output != null) {
308 output.add(perm);
309 }
310 } catch (Throwable ex) {
311 throw new IllegalArgumentException("Permission node '" + entry.getKey().toString() + "' in child of " + name + " is invalid", ex);
312 }
313 } else {
314 throw new IllegalArgumentException("Child '" + entry.getKey().toString() + "' contains invalid value");
315 }
316 }
317
318 return children;
319 }
320 }