PageRenderTime 1788ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/us/icebrg/imp/IMP.java

https://bitbucket.org/jyc/imp
Java | 513 lines | 243 code | 79 blank | 191 comment | 28 complexity | 70862f453d0ba35dbf665034a861f366 MD5 | raw file
Possible License(s): GPL-3.0
  1. package us.icebrg.imp;
  2. import java.util.logging.Level;
  3. import us.icebrg.imp.chat.ChatModule;
  4. import us.icebrg.imp.government.GovernmentModule;
  5. import us.icebrg.imp.transportation.Transportation;
  6. import com.google.gson.Gson;
  7. import com.google.gson.GsonBuilder;
  8. import com.mongodb.DB;
  9. import com.mongodb.DBCollection;
  10. import com.mongodb.Mongo;
  11. import java.io.File;
  12. import java.io.FileInputStream;
  13. import java.io.FilenameFilter;
  14. import java.io.IOException;
  15. import java.io.InputStream;
  16. import java.util.ArrayList;
  17. import java.util.Arrays;
  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Properties;
  21. import org.apache.commons.io.FileUtils;
  22. import org.apache.commons.io.IOUtils;
  23. import org.bukkit.command.CommandSender;
  24. import org.bukkit.entity.Player;
  25. import us.icebrg.imp.lang.LangCommand;
  26. /**
  27. * The "body" of the plugin, which interacts with and routes information to and
  28. * from the various modules.
  29. */
  30. public class IMP {
  31. /**
  32. * A string to prefix all log messages with.
  33. */
  34. public static final String logPrefix = "[IMP] ";
  35. /**
  36. * The Gson instance to use for all Gson operations. A class member so that
  37. * output can be standardized, i.e., always use setPrettyPrinting().
  38. */
  39. private Gson gson;
  40. /**
  41. * The MongoDB connection we are using to store all of our data.
  42. */
  43. private Mongo mongo;
  44. /**
  45. * The database in which we are storing everything.
  46. */
  47. private DB db;
  48. /**
  49. * Configuration for virtually every aspect.
  50. */
  51. private IMPConfiguration config;
  52. /**
  53. * The keys are ISO 693-3 language codes and the values are Properties files
  54. * containing i18n strings.
  55. * @see #getMessage(java.lang.String, java.lang.String) The primary method
  56. * of accessing this data.
  57. */
  58. private HashMap<String, Properties> languages
  59. = new HashMap<String, Properties>();
  60. /**********
  61. * Bukkit *
  62. **********/
  63. /**
  64. * The Bukkit plugin object through which we interact and respond to the
  65. * Bukkit server.
  66. */
  67. private IMPPlugin plugin;
  68. /******************
  69. * Sub-components *
  70. ******************/
  71. /**
  72. * A list of all of the top-level commands that are registered, expected to
  73. * be the root commands of the various modules.
  74. */
  75. private List<IMPCommand> commands = new ArrayList<IMPCommand>();
  76. /**
  77. * The active Chat module instance.
  78. */
  79. private ChatModule chat;
  80. /**
  81. * The active Government module instance.
  82. */
  83. private GovernmentModule government;
  84. /**
  85. * The active Transportation module instance.
  86. */
  87. private Transportation transportation;
  88. /**
  89. * Constructs a new IMP instance and binds it to the given IMPPlugin.
  90. * @param plugin the IMPPlugin instance the new instance should interact
  91. * with.
  92. */
  93. public IMP(IMPPlugin plugin) {
  94. this.plugin = plugin;
  95. // Set pretty printing to make the outputted JSON human-readable
  96. this.gson = new GsonBuilder().setPrettyPrinting().create();
  97. }
  98. /**
  99. * Attempts to initialize the configuration, the i18n messages, and the
  100. * database, logging to the Logger of this instance's IMPPlugin on error.
  101. * @return True on success, false on failure.
  102. */
  103. public boolean init() {
  104. // Clear all of the previously registered commands (they're about to be
  105. // re-registered)
  106. this.commands.clear();
  107. // Instantiate all of the modules.
  108. this.chat = new ChatModule(this);
  109. this.government = new GovernmentModule(this);
  110. this.transportation = new Transportation(this);
  111. // Try to load configuration.
  112. if ( ! ConfigurationService.loadConfigurations(this)) {
  113. return false;
  114. }
  115. // Try to load messages
  116. if ( ! this.loadMessages()) {
  117. return false;
  118. }
  119. // Try to load the database
  120. if ( ! this.loadDatabase()) {
  121. return false;
  122. }
  123. // Call the initializers for the various modules
  124. if ( ! (this.chat.init()
  125. && this.government.init()
  126. && this.transportation.init())) {
  127. this.log(Level.SEVERE, "One or more modules failed to initialize!");
  128. return false;
  129. }
  130. // Add the /imp lang commands
  131. new LangCommand().register(this);
  132. return true;
  133. }
  134. /**
  135. * Attempts to load all of the i18n .properties files, logging to this
  136. * instance's IMPPlugin's Logger on erorr.
  137. * @return True on success, false on failure.
  138. */
  139. private boolean loadMessages() {
  140. // Try to load up all of the messages file
  141. File messagesDir = new File(this.plugin.getDataFolder(), this.config.messagesDir);
  142. // Create the default messages if they don't exist
  143. if ( ! messagesDir.isDirectory()) {
  144. this.log(Level.INFO,
  145. "The messagesDir did not exist - creating a new default one.");
  146. if ( ! messagesDir.mkdir()) {
  147. this.log(Level.SEVERE,
  148. "Failed to create the messagesDir!");
  149. return false;
  150. }
  151. // Directory created successfuly, create the default English file
  152. InputStream stream =
  153. this.getClass().getResourceAsStream("/resources/eng.properties");
  154. try {
  155. FileUtils.writeStringToFile(
  156. new File(messagesDir, "eng.properties"),
  157. IOUtils.toString(stream), "utf-8");
  158. } catch (IOException e) {
  159. this.log(Level.SEVERE,
  160. "Failed to create default 'eng.properties' file!",
  161. e);
  162. return false;
  163. }
  164. }
  165. // Get all files ending with .properties
  166. File[] files = messagesDir.listFiles(new FilenameFilter() {
  167. @Override
  168. public boolean accept(File dir, String name) {
  169. return name.endsWith(".properties");
  170. }
  171. });
  172. // Load all of the files into Properties intsances and store those in
  173. // this.languages
  174. try {
  175. for (File file : files) {
  176. Properties properties = new Properties();
  177. properties.load(new FileInputStream(file));
  178. // Store the Properties file in the languages array as the
  179. // part before the .messages suffix
  180. this.languages.put(
  181. file.getName().substring(0, 3),
  182. properties);
  183. }
  184. } catch (IOException e) {
  185. this.log(Level.SEVERE,
  186. "An error occurred while trying to read .messages files.",
  187. e);
  188. return false;
  189. }
  190. return true;
  191. }
  192. /**
  193. * Attempts to connect to and load the MongoDB database, logging to this
  194. * instance's IMPPlugin's Logger on erorr.
  195. * @return True on success, false on failure.
  196. */
  197. private boolean loadDatabase() {
  198. // Try to load the database.
  199. try {
  200. this.mongo = new Mongo(this.config.databaseHost, this.config.databasePort);
  201. this.db = this.mongo.getDB(this.config.databaseName);
  202. } catch (Exception e) {
  203. this.log(Level.SEVERE,
  204. "Failed to load database.", e);
  205. return false;
  206. }
  207. return true;
  208. }
  209. /**
  210. * Attempts to find an execute the appropriate IMPCommand based on the given
  211. * data. Shows internationalized usage information on failure.
  212. * @param sender The entity that sent this command.
  213. * @param raw The raw command string.
  214. * @param parts The command string separated by spaces and excluding the
  215. * head (/imp or /!)
  216. */
  217. public void executeCommand(CommandSender sender, String raw, String[] parts) {
  218. // If there was a command other than the root command, a subcommand was
  219. // called - show usage for it
  220. if (parts.length > 0) {
  221. // Loop through all of the registered commands, attempting to find one
  222. // that matches
  223. for (IMPCommand command : this.commands) {
  224. IMPCommand.SearchResult match = command.search(
  225. sender, raw, parts, new String[0]);
  226. // If we got a match
  227. if (match != null) {
  228. boolean result = match.getCommand().execute(match.getUserCommand());
  229. // If true was returned, execution was successful
  230. if (result == true) {
  231. return;
  232. }
  233. // If the sender is a Player and doesn't have op, only show them
  234. // commands which don't require op
  235. if (sender instanceof Player && ! ((Player)sender).isOp()) {
  236. this.sendUsage(
  237. sender,
  238. match.getCommand().getUsageMessageKey(),
  239. match.getCommand().getChildren(false));
  240. } else {
  241. this.sendUsage(
  242. sender,
  243. match.getCommand().getUsageMessageKey(),
  244. match.getCommand().getChildren());
  245. }
  246. return;
  247. }
  248. }
  249. }
  250. // If nothing has successfully executed
  251. this.sendUsage(
  252. sender,
  253. "core.usage",
  254. this.commands);
  255. }
  256. /**
  257. * An overloaded version that converts an array of subcommands into a list.
  258. * @see #sendUsage(org.bukkit.command.CommandSender, java.lang.String, java.util.List)
  259. * for the worker method.
  260. * @param target
  261. * @param usageMessageKey
  262. * @param subcommands
  263. */
  264. public void sendUsage(CommandSender target, String usageMessageKey,
  265. IMPCommand[] subcommands) {
  266. this.sendUsage(target, usageMessageKey, Arrays.asList(subcommands));
  267. }
  268. /**
  269. * Sends usage information to the given target, automatically determining
  270. * which localized version(s) of the usage string(s) to send.
  271. * @param target The CommandSender to send the usage information to.
  272. * @param usageMessageKey The key (corresponding to a key in a file in the
  273. * messages directory) whose value to look up, based on the target's
  274. * stored language (in the database).
  275. * @param subcommands A List of all of the subcommands this command.
  276. * possesses, if any, whose name, alias and usage information will also be
  277. * displayed.
  278. */
  279. public void sendUsage(CommandSender target, String usageMessageKey,
  280. List<IMPCommand> subcommands) {
  281. target.sendMessage(this.getMessage(target, usageMessageKey));
  282. // If there are any subcommands, output them in a list.
  283. if ( ! subcommands.isEmpty()) {
  284. target.sendMessage(this.getMessage(target, "core.subcommands"));
  285. for (IMPCommand child : subcommands) {
  286. target.sendMessage(child.getName() + " "
  287. + child.getAlias() + " "
  288. + this.getMessage(
  289. target, child.getUsageMessageKey()));
  290. }
  291. }
  292. }
  293. /**
  294. * Logs a message using this instance's IMPPlugin's Logger.
  295. * @param level The logging level.
  296. * @param message The message to log.
  297. */
  298. public void log(Level level, String message) {
  299. this.plugin.log(level, message);
  300. }
  301. /**
  302. * Logs a message using this instance's IMPPlugin's Logger, printing a
  303. * stacktrace of the provided Exception
  304. * @param level The logging level.
  305. * @param message The message to log.
  306. * @param e The exception to print a stacktrace off.
  307. */
  308. public void log(Level level, String message, Exception e) {
  309. this.plugin.log(level, message, e);
  310. }
  311. /**
  312. * Registers a top-level IMPCommand.
  313. * @param command The command to register.
  314. * @return This instance, for method chaining.
  315. */
  316. public IMP addCommand(IMPCommand command) {
  317. this.commands.add(command);
  318. return this;
  319. }
  320. /**
  321. * Removes a top-level IMPCommand.
  322. * @param command The command to remove.
  323. * @return This instance, for method chaining.
  324. */
  325. public IMP removeCommand(IMPCommand command) {
  326. this.commands.remove(command);
  327. return this;
  328. }
  329. /**
  330. *
  331. * @return The active configuration instance.
  332. */
  333. public IMPConfiguration getConfig() {
  334. return this.config;
  335. }
  336. /**
  337. * @param config The new IMPConfiguration to use.
  338. */
  339. public void setConfig(IMPConfiguration config) {
  340. this.config = config;
  341. }
  342. /**
  343. *
  344. * @return The active Gson instance, recommended for all JSON output by
  345. * the IMP.
  346. */
  347. public Gson getGson() {
  348. return this.gson;
  349. }
  350. /**
  351. *
  352. * @return The active Chat module instance.
  353. */
  354. public ChatModule getChat() {
  355. return this.chat;
  356. }
  357. /**
  358. *
  359. * @return The active Government module instance.
  360. */
  361. public GovernmentModule getGovernment() {
  362. return this.government;
  363. }
  364. /**
  365. *
  366. * @return The active Transportation module instance.
  367. */
  368. public Transportation getTransportation() {
  369. return this.transportation;
  370. }
  371. /**
  372. *
  373. * @param language The language to look up the key in.
  374. * @param key The key to look up.
  375. * @return The provided language's version of the message with the given
  376. * key, or null if none is available.
  377. */
  378. public String getMessage(String language, String key) {
  379. Properties properties = this.languages.get(language);
  380. if (language == null) {
  381. return null;
  382. }
  383. return properties.getProperty(key);
  384. }
  385. /**
  386. * A wrapper for #getMessage(String language, String key) that gets the
  387. * sender's preferred language from the database (defaulting to the
  388. * defaultLanguage in the configuration) and uses that as the language.
  389. * @param sender The CommandSender whose language to look up in the
  390. * database.
  391. * @param key The key to look up.
  392. * @return The proper localized version of the message with the given key,
  393. * or null if none is found.
  394. */
  395. public String getMessage(CommandSender sender, String key) {
  396. String name;
  397. String language;
  398. List<User> result;
  399. if (sender instanceof Player) {
  400. name = ((Player)sender).getName();
  401. } else {
  402. name = User.consoleName;
  403. }
  404. User user = new User.HavingProperties(
  405. this.getCollection(User.getCollectionName()),
  406. name).load();
  407. if (user != null) {
  408. language = user.getLanguage();
  409. } else {
  410. language = this.config.defaultLanguage;
  411. }
  412. if ( ! this.languages.containsKey(language)) {
  413. throw new IndexOutOfBoundsException(
  414. "The language the user has set is not available!");
  415. }
  416. return this.getMessage(language, key);
  417. }
  418. /**
  419. *
  420. * @return This instance's IMPPlugin.
  421. */
  422. public IMPPlugin getPlugin() {
  423. return this.plugin;
  424. }
  425. public HashMap<String, Properties> getLanguages() {
  426. return this.languages;
  427. }
  428. public Properties getLanguage(String name) {
  429. return this.languages.get(name);
  430. }
  431. public DBCollection getCollection(String name) {
  432. return this.db.getCollection(name);
  433. }
  434. }