Date: 2004-09-24T12:00:34
   Editor: MichaelMcGrady <[EMAIL PROTECTED]>
   Wiki: Apache Struts Wiki
   Page: StrutsCatalogFiveMultipleButtonSolutions
   URL: http://wiki.apache.org/struts/StrutsCatalogFiveMultipleButtonSolutions

   no comment

Change Log:

------------------------------------------------------------------------------
@@ -1,741 +1 @@
-##language:en
-== PREFACE ==
-
-=== Notes ===
-
-This presentation is somewhat detailed, so we begin with a Table of Contents for your 
ease in navigating the presentation.  Enjoy!  There is a comment section at the very 
end.
-
-=== Discussion ===
-
-Presently there are two somewhat popular and different solutions to buttons in Struts 
and generally.  They are the ImageButtonBean solution and the DispatchAction 
solutions.  Both suffer from over-engineering in serious ways. 
-
-''Note: Michael's opinion's on this page are his own, and not official Struts project 
guidelines. Of course, this also applies to any other personal opinions expressed on 
the Wiki by anyone, including myself :-) (Steve Raeburn)''
-
-The initial problem was to determine which of multiple tags in a from was pressed, 
e.g.
-
-{{{
-<input type='image' name='update' src='enter.gif'>
-<input type='image' name='delete' src='nuke.gif'>
-<input type='image' name='clear'  src='clear.gif'>
-}}}
-
-Essentially ImageButtonBean utilizes the fact that <input type='image' name='input' 
src='save.gif'> creates two name/value pairs in the request object with suffixes, viz. 
input.x=85 and input.y=93, for example.  Using the Struts framework and reflection, 
this is used to call, for example, new ImageButtonBean().getInput().getX() and, 
'''if''' this is not empty, we know that the input tag was clicked.  The 
ImageButtonBean, unfortunately, requires that at least one object to be created for 
each command in the application, and this is just too heavy a load.  Additionally, 
this solution requires that we use, e.g., "input.x" for method calls when we have a 
simpler solution in just obtaining the prefix to this name in the name/value request 
object pair in the first place.
-
-The DispatchAction solutions are based on using the struts-config.xml to mine the 
value of these tags.  This too is over-engineering and overkill, even though with 
MappingDispatchAction there are different uses for this sort of algorithm.  We provide 
alternative solutions that do all that the DispatchAction classes do without incurring 
their overhead and their coupling to Struts.
-
-There is the further problem of providing some sort of a generic solution.  The work 
on this page is but a small part of a much bigger solution that will soon be 
presented.  I use the following for my buttons:
-{{{
-<crackwillow:image
-  button='Crack_Willow_Chat.gif'
-  mapBean='hkc'
-  bgClrCode='banRtBgClr'
-  txtClrCode='banRtTxtClr'
-  font='Edwardian Script ITC'
-  italic='false'
-  bold='true'
-  size='30'
-  property='chatSend.dispatch'/>
-}}}
-This tag creates the button Crack_Willow_Chat.gif (which reads "Crack Willow Chat" 
and is localized) dynamically and caches it with the attributes listed.  Eventually 
this will be added to this page.  The main point, however, is that we get rid of the 
over-engineering and obtain "input" from "input.x" rather directly.  For those who 
like the ImageButtonBean approach, more efficient ways to do this sort of thing are 
presented on this page, requiring only a single button that exists only for the life 
of a call, for example.
-
-Please do add your ideas at the end.  Because much more is coming to this page, not 
putting something in the middle would be appreciated.
-
-'''-- Michael !McGrady '''
-
-== Table of Contents ==
-{{{
-1     PREFACE
-1.1   Notes
-1.2   Discussion
-2     Table of Contents
-3     PROBLEM: Universal Button Solutions: --------------- PROBLEM
-      SOLUTIONS: ----------------------------------------- SOLUTIONS
-      I.   Action Solution
-             A.   DispatchUtil (SOLUTION ONE)
-             B.  (new) DispatchAction (SOLUTION TWO)
-      II.  Non-Struts Solution
-             ButtonTagUtil (SOLUTION THREE)
-      III. ActionForm Solution
-             ButtonForm (SOLUTION FOUR)
-      IV.  Button Solution
-             Button (SOLUTION FIVE)
-4     Tag Uses
-4.1   Action Class Code
-4.2   Mulitiple Image Tags
-4.3   Link Tags
-4.4   Submit Tags
-4.5   File Browse Tags
-4.6   Button Tags
-4.7   struts-config.xml Uses
-4.7.1 MappingDispatchAction
-4.7.2 LookupDispatchAction
-5     SOLUTION ONE: DispatchUtil Code -------------------- SOLUTION ONE
-6     (new) DispatchAction
-6.1   Discussion
-6.2   SOLUTION TWO: (new) DispatchAction Code ------------ SOLUTION TWO
-7     PAO (Plain Action Object): Solution for Tags without Dispatch, without 
Reflection, without ActionMapping or Even without Struts
-7.1   Discussion
-7.2   SOLUTION THREE: ButtonTagUtil Code ----------------- SOLUTION THREE
-8.    ButtonForm
-8.1   Discussion
-8.1.1 Only One Button Object (Immediately Destroyed)
-8.1.2 Button Command Determined by Button Name and not getX() or getY()
-8.1.3 Clean Up and Reset of the Action Form Occasioned by getX() or getY()
-8.2   SOLUTION FOUR: ButtonForm Code --------------------- SOLUTION FOUR
-9     Button
-9.1   Discussion
-9.2   SOLUTION FIVE: Button Code ------------------------- SOLUTION FIVE
-10    Links
-11    Comments (Peanut Gallery)
-12    Author
-13    Other Solutions
-13.1  Links to Other Solutions
-13.2  Let's Hear from You
-
-}}}
-== Universal Button Solutions ==
-
-There is a persistent problem in web applications in handling multiple buttons, and 
this is especially a problem when the buttons are images.  
-
-
-Struts has provided a solution for this with DispatchAction and its progeny.  I have 
found this solution to be too heavy, too complicated, and too obtuse.  So, I have 
engendered my own.
-
-
-The principle reason for this class is the problem with <input type='image' 
name='whatever'> tags in HTML.  Those tag send the values in the name attribute as 
''whatever.x=9'' and ''whatever.y=26''.  DispatchAction and its progeny provide a 
melange of solutions but no common way to deal with ''<a href='whatever.do'>'', 
''<input type='submit'>'', ''<input type='image'>'', ''<input type='file'>'', or 
whatever.  This class, DispatchUtil does that.  If you prefer to use a subclass, just 
provide the functionality in this class in a class extending Action.  '''Herbert 
Rabago''' came up with the idea of providing the functionality in a utility class and 
I really like that idea.  So, here we go.
-
-== Uses ==
-
-'''N.B. the uses of "dispatch".'''  If you do not like using "dispatch", then you can 
rewrite the code in the getMethodName(...) method.  Essentially, you will need to 
replace ".dispatch" with something else in the tag names which you can identify in the 
enumeration.  You have to use a suffix with this logic. But, if you want a prefix, 
e.g. "dispatch.update" or "method.update", then you will have to change the logic too. 
 Suffix logic seems to best fit the existing methods in String.  Otherwise, I would 
have used a prefix.  You may want to anyway.
-
-=== Action Class Code to Use ===
-
-In all the following tag uses, you use the Action class code in execute(...) or 
process(...) as given directly below.
-
-{{{
-  ActionForward forward = new 
DispatchUtil().dispatch(this,mapping,form,request,response);
-}}}
-
-This means that the above code goes into the execute(...) or process(...) methods, 
whichever of one of those you use in your Action class.  The methods for various 
method names are, of course, separate.
-
-=== Multiple Image Tags ===
-
-Multiple image tags are the primary reason for this class.  Integrating this into a 
common solution is the difficulty.
-{{{
-<input type='image' name='update.dispatch' src='update.gif'>
-<input type='image' name='delete.dispatch' src='delete.gif'>
-}}}
-=== Link Tags ===
-{{{
-<a href='profile.do?update.dispatch=whatever'>
-<a href='profile.do?delete.dispatch=whatever'>
-}}}
-=== Submit Tags ===
-{{{
-<input type='submit' name='update.dispatch' value='whatever'>
-<input type='submit' name='delete.dispatch' value='whatever'>
-}}}
-=== File Browse Tags ===
-{{{
-<input type='file' name='update.dispatch'>
-<input type='file' name='delete.dispatch'>
-}}}
-=== Button Tags ===
-{{{
-<input type='button' name='update.dispatch' value='whatever'>
-<input type='button' name='delete.dispatch' value='whatever'>
-}}}
-=== struts-config.xml Uses ===
-
-If you like to do things the way they are done with DispatchActions and use Struts 
xml for commands, then the following are possibilities.  '''Please note that you never 
have to do this.  Struts xml configuration is wholly voluntary for this solution.'''  
If you want the sort of behavior you get with MappingDispatchAction using 
DispatchUtil, then you do as we indicate.  This does not mean, of course, that you 
have to use MappingDispatchAction.  DispatchUtil completely absorbs all the 
functionality of the DispatchActions.  You will see, further, that no 
struts-config.xml configuration is required at all for LookupDispatchActions.
-
-==== MappingDispatchAction ====
-
-This solution depends not on the value of the name attributes in the html tags, but 
strictly on the value of the path attributes in the html tags, anytime 
/updateProfile.do is called in the example below, any name attributes will be 
overridden and the value of the ActionMapping property called "parameter" will be 
operative instead.
-
-{{{<action path="/updateProfile" 
-          type="org.example.ProfileAction" 
-          parameter="update.dispatch"
-          name="profileForm" 
-          validate="true" 
-          input="/editProfile.jsp" 
-          scope="request">
-      <forward name="success" path="/updatedProfile.jsp"/>
-  </action>
-
-  <action path="/deleteSubscription" 
-          type="org.example.ProfileAction"
-          name="profileForm"
-          scope="request"
-          input="/subscription.jsp"
-          parameter="delete.dispatch">
-      <forward name="success" path="/deletedSubscription.jsp"/>
-  </action>
-}}}
-==== LookupDispatchAction ====
-
-This solution depends not on the value of the path attribute or the parameter 
property in the ActionMapping, notice that no parameter attribute exists below, but 
strictly on the values of the name attributes as indicated in our section on the tags. 
 In other words, we don't have to configure struts-config.xml for what used to be 
LookupDispatchActions.
-
-{{{
-<action path="/updateProfile" 
-          type="org.example.ProfileAction" 
-          name="profileForm" 
-          validate="true" 
-          input="/editProfile.jsp" 
-          scope="request">
-      <forward name="success" path="/updatedProfile.jsp"/>
-  </action>
-
-  <action path="/deleteSubscription" 
-          type="org.example.ProfileAction"
-          name="profileForm"
-          scope="request"
-          input="/subscription.jsp"
-      <forward name="success" path="/deletedSubscription.jsp"/>
-  </action>
-}}}
-
-== SOLUTON ONE: DispatchUtil Solution Code ==
-{{{
-package com.crackwillow.struts.util.dispatch;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Enumeration;
-import java.util.HashMap;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.struts.action.Action;
-import org.apache.struts.action.ActionForm;
-import org.apache.struts.action.ActionForward;
-import org.apache.struts.action.ActionMapping;
-import org.apache.struts.util.MessageResources;
-
-public class DispatchUtil {
-  protected static    Log              log      = 
LogFactory.getLog(DispatchUtil.class);
-  protected static    MessageResources messages = 
MessageResources.getMessageResources ("org.apache.struts.actions.LocalStrings");
-  protected           HashMap          methods  = new HashMap();
-  protected           Class []         types    = { ActionMapping.class,
-                                                   ActionForm.class,
-                                                   HttpServletRequest.class,
-                                                   HttpServletResponse.class };
-
-  public ActionForward dispatch(Action action,
-                               ActionMapping mapping,
-                               ActionForm form,
-                                HttpServletRequest request,
-                               HttpServletResponse response)
-                       throws Exception {
-    String methodName  = getMethodName(request,mapping);
-    Class  clazz       = action.getClass();
-    if ("execute".equals(methodName) || "perform".equals(methodName)){
-      // Prevent recursive calls
-      String message = messages.getMessage("dispatch.recursive", mapping.getPath());
-      log.error(message);
-      throw new ServletException(message);
-    }
-    return dispatchMethod(action,clazz,mapping, form, request, response, methodName);
-  }
-
-  protected ActionForward dispatchMethod(Action action,
-                                        Class clazz,
-                                        ActionMapping mapping,
-                                        ActionForm form,
-                                         HttpServletRequest request,
-                                        HttpServletResponse response,
-                                        String name)
-                       throws Exception {
-    if (name == null) {
-      return this.unspecified(mapping, form, request, response);
-    }
-
-    Method method = null;
-
-    try {
-      method = getMethod(clazz,name);
-    } catch(NoSuchMethodException nsme) {
-      String message = messages.getMessage("dispatch.method", mapping.getPath(), 
name);
-      log.error(message, nsme);
-      throw nsme;
-    }
-
-    ActionForward forward = null;
-
-    try {
-      Object args[] = { mapping, form, request, response };
-      forward = (ActionForward)method.invoke(action, args);
-    } catch(ClassCastException cce) {
-      String message = messages.getMessage("dispatch.return", mapping.getPath(), 
name);
-      log.error(message, cce);
-      throw cce;
-    } catch(IllegalAccessException iae) {
-      String message = messages.getMessage("dispatch.error", mapping.getPath(), name);
-      log.error(message, iae);
-      throw iae;
-    } catch(InvocationTargetException ite) {
-      Throwable t = ite.getTargetException();
-      if (t instanceof Exception) {
-        throw ((Exception) t);
-      } else {
-        String message = messages.getMessage("dispatch.error", mapping.getPath(), 
name);
-        log.error(message, ite);
-        throw new ServletException(t);
-      }
-    }
-    return (forward);
-  }
-
-  protected static String getMethodName(HttpServletRequest request,  ActionMapping 
mapping) {
-    String methodName  = null;
-    String buttonValue = null;
-    String paramProperty = mapping.getParameter();
-    if((paramProperty != null)) {
-      methodName = paramProperty.substring(0,paramProperty.indexOf('.'));
-    } else {
-      Enumeration enum = request.getParameterNames();
-      while(enum.hasMoreElements()) {
-        buttonValue = (String)enum.nextElement();
-       if(buttonValue.indexOf(".dispatch") >= 0) {
-         methodName = buttonValue;
-         break;
-        }
-      }
-    }
-    return methodName.substring(0,methodName.indexOf('.'));
-  }
-
-  protected Method getMethod(Class clazz,String name)
-      throws NoSuchMethodException {
-    synchronized(methods) {
-      Method method = (Method) methods.get(name);
-
-      if (method == null) {
-        method = clazz.getMethod(name, types);
-        methods.put(name, method);
-      }
-
-      return (method);
-    }
-  }
-
-  protected ActionForward unspecified(ActionMapping mapping,
-                                     ActionForm form,
-                                     HttpServletRequest request,
-                                     HttpServletResponse response)
-                       throws Exception {
-    String message = messages.getMessage( "dispatch.parameter", mapping.getPath(), 
getMethodName(request,mapping));
-    log.error(message);
-
-    throw new ServletException(message);
-  }
-}
-}}} 
-
-== (new) DispatchAction (Called "SimpleDispatchAction" on the Struts Lists) ==
-
-=== Discussion ===
-
-If, rather than having a utility class provide the functionality, you prefer an 
Action class.  Here is a redoing of the DispatchAction class to let you do that.  You 
use this class for '''ALL''' the progeny of DispatchAction class, i.e. this class 
replaces completely DispatchAction, LookupDispatchAction, and MappingDispatchAction.  
You can alter this class to get whatever subsidiary functionality you might want and 
which some might consider essential.
-
-=== SOLUTION TWO: (new) DispatchAction Solution Code ===
-{{{
-package com.crackwillow.struts.action;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Enumeration;
-import java.util.HashMap;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.struts.action.Action;
-import org.apache.struts.action.ActionForm;
-import org.apache.struts.action.ActionForward;
-import org.apache.struts.action.ActionMapping;
-import org.apache.struts.util.MessageResources;
-
-public abstract class DispatchAction
-    extends Action {
-  protected           Class            clazz    = this.getClass();
-  protected static    Log              log      = 
LogFactory.getLog(DispatchAction.class);
-  protected static    MessageResources messages = 
MessageResources.getMessageResources ("org.apache.struts.actions.LocalStrings");
-  protected           HashMap          methods  = new HashMap();
-  protected           Class[]          types    = { ActionMapping.class,
-                                                    ActionForm.class,
-                                                    HttpServletRequest.class,
-                                                    HttpServletResponse.class };
-
-  public ActionForward execute(ActionMapping mapping,
-                               ActionForm form,
-                               HttpServletRequest request,
-                               HttpServletResponse response)
-      throws Exception {
-    String name = getMethodName(request,mapping);
-
-    if ("execute".equals(name) || "perform".equals(name)){
-      // Prevent recursive calls
-      String message = messages.getMessage("dispatch.recursive", mapping.getPath());
-      log.error(message);
-      throw new ServletException(message);
-    }
-
-    return dispatchMethod(mapping, form, request, response, name);
-  }
-
-  protected ActionForward dispatchMethod(ActionMapping mapping,
-                                         ActionForm form,
-                                         HttpServletRequest request,
-                                         HttpServletResponse response,
-                                         String name)
-      throws Exception {
-
-    if (name == null) {
-      return this.unspecified(mapping, form, request, response);
-    }
-
-    Method method = null;
-
-    try {
-      method = getMethod(name);
-    } catch(NoSuchMethodException nsme) {
-      String message = messages.getMessage("dispatch.method", mapping.getPath(), 
name);
-      log.error(message, nsme);
-      throw nsme;
-    }
-
-    ActionForward forward = null;
-
-    try {
-      Object args[] = {mapping, form, request, response};
-      forward = (ActionForward) method.invoke(this, args);
-    } catch(ClassCastException cce) {
-      String message = messages.getMessage("dispatch.return", mapping.getPath(), 
name);
-      log.error(message, cce);
-      throw cce;
-    } catch(IllegalAccessException iae) {
-      String message = messages.getMessage("dispatch.error", mapping.getPath(), name);
-      log.error(message, iae);
-      throw iae;
-
-    } catch(InvocationTargetException ite) {
-      Throwable t = ite.getTargetException();
-
-      if (t instanceof Exception) {
-        throw ((Exception) t);
-      } else {
-        String message = messages.getMessage("dispatch.error", mapping.getPath(), 
name);
-        log.error(message, ite);
-        throw new ServletException(t);
-      }
-    }
-    return (forward);
-  }
-
-  protected static String getMethodName(HttpServletRequest request,  ActionMapping 
mapping) {
-    String methodName  = null;
-    String buttonValue = null;
-    String paramProperty = mapping.getParameter();
-    if((paramProperty != null)) {
-      methodName = paramProperty.substring(0,paramProperty.indexOf('.'));
-    } else {
-      Enumeration enum = request.getParameterNames();
-      while(enum.hasMoreElements()) {
-        buttonValue = (String)enum.nextElement();
-        if(buttonValue.indexOf(".dispatch") >= 0) {
-          methodName = buttonValue;
-          break;
-        }
-      }
-    }
-    return methodName.substring(0,methodName.indexOf('.'));
-  }
-
-  protected Method getMethod(String name)
-      throws NoSuchMethodException {
-    synchronized(methods) {
-      Method method = (Method) methods.get(name);
-
-      if (method == null) {
-        method = clazz.getMethod(name, types);
-        methods.put(name, method);
-      }
-
-      return (method);
-    }
-  }
-
-  protected ActionForward unspecified(ActionMapping mapping,
-                                      ActionForm form,
-                                      HttpServletRequest request,
-                                      HttpServletResponse response)
-      throws Exception {
-    String message = messages.getMessage( "dispatch.parameter", mapping.getPath(), 
getMethodName(request,mapping));
-    log.error(message);
-
-    throw new ServletException(message);
-  }
-
-  protected ActionForward cancelled(ActionMapping mapping,
-                                    ActionForm form,
-                                    HttpServletRequest request,
-                                    HttpServletResponse response)
-      throws Exception {
-
-    return null;
-  }
-}
-}}}
-
-== POJO: Solution for Tags without Dispatch, without Reflection, without 
ActionMapping or Even without Struts ==
-
-=== Discussion ===
-
-You can use the same strategy to determine what button tag has been clicked without 
employing Struts.  The best way to do this, in my opinion, in the following solution.  
This solution is completely superior to any of the uses of reflection through 
ActionForms with button objects.
-
-=== SOLUTION THREE: ButtonTagUtil Solution Code ===
-{{{
-public class ButtonTagUtil {
-  public static String getName(HttpServletRequest request) {
-    String methodName  = null;
-    String buttonValue = null;
-    Enumeration enum = request.getParameterNames();
-    while(enum.hasMoreElements()) {
-      buttonValue = (String)enum.nextElement();
-      if(buttonValue.indexOf(".dispatch") >= 0) {
-        methodName = buttonValue;
-        break;
-      }
-    }
-    return methodName.substring(0,methodName.indexOf('.'));
-  }
-}
-}}}
-
-== ButtonForm ==
-Note that this solution does not use ".dispatch" and is not tied to Struts
-{{{
-<input type='image' name='update' src='update.gif'>
-<input type='image' name='delete' src='delete.gif'>
-}}}
-=== Discussion ===
-This solution is for <input type='image'> only.  THIS SOLUTION IS FAR SUPERIOR TO 
ImageButtonBean for lots of reasons.  Remember that if we have "update" as the name, 
ImageButtonBean, a solution I used for a long time, finds out that "update" is the 
command because it determines that the result of getting the following by refection is 
not null: if(new ImageButtonBean().getUpdate().getX() != null).  This is clearly not a 
good idea, because we should just take the value of "update" used to call getUpdate() 
and be done with it.  However, if you just have to use buttons, the following solution 
is much more sophisticated.  This solutions has several advantages to ImageButtonBean. 
 They are:
-
-==== Only One Button Object ====
-There is only one button object created and it is sent for garbage collection 
immediately upon being created.
-==== The Button Command is Determined Prior to Calling getX() or getY() ====
-The use of "update" to call getUpdate() determines the command to use without using 
the getX() or getY().  You do not have to check for nulls, etc.
-==== The "Free" Use of .x for getX() or .y for getY() is Used for Clean Up and 
Resetting the ActionForm ====
-The occurence of getX() or getY() is used as the basis for cleanup and resetting the 
form.  
-
-So, while button solutions are poor in general (too heavy, too slow, too 
over-engineered) this is better than the existing solutions.
-
-This solution is superior to the Button solution which follows and is recommended, 
because only one button object has to be created.  If you use buttons for navigation 
extensively within large forms, this because crucial.  
-
-=== SOLUTION FOUR: ButtonForm Code ===
-{{{
-public class ButtonForm
-    extends ActionForm {
-  protected CrackWillowButton button;
-  protected String command;
-
-  public CrackWillowButton getButton() {
-    if(button == null) { this.button = new CrackWillowButton(); }
-    return button;
-  }
-
-  public String getCommand() {
-    String hold = command; command = null;
-    return hold;
-  }
-
-  public void reset() {
-    button = null;
-  }
-
-  public class CrackWillowButton {
-    private Integer x, y;
-    public CrackWillowButton() { }
-    public CrackWillowButton getSubmit() { command = "submit";    return button; }
-    public CrackWillowButton getClear()  { command = "clear";    return button; }
-
-    public void setX(Integer x) {
-      if(y != null) { reset(); }
-      else          { this.x = x; }
-    }
-
-    public void setY(Integer y) {
-      if(x != null) { reset(); }
-      else          { this.y = y; }
-    }
-  }
-} ///;-) 
-}}}
-== Button ==
-This solution, too, is only for <input type='image'>
-=== Discussion ===
-This solution is as objectionable to me as (old) DispatchAction in Struts and its 
progeny, MappingDispatchAction and LookupDispatchAction.  However, it is the first 
solution I proposed on this wiki, so I am putting it in here as a quasi-historical 
brain matter for those with quasi-hysterical brain matter.
-
-=== SOLUTION FIVE: Button Code ===
-First we have the button:
-{{{
-public final class ButtonCommand {
-  private ButtonCommand() {
-  }
-
-  public final static String getCommand(HttpServletRequest request) {
-    Enumeration enum = request.getParameterNames();
-    String parameterName = null;
-    while(enum.hasMoreElements()) {
-      parameterName = (String)enum.nextElement();
-      if(parameterName.endsWith(".x")) {
-        return parameterName.substring(0,parameterName.indexOf('.'));
-      }
-    }
-    return parameterName;
-  }
-}
-}}}
-Then we have the form:
-{{{
-public class ButtonForm
-    extends ActionForm {
-  protected CrackWillowButton button;
-  protected String command;
-
-  public CrackWillowButton getButton() {
-    if(button == null) { this.button = new CrackWillowButton(); }
-    return button;
-  }
-
-  public String getCommand() {
-    String hold = command; command = null;
-    return hold;
-  }
-
-  public void reset() {
-    button = null;
-  }
-
-  public class CrackWillowButton {
-    private Integer x, y;
-    public CrackWillowButton() { }
-    public CrackWillowButton getSubmit() { command = "submit";    return button; }
-    public CrackWillowButton getClear()  { command = "clear";    return button; }
-
-    public void setX(Integer x) {
-      if(y != null) { reset(); }
-      else          { this.x = x; }
-    }
-
-    public void setY(Integer y) {
-      if(x != null) { reset(); }
-      else          { this.y = y; }
-    }
-  }
-} ///;-) 
-}}}
-In the Action class, we check out which button type has been pressed as follows:
-{{{
-  String command = buttonForm.getCommand();
-  if("submit".equals(command)) {
-    // do whatever
-  } else if ("clear".equals(command)) {
-    // do whatever
-  } else {
-    // some assert code
-  }
-}}}
-
-== Links ==
-
-StrutsCatalogInstrumentableForms
-
-
-StrutsCatalogMappedBeans
-
-["StrutsCatalogHidingImagesAndOtherResourcesUnderWEBINF"]
-== Comments ==
-
-CommentPageForCritiques
-
-Thank you for using the above link for comments instead of this page, since there is 
no easy way to accommodate everyone's style in one place.
-
-== AUTHOR ==
-
-
-If you have any questions contact me at [EMAIL PROTECTED] .
-
-
-'''Michael !McGrady'''
-
-
-If your comments are extensive, you might want to provide a link instead of adding to 
this already incredibly verbose presentation.
-
-== SOLUTION SIX AND BEYOND: Links to Other Solutions ==
-
-=== Links to Other Solutions ===
-
-==== Use an ImageButtonBean to represent an image button ====
-
-
-A member of the list has presented the following alternative solution to the point of 
this page.
-
-
-From Ted Husted's Tips: [http://www.husted.com/struts/tips/001.html Use an 
ImageButtonBean to represent an image button]
-
-
-'''COMMENT:'''This is an old heavy solution (in Michael's opinion) which was proposed 
here originally as an option to Ted's solution using JavaScript.  The guts of this 
solution came from '''Antonio Lagnada''' 
-
-
-    http://j2ee-01.lagnada.com/struts/html-buttons.htm
-
-and was credited at 
-
-    
http://wiki.apache.org/struts/StrutsCatalogMultipleImageButtonsWithNoJavaScript?action=recall&date=1083359181
 
-
-
-This solution uses the update.x nature of the name in the request parameters 
name/value pair to get a [imageButtonBean].getName().getX() rather than finding a 
"field" (name or request parameter) with suffix of .x.  The solutions are equivalent 
except that this one creates an object for each command you need.  
-
-
-This solution requires that you create a button for each command and that 
proliferates the button objects.  
-
-
-The ImageButtonBean sort of solution has better options, and they are presented on 
this page, using only one button in its best form and less in the second best form.  
The button solutions themselves, including especially the one called ImageButtonBean 
are inferior.  My testing, at any rate, indicates that this solution is super slow 
relatively speaking.  
-
-
-Additionally, you can, you might note, use "update.firstChoice", 
"update.secondChoice", etc. and do fancier things with this pattern.  I first awoke 
from my dogmatic slumber on these buttons when '''Larry Young''' noted that there was 
no need to go with all these button objects.  He has his own better solution too.  If 
you look at the actual use of this pattern, it is not particularly inviting.  Larry 
was right and I hope the work I have done on his prompting is worth someone's time. 
-
--- Michael !McGrady
-
-
-==== Image Button Bean Manager ====
-
-
-A member of the Struts list has also presented the following solution and extension 
of the ImageButtonBean solution.
-
-
-Open Source Struts Extension 
[http://kenfitzpatrick.dnsalias.org:8080/imageButtonBeanManager Image Button Bean 
Manager]
-
-
-'''COMMENT:''' This solution is, I would suggest, seriously over-engineered.
-
-
-However, there is one thing about this solution I really, really, really like.  That 
thing is that it creates an extension to struts rather than trying to be part of 
struts itself.  This is the way to go, in my opinion.  I admire this restraint and 
good sense.  
-
-
-And, who knows, maybe I am wrong about this solution.  I don't think so, but the nice 
things about extensions is that it leaves everyone a choice rather than having to 
either do triage to the struts application or having to accept solutions you don't 
like.  
-
-
-Unfortunately, this solution not only embraces the difficulties inherent in 
ImageButtonBean but also combines that with DispatchAction and its progeny, which have 
the same problem in a different way. 
-
--- Michael !McGrady
-
-=== Let's Hear From You Too ===
+See a presentation newer ideas, i.e. not !DispatchAction and its progeny or 
!ImageButtonBean, at [http://www.michaelmcgrady.com/]  Just click on the struts 
buttons link.  Thanks!  Michael !McGrady

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to