Update of /cvsroot/struts/dialogs/src/net/jspcontrols/dialogs/actions In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19672/src/net/jspcontrols/dialogs/actions Modified Files: DialogAction.java SelectAction.java Added Files: DialogMapping.java DialogRuleSet.java EventForward.java RenderForward.java Log Message: --- NEW FILE: DialogMapping.java --- package net.jspcontrols.dialogs.actions; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionForward; import org.apache.struts.config.ForwardConfig; import java.util.Iterator; /** * Created by IntelliJ IDEA. * User: mjouravlev * Date: Jul 20, 2005 * Time: 12:06:41 PM * To change this template use Options | File Templates. */ public class DialogMapping extends ActionMapping { /** * Path to the default view, usually JSP page. */ protected String view; /** * Returns the path to the default view of a corresponding dialog. */ public String getView() { return (this.view); } /** * @param view the path to the default view of a corresponding dialog. */ public void setView(String view) { if (configured) { throw new IllegalStateException("Configuration is frozen"); } this.view = view; } /** * Return name of the form bean, if any, associated with this Action. */ public String getForm() { return super.getName(); } /** * @param name name of the form bean associated with this Action. */ public void setForm(String name) { super.setName(name); } public DialogMapping() { super.setScope("session"); super.setValidate(false); } public ActionForward reload() { return EventForward.DIALOG_RELOAD; } /** * Return the forward configuration for the specified key, if any; * otherwise return <code>null</code>. * * @param name Name of the forward configuration to return */ public ForwardConfig findForwardConfig(String name) { // Standard Struts quick search for simple event name ForwardConfig forwardConfig = (ForwardConfig) forwards.get(name); if (forwardConfig != null) { return forwardConfig; } // Slow search for multi-name; do not use for heavy-traffic sites. // Will work properly, if mappings are separated (comma, space, whatever) Iterator fwIte = forwards.keySet().iterator(); while (fwIte.hasNext()) { String key = (String) fwIte.next(); if (key.indexOf(name) > -1) { return (ForwardConfig) forwards.get(key); } } return null; } } --- NEW FILE: DialogRuleSet.java --- package net.jspcontrols.dialogs.actions; import org.apache.commons.digester.Digester; import org.apache.commons.digester.RuleSetBase; import org.apache.commons.digester.AbstractObjectCreationFactory; import org.apache.struts.config.ModuleConfig; import org.apache.struts.util.RequestUtils; import org.xml.sax.Attributes; /** * Created by IntelliJ IDEA. * User: mjouravlev * Date: Jul 20, 2005 * Time: 10:07:16 AM * To change this template use Options | File Templates. */ public class DialogRuleSet extends RuleSetBase { // RuleSetBase strutsConfigRuleSet = new ConfigRuleSet(); // // public DialogRuleSet() { // super(); // // } public void addRuleInstances(Digester digester) { digester.addObjectCreate ("struts-config/action-mappings/dialog", "net.jspcontrols.dialogs.actions.DialogMapping"); digester.addSetProperties ("struts-config/action-mappings/dialog"); digester.addSetNext ("struts-config/action-mappings/dialog", "addActionConfig", "org.apache.struts.config.ActionConfig"); digester.addSetProperty ("struts-config/action-mappings/dialog/set-property", "property", "value"); digester.addObjectCreate ("struts-config/action-mappings/dialog/exception", "org.apache.struts.config.ExceptionConfig", "className"); digester.addSetProperties ("struts-config/action-mappings/dialog/exception"); digester.addSetNext ("struts-config/action-mappings/dialog/exception", "addExceptionConfig", "org.apache.struts.config.ExceptionConfig"); digester.addSetProperty ("struts-config/action-mappings/dialog/exception/set-property", "property", "value"); // digester.addFactoryCreate // ("struts-config/action-mappings/dialog/event", // new DialogForwardFactory()); digester.addObjectCreate ("struts-config/action-mappings/dialog/event", "net.jspcontrols.dialogs.actions.EventForward"); digester.addSetProperties ("struts-config/action-mappings/dialog/event"); digester.addSetNext ("struts-config/action-mappings/dialog/event", "addForwardConfig", "net.jspcontrols.dialogs.actions.EventForward"); digester.addSetProperty ("struts-config/action-mappings/action/event/set-property", "property", "value"); digester.addObjectCreate ("struts-config/action-mappings/dialog/render", "net.jspcontrols.dialogs.actions.RenderForward"); digester.addSetProperties ("struts-config/action-mappings/dialog/render"); digester.addSetNext ("struts-config/action-mappings/dialog/render", "addForwardConfig", "net.jspcontrols.dialogs.actions.RenderForward"); digester.addSetProperty ("struts-config/action-mappings/action/render/set-property", "property", "value"); } } --- NEW FILE: EventForward.java --- package net.jspcontrols.dialogs.actions; import org.apache.struts.action.ActionForward; /** * Created by IntelliJ IDEA. * User: mjouravlev * Date: Jul 20, 2005 * Time: 11:21:22 AM * To change this template use Options | File Templates. */ public class EventForward extends ActionForward { public static final ActionForward DIALOG_RELOAD = new ActionForward(); /** * <p>Construct a new instance of an <code>ActionForward</code> * object with values, default for an event forward object: * <code>path</code> is set to null, <code>redirect</code> is * set to true.</p> * * <p>Event forward objects are intended to handle the first phase * of two-phase request processing, thus they use redirect * by default.</p> */ public EventForward() { super(null, true); } /** * <p>Construct a new instance with the specified path.</p> * * @param path Path for this instance */ public EventForward(String path) { super(path, true); } } --- NEW FILE: RenderForward.java --- package net.jspcontrols.dialogs.actions; import org.apache.struts.action.ActionForward; /** * Created by IntelliJ IDEA. * User: mjouravlev * Date: Jul 20, 2005 * Time: 11:25:46 AM * To change this template use Options | File Templates. */ public class RenderForward extends ActionForward { /** * <p>Construct a new instance of an <code>ActionForward</code> * object with values, default for an render forward object: * <code>path</code> is set to null, <code>redirect</code> is * set to false.</p> * * <p>Render forward objects are intended to handle the second phase * of two-phase request processing, thus they use server-side * forwarding by default.</p> */ public RenderForward() { super(null, false); } /** * <p>Construct a new instance with the specified path.</p> * * @param path Path for this instance */ public RenderForward(String path) { super(path, false); } } Index: DialogAction.java =================================================================== RCS file: /cvsroot/struts/dialogs/src/net/jspcontrols/dialogs/actions/DialogAction.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** DialogAction.java 11 Jul 2005 07:13:06 -0000 1.1 --- DialogAction.java 4 Aug 2005 07:39:43 -0000 1.2 *************** *** 95,99 **** * An initialization event is one exception from this rule, and can be * processed as either POST or GET request. Initialization event has ! * a default key, DIALOG-EVENT-INIT.</p> * * <p>After input event is processed, DialogAction redirects to itself and --- 95,99 ---- * An initialization event is one exception from this rule, and can be * processed as either POST or GET request. Initialization event has ! * a default key, DIALOG-EVENT.</p> * * <p>After input event is processed, DialogAction redirects to itself and *************** *** 138,142 **** * This is a default prefix for dialog events.</p> * ! * <p>String DIALOG-EVENT-INIT = "DIALOG-EVENT-INIT";<br> * This is a default name of initialization parameter.</p> * --- 138,142 ---- * This is a default prefix for dialog events.</p> * ! * <p>String DIALOG-EVENT = "DIALOG-EVENT";<br> * This is a default name of initialization parameter.</p> * *************** *** 195,207 **** /** - * Returns the initialization key, or initialization key prefix if - * several init keys are possible. Must start with the same prefix as - * dialog buttons. - */ - protected String getInitKey() { - return "DIALOG-EVENT-INIT"; - } - - /** * Cleans messages stored in the session. Subclasses can override * this method if error messages are stored differently, or under --- 195,198 ---- *************** *** 240,251 **** /** - * Returns suffix, used for action mapping. Default suffix is ".do" - * Needed to properly setup location for dialog reloading. - */ - protected String getActionSuffix() { - return ".do"; - } - - /** * Returns true if request is considered "input" request. By convention * dialog input is sent via POST, result page is loaded via GET. --- 231,234 ---- *************** *** 299,303 **** // an in cookie, isRequestedSessionIdFromURL() returns false, // while isRequestedSessionIdFromCookie() returns true. ! // Is it a bug or is required by spec? return request.isRequestedSessionIdFromURL() && request.isRequestedSessionIdFromCookie(); --- 282,286 ---- // an in cookie, isRequestedSessionIdFromURL() returns false, // while isRequestedSessionIdFromCookie() returns true. ! // Is this a bug or a proper response? return request.isRequestedSessionIdFromURL() && request.isRequestedSessionIdFromCookie(); *************** *** 305,338 **** /** - * Returns the method name, given a prefix and a request object. - * This is a helper method, which should not normally be redefined - * by a subclass. - * - * @param mapping The ActionMapping used to select this instance - * @param form The optional ActionForm bean for this request (if any) - * @param request The HTTP request we are processing - * @param response The HTTP response we are creating - * @param parameter The <code>ActionMapping</code> parameter's name - * Contains prefix of submit button names, or several prefixes - * separated by comma or semicolon. - * - * @return The handler method name. - */ - protected String getMethodName(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response, - String parameter) - throws Exception { - - // If event prefix is not provided in action mapping parameter, - // use default dialog prefix - if (parameter == null || parameter.length() == 0) { - parameter = DialogConstants.DIALOG_EVENT_KEY; - } - return super.getMethodName(mapping, form, request, response, parameter); - } - - /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). --- 288,291 ---- *************** *** 377,395 **** } ! // If mapping with DialogConstants.DIALOG_RELOAD_KEY is not defined, ! // build ActionForward object and redirect to self. Works if ! // actions use default ".do" suffix. ! if (actionForward == null) { ! // Appending ".do" suffix to redirect to itself ! String mappingPath = mapping.getPath(); ! if (!mappingPath.endsWith(getActionSuffix())) { ! mappingPath += getActionSuffix(); ! } ! // Action redirect ! return new ActionForward(DialogConstants.DIALOG_RELOAD_KEY, ! mappingPath, ! true /* REDIRECT */); // Has proper redirecting location --- 330,351 ---- } ! // Handler methods usually return valid ActionForward object. ! // Null is used to signify that On the other hand, null If mapping with DialogConstants.DIALOG_RELOAD_KEY is not defined, ! // build ActionForward object and redirect to self. ! if (actionForward == null ) { ! log.info("Action " + mapping.getPath() + " returned null as forward object"); ! return null; ! } else if (actionForward == EventForward.DIALOG_RELOAD) { ! // Use reload path if defined in <dialog input=... /> ! if (mapping instanceof DialogMapping && mapping.getInput() != null) { ! return new EventForward(mapping.getInput()); ! ! // If no reload path defined in the action mapping, use real ! // address since we are redirecting through browser anyway. ! } else { ! return new EventForward(request.getServletPath()); ! } // Has proper redirecting location *************** *** 406,409 **** --- 362,366 ---- true /* REDIRECT */); } + // View is requested via GET: do not clean anything, show dialog page } else { *************** *** 436,440 **** HttpServletRequest request, HttpServletResponse response) throws Exception { ! return mapping.findForward(DialogConstants.DIALOG_VIEW_KEY); } } --- 393,404 ---- HttpServletRequest request, HttpServletResponse response) throws Exception { ! if (mapping instanceof DialogMapping) { ! String viewPath = ((DialogMapping) mapping).getView(); ! RenderForward forward = new RenderForward(); ! forward.setPath(viewPath); ! return forward; ! } else { ! return mapping.findForward(DialogConstants.DIALOG_VIEW_KEY); ! } } } Index: SelectAction.java =================================================================== RCS file: /cvsroot/struts/dialogs/src/net/jspcontrols/dialogs/actions/SelectAction.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** SelectAction.java 11 Jul 2005 07:13:06 -0000 1.1 --- SelectAction.java 4 Aug 2005 07:39:44 -0000 1.2 *************** *** 27,103 **** import java.util.Map; import java.util.Enumeration; - import java.util.StringTokenizer; /** ! * <p>An abstract <strong>Action</strong> that dispatches ! * an HTTP form submission event to a handler method.</p> * ! * <p>The purpose of this class is processing submission of HTML forms. ! * Unlike <nop>DispatchAction and <nop>LookupDispatchAction, which correlate ! * <code>value</code> attribute of <code>submit</code> form element with ! * handler method, this class uses <code>name</code> attribute. ! * Using <code>name</code> attribute allows to display a user-friendly caption ! * on a submit button. Also, it is possible to change button caption without ! * rebuilding the application.</p> * ! * <p>The subclass must define a map, which correlates submit button names ! * with method names, and handler methods themselves. Each method must have ! * the same signature as <code>execute</code> method. Subclass should not ! * redefine <code>execute</code> method.</p> * ! * <img border="0" align="center" src="doc-files/selectaction.gif" width="485" height="540"> * ! * <p>Using <code>name</code> attribute has a flip side: ! * during reset/populate/validate process, Struts assigns request values ! * to corresponding properties of a form bean. Thus, button keys must be named ! * differently from other request attributes. Also, the dispatch action ! * must recognize situation when no submit buttons were activated.</p> * ! * <p>Therefore, all submit buttons use a prefix in their name. ! * The prefix is specified by <code>parameter</code> property ! * of the <nop>ActionMapping. Make sure that you do not use periods ! * in the prefix and in actual button names, or Struts will try to search ! * and set the nested property and most likely will throw exception.</p> * ! * <p><strong>Example of Usage</strong></p> * ! * <p>The action should be configured in <code>struts‑config.xml</code> ! * file like this:</p> ! * <pre> ! * <action path="/test" ! * type="org.example.MyAction" ! * scope="request" ! * parameter="<b>myapp-submit</b>"/> ! * </pre> ! * <p>where: ! * <ul> ! * <li><code>org.example.MyAction</code> extends <code>SelectAction</code>; ! * <li><code>parameter</code> contains prefix of your choice for all ! * submit buttons on an HTML form. ! * </ul> ! * </p> ! * <p> ! * The names of submit elements should start from the aforementioned prefix, ! * for example: ! * </p> ! * <pre> ! * <form action="http://myhost/myapp/test.do" method="post"> ! * <input type="submit" name="<b>myapp-submit</b>-button-add" value="Add button"/> ! * <input type="submit" name="<b>myapp-submit</b>-button-delete" value="Fancy Delete button"/> ! * </form> ! * </pre> ! * <p>The subclass of <nop>SelectAction class must implement ! * <code>getKeyMethodMap</code> method, which defines mapping from button names ! * to method handlers, like this:</p> ! * <pre> * protected Map getKeyMethodMap() { * Map map = new HashMap(); ! * map.put("myapp-submit-button-add", "add"); ! * map.put("myapp-submit-button-delete", "delete"); * return map; * } ! * </pre> ! * <p>The subclass also must implement the methods themselves. For example:</p> ! * <pre> * public ActionForward add(ActionMapping mapping, * ActionForm form, --- 27,88 ---- import java.util.Map; import java.util.Enumeration; /** ! * <p>An abstract <strong>Action</strong> that dispatches a browser event ! * to a handler method. This class allows to process a form submission using ! * either regular submit button or an image button. It works with regular ! * links as well.</p> ! * <ul> ! * <li>Processes submit events (POST) or link events (GET).</li> ! * <li>Handles pushbuttons, image buttons and regular links in ! * universal fashion.</li> ! * <li>Does not use <code>parameter</code> attribute of action mapping.</li> ! * <li>Allows to set an arbitrary caption for a pushbutton.</li> ! * <li>Button caption can be easily changed at runtime.</li> ! * </ul> * ! * <p><strong>Technical details</strong></p> * ! * <p>Unlike <code>DispatchAction</code> and <code>LookupDispatchAction</code> ! * classes, which correlate <code>value</code> attribute of <code>submit</code> ! * form element with name of a handler method, this class uses <code>name</code> ! * attribute. This allows to display a user-friendly caption on a submit button ! * and to change button caption without redeployment.</p> * ! * <p>The subclass must define a map, which correlates event names with method ! * names, and handler methods themselves. Events names must be different from ! * names of action form properties. Each method must have the same signature ! * as <code>execute</code> method.</p> * ! * <p>By definition, an event is a request parameter, which starts from ! * a known prefix. The prefix is specified in <code>getInitKey</code> method ! * and can be redefined in a subclass. Default prefix is "DIALOG-EVENT".</p> * ! * <p>If you decide to redifine the event prefix, make sure that it does not ! * contain periods. Also, do not use periods for specific event names. Struts ! * may throw an exception taking the name for nested property and trying ! * to set its value.</p> * ! * <p><strong>Usage</strong></p> * ! * <p>Subclass <code>SelectAction</code>, implement <code>getKeyMethodMap</code> ! * method, and map specific events to method handlers. Event name must start ! * with event prefix:</p> ! * <pre><code> * protected Map getKeyMethodMap() { * Map map = new HashMap(); ! * map.put(getInitKey()+"-ADD", "add"); ! * map.put(getInitKey()+"-DELETE", "delete"); ! * map.put(getInitKey()+"-CREATE", "create"); ! * map.put(getInitKey()+"-LOGIN", "login"); * return map; * } ! * </code></pre> ! * <p>Remember that standard <code><html:cancel/></code> button is implicitly ! * mapped to <code>cancelled</code> method. You need to implement this method ! * to receive cancel events.</p> ! * ! * <p>Implement handler methods themselves, for example:</p> ! * <pre><code> * public ActionForward add(ActionMapping mapping, * ActionForm form, *************** *** 108,137 **** * return mapping.findForward("success"); * } * ! * public ActionForward delete(ActionMapping mapping, ! * ActionForm form, ! * HttpServletRequest request, ! * HttpServletResponse response) ! * throws IOException, ServletException { ! * // do delete ! * return mapping.findForward("success"); ! * }</pre> ! * <p> ! * <strong>Notes</strong> * <ul> ! * <li>Subclass should not redefine <code>execute</code> method. * <li>If duplicate values exist for the keys returned by * <code>getKeyMethodMap</code>, the first one will be returned. ! * If no corresponding key is found then an exception will be thrown. ! * <li>Cancel button must be handled by implementing standard ! * <code>cancelled</code> method. * <li>According to HTML specification, at most one submit element is sent * from browser to the server when form is submitted. If form is submitted * without explicitly clicking a button, the result depends on a browser. * Either no buttons are submitted, or the first button defined on the form ! * is submitted. You need to override <code>unspecified</code> method ! * to handle default submits. * </ul> - * </p> * * @author Michael Jouravlev --- 93,123 ---- * return mapping.findForward("success"); * } + * </code></pre> * ! * <p>Use event names in <code>name</code> attribute of submit buttons, or ! * as query paramter for regular links:</p> ! * <pre><code> ! * <form action="/selecttest.do" method="post"> ! * <input type="submit" name="<b>DIALOG-EVENT</b>-ADD" value="Add item"/> ! * <input type="submit" name="<b>DIALOG-EVENT</b>-DELETE" value="Delete item"/> ! * </form> ! * <input type="image" name="<b>DIALOG-EVENT</b>-LOGIN" src="login.gif" value="Log In"> ! * <a href="/selecttest.do?<b>DIALOG-EVENT</b>-CREATE">Create item</a> ! * </code></pre> ! * ! * <p><strong>Notes</strong></p> * <ul> ! * <li>Subclass does not have to overridere <code>execute</code> method. * <li>If duplicate values exist for the keys returned by * <code>getKeyMethodMap</code>, the first one will be returned. ! * If no corresponding key is found then an exception will be thrown.</li> ! * <li>Standard Cancel button must be handled by <code>cancelled</code> method.</li> * <li>According to HTML specification, at most one submit element is sent * from browser to the server when form is submitted. If form is submitted * without explicitly clicking a button, the result depends on a browser. * Either no buttons are submitted, or the first button defined on the form ! * is submitted. If no butons is submitted, <code>unspecified</code> method ! * is called. Override this method to handle default submits.</li> * </ul> * * @author Michael Jouravlev *************** *** 140,151 **** /** ! * Request-key-to-method-name map */ protected Map keyMethodMap = null; /** ! * Builds a request param / method name map. Called only once for ! * each instance of this class. This is a helper method, which ! * should not normally be redefined by a subclass. */ synchronized protected void buildLookupMap() throws ServletException { --- 126,136 ---- /** ! * Map, which correlates events with handler methods */ protected Map keyMethodMap = null; /** ! * Builds an event map. Called only once for each instance of this class. ! * This internal method should not normally be redefined by a subclass. */ synchronized protected void buildLookupMap() throws ServletException { *************** *** 154,158 **** Map keyMethodMap = getKeyMethodMap(); if (keyMethodMap != null) { ! this.keyMethodMap = getKeyMethodMap(); } else { // key/method map is not defined in a subclass --- 139,143 ---- Map keyMethodMap = getKeyMethodMap(); if (keyMethodMap != null) { ! this.keyMethodMap = keyMethodMap; } else { // key/method map is not defined in a subclass *************** *** 175,181 **** /** ! * Returns the method name, given a prefix and a request object. ! * This is a helper method, which should not normally be redefined ! * by a subclass. * * @param mapping The ActionMapping used to select this instance --- 160,189 ---- /** ! * Returns the event prefix. All event names must start with this prefix. ! * Override this method if you want to use prefix different than ! * "DIALOG-EVENT" ! */ ! public String getInitKey() { ! return "DIALOG-EVENT"; ! } ! ! /** ! * Name of request parameter, identifying default event. Used to select ! * a request handler for "default submit" by Internet Explorer. If this ! * parameter is not set in request, then <code>unspecified</code> ! * method is called. ! */ ! public String getDefaultKey() { ! return "DIALOG-DEFAULT-EVENT"; ! } ! ! /** ! * Process the specified HTTP request, and create the corresponding HTTP ! * response (or forward to another web component that will create it). ! * Return an <code>ActionForward</code> instance describing where and how ! * control should be forwarded, or <code>null</code> if the response has ! * already been completed. ! * <p> ! * This method should not normally be redefined by a subclass. * * @param mapping The ActionMapping used to select this instance *************** *** 183,189 **** * @param request The HTTP request we are processing * @param response The HTTP response we are creating ! * @param parameter The <code>ActionMapping</code> parameter's name ! * Contains prefix of submit button names, or several prefixes ! * separated by comma or semicolon. * * @return The handler method name. --- 191,237 ---- * @param request The HTTP request we are processing * @param response The HTTP response we are creating ! * ! * @exception Exception if an exception occurs ! */ ! public ActionForward execute(ActionMapping mapping, ! ActionForm form, ! HttpServletRequest request, ! HttpServletResponse response) ! throws Exception { ! if (isCancelled(request)) { ! ActionForward af = cancelled(mapping, form, request, response); ! if (af != null) { ! return af; ! } ! } ! // Identify the request parameter containing the method name ! String parameter = mapping.getParameter(); ! ! // Get the method's name. This could be overridden in subclasses. ! String name = getMethodName(mapping, form, request, response, parameter); ! ! // Prevent recursive calls ! if ("execute".equals(name) || "perform".equals(name)){ ! String message = messages.getMessage("dispatch.recursive", mapping.getPath()); ! ! log.error(message); ! throw new ServletException(message); ! } ! ! // Invoke the named method, and return the result ! return dispatchMethod(mapping, form, request, response, name); ! ! } ! ! /** ! * Returns the method name, given a request object and event name. ! * This helper method should not normally be redefined by a subclass. ! * ! * @param mapping The ActionMapping used to select this instance ! * @param form The optional ActionForm bean for this request (if any) ! * @param request The HTTP request we are processing ! * @param response The HTTP response we are creating ! * @param parameter The <code>ActionMapping</code> parameter's name; ! * not used by <code>SelectAction</code> * * @return The handler method name. *************** *** 196,274 **** throws Exception { ! // Find request parameter, starting with prefix. For example, ! // "myapp-submit-button-add" parameter fits "myapp-submit" prefix . String keyName = null; Enumeration reqEn = request.getParameterNames(); - outmost: while(reqEn.hasMoreElements()) { String reqName = (String) reqEn.nextElement(); ! StringTokenizer params = new StringTokenizer(parameter, ",; "); ! while (params.hasMoreTokens()) { ! String buttonPrefix = params.nextToken(); ! if (reqName.startsWith(buttonPrefix)) { keyName = reqName; - // If using image button, strip coordinates - int imageButtonXPos = keyName.indexOf(".x"); - if (imageButtonXPos > 0) { - keyName = keyName.substring(0, imageButtonXPos); - } else { - int imageButtonYPos = keyName.indexOf(".y"); - if (imageButtonYPos > 0) { - keyName = keyName.substring(0, imageButtonYPos); - } - } - // For image button there can be two or even three parameters: - // MSIE: name.x=xcoord&name.y=ycoord - // Firefox: name.x=xcoord&name.y=ycoord&name=value - // - // We will use only one parameter. Also notice, that MSIE - // does not pass value of image button. This is another - // good reason for using "name" attribute for dispatching. - break outmost; } } } ! // If button is not be recognized, method will return null. ! // DispatchAction will call "unspecified" method. ! String methodName = null; ! // Button recognized ! if (keyName != null) { ! // Build param name to method map if it is not built yet ! buildLookupMap(); ! // Pull out a method name for request parameter ! Object mappedObj = keyMethodMap.get(keyName); ! if (mappedObj == null || !(mappedObj instanceof String)) { ! // Uncomment this, if this action is included in ! // official Struts bundle; make sure that ! // LocationStrings.properties contains ! // dispatch.keyMethodMap.notstring key ! // String message = messages.getMessage( ! // "dispatch.keyMethodMap.notstring", ! // mapping.getPath(), keyName); ! // Delete this if action is included in Struts bundle ! String message = "Dispatch action " + mapping.getPath() + ! " returned null or non-string method name" + ! " for request key " + keyName; ! throw new ServletException(message); ! } ! methodName = (String) mappedObj; ! return methodName; } ! return methodName; } /** ! * Method which is dispatched to when corresponding handler method ! * is not found for an activated submit button. Subclass should ! * override this method if it wish to provide default behavior different ! * than throwing a ServletException. */ protected ActionForward unspecified(ActionMapping mapping, --- 244,319 ---- throws Exception { ! // "parameter" is not required now; can use standard prefix or ! // redefine it with getInitKey() method ! String buttonPrefix = parameter != null ? parameter : getInitKey(); ! ! // If "parameter" attribute was not defined, look for request key ! // starting from event prefix. Standard event prefix is "DIALOG-EVENT", ! // returned by getInitKey() method. String keyName = null; Enumeration reqEn = request.getParameterNames(); while(reqEn.hasMoreElements()) { String reqName = (String) reqEn.nextElement(); ! if (reqName.startsWith(buttonPrefix)) { ! // If using image button, strip coordinate labels. ! // There can be two or even three parameters for an image button: ! // MSIE: name.x=xcoord&name.y=ycoord ! // Firefox: name.x=xcoord&name.y=ycoord&name=value ! // ! // We will use only one parameter. Also notice, that MSIE ! // does not pass "clean" value of image button. This is another ! // good reason for using "name" attribute for dispatching. ! if (reqName.endsWith(".x") || reqName.endsWith(".y")) { ! keyName = reqName.substring(0, reqName.length() - 2); ! } else { keyName = reqName; } + break; } } ! // Event not recognized, check if it was a default submit. ! if (keyName == null) { ! keyName = request.getParameter(getDefaultKey()); ! } ! // Event not recognized, DispatchAction will call "unspecified" method. ! if (keyName == null) { ! return null; ! } ! // Build param name to method map if it is not built yet ! buildLookupMap(); ! // Pull out a method name for request parameter ! Object mappedObj = keyMethodMap.get(keyName); ! if (mappedObj == null || !(mappedObj instanceof String)) { ! // Uncomment this, if this action is included in ! // official Struts bundle; make sure that ! // LocationStrings.properties contains ! // dispatch.keyMethodMap.notstring key ! // String message = messages.getMessage( ! // "dispatch.keyMethodMap.notstring", ! // mapping.getPath(), keyName); ! // Delete this if action is included in Struts bundle ! String message = "Dispatch action " + mapping.getPath() + ! " returned null or non-string method name" + ! " for request key " + keyName; ! throw new ServletException(message); } ! // Method found ! return (String) mappedObj; } /** ! * A handler method for "default submit" situation, that is when ! * an event is not recognized in a request. Default implementation ! * of this method throws a ServletException. ! * <p> ! * Override this method to provide different behavior. */ protected ActionForward unspecified(ActionMapping mapping, *************** *** 284,303 **** /** ! * Provides the mapping from the <code>name</code> attribute of ! * a submit button to method name. For example: ! * <br><pre> ! * protected Map getKeyMethodMap() { ! * Map map = new HashMap(); ! * map.put("myapp-submit-button-add", "add"); ! * map.put("myapp-submit-button-delete", "delete"); ! * return map; ! * }</pre> ! * This method is must be implemented in a subclass. * ! * @return Map, containing association between submit button name and ! * a handler method. Map keys must start from a prefix, ! * defined in <code>parameter</code> property of the action mapping. Map ! * values must contain names of handler methods. Each handler method must ! * have the same signature as <code>execute</code> method. */ protected abstract Map getKeyMethodMap(); --- 329,341 ---- /** ! * Provides mapping between event names (keys) and handler methods (values). ! * Do not forget to define handler methods themselves. Each handler method ! * must have the same signature as <code>execute</code> method. To handle ! * <code><html:cancel/></code> override <code>cancelled</code> method. ! * <p> ! * This method must be implemented by a subclass. * ! * @return Map, containing association between event names (keys) and ! * handler methods (values). */ protected abstract Map getKeyMethodMap(); |