001    package org.bukkit.plugin;
002    
003    import java.io.InputStream;
004    import java.io.Reader;
005    import java.io.Writer;
006    import java.util.HashMap;
007    import java.util.HashSet;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.Set;
011    
012    import org.bukkit.command.CommandExecutor;
013    import org.bukkit.command.PluginCommand;
014    import org.bukkit.plugin.Plugin;
015    import org.bukkit.plugin.java.JavaPlugin;
016    import org.bukkit.permissions.Permissible;
017    import org.bukkit.permissions.Permission;
018    import org.bukkit.permissions.PermissionDefault;
019    import org.yaml.snakeyaml.Yaml;
020    import org.yaml.snakeyaml.constructor.AbstractConstruct;
021    import org.yaml.snakeyaml.constructor.SafeConstructor;
022    import org.yaml.snakeyaml.nodes.Node;
023    import org.yaml.snakeyaml.nodes.Tag;
024    
025    import com.google.common.collect.ImmutableList;
026    import com.google.common.collect.ImmutableMap;
027    import com.google.common.collect.ImmutableSet;
028    
029    /**
030     * This type is the runtime-container for the information in the plugin.yml.
031     * All plugins must have a respective plugin.yml. For plugins written in java
032     * using the standard plugin loader, this file must be in the root of the jar
033     * file.
034     * <p>
035     * When Bukkit loads a plugin, it needs to know some basic information about
036     * it. It reads this information from a YAML file, 'plugin.yml'. This file
037     * consists of a set of attributes, each defined on a new line and with no
038     * indentation.
039     * <p>
040     * Every (almost* every) method corresponds with a specific entry in the
041     * plugin.yml. These are the <b>required</b> entries for every plugin.yml:
042     * <ul>
043     * <li>{@link #getName()} - <code>name</code>
044     * <li>{@link #getVersion()} - <code>version</code>
045     * <li>{@link #getMain()} - <code>main</code>
046     * </ul>
047     * <p>
048     * Failing to include any of these items will throw an exception and cause the
049     * server to ignore your plugin.
050     * <p>
051     * This is a list of the possible yaml keys, with specific details included in
052     * the respective method documentations:
053     * <table border=1>
054     * <tr>
055     *     <th>Node</th>
056     *     <th>Method</th>
057     *     <th>Summary</th>
058     * </tr><tr>
059     *     <td><code>name</code></td>
060     *     <td>{@link #getName()}</td>
061     *     <td>The unique name of plugin</td>
062     * </tr><tr>
063     *     <td><code>version</code></td>
064     *     <td>{@link #getVersion()}</td>
065     *     <td>A plugin revision identifier</td>
066     * </tr><tr>
067     *     <td><code>main</code></td>
068     *     <td>{@link #getMain()}</td>
069     *     <td>The plugin's initial class file</td>
070     * </tr><tr>
071     *     <td><code>author</code><br><code>authors</code></td>
072     *     <td>{@link #getAuthors()}</td>
073     *     <td>The plugin contributors</td>
074     * </tr><tr>
075     *     <td><code>description</code></td>
076     *     <td>{@link #getDescription()}</td>
077     *     <td>Human readable plugin summary</td>
078     * </tr><tr>
079     *     <td><code>website</code></td>
080     *     <td>{@link #getWebsite()}</td>
081     *     <td>The URL to the plugin's site</td>
082     * </tr><tr>
083     *     <td><code>prefix</code></td>
084     *     <td>{@link #getPrefix()}</td>
085     *     <td>The token to prefix plugin log entries</td>
086     * </tr><tr>
087     *     <td><code>database</code></td>
088     *     <td>{@link #isDatabaseEnabled()}</td>
089     *     <td>Indicator to enable database support</td>
090     * </tr><tr>
091     *     <td><code>load</code></td>
092     *     <td>{@link #getLoad()}</td>
093     *     <td>The phase of server-startup this plugin will load during</td>
094     * </tr><tr>
095     *     <td><code>depend</code></td>
096     *     <td>{@link #getDepend()}</td>
097     *     <td>Other required plugins</td>
098     * </tr><tr>
099     *     <td><code>softdepend</code></td>
100     *     <td>{@link #getSoftDepend()}</td>
101     *     <td>Other plugins that add functionality</td>
102     * </tr><tr>
103     *     <td><code>loadbefore</code></td>
104     *     <td>{@link #getLoadBefore()}</td>
105     *     <td>The inverse softdepend</td>
106     * </tr><tr>
107     *     <td><code>commands</code></td>
108     *     <td>{@link #getCommands()}</td>
109     *     <td>The commands the plugin will register</td>
110     * </tr><tr>
111     *     <td><code>permissions</code></td>
112     *     <td>{@link #getPermissions()}</td>
113     *     <td>The permissions the plugin will register</td>
114     * </tr><tr>
115     *     <td><code>default-permission</code></td>
116     *     <td>{@link #getPermissionDefault()}</td>
117     *     <td>The default {@link Permission#getDefault() default} permission
118     *         state for defined {@link #getPermissions() permissions} the plugin
119     *         will register</td>
120     * </tr><tr>
121     *     <td><code>awareness</code></td>
122     *     <td>{@link #getAwareness()}</td>
123     *     <td>The concepts that the plugin acknowledges</td>
124     * </tr>
125     * </table>
126     * <p>
127     * A plugin.yml example:<blockquote><pre>
128     *name: Inferno
129     *version: 1.4.1
130     *description: This plugin is so 31337. You can set yourself on fire.
131     *# We could place every author in the authors list, but chose not to for illustrative purposes
132     *# Also, having an author distinguishes that person as the project lead, and ensures their
133     *# name is displayed first
134     *author: CaptainInflamo
135     *authors: [Cogito, verrier, EvilSeph]
136     *website: http://www.curse.com/server-mods/minecraft/myplugin
137     *
138     *main: com.captaininflamo.bukkit.inferno.Inferno
139     *database: false
140     *depend: [NewFire, FlameWire]
141     *
142     *commands:
143     *  flagrate:
144     *    description: Set yourself on fire.
145     *    aliases: [combust_me, combustMe]
146     *    permission: inferno.flagrate
147     *    usage: Syntax error! Simply type /&lt;command&gt; to ignite yourself.
148     *  burningdeaths:
149     *    description: List how many times you have died by fire.
150     *    aliases: [burning_deaths, burningDeaths]
151     *    permission: inferno.burningdeaths
152     *    usage: |
153     *      /&lt;command&gt; [player]
154     *      Example: /&lt;command&gt; - see how many times you have burned to death
155     *      Example: /&lt;command&gt; CaptainIce - see how many times CaptainIce has burned to death
156     *
157     *permissions:
158     *  inferno.*:
159     *    description: Gives access to all Inferno commands
160     *    children:
161     *      inferno.flagrate: true
162     *      inferno.burningdeaths: true
163     *      inferno.burningdeaths.others: true
164     *  inferno.flagrate:
165     *    description: Allows you to ignite yourself
166     *    default: true
167     *  inferno.burningdeaths:
168     *    description: Allows you to see how many times you have burned to death
169     *    default: true
170     *  inferno.burningdeaths.others:
171     *    description: Allows you to see how many times others have burned to death
172     *    default: op
173     *    children:
174     *      inferno.burningdeaths: true
175     *</pre></blockquote>
176     */
177    public final class PluginDescriptionFile {
178        private static final ThreadLocal<Yaml> YAML = new ThreadLocal<Yaml>() {
179            @Override
180            protected Yaml initialValue() {
181                return new Yaml(new SafeConstructor() {
182                    {
183                        yamlConstructors.put(null, new AbstractConstruct() {
184                            @Override
185                            public Object construct(final Node node) {
186                                if (!node.getTag().startsWith("!@")) {
187                                    // Unknown tag - will fail
188                                    return SafeConstructor.undefinedConstructor.construct(node);
189                                }
190                                // Unknown awareness - provide a graceful substitution
191                                return new PluginAwareness() {
192                                    @Override
193                                    public String toString() {
194                                        return node.toString();
195                                    }
196                                };
197                            }
198                        });
199                        for (final PluginAwareness.Flags flag : PluginAwareness.Flags.values()) {
200                            yamlConstructors.put(new Tag("!@" + flag.name()), new AbstractConstruct() {
201                                @Override
202                                public PluginAwareness.Flags construct(final Node node) {
203                                    return flag;
204                                }
205                            });
206                        }
207                    }
208                });
209            }
210        };
211        String rawName = null;
212        private String name = null;
213        private String main = null;
214        private String classLoaderOf = null;
215        private List<String> depend = ImmutableList.of();
216        private List<String> softDepend = ImmutableList.of();
217        private List<String> loadBefore = ImmutableList.of();
218        private String version = null;
219        private Map<String, Map<String, Object>> commands = null;
220        private String description = null;
221        private List<String> authors = null;
222        private String website = null;
223        private String prefix = null;
224        private boolean database = false;
225        private PluginLoadOrder order = PluginLoadOrder.POSTWORLD;
226        private List<Permission> permissions = null;
227        private Map<?, ?> lazyPermissions = null;
228        private PermissionDefault defaultPerm = PermissionDefault.OP;
229        private Set<PluginAwareness> awareness = ImmutableSet.of();
230    
231        public PluginDescriptionFile(final InputStream stream) throws InvalidDescriptionException {
232            loadMap(asMap(YAML.get().load(stream)));
233        }
234    
235        /**
236         * Loads a PluginDescriptionFile from the specified reader
237         *
238         * @param reader The reader
239         * @throws InvalidDescriptionException If the PluginDescriptionFile is
240         *     invalid
241         */
242        public PluginDescriptionFile(final Reader reader) throws InvalidDescriptionException {
243            loadMap(asMap(YAML.get().load(reader)));
244        }
245    
246        /**
247         * Creates a new PluginDescriptionFile with the given detailed
248         *
249         * @param pluginName Name of this plugin
250         * @param pluginVersion Version of this plugin
251         * @param mainClass Full location of the main class of this plugin
252         */
253        public PluginDescriptionFile(final String pluginName, final String pluginVersion, final String mainClass) {
254            name = pluginName.replace(' ', '_');
255            version = pluginVersion;
256            main = mainClass;
257        }
258    
259        /**
260         * Gives the name of the plugin. This name is a unique identifier for
261         * plugins.
262         * <ul>
263         * <li>Must consist of all alphanumeric characters, underscores, hyphon,
264         *     and period (a-z,A-Z,0-9, _.-). Any other character will cause the
265         *     plugin.yml to fail loading.
266         * <li>Used to determine the name of the plugin's data folder. Data
267         *     folders are placed in the ./plugins/ directory by default, but this
268         *     behavior should not be relied on. {@link Plugin#getDataFolder()}
269         *     should be used to reference the data folder.
270         * <li>It is good practice to name your jar the same as this, for example
271         *     'MyPlugin.jar'.
272         * <li>Case sensitive.
273         * <li>The is the token referenced in {@link #getDepend()}, {@link
274         *     #getSoftDepend()}, and {@link #getLoadBefore()}.
275         * <li>Using spaces in the plugin's name is deprecated.
276         * </ul>
277         * <p>
278         * In the plugin.yml, this entry is named <code>name</code>.
279         * <p>
280         * Example:<blockquote><pre>name: MyPlugin</pre></blockquote>
281         *
282         * @return the name of the plugin
283         */
284        public String getName() {
285            return name;
286        }
287    
288        /**
289         * Gives the version of the plugin.
290         * <ul>
291         * <li>Version is an arbitrary string, however the most common format is
292         *     MajorRelease.MinorRelease.Build (eg: 1.4.1).
293         * <li>Typically you will increment this every time you release a new
294         *     feature or bug fix.
295         * <li>Displayed when a user types <code>/version PluginName</code>
296         * </ul>
297         * <p>
298         * In the plugin.yml, this entry is named <code>version</code>.
299         * <p>
300         * Example:<blockquote><pre>version: 1.4.1</pre></blockquote>
301         *
302         * @return the version of the plugin
303         */
304        public String getVersion() {
305            return version;
306        }
307    
308        /**
309         * Gives the fully qualified name of the main class for a plugin. The
310         * format should follow the {@link ClassLoader#loadClass(String)} syntax
311         * to successfully be resolved at runtime. For most plugins, this is the
312         * class that extends {@link JavaPlugin}.
313         * <ul>
314         * <li>This must contain the full namespace including the class file
315         *     itself.
316         * <li>If your namespace is <code>org.bukkit.plugin</code>, and your class
317         *     file is called <code>MyPlugin</code> then this must be
318         *     <code>org.bukkit.plugin.MyPlugin</code>
319         * <li>No plugin can use <code>org.bukkit.</code> as a base package for
320         *     <b>any class</b>, including the main class.
321         * </ul>
322         * <p>
323         * In the plugin.yml, this entry is named <code>main</code>.
324         * <p>
325         * Example:
326         * <blockquote><pre>main: org.bukkit.plugin.MyPlugin</pre></blockquote>
327         *
328         * @return the fully qualified main class for the plugin
329         */
330        public String getMain() {
331            return main;
332        }
333    
334        /**
335         * Gives a human-friendly description of the functionality the plugin
336         * provides.
337         * <ul>
338         * <li>The description can have multiple lines.
339         * <li>Displayed when a user types <code>/version PluginName</code>
340         * </ul>
341         * <p>
342         * In the plugin.yml, this entry is named <code>description</code>.
343         * <p>
344         * Example:
345         * <blockquote><pre>description: This plugin is so 31337. You can set yourself on fire.</pre></blockquote>
346         *
347         * @return description of this plugin, or null if not specified
348         */
349        public String getDescription() {
350            return description;
351        }
352    
353        /**
354         * Gives the phase of server startup that the plugin should be loaded.
355         * <ul>
356         * <li>Possible values are in {@link PluginLoadOrder}.
357         * <li>Defaults to {@link PluginLoadOrder#POSTWORLD}.
358         * <li>Certain caveats apply to each phase.
359         * <li>When different, {@link #getDepend()}, {@link #getSoftDepend()}, and
360         *     {@link #getLoadBefore()} become relative in order loaded per-phase.
361         *     If a plugin loads at <code>STARTUP</code>, but a dependency loads
362         *     at <code>POSTWORLD</code>, the dependency will not be loaded before
363         *     the plugin is loaded.
364         * </ul>
365         * <p>
366         * In the plugin.yml, this entry is named <code>load</code>.
367         * <p>
368         * Example:<blockquote><pre>load: STARTUP</pre></blockquote>
369         *
370         * @return the phase when the plugin should be loaded
371         */
372        public PluginLoadOrder getLoad() {
373            return order;
374        }
375    
376        /**
377         * Gives the list of authors for the plugin.
378         * <ul>
379         * <li>Gives credit to the developer.
380         * <li>Used in some server error messages to provide helpful feedback on
381         *     who to contact when an error occurs.
382         * <li>A bukkit.org forum handle or email address is recommended.
383         * <li>Is displayed when a user types <code>/version PluginName</code>
384         * <li><code>authors</code> must be in <a
385         *     href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
386         *     format</a>.
387         * </ul>
388         * <p>
389         * In the plugin.yml, this has two entries, <code>author</code> and
390         * <code>authors</code>.
391         * <p>
392         * Single author example:
393         * <blockquote><pre>author: CaptainInflamo</pre></blockquote>
394         * Multiple author example:
395         * <blockquote><pre>authors: [Cogito, verrier, EvilSeph]</pre></blockquote>
396         * When both are specified, author will be the first entry in the list, so
397         * this example:
398         * <blockquote><pre>author: Grum
399         *authors:
400         *- feildmaster
401         *- amaranth</pre></blockquote>
402         * Is equivilant to this example:
403         * <blockquote><pre>authors: [Grum, feildmaster, aramanth]<pre></blockquote>
404         *
405         * @return an immutable list of the plugin's authors
406         */
407        public List<String> getAuthors() {
408            return authors;
409        }
410    
411        /**
412         * Gives the plugin's or plugin's author's website.
413         * <ul>
414         * <li>A link to the Curse page that includes documentation and downloads
415         *     is highly recommended.
416         * <li>Displayed when a user types <code>/version PluginName</code>
417         * </ul>
418         * <p>
419         * In the plugin.yml, this entry is named <code>website</code>.
420         * <p>
421         * Example:
422         * <blockquote><pre>website: http://www.curse.com/server-mods/minecraft/myplugin</pre></blockquote>
423         *
424         * @return description of this plugin, or null if not specified
425         */
426        public String getWebsite() {
427            return website;
428        }
429    
430        /**
431         * Gives if the plugin uses a database.
432         * <ul>
433         * <li>Using a database is non-trivial.
434         * <li>Valid values include <code>true</code> and <code>false</code>
435         * </ul>
436         * <p>
437         * In the plugin.yml, this entry is named <code>database</code>.
438         * <p>
439         * Example:
440         * <blockquote><pre>database: false</pre></blockquote>
441         *
442         * @return if this plugin requires a database
443         * @see Plugin#getDatabase()
444         */
445        public boolean isDatabaseEnabled() {
446            return database;
447        }
448    
449        /**
450         * Gives a list of other plugins that the plugin requires.
451         * <ul>
452         * <li>Use the value in the {@link #getName()} of the target plugin to
453         *     specify the dependency.
454         * <li>If any plugin listed here is not found, your plugin will fail to
455         *     load at startup.
456         * <li>If multiple plugins list each other in <code>depend</code>,
457         *     creating a network with no individual plugin does not list another
458         *     plugin in the <a
459         *     href=https://en.wikipedia.org/wiki/Circular_dependency>network</a>,
460         *     all plugins in that network will fail.
461         * <li><code>depend</code> must be in must be in <a
462         *     href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
463         *     format</a>.
464         * </ul>
465         * <p>
466         * In the plugin.yml, this entry is named <code>depend</code>.
467         * <p>
468         * Example:
469         * <blockquote><pre>depend:
470         *- OnePlugin
471         *- AnotherPlugin</pre></blockquote>
472         *
473         * @return immutable list of the plugin's dependencies
474         */
475        public List<String> getDepend() {
476            return depend;
477        }
478    
479        /**
480         * Gives a list of other plugins that the plugin requires for full
481         * functionality. The {@link PluginManager} will make best effort to treat
482         * all entries here as if they were a {@link #getDepend() dependency}, but
483         * will never fail because of one of these entries.
484         * <ul>
485         * <li>Use the value in the {@link #getName()} of the target plugin to
486         *     specify the dependency.
487         * <li>When an unresolvable plugin is listed, it will be ignored and does
488         *     not affect load order.
489         * <li>When a circular dependency occurs (a network of plugins depending
490         *     or soft-dependending each other), it will arbitrarily choose a
491         *     plugin that can be resolved when ignoring soft-dependencies.
492         * <li><code>softdepend</code> must be in <a
493         *     href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
494         *     format</a>.
495         * </ul>
496         * <p>
497         * In the plugin.yml, this entry is named <code>softdepend</code>.
498         * <p>
499         * Example:
500         * <blockquote><pre>softdepend: [OnePlugin, AnotherPlugin]</pre></blockquote>
501         *
502         * @return immutable list of the plugin's preferred dependencies
503         */
504        public List<String> getSoftDepend() {
505            return softDepend;
506        }
507    
508        /**
509         * Gets the list of plugins that should consider this plugin a
510         * soft-dependency.
511         * <ul>
512         * <li>Use the value in the {@link #getName()} of the target plugin to
513         *     specify the dependency.
514         * <li>The plugin should load before any other plugins listed here.
515         * <li>Specifying another plugin here is strictly equivalent to having the
516         *     specified plugin's {@link #getSoftDepend()} include {@link
517         *     #getName() this plugin}.
518         * <li><code>loadbefore</code> must be in <a
519         *     href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
520         *     format</a>.
521         * </ul>
522         * <p>
523         * In the plugin.yml, this entry is named <code>loadbefore</code>.
524         * <p>
525         * Example:
526         * <blockquote><pre>loadbefore:
527         *- OnePlugin
528         *- AnotherPlugin</pre></blockquote>
529         *
530         * @return immutable list of plugins that should consider this plugin a
531         *     soft-dependency
532         */
533        public List<String> getLoadBefore() {
534            return loadBefore;
535        }
536    
537        /**
538         * Gives the token to prefix plugin-specific logging messages with.
539         * <ul>
540         * <li>This includes all messages using {@link Plugin#getLogger()}.
541         * <li>If not specified, the server uses the plugin's {@link #getName()
542         *     name}.
543         * <li>This should clearly indicate what plugin is being logged.
544         * </ul>
545         * <p>
546         * In the plugin.yml, this entry is named <code>prefix</code>.
547         * <p>
548         * Example:<blockquote><pre>prefix: ex-why-zee</pre></blockquote>
549         *
550         * @return the prefixed logging token, or null if not specified
551         */
552        public String getPrefix() {
553            return prefix;
554        }
555    
556        /**
557         * Gives the map of command-name to command-properties. Each entry in this
558         * map corresponds to a single command and the respective values are the
559         * properties of the command. Each property, <i>with the exception of
560         * aliases</i>, can be defined at runtime using methods in {@link
561         * PluginCommand} and are defined here only as a convenience.
562         * <table border=1>
563         * <tr>
564         *     <th>Node</th>
565         *     <th>Method</th>
566         *     <th>Type</th>
567         *     <th>Description</th>
568         *     <th>Example</th>
569         * </tr><tr>
570         *     <td><code>description</code></td>
571         *     <td>{@link PluginCommand#setDescription(String)}</td>
572         *     <td>String</td>
573         *     <td>A user-friendly description for a command. It is useful for
574         *         documentation purposes as well as in-game help.</td>
575         *     <td><blockquote><pre>description: Set yourself on fire</pre></blockquote></td>
576         * </tr><tr>
577         *     <td><code>aliases</code></td>
578         *     <td>{@link PluginCommand#setAliases(List)}</td>
579         *     <td>String or <a
580         *         href="http://en.wikipedia.org/wiki/YAML#Lists">List</a> of
581         *         strings</td>
582         *     <td>Alternative command names, with special usefulness for commands
583         *         that are already registered. <i>Aliases are not effective when
584         *         defined at runtime,</i> so the plugin description file is the
585         *         only way to have them properly defined.
586         *         <p>
587         *         Note: Command aliases may not have a colon in them.</td>
588         *     <td>Single alias format:
589         *         <blockquote><pre>aliases: combust_me</pre></blockquote> or
590         *         multiple alias format:
591         *         <blockquote><pre>aliases: [combust_me, combustMe]</pre></blockquote></td>
592         * </tr><tr>
593         *     <td><code>permission</code></td>
594         *     <td>{@link PluginCommand#setPermission(String)}</td>
595         *     <td>String</td>
596         *     <td>The name of the {@link Permission} required to use the command.
597         *         A user without the permission will receive the specified
598         *         message (see {@linkplain
599         *         PluginCommand#setPermissionMessage(String) below}), or a
600         *         standard one if no specific message is defined. Without the
601         *         permission node, no {@link
602         *         PluginCommand#setExecutor(CommandExecutor) CommandExecutor} or
603         *         {@link PluginCommand#setTabCompleter(TabCompleter)
604         *         TabCompleter} will be called.</td>
605         *     <td><blockquote><pre>permission: inferno.flagrate</pre></blockquote></td>
606         * </tr><tr>
607         *     <td><code>permission-message</code></td>
608         *     <td>{@link PluginCommand#setPermissionMessage(String)}</td>
609         *     <td>String</td>
610         *     <td><ul>
611         *         <li>Displayed to a player that attempts to use a command, but
612         *             does not have the required permission. See {@link
613         *             PluginCommand#getPermission() above}.
614         *         <li>&lt;permission&gt; is a macro that is replaced with the
615         *             permission node required to use the command.
616         *         <li>Using empty quotes is a valid way to indicate nothing
617         *             should be displayed to a player.
618         *         </ul></td>
619         *     <td><blockquote><pre>permission-message: You do not have /&lt;permission&gt;</pre></blockquote></td>
620         * </tr><tr>
621         *     <td><code>usage</code></td>
622         *     <td>{@link PluginCommand#setUsage(String)}</td>
623         *     <td>String</td>
624         *     <td>This message is displayed to a player when the {@link
625         *         PluginCommand#setExecutor(CommandExecutor)} {@linkplain
626         *         CommandExecutor#onCommand(CommandSender,Command,String,String[])
627         *         returns false}. &lt;command&gt; is a macro that is replaced
628         *         the command issued.</td>
629         *     <td><blockquote><pre>usage: Syntax error! Perhaps you meant /&lt;command&gt; PlayerName?</pre></blockquote>
630         *         It is worth noting that to use a colon in a yaml, like
631         *         <code>`usage: Usage: /god [player]'</code>, you need to
632         *         <a href="http://yaml.org/spec/current.html#id2503232">surround
633         *         the message with double-quote</a>:
634         *         <blockquote><pre>usage: "Usage: /god [player]"</pre></blockquote></td>
635         * </tr>
636         * </table>
637         * The commands are structured as a hiearchy of <a
638         * href="http://yaml.org/spec/current.html#id2502325">nested mappings</a>.
639         * The primary (top-level, no intendentation) node is
640         * `<code>commands</code>', while each individual command name is
641         * indented, indicating it maps to some value (in our case, the
642         * properties of the table above).
643         * <p>
644         * Here is an example bringing together the piecemeal examples above, as
645         * well as few more definitions:<blockquote><pre>
646         *commands:
647         *  flagrate:
648         *    description: Set yourself on fire.
649         *    aliases: [combust_me, combustMe]
650         *    permission: inferno.flagrate
651         *    permission-message: You do not have /&lt;permission&gt;
652         *    usage: Syntax error! Perhaps you meant /&lt;command&gt; PlayerName?
653         *  burningdeaths:
654         *    description: List how many times you have died by fire.
655         *    aliases:
656         *    - burning_deaths
657         *    - burningDeaths
658         *    permission: inferno.burningdeaths
659         *    usage: |
660         *      /&lt;command&gt; [player]
661         *      Example: /&lt;command&gt; - see how many times you have burned to death
662         *      Example: /&lt;command&gt; CaptainIce - see how many times CaptainIce has burned to death
663         *  # The next command has no description, aliases, etc. defined, but is still valid
664         *  # Having an empty declaration is useful for defining the description, permission, and messages from a configuration dynamically
665         *  apocalypse:
666         *</pre></blockquote>
667         * Note: Command names may not have a colon in their name.
668         *
669         * @return the commands this plugin will register
670         */
671        public Map<String, Map<String, Object>> getCommands() {
672            return commands;
673        }
674    
675        /**
676         * Gives the list of permissions the plugin will register at runtime,
677         * immediately proceding enabling. The format for defining permissions is
678         * a map from permission name to properties. To represent a map without
679         * any specific property, empty <a
680         * href="http://yaml.org/spec/current.html#id2502702">curly-braces</a> (
681         * <code>&#123;&#125;</code> ) may be used (as a null value is not
682         * accepted, unlike the {@link #getCommands() commands} above).
683         * <p>
684         * A list of optional properties for permissions:
685         * <table border=1>
686         * <tr>
687         *     <th>Node</th>
688         *     <th>Description</th>
689         *     <th>Example</th>
690         * </tr><tr>
691         *     <td><code>description</code></td>
692         *     <td>Plaintext (user-friendly) description of what the permission
693         *         is for.</td>
694         *     <td><blockquote><pre>description: Allows you to set yourself on fire</pre></blockquote></td>
695         * </tr><tr>
696         *     <td><code>default</code></td>
697         *     <td>The default state for the permission, as defined by {@link
698         *         Permission#getDefault()}. If not defined, it will be set to
699         *         the value of {@link PluginDescriptionFile#getPermissionDefault()}.
700         *         <p>
701         *         For reference:<ul>
702         *         <li><code>true</code> - Represents a positive assignment to
703         *             {@link Permissible permissibles}.
704         *         <li><code>false</code> - Represents no assignment to {@link
705         *             Permissible permissibles}.
706         *         <li><code>op</code> - Represents a positive assignment to
707         *             {@link Permissible#isOp() operator permissibles}.
708         *         <li><code>notop</code> - Represents a positive assignment to
709         *             {@link Permissible#isOp() non-operator permissibiles}.
710         *         </ul></td>
711         *     <td><blockquote><pre>default: true</pre></blockquote></td>
712         * </tr><tr>
713         *     <td><code>children</code></td>
714         *     <td>Allows other permissions to be set as a {@linkplain
715         *         Permission#getChildren() relation} to the parent permission.
716         *         When a parent permissions is assigned, child permissions are
717         *         respectively assigned as well.
718         *         <ul>
719         *         <li>When a parent permission is assigned negatively, child
720         *             permissions are assigned based on an inversion of their
721         *             association.
722         *         <li>When a parent permission is assigned positively, child
723         *             permissions are assigned based on their association.
724         *         </ul>
725         *         <p>
726         *         Child permissions may be defined in a number of ways:<ul>
727         *         <li>Children may be defined as a <a
728         *             href="http://en.wikipedia.org/wiki/YAML#Lists">list</a> of
729         *             names. Using a list will treat all children associated
730         *             positively to their parent.
731         *         <li>Children may be defined as a map. Each permission name maps
732         *             to either a boolean (representing the association), or a
733         *             nested permission definition (just as another permission).
734         *             Using a nested definition treats the child as a positive
735         *             association.
736         *         <li>A nested permission definition must be a map of these same
737         *             properties. To define a valid nested permission without
738         *             defining any specific property, empty curly-braces (
739         *             <code>&#123;&#125;</code> ) must be used.
740         *          <li>A nested permission may carry it's own nested permissions
741         *              as children, as they may also have nested permissions, and
742         *              so forth. There is no direct limit to how deep the
743         *              permission tree is defined.
744         *         </ul></td>
745         *     <td>As a list:
746         *         <blockquote><pre>children: [inferno.flagrate, inferno.burningdeaths]</pre></blockquote>
747         *         Or as a mapping:
748         *         <blockquote><pre>children:
749         *  inferno.flagrate: true
750         *  inferno.burningdeaths: true</pre></blockquote>
751         *         An additional example showing basic nested values can be seen
752         *         <a href="doc-files/permissions-example_plugin.yml">here</a>.
753         *         </td>
754         * </tr>
755         * </table>
756         * The permissions are structured as a hiearchy of <a
757         * href="http://yaml.org/spec/current.html#id2502325">nested mappings</a>.
758         * The primary (top-level, no intendentation) node is
759         * `<code>permissions</code>', while each individual permission name is
760         * indented, indicating it maps to some value (in our case, the
761         * properties of the table above).
762         * <p>
763         * Here is an example using some of the properties:<blockquote><pre>
764         *permissions:
765         *  inferno.*:
766         *    description: Gives access to all Inferno commands
767         *    children:
768         *      inferno.flagrate: true
769         *      inferno.burningdeaths: true
770         *  inferno.flagate:
771         *    description: Allows you to ignite yourself
772         *    default: true
773         *  inferno.burningdeaths:
774         *    description: Allows you to see how many times you have burned to death
775         *    default: true
776         *</pre></blockquote>
777         * Another example, with nested definitions, can be found <a
778         * href="doc-files/permissions-example_plugin.yml">here</a>.
779         */
780        public List<Permission> getPermissions() {
781            if (permissions == null) {
782                if (lazyPermissions == null) {
783                    permissions = ImmutableList.<Permission>of();
784                } else {
785                    permissions = ImmutableList.copyOf(Permission.loadPermissions(lazyPermissions, "Permission node '%s' in plugin description file for " + getFullName() + " is invalid", defaultPerm));
786                    lazyPermissions = null;
787                }
788            }
789            return permissions;
790        }
791    
792        /**
793         * Gives the default {@link Permission#getDefault() default} state of
794         * {@link #getPermissions() permissions} registered for the plugin.
795         * <ul>
796         * <li>If not specified, it will be {@link PermissionDefault#OP}.
797         * <li>It is matched using {@link PermissionDefault#getByName(String)}
798         * <li>It only affects permissions that do not define the
799         *     <code>default</code> node.
800         * <li>It may be any value in {@link PermissionDefault}.
801         * </ul>
802         * <p>
803         * In the plugin.yml, this entry is named <code>default-permission</code>.
804         * <p>
805         * Example:<blockquote><pre>default-permission: NOT_OP</pre></blockquote>
806         *
807         * @return the default value for the plugin's permissions
808         */
809        public PermissionDefault getPermissionDefault() {
810            return defaultPerm;
811        }
812    
813        /**
814         * Gives a set of every {@link PluginAwareness} for a plugin. An awareness
815         * dictates something that a plugin developer acknowledges when the plugin
816         * is compiled. Some implementions may define extra awarenesses that are
817         * not included in the API. Any unrecognized
818         * awareness (one unsupported or in a future version) will cause a dummy
819         * object to be created instead of failing.
820         * <p>
821         * <ul>
822         * <li>Currently only supports the enumerated values in {@link
823         *     PluginAwareness.Flags}.
824         * <li>Each awareness starts the identifier with bang-at
825         *     (<code>!@</code>).
826         * <li>Unrecognized (future / unimplemented) entries are quietly replaced
827         *     by a generic object that implements PluginAwareness.
828         * <li>A type of awareness must be defined by the runtime and acknowledged
829         *     by the API, effectively discluding any derived type from any
830         *     plugin's classpath.
831         * <li><code>awareness</code> must be in <a
832         *     href="http://en.wikipedia.org/wiki/YAML#Lists">YAML list
833         *     format</a>.
834         * </ul>
835         * <p>
836         * In the plugin.yml, this entry is named <code>awareness</code>.
837         * <p>
838         * Example:<blockquote><pre>awareness:
839         *- !@UTF8</pre></blockquote>
840         * <p>
841         * <b>Note:</b> Although unknown versions of some future awareness are
842         * gracefully substituted, previous versions of Bukkit (ones prior to the
843         * first implementation of awareness) will fail to load a plugin that
844         * defines any awareness.
845         *
846         * @return a set containing every awareness for the plugin
847         */
848        public Set<PluginAwareness> getAwareness() {
849            return awareness;
850        }
851    
852        /**
853         * Returns the name of a plugin, including the version. This method is
854         * provided for convenience; it uses the {@link #getName()} and {@link
855         * #getVersion()} entries.
856         *
857         * @return a descriptive name of the plugin and respective version
858         */
859        public String getFullName() {
860            return name + " v" + version;
861        }
862    
863        /**
864         * @deprecated unused
865         */
866        @Deprecated
867        public String getClassLoaderOf() {
868            return classLoaderOf;
869        }
870    
871        public void setDatabaseEnabled(boolean database) {
872            this.database = database;
873        }
874    
875        /**
876         * Saves this PluginDescriptionFile to the given writer
877         *
878         * @param writer Writer to output this file to
879         */
880        public void save(Writer writer) {
881            YAML.get().dump(saveMap(), writer);
882        }
883    
884        private void loadMap(Map<?, ?> map) throws InvalidDescriptionException {
885            try {
886                name = rawName = map.get("name").toString();
887    
888                if (!name.matches("^[A-Za-z0-9 _.-]+$")) {
889                    throw new InvalidDescriptionException("name '" + name + "' contains invalid characters.");
890                }
891                name = name.replace(' ', '_');
892            } catch (NullPointerException ex) {
893                throw new InvalidDescriptionException(ex, "name is not defined");
894            } catch (ClassCastException ex) {
895                throw new InvalidDescriptionException(ex, "name is of wrong type");
896            }
897    
898            try {
899                version = map.get("version").toString();
900            } catch (NullPointerException ex) {
901                throw new InvalidDescriptionException(ex, "version is not defined");
902            } catch (ClassCastException ex) {
903                throw new InvalidDescriptionException(ex, "version is of wrong type");
904            }
905    
906            try {
907                main = map.get("main").toString();
908                if (main.startsWith("org.bukkit.")) {
909                    throw new InvalidDescriptionException("main may not be within the org.bukkit namespace");
910                }
911            } catch (NullPointerException ex) {
912                throw new InvalidDescriptionException(ex, "main is not defined");
913            } catch (ClassCastException ex) {
914                throw new InvalidDescriptionException(ex, "main is of wrong type");
915            }
916    
917            if (map.get("commands") != null) {
918                ImmutableMap.Builder<String, Map<String, Object>> commandsBuilder = ImmutableMap.<String, Map<String, Object>>builder();
919                try {
920                    for (Map.Entry<?, ?> command : ((Map<?, ?>) map.get("commands")).entrySet()) {
921                        ImmutableMap.Builder<String, Object> commandBuilder = ImmutableMap.<String, Object>builder();
922                        if (command.getValue() != null) {
923                            for (Map.Entry<?, ?> commandEntry : ((Map<?, ?>) command.getValue()).entrySet()) {
924                                if (commandEntry.getValue() instanceof Iterable) {
925                                    // This prevents internal alias list changes
926                                    ImmutableList.Builder<Object> commandSubList = ImmutableList.<Object>builder();
927                                    for (Object commandSubListItem : (Iterable<?>) commandEntry.getValue()) {
928                                        if (commandSubListItem != null) {
929                                            commandSubList.add(commandSubListItem);
930                                        }
931                                    }
932                                    commandBuilder.put(commandEntry.getKey().toString(), commandSubList.build());
933                                } else if (commandEntry.getValue() != null) {
934                                    commandBuilder.put(commandEntry.getKey().toString(), commandEntry.getValue());
935                                }
936                            }
937                        }
938                        commandsBuilder.put(command.getKey().toString(), commandBuilder.build());
939                    }
940                } catch (ClassCastException ex) {
941                    throw new InvalidDescriptionException(ex, "commands are of wrong type");
942                }
943                commands = commandsBuilder.build();
944            }
945    
946            if (map.get("class-loader-of") != null) {
947                classLoaderOf = map.get("class-loader-of").toString();
948            }
949    
950            depend = makePluginNameList(map, "depend");
951            softDepend = makePluginNameList(map, "softdepend");
952            loadBefore = makePluginNameList(map, "loadbefore");
953    
954            if (map.get("database") != null) {
955                try {
956                    database = (Boolean) map.get("database");
957                } catch (ClassCastException ex) {
958                    throw new InvalidDescriptionException(ex, "database is of wrong type");
959                }
960            }
961    
962            if (map.get("website") != null) {
963                website = map.get("website").toString();
964            }
965    
966            if (map.get("description") != null) {
967                description = map.get("description").toString();
968            }
969    
970            if (map.get("load") != null) {
971                try {
972                    order = PluginLoadOrder.valueOf(((String) map.get("load")).toUpperCase().replaceAll("\\W", ""));
973                } catch (ClassCastException ex) {
974                    throw new InvalidDescriptionException(ex, "load is of wrong type");
975                } catch (IllegalArgumentException ex) {
976                    throw new InvalidDescriptionException(ex, "load is not a valid choice");
977                }
978            }
979    
980            if (map.get("authors") != null) {
981                ImmutableList.Builder<String> authorsBuilder = ImmutableList.<String>builder();
982                if (map.get("author") != null) {
983                    authorsBuilder.add(map.get("author").toString());
984                }
985                try {
986                    for (Object o : (Iterable<?>) map.get("authors")) {
987                        authorsBuilder.add(o.toString());
988                    }
989                } catch (ClassCastException ex) {
990                    throw new InvalidDescriptionException(ex, "authors are of wrong type");
991                } catch (NullPointerException ex) {
992                    throw new InvalidDescriptionException(ex, "authors are improperly defined");
993                }
994                authors = authorsBuilder.build();
995            } else if (map.get("author") != null) {
996                authors = ImmutableList.of(map.get("author").toString());
997            } else {
998                authors = ImmutableList.<String>of();
999            }
1000    
1001            if (map.get("default-permission") != null) {
1002                try {
1003                    defaultPerm = PermissionDefault.getByName(map.get("default-permission").toString());
1004                } catch (ClassCastException ex) {
1005                    throw new InvalidDescriptionException(ex, "default-permission is of wrong type");
1006                } catch (IllegalArgumentException ex) {
1007                    throw new InvalidDescriptionException(ex, "default-permission is not a valid choice");
1008                }
1009            }
1010    
1011            if (map.get("awareness") instanceof Iterable) {
1012                Set<PluginAwareness> awareness = new HashSet<PluginAwareness>();
1013                try {
1014                    for (Object o : (Iterable<?>) map.get("awareness")) {
1015                        awareness.add((PluginAwareness) o);
1016                    }
1017                } catch (ClassCastException ex) {
1018                    throw new InvalidDescriptionException(ex, "awareness has wrong type");
1019                }
1020                this.awareness = ImmutableSet.copyOf(awareness);
1021            }
1022    
1023            try {
1024                lazyPermissions = (Map<?, ?>) map.get("permissions");
1025            } catch (ClassCastException ex) {
1026                throw new InvalidDescriptionException(ex, "permissions are of the wrong type");
1027            }
1028    
1029            if (map.get("prefix") != null) {
1030                prefix = map.get("prefix").toString();
1031            }
1032        }
1033    
1034        private static List<String> makePluginNameList(final Map<?, ?> map, final String key) throws InvalidDescriptionException {
1035            final Object value = map.get(key);
1036            if (value == null) {
1037                return ImmutableList.of();
1038            }
1039    
1040            final ImmutableList.Builder<String> builder = ImmutableList.<String>builder();
1041            try {
1042                for (final Object entry : (Iterable<?>) value) {
1043                    builder.add(entry.toString().replace(' ', '_'));
1044                }
1045            } catch (ClassCastException ex) {
1046                throw new InvalidDescriptionException(ex, key + " is of wrong type");
1047            } catch (NullPointerException ex) {
1048                throw new InvalidDescriptionException(ex, "invalid " + key + " format");
1049            }
1050            return builder.build();
1051        }
1052    
1053        private Map<String, Object> saveMap() {
1054            Map<String, Object> map = new HashMap<String, Object>();
1055    
1056            map.put("name", name);
1057            map.put("main", main);
1058            map.put("version", version);
1059            map.put("database", database);
1060            map.put("order", order.toString());
1061            map.put("default-permission", defaultPerm.toString());
1062    
1063            if (commands != null) {
1064                map.put("command", commands);
1065            }
1066            if (depend != null) {
1067                map.put("depend", depend);
1068            }
1069            if (softDepend != null) {
1070                map.put("softdepend", softDepend);
1071            }
1072            if (website != null) {
1073                map.put("website", website);
1074            }
1075            if (description != null) {
1076                map.put("description", description);
1077            }
1078    
1079            if (authors.size() == 1) {
1080                map.put("author", authors.get(0));
1081            } else if (authors.size() > 1) {
1082                map.put("authors", authors);
1083            }
1084    
1085            if (classLoaderOf != null) {
1086                map.put("class-loader-of", classLoaderOf);
1087            }
1088    
1089            if (prefix != null) {
1090                map.put("prefix", prefix);
1091            }
1092    
1093            return map;
1094        }
1095    
1096        private Map<?,?> asMap(Object object) throws InvalidDescriptionException {
1097            if (object instanceof Map) {
1098                return (Map<?,?>) object;
1099            }
1100            throw new InvalidDescriptionException(object + " is not properly structured.");
1101        }
1102    
1103        /**
1104         * @deprecated Internal use
1105         */
1106        @Deprecated
1107        public String getRawName() {
1108            return rawName;
1109        }
1110    }