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