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