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    }