Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

ActionManager.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. /*
  2. * Copyright (c) 2006-2011 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.actions;
  23. import com.dmdirc.Main;
  24. import com.dmdirc.Precondition;
  25. import com.dmdirc.actions.interfaces.ActionComparison;
  26. import com.dmdirc.actions.interfaces.ActionComponent;
  27. import com.dmdirc.actions.interfaces.ActionType;
  28. import com.dmdirc.actions.internal.WhoisNumericFormatter;
  29. import com.dmdirc.actions.wrappers.AliasWrapper;
  30. import com.dmdirc.actions.wrappers.PerformWrapper;
  31. import com.dmdirc.config.IdentityManager;
  32. import com.dmdirc.interfaces.ActionListener;
  33. import com.dmdirc.interfaces.ConfigChangeListener;
  34. import com.dmdirc.logger.ErrorLevel;
  35. import com.dmdirc.logger.Logger;
  36. import com.dmdirc.updater.components.ActionGroupComponent;
  37. import com.dmdirc.util.MapList;
  38. import com.dmdirc.util.resourcemanager.ZipResourceManager;
  39. import java.io.File;
  40. import java.io.IOException;
  41. import java.util.ArrayList;
  42. import java.util.HashMap;
  43. import java.util.List;
  44. import java.util.Map;
  45. /**
  46. * Manages all actions for the client.
  47. */
  48. public final class ActionManager {
  49. /** A list of registered action types. */
  50. private static final List<ActionType> ACTION_TYPES
  51. = new ArrayList<ActionType>();
  52. /** A list of registered action components. */
  53. private static final List<ActionComponent> ACTION_COMPONENTS
  54. = new ArrayList<ActionComponent>();
  55. /** A list of registered action comparisons. */
  56. private static final List<ActionComparison> ACTION_COMPARISON
  57. = new ArrayList<ActionComparison>();
  58. /** A map linking types and a list of actions that're registered for them. */
  59. private static final MapList<ActionType, Action> ACTIONS
  60. = new MapList<ActionType, Action>();
  61. /** A map linking groups and a list of actions that're in them. */
  62. private static final Map<String, ActionGroup> GROUPS
  63. = new HashMap<String, ActionGroup>();
  64. /** A map of objects to synchronise on for concurrency groups. */
  65. private static final Map<String, Object> LOCKS
  66. = new HashMap<String, Object>();
  67. /** A map of the action type groups to the action types within. */
  68. private static final MapList<String, ActionType> ACTIONTYPE_GROUPS
  69. = new MapList<String, ActionType>();
  70. /** The listeners that we have registered. */
  71. private static final MapList<ActionType, ActionListener> LISTENERS
  72. = new MapList<ActionType, ActionListener>();
  73. /** Indicates whether or not user actions should be killed (not processed). */
  74. private static boolean killSwitch
  75. = IdentityManager.getGlobalConfig().getOptionBool("actions", "killswitch");
  76. /** Creates a new instance of ActionManager. */
  77. private ActionManager() {
  78. // Shouldn't be instansiated
  79. }
  80. /**
  81. * Initialises the action manager.
  82. */
  83. public static void init() {
  84. registerActionTypes(CoreActionType.values());
  85. registerActionComparisons(CoreActionComparison.values());
  86. registerActionComponents(CoreActionComponent.values());
  87. registerGroup(AliasWrapper.getAliasWrapper());
  88. registerGroup(PerformWrapper.getPerformWrapper());
  89. new WhoisNumericFormatter(IdentityManager.getAddonIdentity()).register();
  90. // Register a listener for the closing event, so we can save actions
  91. addListener(new ActionListener() {
  92. /** {@inheritDoc} */
  93. @Override
  94. public void processEvent(final ActionType type, final StringBuffer format,
  95. final Object... arguments) {
  96. saveActions();
  97. }
  98. }, CoreActionType.CLIENT_CLOSED);
  99. // Make sure we listen for the killswitch
  100. IdentityManager.getGlobalConfig().addChangeListener("actions", "killswitch",
  101. new ConfigChangeListener() {
  102. /** {@inheritDoc} */
  103. @Override
  104. public void configChanged(final String domain, final String key) {
  105. killSwitch = IdentityManager.getGlobalConfig().getOptionBool(
  106. "actions", "killswitch");
  107. }
  108. });
  109. }
  110. /**
  111. * Saves all actions.
  112. */
  113. public static void saveActions() {
  114. for (ActionGroup group : GROUPS.values()) {
  115. for (Action action : group) {
  116. action.save();
  117. }
  118. }
  119. }
  120. /**
  121. * Registers the specified default setting for actions.
  122. *
  123. * @param name The name of the setting to be registered
  124. * @param value The default value for the setting
  125. */
  126. public static void registerDefault(final String name, final String value) {
  127. IdentityManager.getAddonIdentity().setOption("actions", name, value);
  128. }
  129. /**
  130. * Registers the specified group of actions with the manager.
  131. *
  132. * @param group The group of actions to be registered
  133. */
  134. public static void registerGroup(final ActionGroup group) {
  135. GROUPS.put(group.getName(), group);
  136. }
  137. /**
  138. * Registers a set of actiontypes with the manager.
  139. *
  140. * @param types An array of ActionTypes to be registered
  141. */
  142. @Precondition("None of the specified ActionTypes are null")
  143. public static void registerActionTypes(final ActionType[] types) {
  144. for (ActionType type : types) {
  145. Logger.assertTrue(type != null);
  146. if(!ACTION_TYPES.contains(type)) {
  147. ACTION_TYPES.add(type);
  148. ACTIONTYPE_GROUPS.add(type.getType().getGroup(), type);
  149. }
  150. }
  151. }
  152. /**
  153. * Registers a set of action components with the manager.
  154. *
  155. * @param comps An array of ActionComponents to be registered
  156. */
  157. @Precondition("None of the specified ActionComponents are null")
  158. public static void registerActionComponents(final ActionComponent[] comps) {
  159. for (ActionComponent comp : comps) {
  160. Logger.assertTrue(comp != null);
  161. ACTION_COMPONENTS.add(comp);
  162. }
  163. }
  164. /**
  165. * Registers a set of action comparisons with the manager.
  166. *
  167. * @param comps An array of ActionComparisons to be registered
  168. */
  169. @Precondition("None of the specified ActionComparisons are null")
  170. public static void registerActionComparisons(final ActionComparison[] comps) {
  171. for (ActionComparison comp : comps) {
  172. Logger.assertTrue(comp != null);
  173. ACTION_COMPARISON.add(comp);
  174. }
  175. }
  176. /**
  177. * Returns a map of groups to action lists.
  178. *
  179. * @return a map of groups to action lists
  180. */
  181. public static Map<String, ActionGroup> getGroups() {
  182. return GROUPS;
  183. }
  184. /**
  185. * Returns a map of type groups to types.
  186. *
  187. * @return A map of type groups to types
  188. */
  189. public static MapList<String, ActionType> getTypeGroups() {
  190. return ACTIONTYPE_GROUPS;
  191. }
  192. /**
  193. * Loads actions from the user's directory.
  194. */
  195. public static void loadActions() {
  196. ACTIONS.clear();
  197. for (ActionGroup group : GROUPS.values()) {
  198. group.clear();
  199. }
  200. final File dir = new File(getDirectory());
  201. if (!dir.exists()) {
  202. try {
  203. dir.mkdirs();
  204. dir.createNewFile();
  205. } catch (IOException ex) {
  206. Logger.userError(ErrorLevel.HIGH, "I/O error when creating actions directory: "
  207. + ex.getMessage());
  208. }
  209. }
  210. if (dir.listFiles() == null) {
  211. Logger.userError(ErrorLevel.MEDIUM, "Unable to load user action files");
  212. } else {
  213. for (File file : dir.listFiles()) {
  214. if (file.isDirectory()) {
  215. loadActions(file);
  216. }
  217. }
  218. }
  219. registerComponents();
  220. }
  221. /**
  222. * Creates new ActionGroupComponents for each action group.
  223. */
  224. private static void registerComponents() {
  225. for (ActionGroup group : GROUPS.values()) {
  226. new ActionGroupComponent(group);
  227. }
  228. }
  229. /**
  230. * Loads action files from a specified group directory.
  231. *
  232. * @param dir The directory to scan.
  233. */
  234. @Precondition("The specified File is not null and represents a directory")
  235. private static void loadActions(final File dir) {
  236. Logger.assertTrue(dir != null);
  237. Logger.assertTrue(dir.isDirectory());
  238. if (!GROUPS.containsKey(dir.getName())) {
  239. GROUPS.put(dir.getName(), new ActionGroup(dir.getName()));
  240. }
  241. for (File file : dir.listFiles()) {
  242. new Action(dir.getName(), file.getName());
  243. }
  244. }
  245. /**
  246. * Registers an action with the manager.
  247. *
  248. * @param action The action to be registered
  249. */
  250. @Precondition("The specified action is not null")
  251. public static void registerAction(final Action action) {
  252. Logger.assertTrue(action != null);
  253. for (ActionType trigger : action.getTriggers()) {
  254. ACTIONS.add(trigger, action);
  255. }
  256. getGroup(action.getGroup()).add(action);
  257. }
  258. /**
  259. * Retrieves the action group with the specified name. A new group is
  260. * created if it doesn't already exist.
  261. *
  262. * @param name The name of the group to retrieve
  263. * @return The corresponding ActionGroup
  264. */
  265. public static ActionGroup getGroup(final String name) {
  266. if (!GROUPS.containsKey(name)) {
  267. GROUPS.put(name, new ActionGroup(name));
  268. }
  269. return GROUPS.get(name);
  270. }
  271. /**
  272. * Unregisters an action with the manager.
  273. *
  274. * @param action The action to be unregistered
  275. */
  276. @Precondition("The specified action is not null")
  277. public static void unregisterAction(final Action action) {
  278. Logger.assertTrue(action != null);
  279. ACTIONS.removeFromAll(action);
  280. getGroup(action.getGroup()).remove(action);
  281. }
  282. /**
  283. * Reregisters the specified action. Should be used when the action's
  284. * triggers change.
  285. *
  286. * @param action The action to be reregistered
  287. */
  288. public static void reregisterAction(final Action action) {
  289. unregisterAction(action);
  290. registerAction(action);
  291. }
  292. /**
  293. * Deletes the specified action.
  294. *
  295. * @param action The action to be deleted
  296. * @deprecated Use {@link ActionGroup#deleteAction(com.dmdirc.actions.Action)} instead.
  297. */
  298. @Precondition("The specified Action is not null")
  299. @Deprecated
  300. public static void deleteAction(final Action action) {
  301. Logger.assertTrue(action != null);
  302. unregisterAction(action);
  303. action.delete();
  304. }
  305. /**
  306. * Processes an event of the specified type.
  307. *
  308. * @param type The type of the event to process
  309. * @param format The format of the message that's going to be displayed for
  310. * the event. Actions may change this format.
  311. * @param arguments The arguments for the event
  312. * @return True if the event should be processed, or false if an action
  313. * listener has requested the event be skipped.
  314. */
  315. @Precondition({
  316. "The specified ActionType is not null",
  317. "The specified ActionType has a valid ActionMetaType",
  318. "The length of the arguments array equals the arity of the ActionType's ActionMetaType"
  319. })
  320. public static boolean processEvent(final ActionType type,
  321. final StringBuffer format, final Object ... arguments) {
  322. Logger.assertTrue(type != null);
  323. Logger.assertTrue(type.getType() != null);
  324. Logger.assertTrue(type.getType().getArity() == arguments.length);
  325. boolean res = false;
  326. if (LISTENERS.containsKey(type)) {
  327. for (ActionListener listener
  328. : new ArrayList<ActionListener>(LISTENERS.get(type))) {
  329. try {
  330. listener.processEvent(type, format, arguments);
  331. } catch (Exception e) {
  332. Logger.appError(ErrorLevel.MEDIUM, "Error processing action: "
  333. + e.getMessage(), e);
  334. }
  335. }
  336. }
  337. if (!killSwitch) {
  338. res |= triggerActions(type, format, arguments);
  339. }
  340. return !res;
  341. }
  342. /**
  343. * Triggers actions that respond to the specified type.
  344. *
  345. * @param type The type of the event to process
  346. * @param format The format of the message that's going to be displayed for
  347. * the event. Actions may change this format.
  348. * @param arguments The arguments for the event
  349. * @return True if the event should be skipped, or false if it can continue
  350. */
  351. @Precondition("The specified ActionType is not null")
  352. private static boolean triggerActions(final ActionType type,
  353. final StringBuffer format, final Object ... arguments) {
  354. Logger.assertTrue(type != null);
  355. boolean res = false;
  356. if (ACTIONS.containsKey(type)) {
  357. for (Action action : new ArrayList<Action>(ACTIONS.get(type))) {
  358. try {
  359. if (action.getConcurrencyGroup() == null) {
  360. res |= action.trigger(format, arguments);
  361. } else {
  362. synchronized (LOCKS) {
  363. if (!LOCKS.containsKey(action.getConcurrencyGroup())) {
  364. LOCKS.put(action.getConcurrencyGroup(), new Object());
  365. }
  366. }
  367. synchronized (LOCKS.get(action.getConcurrencyGroup())) {
  368. res |= action.trigger(format, arguments);
  369. }
  370. }
  371. } catch (LinkageError e) {
  372. Logger.appError(ErrorLevel.MEDIUM, "Error processing action: "
  373. + e.getMessage(), e);
  374. } catch (Exception e) {
  375. Logger.appError(ErrorLevel.MEDIUM, "Error processing action: "
  376. + e.getMessage(), e);
  377. }
  378. }
  379. }
  380. return res;
  381. }
  382. /**
  383. * Returns the directory that should be used to store actions.
  384. *
  385. * @return The directory that should be used to store actions
  386. */
  387. public static String getDirectory() {
  388. return Main.getConfigDir() + "actions" + System.getProperty("file.separator");
  389. }
  390. /**
  391. * Creates a new group with the specified name.
  392. *
  393. * @param group The group to be created
  394. *
  395. * @return The newly created group
  396. */
  397. @Precondition({
  398. "The specified group is non-null and not empty",
  399. "The specified group is not an existing group"
  400. })
  401. public static ActionGroup makeGroup(final String group) {
  402. Logger.assertTrue(group != null);
  403. Logger.assertTrue(!group.isEmpty());
  404. Logger.assertTrue(!GROUPS.containsKey(group));
  405. final File file = new File(getDirectory() + group);
  406. if (file.isDirectory() || file.mkdir()) {
  407. final ActionGroup actionGroup = new ActionGroup(group);
  408. GROUPS.put(group, actionGroup);
  409. return actionGroup;
  410. } else {
  411. throw new IllegalArgumentException("Unable to create action group directory"
  412. + "\n\nDir: " + getDirectory() + group);
  413. }
  414. }
  415. /**
  416. * Removes the group with the specified name.
  417. *
  418. * @param group The group to be removed
  419. */
  420. @Precondition({
  421. "The specified group is non-null and not empty",
  422. "The specified group is an existing group"
  423. })
  424. public static void removeGroup(final String group) {
  425. Logger.assertTrue(group != null);
  426. Logger.assertTrue(!group.isEmpty());
  427. Logger.assertTrue(GROUPS.containsKey(group));
  428. for (Action action : GROUPS.get(group).getActions()) {
  429. unregisterAction(action);
  430. }
  431. final File dir = new File(getDirectory() + group);
  432. if (dir.isDirectory()) {
  433. for (File file : dir.listFiles()) {
  434. if (!file.delete()) {
  435. Logger.userError(ErrorLevel.MEDIUM, "Unable to remove file: "
  436. + file.getAbsolutePath());
  437. return;
  438. }
  439. }
  440. }
  441. if (!dir.delete()) {
  442. Logger.userError(ErrorLevel.MEDIUM, "Unable to remove directory: "
  443. + dir.getAbsolutePath());
  444. return;
  445. }
  446. GROUPS.remove(group);
  447. }
  448. /**
  449. * Renames the specified group.
  450. *
  451. * @param oldName The old name of the group
  452. * @param newName The new name of the group
  453. */
  454. @Precondition({
  455. "The old name is non-null and not empty",
  456. "The old name is an existing group",
  457. "The new name is non-null and not empty",
  458. "The new name is not an existing group",
  459. "The old name does not equal the new name"
  460. })
  461. public static void renameGroup(final String oldName, final String newName) {
  462. Logger.assertTrue(oldName != null);
  463. Logger.assertTrue(!oldName.isEmpty());
  464. Logger.assertTrue(newName != null);
  465. Logger.assertTrue(!newName.isEmpty());
  466. Logger.assertTrue(GROUPS.containsKey(oldName));
  467. Logger.assertTrue(!GROUPS.containsKey(newName));
  468. Logger.assertTrue(!newName.equals(oldName));
  469. makeGroup(newName);
  470. for (Action action : GROUPS.get(oldName).getActions()) {
  471. action.setGroup(newName);
  472. getGroup(oldName).remove(action);
  473. getGroup(newName).add(action);
  474. }
  475. removeGroup(oldName);
  476. }
  477. /**
  478. * Returns the action comparison specified by the given string, or null if it
  479. * doesn't match a valid registered action comparison.
  480. *
  481. * @param type The name of the action comparison to try and find
  482. * @return The actioncomparison with the specified name, or null on failure
  483. */
  484. public static ActionType getActionType(final String type) {
  485. if (type == null || type.isEmpty()) {
  486. return null;
  487. }
  488. for (ActionType target : ACTION_TYPES) {
  489. if (target.name().equals(type)) {
  490. return target;
  491. }
  492. }
  493. return null;
  494. }
  495. /**
  496. * Returns a list of action types that are compatible with the one
  497. * specified.
  498. *
  499. * @param type The type to be checked against
  500. * @return A list of compatible action types
  501. */
  502. @Precondition("The specified type is not null")
  503. public static List<ActionType> getCompatibleTypes(final ActionType type) {
  504. Logger.assertTrue(type != null);
  505. final List<ActionType> res = new ArrayList<ActionType>();
  506. for (ActionType target : ACTION_TYPES) {
  507. if (!target.equals(type) && target.getType().equals(type.getType())) {
  508. res.add(target);
  509. }
  510. }
  511. return res;
  512. }
  513. /**
  514. * Returns a list of action components that are compatible with the
  515. * specified class.
  516. *
  517. * @param target The class to be tested
  518. * @return A list of compatible action components
  519. */
  520. @Precondition("The specified target is not null")
  521. public static List<ActionComponent> getCompatibleComponents(final Class<?> target) {
  522. Logger.assertTrue(target != null);
  523. final List<ActionComponent> res = new ArrayList<ActionComponent>();
  524. for (ActionComponent subject : ACTION_COMPONENTS) {
  525. if (subject.appliesTo().equals(target)) {
  526. res.add(subject);
  527. }
  528. }
  529. return res;
  530. }
  531. /**
  532. * Returns a list of action comparisons that are compatible with the
  533. * specified class.
  534. *
  535. * @param target The class to be tested
  536. * @return A list of compatible action comparisons
  537. */
  538. @Precondition("The specified target is not null")
  539. public static List<ActionComparison> getCompatibleComparisons(final Class<?> target) {
  540. Logger.assertTrue(target != null);
  541. final List<ActionComparison> res = new ArrayList<ActionComparison>();
  542. for (ActionComparison subject : ACTION_COMPARISON) {
  543. if (subject.appliesTo().equals(target)) {
  544. res.add(subject);
  545. }
  546. }
  547. return res;
  548. }
  549. /**
  550. * Returns a list of all the action types registered by this manager.
  551. *
  552. * @return A list of registered action types
  553. */
  554. public static List<ActionType> getTypes() {
  555. return ACTION_TYPES;
  556. }
  557. /**
  558. * Returns a list of all the action types registered by this manager.
  559. *
  560. * @return A list of registered action comparisons
  561. */
  562. public static List<ActionComparison> getComparisons() {
  563. return ACTION_COMPARISON;
  564. }
  565. /**
  566. * Returns the action component specified by the given string, or null if it
  567. * doesn't match a valid registered action component.
  568. *
  569. * @param type The name of the action component to try and find
  570. * @return The actioncomponent with the specified name, or null on failure
  571. */
  572. @Precondition("The specified type is non-null and not empty")
  573. public static ActionComponent getActionComponent(final String type) {
  574. Logger.assertTrue(type != null);
  575. Logger.assertTrue(!type.isEmpty());
  576. for (ActionComponent target : ACTION_COMPONENTS) {
  577. if (target.name().equals(type)) {
  578. return target;
  579. }
  580. }
  581. return null;
  582. }
  583. /**
  584. * Returns the action type specified by the given string, or null if it
  585. * doesn't match a valid registered action type.
  586. *
  587. * @param type The name of the action type to try and find
  588. * @return The actiontype with the specified name, or null on failure
  589. */
  590. @Precondition("The specified type is non-null and not empty")
  591. public static ActionComparison getActionComparison(final String type) {
  592. Logger.assertTrue(type != null);
  593. Logger.assertTrue(!type.isEmpty());
  594. for (ActionComparison target : ACTION_COMPARISON) {
  595. if (target.name().equals(type)) {
  596. return target;
  597. }
  598. }
  599. return null;
  600. }
  601. /**
  602. * Installs an action pack located at the specified path.
  603. *
  604. * @param path The full path of the action pack .zip.
  605. * @throws IOException If the zip cannot be extracted
  606. */
  607. public static void installActionPack(final String path) throws IOException {
  608. final ZipResourceManager ziprm = ZipResourceManager.getInstance(path);
  609. ziprm.extractResources("", getDirectory());
  610. loadActions();
  611. new File(path).delete();
  612. }
  613. /**
  614. * Adds a new listener for the specified action type.
  615. *
  616. * @param types The action types that are to be listened for
  617. * @param listener The listener to be added
  618. */
  619. public static void addListener(final ActionListener listener, final ActionType ... types) {
  620. for (ActionType type : types) {
  621. LISTENERS.add(type, listener);
  622. }
  623. }
  624. /**
  625. * Removes a listener for the specified action type.
  626. *
  627. * @param types The action types that were being listened for
  628. * @param listener The listener to be removed
  629. */
  630. public static void removeListener(final ActionListener listener, final ActionType ... types) {
  631. for (ActionType type : types) {
  632. LISTENERS.remove(type, listener);
  633. }
  634. }
  635. /**
  636. * Removes a listener for all action types.
  637. *
  638. * @param listener The listener to be removed
  639. */
  640. public static void removeListener(final ActionListener listener) {
  641. LISTENERS.removeFromAll(listener);
  642. }
  643. }