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.

ActionSubstitutor.java 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /*
  2. * Copyright (c) 2006-2008 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.actions.interfaces.ActionType;
  24. import com.dmdirc.actions.interfaces.ActionComponent;
  25. import com.dmdirc.FrameContainer;
  26. import com.dmdirc.Server;
  27. import com.dmdirc.ServerState;
  28. import com.dmdirc.config.IdentityManager;
  29. import java.util.HashMap;
  30. import java.util.List;
  31. import java.util.Map;
  32. /**
  33. * Handles the substitution of variables into action targets and responses.
  34. *
  35. * @author Chris
  36. */
  37. public class ActionSubstitutor {
  38. /** The action type this substitutor is for. */
  39. private final ActionType type;
  40. /**
  41. * Creates a new substitutor for the specified action type.
  42. *
  43. * @param type The action type this substitutor is for
  44. */
  45. public ActionSubstitutor(final ActionType type) {
  46. this.type = type;
  47. }
  48. /**
  49. * Retrieves a list of global config variables that will be substituted.
  50. * Note: does not include initial $.
  51. *
  52. * @return A list of global variable names that will be substituted
  53. */
  54. public List<String> getConfigSubstitutions() {
  55. return IdentityManager.getGlobalConfig().getOptions("actions");
  56. }
  57. /**
  58. * Substitutes in config variables into the specified target.
  59. *
  60. * @param target The StringBuilder to modify
  61. */
  62. private void doConfigSubstitutions(final StringBuilder target) {
  63. for (String option : IdentityManager.getGlobalConfig().getOptions("actions")) {
  64. doReplacement(target, "$" + option,
  65. IdentityManager.getGlobalConfig().getOption("actions", option));
  66. }
  67. }
  68. /**
  69. * Retrieves a list of substitutions derived from argument and component
  70. * combinations, along with a corresponding friendly name for them.
  71. * Note: does not include initial $.
  72. *
  73. * @return A map of component substitution names and their descriptions
  74. */
  75. public Map<String, String> getComponentSubstitutions() {
  76. final Map<String, String> res = new HashMap<String, String>();
  77. int i = 0;
  78. for (Class myClass : type.getType().getArgTypes()) {
  79. for (ActionComponent comp : ActionManager.getCompatibleComponents(myClass)) {
  80. final String key = "{" + i + "." + comp.toString() + "}";
  81. final String desc = type.getType().getArgNames()[i] + "'s " + comp.getName();
  82. res.put(key, desc);
  83. }
  84. i++;
  85. }
  86. return res;
  87. }
  88. /**
  89. * Substitutes in component-style substitutions.
  90. *
  91. * @param target The stringbuilder to be changed
  92. * @param args The arguments passed for this action type
  93. */
  94. private void doComponentSubstitutions(final StringBuilder target, final Object ... args) {
  95. int i = 0;
  96. for (Class myClass : type.getType().getArgTypes()) {
  97. if (args[i] != null) {
  98. for (ActionComponent comp : ActionManager.getCompatibleComponents(myClass)) {
  99. final String needle = "${" + i + "." + comp.toString() + "}";
  100. final Object replacement = comp.get(args[i]);
  101. if (replacement != null) {
  102. doReplacement(target, needle, replacement.toString());
  103. }
  104. }
  105. }
  106. i++;
  107. }
  108. }
  109. /**
  110. * Retrieves a list of server substitutions, if this action type supports
  111. * them.
  112. * Note: does not include initial $.
  113. *
  114. * @return A map of server substitution names and their descriptions.
  115. */
  116. public Map<String, String> getServerSubstitutions() {
  117. final Map<String, String> res = new HashMap<String, String>();
  118. if (hasFrameContainer()) {
  119. for (ActionComponent comp : ActionManager.getCompatibleComponents(Server.class)) {
  120. final String key = "{" + comp.toString() + "}";
  121. final String desc = "The connection's " + comp.getName();
  122. res.put(key, desc);
  123. }
  124. }
  125. return res;
  126. }
  127. /**
  128. * Substitutes in server substitutions.
  129. *
  130. * @param target The stringbuilder to be changed
  131. * @param args The arguments passed for this action type
  132. */
  133. private void doServerSubstitutions(final StringBuilder target, final Object ... args) {
  134. if (args.length > 0 && args[0] instanceof FrameContainer) {
  135. final Server server = ((FrameContainer) args[0]).getServer();
  136. if (server != null && server.getState().equals(ServerState.CONNECTED)) {
  137. for (ActionComponent comp : ActionManager.getCompatibleComponents(Server.class)) {
  138. final String key = "${" + comp.toString() + "}";
  139. final Object res = comp.get(((FrameContainer) args[0]).getServer());
  140. if (res != null) {
  141. doReplacement(target, key, res.toString());
  142. }
  143. }
  144. }
  145. }
  146. }
  147. /**
  148. * Returns true if this action type's first argument is a frame container,
  149. * or descendent of one.
  150. *
  151. * @return True if this action type's first arg extends or is a FrameContainer
  152. */
  153. private boolean hasFrameContainer() {
  154. Class target = null;
  155. if (type.getType().getArgTypes().length > 0) {
  156. target = type.getType().getArgTypes()[0];
  157. while (target != null && target != FrameContainer.class) {
  158. target = target.getSuperclass();
  159. }
  160. }
  161. return target == FrameContainer.class;
  162. }
  163. /**
  164. * Determines whether or not word substitutions will work for this action
  165. * type. Word substitutions take the form $1, $1-5, $6-, etc.
  166. *
  167. * @return True if word substitutions are supported, false otherwise.
  168. */
  169. public boolean usesWordSubstitutions() {
  170. return type.getType().getArgTypes().length > 2 && type.getType().getArgTypes()[2] == String[].class;
  171. }
  172. /**
  173. * Substitutes in word substitutions.
  174. *
  175. * @param target The stringbuilder to be changed
  176. * @param args The arguments passed for this action type
  177. */
  178. private void doWordSubstitutions(final StringBuilder target, final Object ... args) {
  179. if (args.length > 1) {
  180. String[] words = null;
  181. if (args.length > 2 && args[2] instanceof String[]) {
  182. words = (String[]) args[2];
  183. } else if (args.length > 2 && args[2] instanceof String) {
  184. words = ((String) args[2]).split(" ");
  185. } else if (args[1] instanceof String[]) {
  186. words = (String[]) args[1];
  187. } else if (args[1] instanceof String) {
  188. words = ((String) args[1]).split(" ");
  189. } else {
  190. return;
  191. }
  192. final StringBuffer compound = new StringBuffer();
  193. for (int i = words.length - 1; i >= 0; i--) {
  194. if (compound.length() > 0) {
  195. compound.insert(0, ' ');
  196. }
  197. compound.insert(0, words[i]);
  198. doReplacement(target, "$" + (i + 1) + "-", compound.toString());
  199. doReplacement(target, "$" + (i + 1), words[i]);
  200. }
  201. }
  202. }
  203. /**
  204. * Performs all applicable substitutions on the specified string, with the
  205. * specified arguments.
  206. *
  207. * @param target The string to be altered
  208. * @param args The arguments for the action type
  209. * @return The substituted string
  210. */
  211. public String doSubstitution(final String target, final Object ... args) {
  212. final StringBuilder res = new StringBuilder(target);
  213. doConfigSubstitutions(res);
  214. doServerSubstitutions(res, args);
  215. doComponentSubstitutions(res, args);
  216. doWordSubstitutions(res, args);
  217. return res.toString();
  218. }
  219. /**
  220. * Replaces all occurances of needle in haystack with replacement.
  221. *
  222. * @param haystack The stringbuilder that is to be modified
  223. * @param needle The search string
  224. * @param replacement The string to be substituted in
  225. */
  226. private void doReplacement(final StringBuilder haystack, final String needle,
  227. final String replacement) {
  228. int i = -1;
  229. do {
  230. i = haystack.indexOf(needle);
  231. if (i != -1) {
  232. haystack.replace(i, i + needle.length(), replacement);
  233. }
  234. } while (i != -1);
  235. }
  236. }