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ů.

CommandFlagHandler.java 10.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * Copyright (c) 2006-2014 DMDirc Developers
  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.commandparser.commands.flags;
  23. import com.dmdirc.FrameContainer;
  24. import com.dmdirc.commandparser.CommandArguments;
  25. import java.util.ArrayList;
  26. import java.util.HashMap;
  27. import java.util.LinkedList;
  28. import java.util.List;
  29. import java.util.Map;
  30. /**
  31. * Utility class for commands which allow the user to specify shell-like flags (
  32. * <code>--foo</code>).
  33. *
  34. * @since 0.6.5
  35. */
  36. public class CommandFlagHandler {
  37. /** A map of all known flag names to their flag objects. */
  38. private final Map<String, CommandFlag> flags = new HashMap<>();
  39. /** A map of currently enabled flag names to their flag objects. */
  40. private final Map<String, CommandFlag> enabledFlags = new HashMap<>();
  41. /** A map of currently disabled flag names to their flag objects. */
  42. private final Map<String, CommandFlag> disabledFlags = new HashMap<>();
  43. /** A map of disabled flag names to the flag objects that caused them to be disabled. */
  44. private final Map<String, CommandFlag> disabledBy = new HashMap<>();
  45. /**
  46. * Creates a new command flag handler which will handle all of the specified flags.
  47. *
  48. * @param flags The flags that will be handled
  49. */
  50. public CommandFlagHandler(final CommandFlag... flags) {
  51. for (CommandFlag flag : flags) {
  52. this.flags.put(flag.getName(), flag);
  53. }
  54. }
  55. /**
  56. * Processes the specified arguments and parses out command flags. If the specified arguments
  57. * aren't valid given the flags belonging to this command flag handler, an error message is sent
  58. * to the origin and <code>null</code> is returned from this method.
  59. *
  60. * @param origin The container where the command was entered
  61. * @param arguments The arguments passed to the command
  62. *
  63. * @return A corresponding {@link CommandFlagResult} object, or null if some problem was
  64. * encountered.
  65. */
  66. public CommandFlagResult process(final FrameContainer origin,
  67. final CommandArguments arguments) {
  68. final Map<CommandFlag, Integer> results = parse(origin, arguments);
  69. return results == null ? null : new CommandFlagResult(arguments, results);
  70. }
  71. /**
  72. * Parses the specified arguments and returns the offsets of the arguments for each found
  73. * command flag.
  74. *
  75. * @param origin The container where the command was entered
  76. * @param arguments The arguments passed to the command
  77. *
  78. * @return A map of discovered command flags to the offset of the flag's first argument within
  79. * the <code>arguments</code> object. If an error occurs, null is returned.
  80. */
  81. protected Map<CommandFlag, Integer> parse(final FrameContainer origin,
  82. final CommandArguments arguments) {
  83. enabledFlags.clear();
  84. disabledBy.clear();
  85. disabledFlags.clear();
  86. for (CommandFlag flag : flags.values()) {
  87. (flag.isEnabled() ? enabledFlags : disabledFlags).put(flag.getName(), flag);
  88. }
  89. final Map<CommandFlag, Integer> results = new HashMap<>();
  90. final List<CommandFlag> delayedFlags = new ArrayList<>(flags.size());
  91. int offset;
  92. for (offset = 0; offset < arguments.getArguments().length; offset++) {
  93. final String arg = arguments.getArguments()[offset];
  94. String name;
  95. if (arg.startsWith("--")
  96. && flags.containsKey(name = arg.substring(2).toLowerCase())) {
  97. final CommandFlag flag = flags.get(name);
  98. if (enabledFlags.containsKey(name)) {
  99. // It's enabled!
  100. handleEnable(flag);
  101. // Handle any immediate arguments
  102. if ((offset = readArguments(flag, arguments, offset + 1,
  103. flag.getImmediateArgs(), origin, results)) == -1) {
  104. return null;
  105. }
  106. // Handle delayed arguments (if any)
  107. if (flag.getDelayedArgs() > 0) {
  108. delayedFlags.add(flag);
  109. }
  110. } else if (disabledBy.containsKey(name)) {
  111. // Disabled by another flag
  112. sendLine(origin, arguments.isSilent(), "commandError",
  113. "Cannot use flag --" + name
  114. + " in conjunction with --" + disabledBy.get(name).getName());
  115. return null;
  116. } else {
  117. // Disabled because not yet enabled
  118. sendLine(origin, arguments.isSilent(), "commandError",
  119. "Cannot use flag --" + name
  120. + " without " + getEnablers(flag));
  121. return null;
  122. }
  123. } else {
  124. break;
  125. }
  126. }
  127. // Handle any stored delayed arguments
  128. for (CommandFlag flag : delayedFlags) {
  129. if ((offset = readArguments(flag, arguments, offset,
  130. flag.getDelayedArgs(), origin, results)) == -1) {
  131. return null;
  132. }
  133. offset++;
  134. }
  135. results.put(null, offset);
  136. return results;
  137. }
  138. /**
  139. * Reads the arguments for the specified flag.
  140. *
  141. * @param flag The flag that is being read
  142. * @param arguments The raw arguments for the command
  143. * @param offset The index that the first argument will be at
  144. * @param argCount The number of arguments that need to be read
  145. * @param origin The source of the command (for error messages)
  146. * @param results The map to place results into
  147. *
  148. * @return The index of the last argument that was handled, or -1 if there were insufficient
  149. * arguments for the flag
  150. */
  151. protected int readArguments(final CommandFlag flag,
  152. final CommandArguments arguments, final int offset, final int argCount,
  153. final FrameContainer origin, final Map<CommandFlag, Integer> results) {
  154. final int lastArg = argCount + offset - 1;
  155. if (arguments.getArguments().length <= lastArg) {
  156. sendLine(origin, arguments.isSilent(),
  157. "commandError", "Flag --" + flag.getName() + " expects "
  158. + argCount + " argument"
  159. + (argCount == 1 ? "" : "s"));
  160. return -1;
  161. }
  162. results.put(flag, offset);
  163. return lastArg;
  164. }
  165. /**
  166. * Processes the enabled and disabled lists for the specified flag, and adds them to the
  167. * relevant properties.
  168. *
  169. * @param flag The flag whose enables/disables lists should be processed
  170. */
  171. protected void handleEnable(final CommandFlag flag) {
  172. for (CommandFlag target : flag.getDisables()) {
  173. if (enabledFlags.containsKey(target.getName())) {
  174. enabledFlags.remove(target.getName());
  175. disabledFlags.put(target.getName(), target);
  176. disabledBy.put(target.getName(), flag);
  177. }
  178. }
  179. for (CommandFlag target : flag.getEnables()) {
  180. if (disabledFlags.containsKey(target.getName())) {
  181. disabledFlags.remove(target.getName());
  182. enabledFlags.put(target.getName(), target);
  183. disabledBy.remove(target.getName());
  184. }
  185. }
  186. }
  187. /**
  188. * Constructs a user-friendly string describing the flag(s) which must be used in order to
  189. * enable the specified flag. This is useful for error messages when the user tries to use a
  190. * disabled flag.
  191. *
  192. * @param flag The flag to find enablers for
  193. *
  194. * @return A user-friendly string describing flags which enable the specified flag.
  195. */
  196. protected String getEnablers(final CommandFlag flag) {
  197. final List<CommandFlag> enablers = new LinkedList<>();
  198. for (CommandFlag target : flags.values()) {
  199. if (target.getEnables().contains(flag)) {
  200. enablers.add(target);
  201. }
  202. }
  203. if (enablers.size() == 1) {
  204. return "--" + enablers.get(0).getName();
  205. }
  206. final StringBuilder res = new StringBuilder("one of ");
  207. for (CommandFlag enabler : enablers) {
  208. res.append("--");
  209. res.append(enabler.getName());
  210. res.append(", ");
  211. }
  212. return res.substring(0, res.length() - 2);
  213. }
  214. /**
  215. * Convenience method to send a line to the specified frame container.
  216. *
  217. * @param origin The container to send the line to
  218. * @param isSilent Whether the command is silenced or not
  219. * @param messageType The type of the line to be sent
  220. * @param args The arguments for the specified messageType
  221. */
  222. protected static void sendLine(final FrameContainer origin, final boolean isSilent,
  223. final String messageType, final Object... args) {
  224. if (origin != null && !isSilent) {
  225. origin.addLine(messageType, args);
  226. }
  227. }
  228. }