ソースを参照

Add command flag utility classes and make /echo and /set use them.

Fixes CLIENT-119
Fixes CLIENT-120

Change-Id: I2e7ad2c3b8f0db591031fc281a24aa6627331a6e
Reviewed-on: http://gerrit.dmdirc.com/1680
Reviewed-by: Greg Holmes <greg@dmdirc.com>
Automatic-Compile: DMDirc Local Commits <dmdirc@googlemail.com>
tags/0.6.5b1
Chris Smith 13年前
コミット
da78032175

+ 15
- 4
src/com/dmdirc/commandparser/CommandArguments.java ファイルの表示

@@ -122,7 +122,7 @@ public class CommandArguments {
122 122
     public String[] getArguments() {
123 123
         parse();
124 124
 
125
-        return Arrays.copyOfRange(words, 1, words.length);
125
+        return Arrays.copyOfRange(words, Math.min(1, words.length), words.length);
126 126
     }
127 127
 
128 128
     /**
@@ -132,8 +132,6 @@ public class CommandArguments {
132 132
      * @return A String representation of the command arguments
133 133
      */
134 134
     public String getArgumentsAsString() {
135
-        parse();
136
-
137 135
         return getArgumentsAsString(0);
138 136
     }
139 137
 
@@ -147,8 +145,21 @@ public class CommandArguments {
147 145
      */
148 146
     public String getArgumentsAsString(final int start) {
149 147
         parse();
148
+        
149
+        return getArgumentsAsString(start, words.length - 2);
150
+    }
150 151
 
151
-        return getWordsAsString(start + 1);
152
+    /**
153
+     * Retrieves arguments to the command (i.e., not including the
154
+     * command name) starting with the specified argument, with their original
155
+     * whitespace separation preserved.
156
+     *
157
+     * @param start The index of the first argument to include
158
+     * @param end The index of the last argument to include
159
+     * @return A String representation of the command arguments
160
+     */
161
+    public String getArgumentsAsString(final int start, final int end) {
162
+        return getWordsAsString(start + 1, end + 1);
152 163
     }
153 164
 
154 165
     /**

+ 171
- 0
src/com/dmdirc/commandparser/commands/flags/CommandFlag.java ファイルの表示

@@ -0,0 +1,171 @@
1
+/*
2
+ * Copyright (c) 2006-2010 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
+
23
+package com.dmdirc.commandparser.commands.flags;
24
+
25
+import java.util.Arrays;
26
+import java.util.Collection;
27
+import java.util.Collections;
28
+import java.util.LinkedList;
29
+import java.util.List;
30
+
31
+/**
32
+ * Describes a flag that may be used in a command. Each flag may be enabled
33
+ * or disabled initially, can specify a number of immediate arguments (which
34
+ * must follow the flag directly), a number of delayed arguments (which
35
+ * are specified after any other flags), and a list of other flags that
36
+ * the use of this one causes to be enabled or disabled.
37
+ *
38
+ * @since 0.6.5
39
+ * @see CommandFlagHandler
40
+ */
41
+public class CommandFlag {
42
+
43
+    /** The name of the flag. */
44
+    private final String name;
45
+    /** The list of flags that become enabled if this one is used. */
46
+    private final List<CommandFlag> enables = new LinkedList<CommandFlag>();
47
+    /** The list of flags that become disabled if this one is used. */
48
+    private final List<CommandFlag> disables = new LinkedList<CommandFlag>();
49
+    /** The number of args expected following this flag. */
50
+    private final int immediateArgs;
51
+    /** The number of args expected after flags are finished. */
52
+    private final int delayedArgs;
53
+    /** The initial state of this flag. */
54
+    private final boolean enabled;
55
+
56
+    /**
57
+     * Creates a new enabled flag with the specified name and no arguments.
58
+     *
59
+     * @param name The name of this flag
60
+     */
61
+    public CommandFlag(final String name) {
62
+        this(name, true);
63
+    }
64
+
65
+    /**
66
+     * Creates a new flag with the specified name and no arguments.
67
+     *
68
+     * @param name The name of this flag
69
+     * @param enabled Whether or not this flag is initially enabled
70
+     */
71
+    public CommandFlag(final String name, final boolean enabled) {
72
+        this(name, enabled, 0, 0);
73
+    }
74
+
75
+    /**
76
+     * Creates a new flag with the specified name and the specified number
77
+     * of arguments. Flags may only use immediate OR delayed arguments, not
78
+     * both.
79
+     *
80
+     * @param name The name of this flag
81
+     * @param enabled Whether or not this flag is initially enabled
82
+     * @param immediateArgs The number of immediate arguments
83
+     * @param delayedArgs The number of delayed arguments
84
+     */
85
+    public CommandFlag(final String name, final boolean enabled,
86
+            final int immediateArgs, final int delayedArgs) {
87
+        this.name = name;
88
+        this.immediateArgs = immediateArgs;
89
+        this.delayedArgs = delayedArgs;
90
+        this.enabled = enabled;
91
+
92
+        if (delayedArgs > 0 && immediateArgs > 0) {
93
+            throw new IllegalArgumentException("May not use both delayed and immediate arguments");
94
+        }
95
+    }
96
+
97
+    /**
98
+     * Indicates that the specified flags will be enabled if this flag
99
+     * is used.
100
+     *
101
+     * @param enabled The flags which will be enabled
102
+     */
103
+    public void addEnabled(final CommandFlag ... enabled) {
104
+        this.enables.addAll(Arrays.asList(enabled));
105
+    }
106
+
107
+    /**
108
+     * Indicates that the specified flags will be disabled if this flag
109
+     * is used.
110
+     *
111
+     * @param disabled The flags which will be disabled
112
+     */
113
+    public void addDisabled(final CommandFlag ... disabled) {
114
+        this.disables.addAll(Arrays.asList(disabled));
115
+    }
116
+
117
+    /**
118
+     * Retrieves the number of delayed arguments required by this flag.
119
+     *
120
+     * @return The number of delayed arguments
121
+     */
122
+    public int getDelayedArgs() {
123
+        return delayedArgs;
124
+    }
125
+
126
+    /**
127
+     * Retrieves the set of flags which become disabled if this one is used.
128
+     *
129
+     * @return A set of flags disabled by the use of this one
130
+     */
131
+    public Collection<CommandFlag> getDisables() {
132
+        return Collections.unmodifiableCollection(disables);
133
+    }
134
+
135
+    /**
136
+     * Determines whether or not this flag is enabled initially.
137
+     *
138
+     * @return The initial enabled/disabled state of this action
139
+     */
140
+    public boolean isEnabled() {
141
+        return enabled;
142
+    }
143
+
144
+    /**
145
+     * Retrieves the set of flags which become enabled if this one is used.
146
+     *
147
+     * @return A set of flags enabled by the use of this one
148
+     */
149
+    public Collection<CommandFlag> getEnables() {
150
+        return Collections.unmodifiableCollection(enables);
151
+    }
152
+
153
+    /**
154
+     * Retrieves the number of immediate arguments required by this flag.
155
+     *
156
+     * @return The number of immediate arguments
157
+     */
158
+    public int getImmediateArgs() {
159
+        return immediateArgs;
160
+    }
161
+
162
+    /**
163
+     * Retrieves the name of this flag.
164
+     *
165
+     * @return This flag's name
166
+     */
167
+    public String getName() {
168
+        return name;
169
+    }
170
+    
171
+}

+ 260
- 0
src/com/dmdirc/commandparser/commands/flags/CommandFlagHandler.java ファイルの表示

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

+ 133
- 0
src/com/dmdirc/commandparser/commands/flags/CommandFlagResult.java ファイルの表示

@@ -0,0 +1,133 @@
1
+/*
2
+ * Copyright (c) 2006-2010 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
+
23
+package com.dmdirc.commandparser.commands.flags;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+
27
+import java.util.Map;
28
+
29
+/**
30
+ * Convenient wrapper around the results of a {@link CommandFlagHandler}'s
31
+ * parsing routines.
32
+ */
33
+public class CommandFlagResult {
34
+
35
+    /** The original arguments for the command. */
36
+    private final CommandArguments arguments;
37
+
38
+    /** The offsets of the first argument for each flag. */
39
+    private final Map<CommandFlag, Integer> offsets;
40
+
41
+    /**
42
+     * Creates a new CommandFlagResult with the specified results.
43
+     *
44
+     * @param arguments The original arguments for the command
45
+     * @param offsets The offsets for each flag's arguments
46
+     */
47
+    public CommandFlagResult(final CommandArguments arguments,
48
+            final Map<CommandFlag, Integer> offsets) {
49
+        this.arguments = arguments;
50
+        this.offsets = offsets;
51
+    }
52
+
53
+    /**
54
+     * Determines if the specified command flag has been used when the command
55
+     * was executed.
56
+     *
57
+     * @param flag The flag to be checked
58
+     * @return True iff the flag was specified legally, false otherwise
59
+     */
60
+    public boolean hasFlag(final CommandFlag flag) {
61
+        return offsets.containsKey(flag);
62
+    }
63
+
64
+    /**
65
+     * Retrieves all of the arguments passed for the specified flag as a string.
66
+     *
67
+     * @param flag The flag to retrieve arguments for
68
+     * @return The arguments passed with the specified flag
69
+     * @see #getArguments(com.dmdirc.commandparser.commands.flags.CommandFlag)
70
+     */
71
+    public String getArgumentsAsString(final CommandFlag flag) {
72
+        return flag == null ? (offsets.get(flag) > arguments.getArguments().length
73
+                ? "" : arguments.getArgumentsAsString(offsets.get(flag))) :
74
+                arguments.getArgumentsAsString(offsets.get(flag),
75
+                offsets.get(flag) + flag.getDelayedArgs() + flag.getImmediateArgs() - 1);
76
+    }
77
+
78
+    /**
79
+     * Retrieves all arguments not associated with any flags as a string.
80
+     *
81
+     * @see CommandArguments#getArgumentsAsString()
82
+     * @see #getArguments()
83
+     * @return All arguments that aren't associated with a flag, as a string
84
+     */
85
+    public String getArgumentsAsString() {
86
+        return getArgumentsAsString(null);
87
+    }
88
+
89
+    /**
90
+     * Retrieves a subset of the arguments not associated with any flags,
91
+     * starting at the specified offset.
92
+     *
93
+     * @see CommandArguments#getArgumentsAsString(int)
94
+     * @param offset The offset of the argument to start at
95
+     * @return All arguments that aren't associated with a flag, as a string,
96
+     * starting with the argument at the specified offset
97
+     */
98
+    public String getArgumentsAsString(final int offset) {
99
+        return arguments.getArgumentsAsString(offsets.get(null) + offset);
100
+    }
101
+
102
+    /**
103
+     * Retrieves the arguments passed with the specified flag as an array.
104
+     *
105
+     * @param flag The flag to be checked
106
+     * @return The arguments passed with the specified flag
107
+     * @see #getArgumentsAsString(com.dmdirc.commandparser.commands.flags.CommandFlag)
108
+     */
109
+    public String[] getArguments(final CommandFlag flag) {
110
+        final String[] args = arguments.getArguments();
111
+        final int end = flag == null ? args.length
112
+                : offsets.get(flag) + flag.getDelayedArgs() + flag.getImmediateArgs();
113
+        final int offset = offsets.get(flag);
114
+        final int size = end - offset;
115
+
116
+        final String[] result = new String[size];
117
+        System.arraycopy(args, offset, result, 0, size);
118
+
119
+        return result;
120
+    }
121
+
122
+    /**
123
+     * Retrieves all arguments not associated with any flags.
124
+     *
125
+     * @see CommandArguments#getArguments()
126
+     * @see #getArgumentsAsString()
127
+     * @return All arguments that aren't associated with a flag
128
+     */
129
+    public String[] getArguments() {
130
+        return getArguments(null);
131
+    }
132
+
133
+}

+ 34
- 17
src/com/dmdirc/commandparser/commands/global/Echo.java ファイルの表示

@@ -31,6 +31,9 @@ import com.dmdirc.commandparser.CommandType;
31 31
 import com.dmdirc.commandparser.commands.Command;
32 32
 import com.dmdirc.commandparser.commands.IntelligentCommand;
33 33
 import com.dmdirc.commandparser.commands.context.CommandContext;
34
+import com.dmdirc.commandparser.commands.flags.CommandFlag;
35
+import com.dmdirc.commandparser.commands.flags.CommandFlagHandler;
36
+import com.dmdirc.commandparser.commands.flags.CommandFlagResult;
34 37
 import com.dmdirc.ui.WindowManager;
35 38
 import com.dmdirc.ui.input.AdditionalTabTargets;
36 39
 
@@ -46,60 +49,74 @@ import java.util.List;
46 49
 public final class Echo extends Command implements IntelligentCommand,
47 50
         CommandInfo {
48 51
 
52
+    /** The flag used to specify a timestamp for the echo command. */
53
+    private final CommandFlag timeStampFlag = new CommandFlag("ts", true, 1, 0);
54
+    /** The flag used to specify the echo command should use the active window. */
55
+    private final CommandFlag activeFlag = new CommandFlag("active");
56
+    /** The flag used to specify a target for the echo command. */
57
+    private final CommandFlag targetFlag = new CommandFlag("target", true, 1, 0);
58
+    /** The command flag handler for this command. */
59
+    private final CommandFlagHandler handler;
60
+
49 61
     /**
50 62
      * Creates a new instance of Echo.
51 63
      */
52 64
     public Echo() {
53 65
         super();
66
+
67
+        activeFlag.addDisabled(targetFlag);
68
+        targetFlag.addDisabled(activeFlag);
69
+
70
+        handler = new CommandFlagHandler(timeStampFlag, activeFlag, targetFlag);
54 71
     }
55 72
 
56 73
     /** {@inheritDoc} */
57 74
     @Override
58 75
     public void execute(final FrameContainer<?> origin,
59 76
             final CommandArguments args, final CommandContext context) {
60
-        int offset = 0;
61
-        Date time = new Date();
77
+        final CommandFlagResult results = handler.process(origin, args);
62 78
 
63
-        if (args.getArguments().length > 1
64
-                && args.getArguments()[offset].equalsIgnoreCase("--ts")) {
79
+        if (results == null) {
80
+            return;
81
+        }
82
+
83
+        Date time = new Date();
84
+        if (results.hasFlag(timeStampFlag)) {
65 85
             try {
66
-                time = new Date(Long.parseLong(args.getWordsAsString(2, 2)));
86
+                time = new Date(Long.parseLong(results.getArgumentsAsString(timeStampFlag)));
67 87
             } catch (NumberFormatException ex) {
68 88
                 sendLine(origin, args.isSilent(), FORMAT_ERROR, "Unable to process timestamp");
69 89
                 return;
70 90
             }
71
-
72
-            offset = 2;
73 91
         }
74 92
 
75
-        if (args.getArguments().length > offset
76
-                && args.getArguments()[offset].equalsIgnoreCase("--active")) {
93
+        if (results.hasFlag(activeFlag)) {
77 94
             if (!args.isSilent()) {
78 95
                 final FrameContainer<?> frame = WindowManager.getActiveWindow();
79
-                frame.addLine(FORMAT_OUTPUT, time, args.getArgumentsAsString(offset + 1));
96
+                frame.addLine(FORMAT_OUTPUT, time, results.getArgumentsAsString());
80 97
             }
81
-        } else if (args.getArguments().length > offset + 1
82
-                && args.getArguments()[offset].equalsIgnoreCase("--target")) {
98
+        } else if (results.hasFlag(targetFlag)) {
83 99
             FrameContainer<?> frame = null;
84 100
             FrameContainer<?> target = origin;
85 101
 
86 102
             while (frame == null && target != null) {
87
-                frame = WindowManager.findCustomWindow(target, args.getArguments()[offset + 1]);
103
+                frame = WindowManager.findCustomWindow(target,
104
+                        results.getArgumentsAsString(targetFlag));
105
+                target = target.getParent();
88 106
             }
89 107
 
90 108
             if (frame == null) {
91
-                frame = WindowManager.findCustomWindow(args.getArguments()[offset + 1]);
109
+                frame = WindowManager.findCustomWindow(results.getArgumentsAsString(targetFlag));
92 110
             }
93 111
 
94 112
             if (frame == null) {
95 113
                 sendLine(origin, args.isSilent(), FORMAT_ERROR,
96 114
                         "Unable to find target window");
97 115
             } else if (!args.isSilent()) {
98
-                frame.addLine(FORMAT_OUTPUT, time, args.getArgumentsAsString(offset + 2));
116
+                frame.addLine(FORMAT_OUTPUT, time, results.getArgumentsAsString());
99 117
             }
100
-
101 118
         } else if (origin != null && !args.isSilent()) {
102
-            origin.addLine(FORMAT_OUTPUT, time, args.getArgumentsAsString(offset));
119
+            origin.addLine(FORMAT_OUTPUT, time, results.getArgumentsAsString());
103 120
         }
104 121
     }
105 122
 

+ 78
- 26
src/com/dmdirc/commandparser/commands/global/Set.java ファイルの表示

@@ -22,6 +22,7 @@
22 22
 
23 23
 package com.dmdirc.commandparser.commands.global;
24 24
 
25
+import com.dmdirc.Channel;
25 26
 import com.dmdirc.FrameContainer;
26 27
 import com.dmdirc.commandparser.CommandArguments;
27 28
 import com.dmdirc.commandparser.CommandInfo;
@@ -29,7 +30,11 @@ import com.dmdirc.commandparser.CommandManager;
29 30
 import com.dmdirc.commandparser.CommandType;
30 31
 import com.dmdirc.commandparser.commands.Command;
31 32
 import com.dmdirc.commandparser.commands.IntelligentCommand;
33
+import com.dmdirc.commandparser.commands.context.ChannelCommandContext;
32 34
 import com.dmdirc.commandparser.commands.context.CommandContext;
35
+import com.dmdirc.commandparser.commands.flags.CommandFlag;
36
+import com.dmdirc.commandparser.commands.flags.CommandFlagHandler;
37
+import com.dmdirc.commandparser.commands.flags.CommandFlagResult;
33 38
 import com.dmdirc.config.ConfigManager;
34 39
 import com.dmdirc.config.Identity;
35 40
 import com.dmdirc.config.IdentityManager;
@@ -44,54 +49,98 @@ import java.util.List;
44 49
  */
45 50
 public final class Set extends Command implements IntelligentCommand, CommandInfo {
46 51
 
52
+    /** The flag to indicate the set command should apply to a server's settings. */
53
+    private final CommandFlag serverFlag = new CommandFlag("server");
54
+    /** The flag to indicate the set command should apply to a channel's settings. */
55
+    private final CommandFlag channelFlag = new CommandFlag("channel");
56
+    /** The flag to indicate that the specified setting should be unset. */
57
+    private final CommandFlag unsetFlag = new CommandFlag("unset", true, 0, 2);
58
+    /** The flag to indicate that the specified setting should be appended to. */
59
+    private final CommandFlag appendFlag = new CommandFlag("append", true, 0, 2);
60
+    /** The command flag handler for this command. */
61
+    private final CommandFlagHandler handler;
62
+
47 63
     /**
48 64
      * Creates a new instance of Set.
49 65
      */
50 66
     public Set() {
51 67
         super();
68
+        
69
+        unsetFlag.addDisabled(appendFlag);
70
+        appendFlag.addDisabled(unsetFlag);
71
+        
72
+        channelFlag.addDisabled(serverFlag);
73
+        serverFlag.addDisabled(channelFlag);
74
+
75
+        handler = new CommandFlagHandler(serverFlag, channelFlag, unsetFlag, appendFlag);
52 76
     }
53 77
 
54 78
     /** {@inheritDoc} */
55 79
     @Override
56 80
     public void execute(final FrameContainer<?> origin,
57 81
             final CommandArguments args, final CommandContext context) {
58
-        int i = 0;
82
+        final CommandFlagResult res = handler.process(origin, args);
83
+
84
+        if (res == null) {
85
+            return;
86
+        }
59 87
 
60 88
         Identity identity = IdentityManager.getConfigIdentity();
61 89
         ConfigManager manager = IdentityManager.getGlobalConfig();
62 90
 
63
-        if (args.getArguments().length > 0
64
-                && "--server".equalsIgnoreCase(args.getArguments()[0]) && origin != null
65
-                && origin.getServer() != null) {
66
-            i = 1;
91
+        if (res.hasFlag(serverFlag)) {
92
+            if (origin.getServer() == null) {
93
+                sendLine(origin, args.isSilent(), FORMAT_ERROR,
94
+                        "Cannot use --server in this context");
95
+                return;
96
+            }
97
+
67 98
             identity = origin.getServer().getServerIdentity();
68 99
             manager = origin.getServer().getConfigManager();
69 100
         }
70 101
 
71
-        switch (args.getArguments().length - i) {
102
+        if (res.hasFlag(channelFlag)) {
103
+            if (!(context instanceof ChannelCommandContext)) {
104
+                sendLine(origin, args.isSilent(), FORMAT_ERROR,
105
+                        "Cannot use --channel in this context");
106
+                return;
107
+            }
108
+
109
+            final Channel channel = ((ChannelCommandContext) context).getChannel();
110
+            identity = IdentityManager.getChannelConfig(origin.getServer().getNetwork(),
111
+                    channel.getName());
112
+            manager = channel.getConfigManager();
113
+        }
114
+
115
+        if (res.hasFlag(unsetFlag)) {
116
+            final String[] arguments = res.getArguments(unsetFlag);
117
+            doUnsetOption(origin, args.isSilent(), identity, arguments[0], arguments[1]);
118
+            return;
119
+        }
120
+        
121
+        if (res.hasFlag(appendFlag)) {
122
+            final String[] arguments = res.getArguments(appendFlag);
123
+            doAppendOption(origin, args.isSilent(), identity, manager,
124
+                    arguments[0], arguments[1], res.getArgumentsAsString());
125
+            return;
126
+        }
127
+        
128
+        final String[] arguments = res.getArguments();
129
+
130
+        switch (arguments.length) {
72 131
         case 0:
73 132
             doDomainList(origin, args.isSilent(), manager);
74 133
             break;
75 134
         case 1:
76
-            doOptionsList(origin, args.isSilent(), manager, args.getArguments()[i]);
135
+            doOptionsList(origin, args.isSilent(), manager, arguments[0]);
77 136
             break;
78 137
         case 2:
79
-            doShowOption(origin, args.isSilent(), manager, args.getArguments()[i],
80
-                    args.getArguments()[1 + i]);
138
+            doShowOption(origin, args.isSilent(), manager, arguments[0],
139
+                    arguments[1]);
81 140
             break;
82 141
         default:
83
-            if (args.getArguments()[i].equalsIgnoreCase("--unset")) {
84
-                doUnsetOption(origin, args.isSilent(), identity, args.getArguments()[1 + i],
85
-                        args.getArguments()[2 + i]);
86
-            } else if (args.getArguments()[i].equalsIgnoreCase("--append")
87
-                    && args.getArguments().length > 3 + i) {
88
-                doAppendOption(origin, args.isSilent(), identity, manager,
89
-                        args.getArguments()[1 + i], args.getArguments()[2 + i],
90
-                        args.getArgumentsAsString(3 + i));
91
-            } else {
92
-                doSetOption(origin, args.isSilent(), identity, args.getArguments()[i],
93
-                        args.getArguments()[1 + i], args.getArgumentsAsString(2 + i));
94
-            }
142
+            doSetOption(origin, args.isSilent(), identity, arguments[0],
143
+                    arguments[1], res.getArgumentsAsString(2));
95 144
             break;
96 145
         }
97 146
     }
@@ -247,9 +296,9 @@ public final class Set extends Command implements IntelligentCommand, CommandInf
247 296
     /** {@inheritDoc} */
248 297
     @Override
249 298
     public String getHelp() {
250
-        return "set [--server] [domain [option [newvalue]]] - inspect or change configuration settings"
251
-                + "\nset [--server] --append <domain> <option> <data> - appends data to the specified option"
252
-                + "\nset [--server] --unset <domain> <option> - unsets the specified option";
299
+        return "set [--server|--channel] [domain [option [newvalue]]] - inspect or change configuration settings"
300
+                + "\nset [--server|--channel] --append <domain> <option> <data> - appends data to the specified option"
301
+                + "\nset [--server|--channel] --unset <domain> <option> - unsets the specified option";
253 302
     }
254 303
 
255 304
     /** {@inheritDoc} */
@@ -265,11 +314,13 @@ public final class Set extends Command implements IntelligentCommand, CommandInf
265 314
             res.add("--unset");
266 315
             res.add("--append");
267 316
             res.add("--server");
317
+            res.add("--channel");
268 318
             res.excludeAll();
269 319
         } else if (arg == 1 && previousArgs.size() >= 1) {
270 320
             if (previousArgs.get(0).equalsIgnoreCase("--unset")
271 321
                     || previousArgs.get(0).equalsIgnoreCase("--append")
272
-                    || previousArgs.get(0).equalsIgnoreCase("--server")) {
322
+                    || previousArgs.get(0).equalsIgnoreCase("--server")
323
+                    || previousArgs.get(0).equalsIgnoreCase("--channel")) {
273 324
                 res.addAll(context.getWindow().getContainer().getConfigManager()
274 325
                         .getDomains());
275 326
             } else {
@@ -279,7 +330,8 @@ public final class Set extends Command implements IntelligentCommand, CommandInf
279 330
             res.excludeAll();
280 331
         } else if (arg == 2 && (previousArgs.get(0).equalsIgnoreCase("--unset")
281 332
                 || previousArgs.get(0).equalsIgnoreCase("--append")
282
-                || previousArgs.get(0).equalsIgnoreCase("--server"))) {
333
+                || previousArgs.get(0).equalsIgnoreCase("--server")
334
+                || previousArgs.get(0).equalsIgnoreCase("--channel"))) {
283 335
             res.addAll(context.getWindow().getContainer().getConfigManager()
284 336
                     .getOptions(previousArgs.get(1)).keySet());
285 337
             res.excludeAll();

+ 142
- 0
test/com/dmdirc/commandparser/commands/flags/CommandFlagHandlerTest.java ファイルの表示

@@ -0,0 +1,142 @@
1
+/*
2
+ * Copyright (c) 2006-2010 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.commandparser.commands.flags;
23
+
24
+import com.dmdirc.config.InvalidIdentityFileException;
25
+import java.util.Map;
26
+import com.dmdirc.FrameContainer;
27
+import com.dmdirc.commandparser.CommandArguments;
28
+import com.dmdirc.config.IdentityManager;
29
+import java.util.Arrays;
30
+import java.util.List;
31
+import org.junit.BeforeClass;
32
+import org.junit.Test;
33
+import org.junit.runner.RunWith;
34
+import org.junit.runners.Parameterized;
35
+import static org.junit.Assert.*;
36
+import static org.mockito.Mockito.*;
37
+
38
+@RunWith(Parameterized.class)
39
+public class CommandFlagHandlerTest {
40
+    
41
+    private static final CommandFlag noArgsFlag1 = new CommandFlag("noargs1");
42
+    private static final CommandFlag noArgsFlag2 = new CommandFlag("noargs2", false);
43
+    private static final CommandFlag noArgsFlag3 = new CommandFlag("noargs3");
44
+    private static final CommandFlag noArgsFlag4 = new CommandFlag("noargs4", false);
45
+    private static final CommandFlag immArgsFlag1 = new CommandFlag("immargs1", true, 1, 0);
46
+    private static final CommandFlag immArgsFlag2 = new CommandFlag("immargs2", true, 2, 0);
47
+    private static final CommandFlag defArgsFlag1 = new CommandFlag("defargs1", true, 0, 1);
48
+    private static final CommandFlag defArgsFlag2 = new CommandFlag("defargs2", true, 0, 2);
49
+    private final CommandFlagHandler handler;
50
+    
51
+    private final String input;
52
+    private final CommandFlag[] flags;
53
+    private final int[] offsets;
54
+    
55
+    @BeforeClass
56
+    public static void setupClass() throws InvalidIdentityFileException {
57
+        IdentityManager.load();
58
+    }
59
+    
60
+    public CommandFlagHandlerTest(String input, CommandFlag[] flags, int[] offsets) {
61
+        noArgsFlag3.addEnabled(noArgsFlag4);
62
+        noArgsFlag3.addDisabled(noArgsFlag1);
63
+
64
+        handler = new CommandFlagHandler(noArgsFlag1, noArgsFlag2, noArgsFlag3,
65
+                noArgsFlag4, immArgsFlag1, immArgsFlag2, defArgsFlag1, defArgsFlag2);
66
+        
67
+        this.input = input;
68
+        this.flags = flags;
69
+        this.offsets = offsets;
70
+    }
71
+    
72
+    @Test
73
+    public void testParse() {
74
+        final FrameContainer<?> container = mock(FrameContainer.class);
75
+        
76
+        final Map<CommandFlag, Integer> results
77
+                = handler.parse(container, new CommandArguments(input));
78
+        
79
+        if (flags == null) {
80
+            assertNull("Command should fail: " + input, results);
81
+        } else {
82
+            assertNotNull("Command should NOT fail: " + input, results);
83
+            assertTrue("Result must contain a null element: " + input,
84
+                    results.containsKey(null));
85
+
86
+            int i = 0;
87
+            for (CommandFlag flag : flags) {
88
+                assertTrue("Result must contain flag: " + flag.getName()
89
+                        + ": " + input, results.containsKey(flag));
90
+                assertEquals("Offset for flag " + flag.getName()
91
+                        + " should be " + offsets[i] + ": " + input,
92
+                        offsets[i++], (int) results.get(flag));
93
+            }
94
+
95
+            assertEquals("Global offset should be " + offsets[i],
96
+                    offsets[i], (int) results.get(null));
97
+
98
+            assertEquals("Result should contain " + (flags.length + 1)
99
+                    + " elements: " + input, flags.length + 1, results.size());
100
+        }
101
+    }
102
+
103
+    @Parameterized.Parameters
104
+    public static List<Object[]> data() {
105
+        final Object[][] tests = {
106
+            {"/foo", new CommandFlag[0], new int[]{0}},
107
+            {"/foo --noargs1", new CommandFlag[]{noArgsFlag1}, new int[]{1, 1}},
108
+            {"/foo --noargs2", null, null},
109
+            {"/foo --noargs3", new CommandFlag[]{noArgsFlag3}, new int[]{1, 1}},
110
+            {"/foo --noargs4", null, null},
111
+            {"/foo --noargs5", new CommandFlag[0], new int[]{0}},
112
+            {"/foo --noargs3 --noargs4", new CommandFlag[]{noArgsFlag3, noArgsFlag4}, new int[]{1, 2, 2}},
113
+            {"/foo --noargs3 --noargs1", null, null},
114
+            {"/foo --noargs1 --noargs3", new CommandFlag[]{noArgsFlag1, noArgsFlag3}, new int[]{1, 2, 2}},
115
+            {"/foo --noargs3 --noargs4 --noargs1", null, null},
116
+            {"/foo --noargs3 --noargs4 --noargs2", null, null},
117
+
118
+            {"/foo --immargs1", null, null},
119
+            {"/foo --noargs1 --immargs1", null, null},
120
+            {"/foo --immargs2", null, null},
121
+            {"/foo --immargs2 xxx", null, null},
122
+            {"/foo --immargs1 arg1", new CommandFlag[]{immArgsFlag1}, new int[]{1, 2}},
123
+            {"/foo --noargs1 --immargs1 arg1", new CommandFlag[]{immArgsFlag1, noArgsFlag1},
124
+                    new int[]{2, 1, 3}},
125
+            {"/foo --immargs1 arg1 --noargs1", new CommandFlag[]{immArgsFlag1, noArgsFlag1},
126
+                    new int[]{1, 3, 3}},
127
+
128
+            {"/foo --defargs1", null, null},
129
+            {"/foo --noargs1 --defargs1", null, null},
130
+            {"/foo --defargs2", null, null},
131
+            {"/foo --defargs2 xxx", null, null},
132
+            {"/foo --defargs1 arg1", new CommandFlag[]{defArgsFlag1}, new int[]{1, 2}},
133
+            {"/foo --noargs1 --defargs1 arg1", new CommandFlag[]{defArgsFlag1, noArgsFlag1},
134
+                    new int[]{2, 1, 3}},
135
+            {"/foo --defargs1 --noargs1 arg1", new CommandFlag[]{defArgsFlag1, noArgsFlag1},
136
+                    new int[]{2, 2, 3}},
137
+        };
138
+
139
+        return Arrays.asList(tests);
140
+    }    
141
+
142
+}

読み込み中…
キャンセル
保存