KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > controller > Transition


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64
65 package com.jcorporate.expresso.core.controller;
66
67 import com.jcorporate.expresso.core.ExpressoConstants;
68 import com.jcorporate.expresso.core.misc.ConfigManager;
69 import com.jcorporate.expresso.core.misc.StringDOMParser;
70 import com.jcorporate.expresso.core.misc.StringUtil;
71 import com.jcorporate.expresso.core.misc.URLUTF8Encoder;
72 import com.jcorporate.expresso.kernel.util.FastStringBuffer;
73 import org.apache.log4j.Logger;
74 import org.apache.struts.config.ActionConfig;
75 import org.w3c.dom.Document JavaDoc;
76 import org.w3c.dom.NamedNodeMap JavaDoc;
77 import org.w3c.dom.Node JavaDoc;
78 import org.w3c.dom.NodeList JavaDoc;
79
80 import javax.servlet.RequestDispatcher JavaDoc;
81 import javax.servlet.http.HttpServletRequest JavaDoc;
82 import javax.servlet.http.HttpServletResponse JavaDoc;
83 import java.io.IOException JavaDoc;
84 import java.util.Enumeration JavaDoc;
85 import java.util.Hashtable JavaDoc;
86 import java.util.Map JavaDoc;
87 import java.util.Vector JavaDoc;
88
89
90 /**
91  * <p>An Transition is a choice that the user can make that initiates
92  * either another sequence in this same controller or some new
93  * controller. A transition is one of the three types of objects that
94  * a controller produces when it enters a new state, the others
95  * being Input objects and Output objects.</p>
96  * <p>Another use of a Transition object is for internal transitioning between
97  * various controllers and their states. Typical example is as follows: <br/>
98  * <code>
99  * <pre>
100  * Transition t = new Transition();
101  * t.setControllerObject(com.myapp.MyController.class);
102  * t.setState("State2");
103  * t.addParameter("SampleParam","This is a parameter value");
104  * return t.transition();
105  * </pre>
106  * </code>
107  * </p>
108  * <p/>
109  * <h4>Recognized Attributes:</h4>
110  * <p>The following types are recognized by the expresso framework and
111  * automatically rendered: You may add your own types or ignore them
112  * if you are doing your own page rendering.</p>
113  * <p/>
114  * <p>header: Renders the transition in the jc-header class style</p>
115  * <p/>
116  * <p>button: Renders the transition as a button.
117  * <p/>
118  * <p><i>default behavior:</i> Renders as a clickable button</p>
119  */

120 public class Transition
121         extends ControllerElement
122         implements Cloneable JavaDoc,
123         java.io.Serializable JavaDoc {
124
125     private static Logger log = Logger.getLogger(Transition.class);
126
127     /**
128      * name of the controller object that created this transition
129      */

130     private String JavaDoc ownerObject = null;
131
132     /**
133      * The name of a controller object that handles this action
134      */

135     private String JavaDoc controllerObject = null;
136
137     /**
138      * The parameters to the controller object
139      */

140     private Hashtable JavaDoc params = new Hashtable JavaDoc(1);
141     private String JavaDoc myState = null;
142     private boolean returnToSender = false;
143
144     /**
145      * This is used to store a constructed parameter string to save on
146      * re-constructing multiple times.
147      */

148     private transient String JavaDoc cacheParamStringSansController = null;
149
150     /**
151      * This is used to store a constructed parameter string to save on
152      * re-constructing multiple times.
153      */

154     private transient String JavaDoc cacheParamStringWithController = null;
155
156
157     /**
158      * Used in place of ControllerResponse for URL encoding
159      */

160     private transient HttpServletResponse JavaDoc servletResponse = null;
161
162     /**
163      * Default Constructor. Normally you don't use this.
164      */

165     public Transition() {
166     }
167
168     /**
169      * Convenience method to transition to another state in this same controller.
170      *
171      * @param newState the new name of the state.
172      * @param myController An instantiated Controller object. Use a <code>ControllerFactory</code>
173      * to instantiate the controller if you must use this constructor.
174      */

175     public Transition(String JavaDoc newState, Controller myController) {
176
177         //
178
//Precalculate the class name because it can take significant CPU time.
179
//
180
String JavaDoc className = myController.getClass().getName();
181         myState = newState;
182         setControllerObject(className);
183         setOwnerController(className);
184         setName(newState);
185
186         //
187
//Performance: get the hashmap because that way we don't invoke a state
188
//copy for each object.
189
//
190
Hashtable JavaDoc allstates = myController.getStates();
191         State theState = null;
192         if (allstates != null) {
193             theState = (State) allstates.get(newState);
194         }
195
196         if (theState == null) {
197             throw new IllegalArgumentException JavaDoc("No such state as '" +
198                     newState + "' in controller '" +
199                     className +
200                     "'");
201         }
202
203         setLabel(theState.getDescription());
204         addParam(Controller.STATE_PARAM_KEY, newState);
205     } /* Transition(String, Controller) */
206
207     /**
208      * Convenience constructor to create an action with a label
209      * and a controller already set.
210      *
211      * @param newLabel Label for the new action
212      * @param newObject The name of the object this action referred to
213      */

214     public Transition(String JavaDoc newLabel, String JavaDoc newObject) {
215         super();
216         setName(StringUtil.replace(newLabel, " ", ""));
217         setLabel(newLabel);
218         setControllerObject(newObject);
219     } /* Transition(String, String) */
220
221     /**
222      * Convenience constructor to create an action with a label
223      * and a controller already set.
224      *
225      * @param newName Name of this Transition object
226      * @param newLabel Label for the new action
227      * @param newObject The name of the Controller object this action referred to
228      */

229     public Transition(String JavaDoc newName, String JavaDoc newLabel, String JavaDoc newObject) {
230         super();
231         setName(newName);
232         setLabel(newLabel);
233         setControllerObject(newObject);
234     } /* Transition(String, String, String) */
235
236     /**
237      * Convenience method to allow for one line of code to construct a transition.
238      *
239      * @param name The name of the transition
240      * @param label The label to use for the transition
241      * @param controllerClass The <code>Class</code> of the controller to use
242      * @param controllerState The name of the controller's state.
243      */

244     public Transition(String JavaDoc name,
245                       String JavaDoc label,
246                       Class JavaDoc controllerClass,
247                       String JavaDoc controllerState) {
248         super();
249         setName(name);
250         setLabel(label);
251         setControllerObject(controllerClass);
252         setState(controllerState);
253     }
254
255     /**
256      * Convenience method to allow for one line of code to construct a transition.
257      * The (internal) name of the transition will be the state name.
258      *
259      * @param label The label to use for the transition
260      * @param controllerClass The <code>Class</code> of the controller to use
261      * @param controllerState The name of the controller's state.
262      */

263     public Transition(String JavaDoc label,
264                       Class JavaDoc controllerClass,
265                       String JavaDoc controllerState) {
266         super();
267         setName(controllerState);
268         setLabel(label);
269         setControllerObject(controllerClass);
270         setState(controllerState);
271     }
272
273     /**
274      * Adds a parameter to a transition. These parameters are meant to
275      * be eventually consumed by the target controller via the request.getParameter()
276      * method.
277      *
278      * @param paramCode The code name of the parameter
279      * @param paramValue The value for the paramter
280      */

281     public synchronized void addParam(String JavaDoc paramCode, String JavaDoc paramValue) {
282         clearCache();
283         if (paramCode.equals(Controller.STATE_PARAM_KEY)) {
284             setState(StringUtil.notNull(paramValue));
285
286             return;
287         }
288         if (paramCode.equals(Controller.CONTROLLER_PARAM_KEY)) {
289             setControllerObject(StringUtil.notNull(paramValue));
290
291             return;
292         }
293
294         params.put(paramCode, StringUtil.notNull(paramValue));
295     } /* addParam(String, String) */
296
297     private synchronized void clearCache() {
298         cacheParamStringWithController = null;
299         cacheParamStringSansController = null;
300     }
301
302     /**
303      * Sets the target state to transition to.
304      *
305      * @param newState java.lang.String
306      */

307     public synchronized void setState(String JavaDoc newState) {
308         clearCache();
309         myState = newState;
310     }
311
312     /**
313      * Retrieve the currently set state
314      *
315      * @return java.lang.String
316      */

317     public String JavaDoc getState() {
318         if (myState == null) {
319             return getParam(Controller.STATE_PARAM_KEY);
320         }
321
322         return myState;
323     }
324
325     /**
326      * Returns a copy of itself
327      *
328      * @return a cloned and instantiated <code>Transition</code> object.
329      * @throws CloneNotSupportedException as required by the method
330      * signature.
331      */

332     public Object JavaDoc clone()
333             throws CloneNotSupportedException JavaDoc {
334         Transition t;
335
336         synchronized (this) {
337             t = (Transition) super.clone();
338             t.params = (Hashtable JavaDoc) params.clone();
339             t.controllerObject = controllerObject;
340             t.myState = myState;
341             t.returnToSender = returnToSender;
342         }
343
344         return t;
345     }
346
347     /**
348      * Call this method when the state/controller being transitioned to should return control back
349      * to the calling state once it has 'completed' successfully. Th 'completion' point depends on
350      * whether the transition is to an external (new controller) or internal state. For external
351      * transitions, the completion point is once the Final state has run successfully. For internal
352      * transitions, the completion point is once the called state has run successfully.
353      * <p/>
354      * When this method is called with a non-null response parameter, this indicates the currently
355      * running state should be the return address. If this method is called with a null response
356      * parameter then this indicates that the return address should be determined at transition
357      * execution time. This is useful/necessary when a transition is instantiated outside the scope
358      * of a state execution. For example, when the controllerSecurityTransition value is set in
359      * the controller constructor, the return state is not known/active.
360      *
361      * @param response the ControllerResponse object
362      * @throws ControllerException upon error
363      */

364     public void enableReturnToSender(ControllerResponse response)
365             throws ControllerException {
366         returnToSender = true;
367         String JavaDoc returnToSender = null;
368         if (response != null) {
369             FastStringBuffer fsb = FastStringBuffer.getInstance();
370             try {
371                 Transition t = response.getCurrentState().getReturnToSender();
372                 returnToSender = t.toXML(fsb).toString();
373             } finally {
374                 fsb.release();
375                 fsb = null;
376             }
377
378             if (isExternalTransition(response.getControllerClass())) {
379                 if (getParam(Controller.CTL_SUCC_TRAN) == null) { //don't overwrite if already set while in run()
380
addParam(Controller.CTL_SUCC_TRAN, returnToSender);
381                 }
382             } else {
383                 if (getParam(Controller.STATE_SUCC_TRAN) == null) { //don't overwrite if already set while in run()
384
addParam(Controller.STATE_SUCC_TRAN, returnToSender);
385                 }
386             }
387         }
388     }
389
390     /**
391      * Returns True if the destination for this transition is a different
392      * controller from the one currently active.
393      *
394      * @param runningController the name of the controller we're currently in.
395      * Usually you would
396      * use <code>this.getClass().getName()</code> within your own controller as
397      * a parameter.
398      * @return true if this is a transition to a another controller.
399      */

400     public boolean isExternalTransition(String JavaDoc runningController) {
401         if (controllerObject == null || controllerObject.equals(runningController)) {
402             return false;
403         } else {
404             return true;
405         }
406     }
407
408     /**
409      * Return the name of the controller object that this Transition
410      * referred to. If this is null, then it refers to the same controller
411      * object (e.g. intra-controller transition)
412      *
413      * @return The class name of the controller object this action
414      * refers to.
415      */

416     public String JavaDoc getControllerObject() {
417         if (controllerObject == null) {
418             return getParam(Controller.CONTROLLER_PARAM_KEY);
419         }
420
421         return controllerObject;
422     } /* getControllerObject() */
423
424     /**
425      * Sets the controller that created this transition. If controllerObject
426      * equals owner controller, then no controller= parameter is generated.
427      *
428      * @return the owner controller
429      */

430     public String JavaDoc getOwnerController() {
431         return this.ownerObject;
432     }
433
434     /**
435      * Return the value for a specific parameter for this transition object.
436      *
437      * @param paramCode The code (name) of the parameter
438      * @return The value of the parameter as a string
439      */

440     public String JavaDoc getParam(String JavaDoc paramCode) {
441         return (String JavaDoc) params.get(paramCode);
442     } /* getParam(String) */
443
444     /**
445      * Return the hashtable of parameters for this transition object.
446      * These parameters are to be handed to the new controller
447      * when the action is called.
448      *
449      * @return A hashtable of name/value pairs for the parameters
450      */

451     public Hashtable JavaDoc getParams() {
452         return params;
453     } /* getParams() */
454
455     /**
456      * @param includeControllerParameter whether to include controller param or not.
457      * @return parameter string which includes all params added to trans, as well
458      * as state param. controller param added optionally
459      */

460     public synchronized String JavaDoc getParamString(boolean includeControllerParameter) {
461         if (includeControllerParameter && cacheParamStringWithController == null) {
462             FastStringBuffer paramString = FastStringBuffer.getInstance();
463             try {
464                 if (controllerObject != null) {
465                     paramString.append("controller=");
466                     paramString.append(controllerObject);
467                 }
468
469                 addNonControllerParams(paramString);
470
471                 cacheParamStringWithController = paramString.toString();
472             } finally {
473                 paramString.release();
474                 paramString = null;
475             }
476
477         }
478
479         if (!includeControllerParameter && cacheParamStringSansController == null) {
480             FastStringBuffer paramString = FastStringBuffer.getInstance();
481             try {
482                 addNonControllerParams(paramString);
483
484                 cacheParamStringSansController = paramString.toString();
485             } finally {
486                 paramString.release();
487                 paramString = null;
488             }
489
490         }
491
492
493         if (includeControllerParameter) {
494             return cacheParamStringWithController;
495         } else {
496             return cacheParamStringSansController;
497         }
498
499     }
500
501     private void addNonControllerParams(FastStringBuffer paramString) {
502         if (this.params != null) {
503             if (!this.params.isEmpty()) {
504                 String JavaDoc oneKey = null;
505
506                 for (Enumeration JavaDoc e = this.params.keys(); e.hasMoreElements();) {
507                     if (paramString.length() != 0) {
508                         paramString.append("&");
509                     }
510                     oneKey = (String JavaDoc) e.nextElement();
511
512                     //Encode user's Transition parameters otherwise is ueer's parameters has '&' then
513
//it will mess up the addButtonParams() method when using Tokenizer.
514
FastStringBuffer fsb = FastStringBuffer.getInstance();
515                     try {
516                         fsb.append(oneKey);
517                         fsb.append("=");
518                         fsb.append(URLUTF8Encoder.encode((String JavaDoc) this.params.get(oneKey)));
519                         paramString.append(fsb.toString());
520                     } finally {
521                         fsb.release();
522                         fsb = null;
523                     }
524
525                 }
526             }
527         }
528
529         String JavaDoc stateString = StringUtil.notNull(getState());
530         if (stateString.length() != 0) {
531             if (paramString.length() != 0) {
532                 paramString.append("&");
533             }
534             paramString.append("state=");
535             paramString.append(stateString);
536         }
537     }
538
539     /**
540      * Return a string of the current params This is NOT URL encoded string.
541      * Use either getUrl() OR java.net.URLEncoder() to do this job.
542      *
543      * @return java.lang.String
544      */

545     public String JavaDoc getParamString() {
546
547         return getParamString(true);
548
549     } /* getParamString() */
550
551     /**
552      * This method invokes a new controller by dispatching to it rather than
553      * calling it directly. This is required when transitioning to external
554      * states so that Struts can setup the controller form.
555      * <p/>
556      * The currently active controller request is passed to the new state to
557      * simulate a direct call to the state. The request is then picked up
558      * by the controller's perform method. Any exceptions raised by the
559      * target state will filter back here to be passed on. This approach
560      * was needed in order to simulate a direct call the the state while at
561      * the same time allowing Struts to setup the controller form.
562      *
563      * @param request the <code>ControllerRequest</code> Object
564      * @return an instantiated <code>ControllerResponse</code> object
565      */

566     protected ControllerResponse newStateDispatch(ControllerRequest request)
567             throws ControllerException,
568             NonHandleableException {
569         ServletControllerRequest servletRequest = (ServletControllerRequest) request;
570         HttpServletRequest JavaDoc httpRequest = (HttpServletRequest JavaDoc) servletRequest.getServletRequest();
571         HttpServletResponse JavaDoc httpResponse = (HttpServletResponse JavaDoc) servletRequest.getServletResponse();
572         //Blank state required to avoid picking up an old state param while really we want
573
//to transition to the initial/default state (ie state is not specified)
574
// ActionMapping mm = ConfigManager.getMapping(controllerObject, "");
575
ActionConfig ac = ConfigManager.getActionConfig("", controllerObject, "");
576
577         if (ac == null) {
578             throw new ControllerException("Cannot transition to controller: " +
579                     controllerObject +
580                     " controller not defined in Struts configuration");
581         }
582
583         String JavaDoc apath = ac.getPath();
584         FastStringBuffer newURL = FastStringBuffer.getInstance();
585         RequestDispatcher JavaDoc dispatcher = null;
586         try {
587             newURL.append(apath);
588             newURL.append(".do");
589             newURL.append("?controller=" + getControllerObject());
590
591             if (getState() != null) {
592                 newURL.append("&state=" + getState());
593             }
594
595             httpRequest.setAttribute(ExpressoConstants.CONTROLLER_REQUEST_KEY, request); //Push our request onto the 'queue'
596

597             String JavaDoc urlValue = newURL.toString();
598             dispatcher = httpRequest.getRequestDispatcher(urlValue);
599
600             if (dispatcher == null) {
601                 throw new ControllerException("Request dispatcher was null - cannot include URL '" +
602                         urlValue + "'");
603             }
604         } finally {
605             newURL.release();
606             newURL = null;
607         }
608         try {
609             dispatcher.include(httpRequest, httpResponse);
610         } catch (Exception JavaDoc e) {
611             throw new ControllerException(e);
612         }
613
614         //Pop our request & response off the 'queue'
615
Exception JavaDoc newStateException = (Exception JavaDoc) httpRequest.getAttribute(ExpressoConstants.NEWSTATE_EXCEPTION_KEY);
616
617         if (newStateException != null) { //bubble up any exception
618
if (newStateException instanceof ControllerException) {
619                 throw (ControllerException) newStateException;
620             } else {
621                 if (newStateException instanceof NonHandleableException) {
622                     throw (NonHandleableException) newStateException;
623                 } else {
624                     throw (RuntimeException JavaDoc) newStateException;
625                 }
626             }
627         }
628
629         request = (ControllerRequest) httpRequest.getAttribute(ExpressoConstants.CONTROLLER_REQUEST_KEY);
630         httpRequest.removeAttribute(ExpressoConstants.CONTROLLER_REQUEST_KEY);
631
632         ControllerResponse response = (ControllerResponse) httpRequest.getAttribute(
633                 ExpressoConstants.CONTROLLER_RESPONSE_KEY);
634         httpRequest.removeAttribute(ExpressoConstants.CONTROLLER_RESPONSE_KEY);
635
636         return response;
637     }
638
639     /**
640      * Returns True if the destination for this transition is the same as the
641      * currently active state. Avoid infinite loop.
642      *
643      * @param runningState the current state
644      * @param runningController the current controller
645      * @return true if we're recusing to the same state and controller as we're
646      * already in.
647      */

648     public boolean isRecursiveTransition(String JavaDoc runningState,
649                                          String JavaDoc runningController) {
650         String JavaDoc targetController = StringUtil.notNull(controllerObject);
651         String JavaDoc targetState = StringUtil.notNull(myState);
652
653         if (targetController.equals(runningController) &&
654                 targetState.equals(runningState)) {
655             return true;
656         }
657
658         return false;
659     }
660
661     /**
662      * Return the return-to-sender flag.
663      *
664      * @return <code>boolean</code>
665      */

666     public boolean isReturnToSenderEnabled() {
667         return returnToSender;
668     }
669
670     /**
671      * Returns a hidden form field string that is safe in either the GET or POST
672      * case.
673      * <p/>
674      * Creation date: (1/10/01 11:24:00 AM)
675      * author: Adam Rossi, PlatinumSolutions
676      *
677      * @return java.lang.String
678      */

679     public String JavaDoc getHTMLParamString() {
680         String JavaDoc paramString = this.getParamString();
681         paramString = URLUTF8Encoder.encode(paramString);
682
683         //
684
//Guess at a memory allocation to reduce re-allocation/copy
685
//Alloc Adjust was guessed at based upon watching string lengths and
686
//guessing a reasonable maximum size that would fit most applciations.
687
//
688

689         FastStringBuffer sb = FastStringBuffer.getInstance();
690         String JavaDoc returnValue = null;
691         try {
692             sb.append("<input type=\"HIDDEN\" name=\"");
693             sb.append(this.getName());
694             sb.append("_params");
695             sb.append("\" value=\"");
696             sb.append(paramString);
697             sb.append("\">");
698             sb.append("<input type=\"HIDDEN\" name=\"");
699             sb.append(this.getName());
700             sb.append("_encoding");
701             sb.append("\" value=\"u\">");
702             returnValue = sb.toString();
703         } finally {
704             sb.release();
705             sb = null;
706         }
707         return returnValue;
708     } /* getHTMLParamStriong() */
709
710     /**
711      * Set the Controller that this action referrs to
712      *
713      * @param newObject Name of the Controller object that this Transition refers to
714      */

715     public synchronized void setControllerObject(String JavaDoc newObject) {
716         clearCache();
717         controllerObject = newObject;
718     } /* setControllerObject(String) */
719
720     /**
721      * Mor Typesafe way of setting the controller object. Any typos will be caught
722      * at compile time. Example usage:
723      * <code><pre>
724      * Transition t = new Transition();
725      * t.setControllerObject(com.jcorporate.expresso.services.Status.class);
726      * </pre></code>
727      *
728      * @param c the class of the controller object to add.
729      */

730     public synchronized void setControllerObject(Class JavaDoc c) {
731         clearCache();
732         if (c != null) {
733             setControllerObject(c.getName());
734         } else {
735             setControllerObject((String JavaDoc) null);
736         }
737     }
738
739     /**
740      * Sets the controller that created this transition. If controllerObject
741      * equals owner controller, then no controller= parameter is generated.
742      *
743      * @param newController the classname of the new controller
744      */

745     public synchronized void setOwnerController(String JavaDoc newController) {
746         clearCache();
747         ownerObject = newController;
748     }
749
750     /**
751      * Set this transition's parameters to the passed in collection.
752      *
753      * @param newParams the new parameters in bulk
754      */

755     public synchronized void setParams(Hashtable JavaDoc newParams) {
756         clearCache();
757         params = new Hashtable JavaDoc(newParams);
758     }
759
760     /**
761      * This method will take the request parameters that were passed to this state
762      * and will copy them into this transition's parameters. These parameters
763      * will then be used when this state is reinvoked (return-to-sender) in order
764      * to re-establish the parameter context.
765      *
766      * @param newReturnToSenderRequest The <code>ControllerRequest</code> object
767      */

768     public synchronized void setReturnToSenderParms(ControllerRequest newReturnToSenderRequest) {
769         clearCache();
770         String JavaDoc oneParamName = null;
771         Object JavaDoc oneParamValue = null;
772         Hashtable JavaDoc params = newReturnToSenderRequest.getParameters();
773         Hashtable JavaDoc newParams = new Hashtable JavaDoc();
774
775         //Copy all parameters except hidden parameters xxx_params and xxx_encoding.
776
//The xxx_params have already been unencoded and untokenized into separate parameters.
777
//Passing the xxx_params caused problems when the transition was encoded again and sent to HTML.
778
//When the double-encoded parameter comes back with the next user request it is never unencoded.
779
//This ends up causing problems with the fromXML/toXML methods in Transition.
780
for (Enumeration JavaDoc e = params.keys(); e.hasMoreElements();) {
781             oneParamName = (String JavaDoc) e.nextElement();
782
783             if (!oneParamName.endsWith("_params") &&
784                     !oneParamName.endsWith("_encoding") &&
785                     !oneParamName.equals(Controller.STATE_PARAM_KEY) &&
786                     !oneParamName.equals(Controller.CONTROLLER_PARAM_KEY)) {
787                 oneParamValue = params.get(oneParamName);
788                 newParams.put(oneParamName, oneParamValue);
789             }
790         }
791
792         setParams(newParams);
793     }
794
795     /**
796      * Convert the object to an xml fragment.
797      *
798      * @param stream an instantiated FastStringBuffer to which we append to.
799      * @return a FastStringBuffer object
800      */

801     public FastStringBuffer toXML(FastStringBuffer stream) {
802         stream.append("<transition");
803
804         if (this.getName() != null && this.getName().length() > 0) {
805             stream.append(" name=\"");
806             stream.append(StringUtil.xmlEscape(getName()));
807             stream.append("\"");
808         }
809
810         String JavaDoc controllerName = this.getControllerObject();
811
812         if (controllerName != null && controllerName.length() > 0) {
813             stream.append(" controller=\"");
814             stream.append(StringUtil.xmlEscape(controllerName));
815             stream.append("\"");
816         }
817
818         String JavaDoc stateName = this.getState();
819
820         if (stateName != null && stateName.length() > 0) {
821             stream.append(" state=\"");
822             stream.append(StringUtil.xmlEscape(stateName));
823             stream.append("\"");
824         }
825
826         stream.append(">\n");
827
828         Hashtable JavaDoc params = this.getParams();
829         String JavaDoc oneKey = null;
830
831         if (!params.isEmpty()) {
832             stream.append("\t<transition-parameters>\n");
833
834             for (Enumeration JavaDoc ap = params.keys(); ap.hasMoreElements();) {
835                 oneKey = (String JavaDoc) ap.nextElement();
836                 stream.append("\t\t<transition-param name=\"");
837                 stream.append(StringUtil.xmlEscape(oneKey));
838                 stream.append("\" value=\"");
839                 stream.append(StringUtil.xmlEscape((String JavaDoc) params.get(oneKey)));
840                 stream.append("\"/>\n");
841             }
842
843             stream.append("\t</transition-parameters>\n");
844         }
845
846         stream = super.toXML(stream);
847         stream.append("</transition>\n");
848
849         return stream;
850