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