You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PerformWrapper.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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.wrappers;
  23. import com.dmdirc.Precondition;
  24. import com.dmdirc.Server;
  25. import com.dmdirc.actions.Action;
  26. import com.dmdirc.actions.ActionComponentChain;
  27. import com.dmdirc.actions.ActionCondition;
  28. import com.dmdirc.actions.ActionGroup;
  29. import com.dmdirc.actions.CoreActionComparison;
  30. import com.dmdirc.actions.CoreActionComponent;
  31. import com.dmdirc.actions.CoreActionType;
  32. import com.dmdirc.actions.interfaces.ActionComponent;
  33. import com.dmdirc.actions.interfaces.ActionType;
  34. import com.dmdirc.logger.ErrorLevel;
  35. import com.dmdirc.logger.Logger;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. /**
  39. * An action wrapper for performs.
  40. *
  41. * @author Chris
  42. */
  43. public class PerformWrapper extends ActionGroup {
  44. /**
  45. * Describes one specific perform.
  46. *
  47. * @since 0.6.4
  48. */
  49. public static class PerformDescription {
  50. /** The type of the perform being described. */
  51. private final PerformType type;
  52. /** The target of the perform. */
  53. private final String target;
  54. /** The profile (if any) of the perform. */
  55. private final String profile;
  56. /**
  57. * Creates a new perform description with the specified arguments.
  58. *
  59. * @param type The type of the perform in question
  60. * @param target The target of the perform
  61. * @param profile The profile of the perform (or null)
  62. */
  63. public PerformDescription(final PerformType type, final String target,
  64. final String profile) {
  65. this.type = type;
  66. this.target = target;
  67. this.profile = profile;
  68. if (target == null) {
  69. throw new NullPointerException("Target may not be null");
  70. }
  71. }
  72. /**
  73. * Creates a new perform description with the specified arguments.
  74. *
  75. * @param type The type of the perform in question
  76. * @param target The target of the perform
  77. */
  78. public PerformDescription(final PerformType type, final String target) {
  79. this.type = type;
  80. this.target = target;
  81. this.profile = null;
  82. if (target == null) {
  83. throw new NullPointerException("Target may not be null");
  84. }
  85. }
  86. /**
  87. * Retrieves the profile of this perform.
  88. *
  89. * @return This perform's profile
  90. */
  91. public String getProfile() {
  92. return profile;
  93. }
  94. /**
  95. * Retrieves the target of this perform.
  96. *
  97. * @return This perform's target
  98. */
  99. public String getTarget() {
  100. return target;
  101. }
  102. /**
  103. * Retrieves the type of this perform.
  104. *
  105. * @return This perform's type
  106. */
  107. public PerformType getType() {
  108. return type;
  109. }
  110. /** {@inheritDoc} */
  111. @Override
  112. public boolean equals(final Object obj) {
  113. if (obj == null || getClass() != obj.getClass()) {
  114. return false;
  115. }
  116. final PerformDescription other = (PerformDescription) obj;
  117. if (this.type != other.type || !this.target.equals(other.target)) {
  118. return false;
  119. }
  120. if ((this.profile == null) ? (other.profile != null)
  121. : !this.profile.equals(other.profile)) {
  122. return false;
  123. }
  124. return true;
  125. }
  126. /** {@inheritDoc} */
  127. @Override
  128. public int hashCode() {
  129. int hash = 7;
  130. hash = 89 * hash + (this.type != null ? this.type.hashCode() : 0);
  131. hash = 89 * hash + (this.target != null ? this.target.hashCode() : 0);
  132. hash = 89 * hash + (this.profile != null ? this.profile.hashCode() : 0);
  133. return hash;
  134. }
  135. }
  136. /** A singleton instance of the Perform Wrapper. */
  137. private static final PerformWrapper ME = new PerformWrapper();
  138. /** The component name for per-profile perform conditions. */
  139. private static final String PP_COMP_NAME = "SERVER_PROFILE.IDENTITY_NAME";
  140. /**
  141. * Creates a new instance of PerformWrapper.
  142. */
  143. private PerformWrapper() {
  144. super("performs");
  145. }
  146. /**
  147. * Retrieves a singleton instance of this perform wrapper.
  148. *
  149. * @return A singleton instance of PerformWrapper
  150. */
  151. public static PerformWrapper getPerformWrapper() {
  152. return ME;
  153. }
  154. /** {@inheritDoc} */
  155. @Override
  156. public void add(final Action action) {
  157. if (action.getTriggers().length != 1) {
  158. Logger.userError(ErrorLevel.MEDIUM,
  159. "Invalid perform action: " + action.getName(),
  160. "Perform actions may only have one trigger");
  161. } else if (action.getTriggers()[0] != CoreActionType.SERVER_CONNECTED) {
  162. Logger.userError(ErrorLevel.MEDIUM,
  163. "Invalid perform action: " + action.getName(),
  164. "Perform actions must be triggered when a server connects");
  165. } else if (!checkConditions(action.getConditions())) {
  166. Logger.userError(ErrorLevel.MEDIUM,
  167. "Invalid perform action: " + action.getName(),
  168. "Perform actions must have exactly one or two conditions, "
  169. + "one may target the server's name or network, and one may "
  170. + "target the server's profile name. No other targets are "
  171. + "allowed.");
  172. } else {
  173. synchronized (this) {
  174. super.add(action);
  175. }
  176. }
  177. }
  178. /** {@inheritDoc} */
  179. @Override
  180. public void deleteAction(final Action action) {
  181. synchronized (this) {
  182. super.deleteAction(action);
  183. }
  184. }
  185. /**
  186. * Sets the perform for the specified target of the specified type.
  187. * If the specified perform is empty - that is, any non-null elements are
  188. * empty Strings - then the perform is removed. If a profile is specified,
  189. * the perform will only be executed for that profile.
  190. *
  191. * @param perform The perform to be set
  192. * @param content The new content of that perform
  193. * @since 0.6.4
  194. */
  195. public void setPerform(final PerformDescription perform, final String[] content) {
  196. synchronized (this) {
  197. Action action = getAction(perform.getType() == PerformType.NETWORK
  198. ? CoreActionComponent.SERVER_NETWORK : CoreActionComponent.SERVER_NAME,
  199. perform.getTarget(), perform.getProfile());
  200. final boolean empty = isEmpty(content);
  201. if (action == null && !empty) {
  202. // They want to set a perform but we don't have an action
  203. action = createAction(
  204. perform.getType() == PerformType.SERVER ? perform.getTarget() : "",
  205. perform.getType() == PerformType.NETWORK ? perform.getTarget() : "",
  206. perform.getProfile());
  207. action.setResponse(content);
  208. action.save();
  209. }
  210. if (action != null) {
  211. if (empty) {
  212. // They want to clear the perform but we have an action
  213. deleteAction(action);
  214. } else {
  215. // They want to set a perform and we have an action
  216. action.setResponse(content);
  217. action.save();
  218. }
  219. }
  220. }
  221. }
  222. /**
  223. * Retrieves the perform for the relevant target. If no such perform exists,
  224. * a zero-length array is returned.
  225. *
  226. * @param perform The perform to be retrieved
  227. * @return The corresponding perform, or a zero-length array if none set
  228. * @since 0.6.4
  229. */
  230. public String[] getPerform(final PerformDescription perform) {
  231. final Action action = getAction(perform.getType() == PerformType.NETWORK
  232. ? CoreActionComponent.SERVER_NETWORK : CoreActionComponent.SERVER_NAME,
  233. perform.getTarget(), perform.getProfile());
  234. if (action == null || action.getResponse() == null) {
  235. return new String[0];
  236. } else {
  237. return action.getResponse();
  238. }
  239. }
  240. /**
  241. * Determines if the specified perform is "empty". An empty perform is one
  242. * that does not contain any non-empty Strings.
  243. *
  244. * @param perform The perform to test
  245. * @return True if the perform is empty, false otherwise
  246. * @since 0.6.4
  247. */
  248. private static boolean isEmpty(final String[] perform) {
  249. for (String part : perform) {
  250. if (part != null && !part.isEmpty()) {
  251. return false;
  252. }
  253. }
  254. return true;
  255. }
  256. /**
  257. * Checks that the specified conditions are valid for a perform action.
  258. *
  259. * @param conditions The conditions to be checked
  260. * @since 0.6.3m2
  261. * @return True if the conditions are valid, false otherwise
  262. */
  263. protected boolean checkConditions(final List<ActionCondition> conditions) {
  264. boolean target = false, profile = false;
  265. for (ActionCondition condition : conditions) {
  266. if ((condition.getComponent() == CoreActionComponent.SERVER_NETWORK
  267. || condition.getComponent() == CoreActionComponent.SERVER_NAME)
  268. && !target) {
  269. target = true;
  270. } else if (condition.getComponent() instanceof ActionComponentChain
  271. && PP_COMP_NAME.equals(condition.getComponent().toString())
  272. && !profile) {
  273. profile = true;
  274. } else {
  275. return false;
  276. }
  277. }
  278. return target || profile;
  279. }
  280. /**
  281. * Determines if the specified action is a per-profile perform, as opposed
  282. * to a generic perform.
  283. *
  284. * @param action The action to be tested
  285. * @return True if the action is per-profile, false otherwise
  286. * @since 0.6.3
  287. * @deprecated Actions shouldn't be used directly
  288. */
  289. @Deprecated
  290. public boolean isPerProfilePerform(final Action action) {
  291. return contains(action) && action.getConditions().size() == 2;
  292. }
  293. /**
  294. * Retrieves the name of the profile that is targetted by the specified
  295. * per-profile perform action.
  296. *
  297. * @param action The action whose perform should be retrieved
  298. * @return The action's targetted profile name
  299. * @since 0.6.3
  300. * @deprecated Actions shouldn't be used directly
  301. */
  302. @Deprecated
  303. @Precondition("The action is a per-profile perform")
  304. public String getProfileName(final Action action) {
  305. Logger.assertTrue(isPerProfilePerform(action));
  306. for (ActionCondition condition : action.getConditions()) {
  307. if (PP_COMP_NAME.equals(condition.getComponent().toString())) {
  308. return condition.getTarget();
  309. }
  310. }
  311. throw new IllegalStateException("Profile component not found in action");
  312. }
  313. /**
  314. * Retrieve the action that handles the perform for the specified server,
  315. * or null if no such action exists.
  316. *
  317. * @param server The server to look for
  318. * @return The action that handles the server's perform, or null
  319. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  320. */
  321. @Deprecated
  322. public Action getActionForServer(final String server) {
  323. return getActionForServer(server, null);
  324. }
  325. /**
  326. * Retrieve the action that handles the perform for the specified server,
  327. * or null if no such action exists.
  328. *
  329. * @param server The server to look for
  330. * @param profile The name of the profile the perform works for
  331. * @return The action that handles the servers's perform for the specified
  332. * profile, or null
  333. * @since 0.6.3
  334. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  335. */
  336. @Deprecated
  337. public Action getActionForServer(final String server, final String profile) {
  338. return getAction(CoreActionComponent.SERVER_NAME, server, profile);
  339. }
  340. /**
  341. * Retrieve the action that handles the perform for the specified network,
  342. * or null if no such action exists.
  343. *
  344. * @param network The network to look for
  345. * @return The action that handles the network's perform, or null
  346. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  347. */
  348. @Deprecated
  349. public Action getActionForNetwork(final String network) {
  350. return getActionForNetwork(network, null);
  351. }
  352. /**
  353. * Retrieve the action that handles the perform for the specified network,
  354. * or null if no such action exists.
  355. *
  356. * @param network The network to look for
  357. * @param profile The name of the profile the perform works for
  358. * @return The action that handles the network's perform for the specified
  359. * profile, or null
  360. * @since 0.6.3
  361. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  362. */
  363. @Deprecated
  364. public Action getActionForNetwork(final String network, final String profile) {
  365. return getAction(CoreActionComponent.SERVER_NETWORK, network, profile);
  366. }
  367. /**
  368. * Creates a new, empty, perform wrapper for the specified server.
  369. *
  370. * @param server The server to create the action for
  371. * @return The new perform wrapper action
  372. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  373. */
  374. @Deprecated
  375. public Action createActionForServer(final String server) {
  376. return createActionForServer(server, null);
  377. }
  378. /**
  379. * Creates a new, empty, perform wrapper for the specified server, which
  380. * is only applicable when the specified profile is in use.
  381. *
  382. * @param server The server to create the action for
  383. * @param profile The name of the profile which must be in use
  384. * @return The new perform wrapper action
  385. * @since 0.6.3
  386. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  387. */
  388. @Deprecated
  389. public Action createActionForServer(final String server, final String profile) {
  390. return createAction(server, "", profile);
  391. }
  392. /**
  393. * Creates a new, empty, perform wrapper for the specified network.
  394. *
  395. * @param network The network to create the action for
  396. * @return The new perform wrapper action
  397. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  398. */
  399. @Deprecated
  400. public Action createActionForNetwork(final String network) {
  401. return createActionForNetwork(network, null);
  402. }
  403. /**
  404. * Creates a new, empty, perform wrapper for the specified network, which
  405. * is only applicable when the specified profile is in use.
  406. *
  407. * @param network The network to create the action for
  408. * @param profile The name of the profile which must be in use
  409. * @return The new perform wrapper action
  410. * @since 0.6.3
  411. * @deprecated Actions shouldn't be used directly, use get/setPerform instead
  412. */
  413. @Deprecated
  414. public Action createActionForNetwork(final String network, final String profile) {
  415. return createAction("", network, profile);
  416. }
  417. /**
  418. * Creates a new, empty, perform wrapper for the specified server or
  419. * network. Note that both server and network must be specified, and
  420. * exactly one of them must be empty.
  421. *
  422. * @param server The server to create the action for
  423. * @param network The network to create the action for
  424. * @param profile The profile the action is for (or null if "global")
  425. * @since 0.6.3
  426. * @return The new perform wrapper action
  427. */
  428. private Action createAction(final String server, final String network,
  429. final String profile) {
  430. final List<ActionCondition> conditions = new ArrayList<ActionCondition>();
  431. final CoreActionComponent component =
  432. server.isEmpty() ? CoreActionComponent.SERVER_NETWORK
  433. : CoreActionComponent.SERVER_NAME;
  434. conditions.add(new ActionCondition(0, component,
  435. CoreActionComparison.STRING_EQUALS, server + network));
  436. if (profile != null) {
  437. conditions.add(new ActionCondition(0,
  438. new ActionComponentChain(Server.class, PP_COMP_NAME),
  439. CoreActionComparison.STRING_EQUALS, profile));
  440. }
  441. return new Action(getName(), server + network
  442. + (profile == null ? "" : " - " + profile),
  443. new ActionType[]{CoreActionType.SERVER_CONNECTED},
  444. new String[0], conditions, null);
  445. }
  446. /**
  447. * Retrieve an action with a condition that checks the specified component,
  448. * and matches it against the specified target.
  449. *
  450. * @param component The action component to look for
  451. * @param target The string the component is matched against
  452. * @param profile The name of the profile that the action must target, or
  453. * null for a non-profile specific action
  454. * @since 0.6.3
  455. * @return The matching action if one exists, or null
  456. */
  457. private Action getAction(final ActionComponent component, final String target,
  458. final String profile) {
  459. for (Action action : this) {
  460. int matches = profile == null ? 1 : 2;
  461. for (ActionCondition condition : action.getConditions()) {
  462. if (condition.getComponent() == component
  463. && condition.getTarget().equalsIgnoreCase(target)) {
  464. matches--;
  465. } else if (profile != null
  466. && PP_COMP_NAME.equals(condition.getComponent().toString())
  467. && condition.getTarget().equalsIgnoreCase(profile)) {
  468. matches--;
  469. }
  470. }
  471. if (matches == 0) {
  472. return action;
  473. }
  474. }
  475. return null;
  476. }
  477. /** {@inheritDoc} */
  478. @Override
  479. public boolean isDelible() {
  480. return false;
  481. }
  482. /** {@inheritDoc} */
  483. @Override
  484. public String getDescription() {
  485. return "Performs allow you to automatically execute commands when"
  486. + " you connect to a specific server or network. You can edit"
  487. + " the perform for the current server or network in the "
  488. + "\"Server Settings\" dialog, which can be accessed through "
  489. + "the Settings menu.";
  490. }
  491. }