Bukkit-API  1.7.9-R0.2
The inofficial Bukkit-API
PluginDescriptionFile.java
1 package org.bukkit.plugin;
2 
3 import java.io.InputStream;
4 import java.io.Reader;
5 import java.io.Writer;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11 
14 import org.bukkit.plugin.Plugin;
19 import org.yaml.snakeyaml.Yaml;
20 import org.yaml.snakeyaml.constructor.AbstractConstruct;
21 import org.yaml.snakeyaml.constructor.SafeConstructor;
22 import org.yaml.snakeyaml.nodes.Node;
23 import org.yaml.snakeyaml.nodes.Tag;
24 
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
28 
29 /**
30  * This type is the runtime-container for the information in the plugin.yml.
31  * All plugins must have a respective plugin.yml. For plugins written in java
32  * using the standard plugin loader, this file must be in the root of the jar
33  * file.
34  * <p>
35  * When Bukkit loads a plugin, it needs to know some basic information about
36  * it. It reads this information from a YAML file, 'plugin.yml'. This file
37  * consists of a set of attributes, each defined on a new line and with no
38  * indentation.
39  * <p>
40  * Every (almost* every) method corresponds with a specific entry in the
41  * plugin.yml. These are the <b>required</b> entries for every plugin.yml:
42  * <ul>
43  * <li>{@link #getName()} - <code>name</code>
44  * <li>{@link #getVersion()} - <code>version</code>
45  * <li>{@link #getMain()} - <code>main</code>
46  * </ul>
47  * <p>
48  * Failing to include any of these items will throw an exception and cause the
49  * server to ignore your plugin.
50  * <p>
51  * This is a list of the possible yaml keys, with specific details included in
52  * the respective method documentations:
53  * <table border=1>
54  * <tr>
55  * <th>Node</th>
56  * <th>Method</th>
57  * <th>Summary</th>
58  * </tr><tr>
59  * <td><code>name</code></td>
60  * <td>{@link #getName()}</td>
61  * <td>The unique name of plugin</td>
62  * </tr><tr>
63  * <td><code>version</code></td>
64  * <td>{@link #getVersion()}</td>
65  * <td>A plugin revision identifier</td>
66  * </tr><tr>
67  * <td><code>main</code></td>
68  * <td>{@link #getMain()}</td>
69  * <td>The plugin's initial class file</td>
70  * </tr><tr>
71  * <td><code>author</code><br><code>authors</code></td>
72  * <td>{@link #getAuthors()}</td>
73  * <td>The plugin contributors</td>
74  * </tr><tr>
75  * <td><code>description</code></td>
76  * <td>{@link #getDescription()}</td>
77  * <td>Human readable plugin summary</td>
78  * </tr><tr>
79  * <td><code>website</code></td>
80  * <td>{@link #getWebsite()}</td>
81  * <td>The URL to the plugin's site</td>
82  * </tr><tr>
83  * <td><code>prefix</code></td>
84  * <td>{@link #getPrefix()}</td>
85  * <td>The token to prefix plugin log entries</td>
86  * </tr><tr>
87  * <td><code>database</code></td>
88  * <td>{@link #isDatabaseEnabled()}</td>
89  * <td>Indicator to enable database support</td>
90  * </tr><tr>
91  * <td><code>load</code></td>
92  * <td>{@link #getLoad()}</td>
93  * <td>The phase of server-startup this plugin will load during</td>
94  * </tr><tr>
95  * <td><code>depend</code></td>
96  * <td>{@link #getDepend()}</td>
97  * <td>Other required plugins</td>
98  * </tr><tr>
99  * <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  */
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  */
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 }
static PermissionDefault getByName(String name)
PluginDescriptionFile(final String pluginName, final String pluginVersion, final String mainClass)
static List< Permission > loadPermissions(Map<?,?> data, String error, PermissionDefault def)
Map< String, Map< String, Object > > getCommands()