File Source: ActionModel.java
1 /*
2 * Copyright (c) 2006-2009 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.actions;
24
25 import com.dmdirc.actions.interfaces.ActionType;
26 import com.dmdirc.Main;
27 import com.dmdirc.Precondition;
28 import com.dmdirc.ServerManager;
29 import com.dmdirc.WritableFrameContainer;
30 import com.dmdirc.commandparser.parsers.CommandParser;
31 import com.dmdirc.commandparser.parsers.GlobalCommandParser;
32 import com.dmdirc.ui.interfaces.InputWindow;
33 import com.dmdirc.ui.interfaces.Window;
34
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38
39 /**
40 * Represents the basic model of an action, and its triggering mechanism.
41 * Saving and loading are handled by the Action class.
42 *
43 * @author chris
44 */
/*
P/P * Method: com.dmdirc.actions.ActionModel__static_init
*/
45 public class ActionModel {
46
47 /** The group this action belongs to. */
48 protected String group;
49
50 /** The name of this action. */
51 protected String name;
52
53 /** The ActionTypes that trigger this action. */
54 protected ActionType[] triggers;
55
56 /** The commands to execute if this action is triggered. */
57 protected String[] response;
58
59 /** The change that should be made to the format string, if any. */
60 protected String newFormat;
61
62 /** The conditions for this action. */
63 protected List<ActionCondition> conditions = new ArrayList<ActionCondition>();
64
65 /** The condition tree used for evaluating conditions. */
66 protected ConditionTree conditionTree;
67
68 /** Whether this action has been modified or not. */
69 protected boolean modified;
70
71 /**
72 * Creates a new instance of ActionModel with the specified properties.
73 *
74 * @param group The group the action belongs to
75 * @param name The name of the action
76 */
/*
P/P * Method: void com.dmdirc.actions.ActionModel(String, String)
*
* Postconditions:
* this.conditions == &new ArrayList(ActionModel#1)
* this.group == group
* init'ed(this.group)
* this.name == name
* init'ed(this.name)
* new ArrayList(ActionModel#1) num objects == 1
*/
77 public ActionModel(final String group, final String name) {
78 this.group = group;
79 this.name = name;
80 }
81
82 /**
83 * Creates a new instance of ActionModel with the specified properties.
84 *
85 * @param group The group the action belongs to
86 * @param name The name of the action
87 * @param triggers The triggers to use
88 * @param response The response to use
89 * @param conditions The conditions to use
90 * @param conditionTree The condition tree to use
91 * @param newFormat The new formatter to use
92 */
93 public ActionModel(final String group, final String name,
94 final ActionType[] triggers, final String[] response,
95 final List<ActionCondition> conditions,
/*
P/P * Method: void com.dmdirc.actions.ActionModel(String, String, ActionType[], String[], List, ConditionTree, String)
*
* Preconditions:
* response != null
* triggers != null
* (soft) init'ed(response[...])
* (soft) init'ed(triggers[...])
*
* Postconditions:
* this.conditionTree == conditionTree
* init'ed(this.conditionTree)
* this.conditions == conditions
* init'ed(this.conditions)
* this.group == group
* init'ed(this.group)
* this.modified == 1
* new ActionType[](ActionModel#2) num objects == 1
* new ArrayList(ActionModel#1) num objects == 1
* new String[](ActionModel#3) num objects == 1
* ...
*/
96 final ConditionTree conditionTree, final String newFormat) {
97 this.group = group;
98 this.name = name;
99 this.triggers = triggers.clone();
100 this.response = response.clone();
101 this.conditions = conditions;
102 this.conditionTree = conditionTree;
103 this.newFormat = newFormat;
104 this.modified = true;
105 }
106
107 /**
108 * Triggers this action.
109 *
110 * @param format The format of the message that's going to be displayed.
111 * @param arguments The arguments from the action that caused this trigger.
112 */
113 @Precondition({
114 "This action has at least one trigger",
115 "This action's primary trigger is non-null"
116 })
117 public void trigger(final StringBuffer format, final Object... arguments) {
/*
P/P * Method: void trigger(StringBuffer, Object[])
*
* Preconditions:
* init'ed(this.conditionTree)
* this.conditions != null
* this.triggers != null
* this.triggers.length >= 1
* this.triggers[0] != null
* (soft) arguments != null
* (soft) init'ed(arguments[0])
* (soft) init'ed(arguments[1])
* (soft) arguments[1].length in {1..232-1}
* (soft) init'ed(arguments[2])
* ...
*
* Presumptions:
* com.dmdirc.commandparser.parsers.GlobalCommandParser:getGlobalCommandParser(...)@140 != null
* com.dmdirc.ui.interfaces.InputWindow:getCommandParser(...)@142 != null
* getServerManager(...).servers != null
* java.util.List:get(...)@136 != null
*
* Postconditions:
* init'ed(com/dmdirc/ServerManager.me)
* init'ed(format._tainted)
* new ArrayList(ServerManager#1) num objects <= 1
* new ArrayList(ServerManager#1) num objects == 0
* new ServerManager(getServerManager#1) num objects <= 1
* new ServerManager(getServerManager#1) num objects == 0
* init'ed(new ServerManager(getServerManager#1).servers)
*
* Test Vectors:
* arguments.length: {0}, {1..+Inf}
* format: Addr_Set{null}, Inverse{null}
* this.newFormat: Addr_Set{null}, Inverse{null}
* java.util.List:size(...)@140: {-231..0}, {1..232-1}
*/
118 assert(triggers.length > 0);
119 assert(triggers[0] != null);
120
121 final ActionSubstitutor sub = new ActionSubstitutor(triggers[0]);
122
123 if (!test(sub, arguments)) {
124 return;
125 }
126
127 final Window active = Main.getUI().getActiveWindow();
128 InputWindow cw = null;
129 CommandParser cp = null;
130
131 if (arguments.length > 0 && arguments[0] instanceof WritableFrameContainer) {
132 cw = ((WritableFrameContainer) arguments[0]).getFrame();
133 } else if (active instanceof InputWindow) {
134 cw = (InputWindow) active;
135 } else if (ServerManager.getServerManager().numServers() > 0) {
136 cw = ServerManager.getServerManager().getServers().get(0).getFrame();
137 }
138
139 if (cw == null) {
140 cp = GlobalCommandParser.getGlobalCommandParser();
141 } else {
142 cp = cw.getCommandParser();
143 }
144
145 for (String command : response) {
146 cp.parseCommand(cw, sub.doSubstitution(command, arguments));
147 }
148
149 if (newFormat != null && format != null) {
150 format.setLength(0);
151 format.append(newFormat);
152 }
153 }
154
155 /**
156 * Tests to see if this action should be triggered or not.
157 *
158 * @param sub The ActionsSubstitutor to use to substitute args
159 * @param arguments The arguments for the action event
160 * @return True if the action should be executed, false otherwise
161 */
162 public boolean test(final ActionSubstitutor sub, final Object ... arguments) {
/*
P/P * Method: bool test(ActionSubstitutor, Object[])
*
* Preconditions:
* init'ed(this.conditionTree)
* this.conditions != null
* (soft) arguments != null
* (soft) init'ed(arguments[0])
* (soft) init'ed(arguments[1])
* (soft) arguments[1].length in {1..232-1}
* (soft) init'ed(arguments[2])
* (soft) arguments[2].length in {1..232-1}
* (soft) init'ed(arguments[...])
* (soft) init'ed(com.dmdirc.actions.ConditionTree$1__static_init.new int[](ConditionTree$1__static_init#1)[...])
* ...
*
* Presumptions:
* condition.arg@166 >= -1
* condition.arg@166 < arguments.length
* condition.comparison@166 != null
* condition.component@166 != null
* getRealConditionTree(...)...argument@170 >= 0
* ...
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@166: {0}, {1}
*/
163 final boolean[] results = new boolean[conditions.size()];
164
165 int i = 0;
166 for (ActionCondition condition : conditions) {
167 results[i++] = condition.test(sub, arguments);
168 }
169
170 return getRealConditionTree().evaluate(results);
171 }
172
173 /**
174 * Retrieves a list of this action's conditions.
175 *
176 * @return A list of this action's conditions
177 */
178 public List<ActionCondition> getConditions() {
/*
P/P * Method: List getConditions()
*
* Preconditions:
* init'ed(this.conditions)
*
* Postconditions:
* return_value == this.conditions
* init'ed(return_value)
*/
179 return conditions;
180 }
181
182 /**
183 * Sets this action's conditions.
184 *
185 * @param conditions A list of conditions to use
186 */
187 public void setConditions(final List<ActionCondition> conditions) {
/*
P/P * Method: void setConditions(List)
*
* Postconditions:
* this.conditions == conditions
* init'ed(this.conditions)
* this.modified == 1
*/
188 this.conditions = conditions;
189 this.modified = true;
190 }
191
192 /**
193 * Retrieves this action's triggers.
194 *
195 * @return The triggers used by this action
196 */
197 public ActionType[] getTriggers() {
/*
P/P * Method: ActionType[] getTriggers()
*
* Preconditions:
* init'ed(this.triggers)
* (soft) init'ed(this.triggers[...])
*
* Postconditions:
* return_value == One-of{this.triggers, &new ActionType[](getTriggers#1)}
* return_value in Addr_Set{null,&new ActionType[](getTriggers#1)}
* new ActionType[](getTriggers#1) num objects <= 1
* new ActionType[](getTriggers#1).length == this.triggers.length
* init'ed(new ActionType[](getTriggers#1).length)
* new ActionType[](getTriggers#1)[...] == One-of{this.triggers[...], undefined}
*/
198 return triggers == null ? triggers : triggers.clone();
199 }
200
201 /**
202 * Sets this action's triggers.
203 *
204 * @param triggers The new triggers to use
205 */
206 public void setTriggers(final ActionType[] triggers) {
/*
P/P * Method: void setTriggers(ActionType[])
*
* Preconditions:
* triggers != null
* (soft) init'ed(triggers[...])
*
* Postconditions:
* this.modified == 1
* new ActionType[](setTriggers#1) num objects == 1
* this.triggers == &new ActionType[](setTriggers#1)
* this.triggers.length == triggers.length
* init'ed(this.triggers.length)
* possibly_updated(this.triggers[...])
*/
207 this.triggers = triggers.clone();
208 this.modified = true;
209 }
210
211 /**
212 * Retrieves this action's new format setting.
213 *
214 * @return The format that this action will use, or null if no change
215 */
216 public String getNewFormat() {
/*
P/P * Method: String getNewFormat()
*
* Preconditions:
* init'ed(this.newFormat)
*
* Postconditions:
* return_value == this.newFormat
* init'ed(return_value)
*/
217 return newFormat;
218 }
219
220 /**
221 * Sets this action's new format setting.
222 *
223 * @param newFormat The new 'new format' setting
224 */
225 public void setNewFormat(final String newFormat) {
/*
P/P * Method: void setNewFormat(String)
*
* Postconditions:
* this.modified == 1
* this.newFormat == newFormat
* init'ed(this.newFormat)
*/
226 this.newFormat = newFormat;
227 this.modified = true;
228 }
229
230 /**
231 * Retrieves this action's response.
232 *
233 * @return The commands that will be executed if this action is triggered
234 */
235 public String[] getResponse() {
/*
P/P * Method: String[] getResponse()
*
* Preconditions:
* init'ed(this.response)
* (soft) init'ed(this.response[...])
*
* Postconditions:
* return_value == One-of{this.response, &new String[](getResponse#1)}
* return_value in Addr_Set{null,&new String[](getResponse#1)}
* new String[](getResponse#1) num objects <= 1
* new String[](getResponse#1).length == this.response.length
* init'ed(new String[](getResponse#1).length)
* new String[](getResponse#1)[...] == One-of{this.response[...], undefined}
*/
236 return response == null ? response : response.clone();
237 }
238
239 /**
240 * Sets this action's response.
241 *
242 * @param response The new response to use
243 */
244 public void setResponse(final String[] response) {
/*
P/P * Method: void setResponse(String[])
*
* Preconditions:
* response != null
* (soft) init'ed(response[...])
*
* Postconditions:
* this.modified == 1
* new String[](setResponse#1) num objects == 1
* this.response == &new String[](setResponse#1)
* this.response.length == response.length
* init'ed(this.response.length)
* possibly_updated(this.response[...])
*/
245 this.response = response.clone();
246 this.modified = true;
247 }
248
249 /**
250 * Retrieves this action's group name.
251 *
252 * @return This action's group name
253 */
254 public String getGroup() {
/*
P/P * Method: String getGroup()
*
* Preconditions:
* init'ed(this.group)
*
* Postconditions:
* return_value == this.group
* init'ed(return_value)
*/
255 return group;
256 }
257
258 /**
259 * Sets the group of this action.
260 *
261 * @param newGroup The new group for this action
262 */
263 public void setGroup(final String newGroup) {
/*
P/P * Method: void setGroup(String)
*
* Postconditions:
* this.group == newGroup
* init'ed(this.group)
* this.modified == 1
*/
264 this.group = newGroup;
265 this.modified = true;
266 }
267
268 /**
269 * Retrieves this action's name.
270 *
271 * @return This action's name
272 */
273 public String getName() {
/*
P/P * Method: String getName()
*
* Preconditions:
* init'ed(this.name)
*
* Postconditions:
* return_value == this.name
* init'ed(return_value)
*/
274 return name;
275 }
276
277 /**
278 * Sets the name of this action.
279 *
280 * @param newName The new name for this action
281 */
282 public void setName(final String newName) {
/*
P/P * Method: void setName(String)
*
* Postconditions:
* this.modified == 1
* this.name == newName
* init'ed(this.name)
*/
283 this.name = newName;
284 this.modified = true;
285 }
286
287 /**
288 * Retrieves the condition tree used for this action. Condition trees may
289 * be null, in which case the arguments are conjoined together.
290 *
291 * @return This action's condition tree
292 */
293 public ConditionTree getConditionTree() {
/*
P/P * Method: ConditionTree getConditionTree()
*
* Preconditions:
* init'ed(this.conditionTree)
*
* Postconditions:
* return_value == this.conditionTree
* init'ed(return_value)
*/
294 return conditionTree;
295 }
296
297 /**
298 * Retrieves a concrete condition tree used for this action. If there is
299 * no condition tree defined for this action, returns a conjunction tree
300 * for the arguments.
301 *
302 * @since 0.6
303 * @return A {@link ConditionTree} object for this action
304 */
305 public ConditionTree getRealConditionTree() {
/*
P/P * Method: ConditionTree getRealConditionTree()
*
* Preconditions:
* init'ed(this.conditionTree)
* (soft) this.conditions != null
*
* Postconditions:
* init'ed(return_value)
* new ConditionTree(parseStack#3) num objects <= 1
* new ConditionTree(parseStack#3).argument == -1
* new ConditionTree(parseStack#3).leftArg == null
* new ConditionTree(parseStack#3).op == &com.dmdirc.actions.ConditionTree$OPERATION__static_init.new ConditionTree$OPERATION(ConditionTree$OPERATION__static_init#5)
* new ConditionTree(parseStack#3).rightArg == null
* init'ed(new ConditionTree(readTerm#1) num objects)
* new ConditionTree(readTerm#1).argument == 0, if init'ed
* new ConditionTree(readTerm#1).leftArg == null
* new ConditionTree(readTerm#1).op == null
* ...
*/
306 return conditionTree == null
307 ? ConditionTree.createConjunction(conditions.size()) : conditionTree;
308 }
309
310 /**
311 * Sets the condition tree used for this action.
312 *
313 * @param conditionTree The new condition tree to be used
314 */
315 public void setConditionTree(final ConditionTree conditionTree) {
/*
P/P * Method: void setConditionTree(ConditionTree)
*
* Postconditions:
* this.conditionTree == conditionTree
* init'ed(this.conditionTree)
* this.modified == 1
*/
316 this.conditionTree = conditionTree;
317 this.modified = true;
318 }
319
320 /**
321 * Determine if this model has been modified since it was constructed or
322 * its modified status was reset.
323 *
324 * @return True if this model has been modified, false otherwise
325 */
326 public boolean isModified() {
/*
P/P * Method: bool isModified()
*
* Preconditions:
* init'ed(this.modified)
*
* Postconditions:
* return_value == this.modified
* init'ed(return_value)
*/
327 return modified;
328 }
329
330 /**
331 * Resets the modified status of this model. After a call to
332 * resetModified(), this model will report that it has not been modified,
333 * until one of the set* methods is used.
334 */
335 public void resetModified() {
/*
P/P * Method: void resetModified()
*
* Postconditions:
* this.modified == 0
*/
336 this.modified = false;
337 }
338
339 /** {@inheritDoc} */
340 @Override
341 public String toString() {
/*
P/P * Method: String toString()
*
* Preconditions:
* init'ed(this.conditions)
* init'ed(this.group)
* init'ed(this.name)
* init'ed(this.newFormat)
* init'ed(this.response)
* init'ed(this.triggers)
*
* Postconditions:
* java.lang.StringBuilder:toString(...)._tainted == this.conditions._tainted | this.group._tainted | this.name._tainted | One-of{this.conditions._tainted | this.group._tainted | this.name._tainted, this.newFormat._tainted}
* init'ed(java.lang.StringBuilder:toString(...)._tainted)
* return_value == &java.lang.StringBuilder:toString(...)
*/
342 return "[name=" + group + "/" + name + ", triggers="
343 + Arrays.toString(triggers) + ", response="
344 + Arrays.toString(response) + ", "
345 + conditions + ", format='" + newFormat + "']";
346 }
347 }
SofCheck Inspector Build Version : 2.17854
| ActionModel.java |
2009-Jun-25 01:54:24 |
| ActionModel.class |
2009-Sep-02 17:04:13 |