001: package org.apache.turbine.modules;
002:
003: /*
004: * Copyright 2001-2005 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License")
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.util.Iterator;
022:
023: import org.apache.commons.lang.StringUtils;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import org.apache.turbine.Turbine;
029: import org.apache.turbine.TurbineConstants;
030: import org.apache.turbine.util.RunData;
031: import org.apache.turbine.util.parser.ParameterParser;
032: import org.apache.turbine.util.parser.ParserUtils;
033:
034: /**
035: * <p>
036: *
037: * This is an alternative to the Action class that allows you to do
038: * event based actions. Essentially, you label all your submit buttons
039: * with the prefix of "eventSubmit_" and the suffix of "methodName".
040: * For example, "eventSubmit_doDelete". Then any class that subclasses
041: * this class will get its "doDelete(RunData data)" method executed.
042: * If for any reason, it was not able to execute the method, it will
043: * fall back to executing the doPeform() method which is required to
044: * be implemented.
045: *
046: * <p>
047: *
048: * Limitations:
049: *
050: * <p>
051: *
052: * Because ParameterParser makes all the key values lowercase, we have
053: * to do some work to format the string into a method name. For
054: * example, a button name eventSubmit_doDelete gets converted into
055: * eventsubmit_dodelete. Thus, we need to form some sort of naming
056: * convention so that dodelete can be turned into doDelete.
057: *
058: * <p>
059: *
060: * Thus, the convention is this:
061: *
062: * <ul>
063: * <li>The variable name MUST have the prefix "eventSubmit_".</li>
064: * <li>The variable name after the prefix MUST begin with the letters
065: * "do".</li>
066: * <li>The first letter after the "do" will be capitalized and the
067: * rest will be lowercase</li>
068: * </ul>
069: *
070: * If you follow these conventions, then you should be ok with your
071: * method naming in your Action class.
072: *
073: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens </a>
074: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
075: * @author <a href="quintonm@bellsouth.net">Quinton McCombs</a>
076: * @version $Id: ActionEvent.java 292717 2005-09-30 12:56:23Z seade $
077: */
078: public abstract class ActionEvent extends Action {
079: /** Logging */
080: protected Log log = LogFactory.getLog(this .getClass());
081:
082: /** Constant needed for Reflection */
083: private static final Class[] methodParams = new Class[] { RunData.class };
084:
085: /**
086: * You need to implement this in your classes that extend this class.
087: *
088: * @param data Turbine information.
089: * @exception Exception a generic exception.
090: */
091: public abstract void doPerform(RunData data) throws Exception;
092:
093: /** The name of the button to look for. */
094: protected static final String BUTTON = "eventSubmit_";
095: /** The length of the button to look for. */
096: protected static final int BUTTON_LENGTH = BUTTON.length();
097: /** The prefix of the method name. */
098: protected static final String METHOD_NAME_PREFIX = "do";
099: /** The length of the method name. */
100: protected static final int METHOD_NAME_LENGTH = METHOD_NAME_PREFIX
101: .length();
102: /** The length of the button to look for. */
103: protected static final int LENGTH = BUTTON.length();
104:
105: /**
106: * If true, the eventSubmit_do<xxx> variable must contain
107: * a not null value to be executed.
108: */
109: private boolean submitValueKey = false;
110:
111: /**
112: * C'tor
113: */
114: public ActionEvent() {
115: super ();
116:
117: submitValueKey = Turbine.getConfiguration().getBoolean(
118: TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_KEY,
119: TurbineConstants.ACTION_EVENTSUBMIT_NEEDSVALUE_DEFAULT);
120:
121: log
122: .debug(submitValueKey ? "ActionEvent accepts only eventSubmit_do Keys with a value != 0"
123: : "ActionEvent accepts all eventSubmit_do Keys");
124: }
125:
126: /**
127: * This overrides the default Action.perform() to execute the
128: * doEvent() method. If that fails, then it will execute the
129: * doPerform() method instead.
130: *
131: * @param data Turbine information.
132: * @exception Exception a generic exception.
133: */
134: protected void perform(RunData data) throws Exception {
135: try {
136: executeEvents(data);
137: } catch (NoSuchMethodException e) {
138: doPerform(data);
139: }
140: }
141:
142: /**
143: * This method should be called to execute the event based system.
144: *
145: * @param data Turbine information.
146: * @exception Exception a generic exception.
147: */
148: public void executeEvents(RunData data) throws Exception {
149: // Name of the button.
150: String theButton = null;
151: // Parameter parser.
152: ParameterParser pp = data.getParameters();
153:
154: String button = pp.convert(BUTTON);
155: String key = null;
156:
157: // Loop through and find the button.
158: for (Iterator it = pp.keySet().iterator(); it.hasNext();) {
159: key = (String) it.next();
160: if (key.startsWith(button)) {
161: if (considerKey(key, pp)) {
162: theButton = formatString(key);
163: break;
164: }
165: }
166: }
167:
168: if (theButton == null) {
169: throw new NoSuchMethodException(
170: "ActionEvent: The button was null");
171: }
172:
173: try {
174: Method method = getClass().getMethod(theButton,
175: methodParams);
176: Object[] methodArgs = new Object[] { data };
177:
178: if (log.isDebugEnabled()) {
179: log.debug("Invoking " + method);
180: }
181:
182: method.invoke(this , methodArgs);
183: } catch (InvocationTargetException ite) {
184: Throwable t = ite.getTargetException();
185: if (t instanceof Exception) {
186: throw (Exception) t;
187: } else {
188: throw ite;
189: }
190: } finally {
191: pp.remove(key);
192: }
193: }
194:
195: /**
196: * This method does the conversion of the lowercase method name
197: * into the proper case.
198: *
199: * @param input The unconverted method name.
200: * @return A string with the method name in the proper case.
201: */
202: protected final String formatString(String input) {
203: String tmp = input;
204:
205: if (StringUtils.isNotEmpty(input)) {
206: tmp = input.toLowerCase();
207:
208: // Chop off suffixes (for image type)
209: input = (tmp.endsWith(".x") || tmp.endsWith(".y")) ? input
210: .substring(0, input.length() - 2) : input;
211:
212: if (ParserUtils.getUrlFolding() != ParserUtils.URL_CASE_FOLDING_NONE) {
213: tmp = input.toLowerCase().substring(
214: BUTTON_LENGTH + METHOD_NAME_LENGTH);
215: tmp = METHOD_NAME_PREFIX + StringUtils.capitalize(tmp);
216: } else {
217: tmp = input.substring(BUTTON_LENGTH);
218: }
219: }
220: return tmp;
221: }
222:
223: /**
224: * Checks whether the selected key really is a valid event.
225: *
226: * @param key The selected key
227: * @param pp The parameter parser to look for the key value
228: *
229: * @return true if this key is really an ActionEvent Key
230: */
231: protected boolean considerKey(String key, ParameterParser pp) {
232: if (!submitValueKey) {
233: log.debug("No Value required, accepting " + key);
234: return true;
235: } else {
236: // If the action.eventsubmit.needsvalue key is true,
237: // events with a "0" or empty value are ignored.
238: // This can be used if you have multiple eventSubmit_do<xxx>
239: // fields in your form which are selected by client side code,
240: // e.g. JavaScript.
241: //
242: // If this key is unset or missing, nothing changes for the
243: // current behaviour.
244: //
245: String keyValue = pp.getString(key);
246: log.debug("Key Value is " + keyValue);
247: if (StringUtils.isEmpty(keyValue)) {
248: log.debug("Key is empty, rejecting " + key);
249: return false;
250: }
251:
252: try {
253: if (Integer.parseInt(keyValue) != 0) {
254: log.debug("Integer != 0, accepting " + key);
255: return true;
256: }
257: } catch (NumberFormatException nfe) {
258: // Not a number. So it might be a
259: // normal Key like "continue" or "exit". Accept
260: // it.
261: log.debug("Not a number, accepting " + key);
262: return true;
263: }
264: }
265: log.debug("Rejecting " + key);
266: return false;
267: }
268: }
|