1 package org.apache.turbine.modules;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.Iterator;
22
23 import org.apache.commons.lang.StringUtils;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import org.apache.turbine.Turbine;
29 import org.apache.turbine.TurbineConstants;
30 import org.apache.turbine.util.RunData;
31 import org.apache.turbine.util.parser.ParameterParser;
32 import org.apache.turbine.util.parser.ParserUtils;
33
34 /***
35 * <p>
36 *
37 * This is an alternative to the Action class that allows you to do
38 * event based actions. Essentially, you label all your submit buttons
39 * with the prefix of "eventSubmit_" and the suffix of "methodName".
40 * For example, "eventSubmit_doDelete". Then any class that subclasses
41 * this class will get its "doDelete(RunData data)" method executed.
42 * If for any reason, it was not able to execute the method, it will
43 * fall back to executing the doPeform() method which is required to
44 * be implemented.
45 *
46 * <p>
47 *
48 * Limitations:
49 *
50 * <p>
51 *
52 * Because ParameterParser makes all the key values lowercase, we have
53 * to do some work to format the string into a method name. For
54 * example, a button name eventSubmit_doDelete gets converted into
55 * eventsubmit_dodelete. Thus, we need to form some sort of naming
56 * convention so that dodelete can be turned into doDelete.
57 *
58 * <p>
59 *
60 * Thus, the convention is this:
61 *
62 * <ul>
63 * <li>The variable name MUST have the prefix "eventSubmit_".</li>
64 * <li>The variable name after the prefix MUST begin with the letters
65 * "do".</li>
66 * <li>The first letter after the "do" will be capitalized and the
67 * rest will be lowercase</li>
68 * </ul>
69 *
70 * If you follow these conventions, then you should be ok with your
71 * method naming in your Action class.
72 *
73 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens </a>
74 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
75 * @author <a href="quintonm@bellsouth.net">Quinton McCombs</a>
76 * @version $Id: ActionEvent.java 292717 2005-09-30 12:56:23Z seade $
77 */
78 public abstract class ActionEvent extends Action
79 {
80 /*** Logging */
81 protected Log log = LogFactory.getLog(this.getClass());
82
83 /*** Constant needed for Reflection */
84 private static final Class [] methodParams
85 = new Class [] { RunData.class };
86
87 /***
88 * You need to implement this in your classes that extend this class.
89 *
90 * @param data Turbine information.
91 * @exception Exception a generic exception.
92 */
93 public abstract void doPerform(RunData data)
94 throws Exception;
95
96 /*** The name of the button to look for. */
97 protected static final String BUTTON = "eventSubmit_";
98 /*** The length of the button to look for. */
99 protected static final int BUTTON_LENGTH = BUTTON.length();
100 /*** The prefix of the method name. */
101 protected static final String METHOD_NAME_PREFIX = "do";
102 /*** The length of the method name. */
103 protected static final int METHOD_NAME_LENGTH = METHOD_NAME_PREFIX.length();
104 /*** The length of the button to look for. */
105 protected static final int LENGTH = BUTTON.length();
106
107 /***
108 * If true, the eventSubmit_do<xxx> variable must contain
109 * a not null value to be executed.
110 */
111 private boolean submitValueKey = false;
112
113 /***
114 * C'tor
115 */
116 public ActionEvent()
117 {
118 super();
119
120 submitValueKey = Turbine.getConfiguration()
121 .getBoolean(TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_KEY,
122 TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_DEFAULT);
123
124 log.debug(submitValueKey
125 ? "ActionEvent accepts only eventSubmit_do Keys with a value != 0"
126 : "ActionEvent accepts all eventSubmit_do Keys");
127 }
128
129 /***
130 * This overrides the default Action.perform() to execute the
131 * doEvent() method. If that fails, then it will execute the
132 * doPerform() method instead.
133 *
134 * @param data Turbine information.
135 * @exception Exception a generic exception.
136 */
137 protected void perform(RunData data)
138 throws Exception
139 {
140 try
141 {
142 executeEvents(data);
143 }
144 catch (NoSuchMethodException e)
145 {
146 doPerform(data);
147 }
148 }
149
150 /***
151 * This method should be called to execute the event based system.
152 *
153 * @param data Turbine information.
154 * @exception Exception a generic exception.
155 */
156 public void executeEvents(RunData data)
157 throws Exception
158 {
159
160 String theButton = null;
161
162 ParameterParser pp = data.getParameters();
163
164 String button = pp.convert(BUTTON);
165 String key = null;
166
167
168 for (Iterator it = pp.keySet().iterator(); it.hasNext();)
169 {
170 key = (String) it.next();
171 if (key.startsWith(button))
172 {
173 if (considerKey(key, pp))
174 {
175 theButton = formatString(key);
176 break;
177 }
178 }
179 }
180
181 if (theButton == null)
182 {
183 throw new NoSuchMethodException("ActionEvent: The button was null");
184 }
185
186 try
187 {
188 Method method = getClass().getMethod(theButton, methodParams);
189 Object[] methodArgs = new Object[] { data };
190
191 if (log.isDebugEnabled())
192 {
193 log.debug("Invoking " + method);
194 }
195
196 method.invoke(this, methodArgs);
197 }
198 catch (InvocationTargetException ite)
199 {
200 Throwable t = ite.getTargetException();
201 if (t instanceof Exception)
202 {
203 throw (Exception) t;
204 }
205 else
206 {
207 throw ite;
208 }
209 }
210 finally
211 {
212 pp.remove(key);
213 }
214 }
215
216 /***
217 * This method does the conversion of the lowercase method name
218 * into the proper case.
219 *
220 * @param input The unconverted method name.
221 * @return A string with the method name in the proper case.
222 */
223 protected final String formatString(String input)
224 {
225 String tmp = input;
226
227 if (StringUtils.isNotEmpty(input))
228 {
229 tmp = input.toLowerCase();
230
231
232 input = (tmp.endsWith(".x") || tmp.endsWith(".y"))
233 ? input.substring(0, input.length() - 2)
234 : input;
235
236 if (ParserUtils.getUrlFolding()
237 != ParserUtils.URL_CASE_FOLDING_NONE)
238 {
239 tmp = input.toLowerCase().substring(BUTTON_LENGTH + METHOD_NAME_LENGTH);
240 tmp = METHOD_NAME_PREFIX + StringUtils.capitalize(tmp);
241 }
242 else
243 {
244 tmp = input.substring(BUTTON_LENGTH);
245 }
246 }
247 return tmp;
248 }
249
250 /***
251 * Checks whether the selected key really is a valid event.
252 *
253 * @param key The selected key
254 * @param pp The parameter parser to look for the key value
255 *
256 * @return true if this key is really an ActionEvent Key
257 */
258 protected boolean considerKey(String key, ParameterParser pp)
259 {
260 if (!submitValueKey)
261 {
262 log.debug("No Value required, accepting " + key);
263 return true;
264 }
265 else
266 {
267
268
269
270
271
272
273
274
275
276 String keyValue = pp.getString(key);
277 log.debug("Key Value is " + keyValue);
278 if (StringUtils.isEmpty(keyValue))
279 {
280 log.debug("Key is empty, rejecting " + key);
281 return false;
282 }
283
284 try
285 {
286 if (Integer.parseInt(keyValue) != 0)
287 {
288 log.debug("Integer != 0, accepting " + key);
289 return true;
290 }
291 }
292 catch (NumberFormatException nfe)
293 {
294
295
296
297 log.debug("Not a number, accepting " + key);
298 return true;
299 }
300 }
301 log.debug("Rejecting " + key);
302 return false;
303 }
304 }