2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.dialog.basic;
19  
20  import java.io.IOException;
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.faces.FacesException;
27  import javax.faces.application.ViewHandler;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.faces.el.MethodBinding;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.shale.dialog.Constants;
36  import org.apache.shale.dialog.DialogContext;
37  import org.apache.shale.dialog.DialogContextListener;
38  import org.apache.shale.dialog.DialogContextManager;
39  import org.apache.shale.dialog.base.AbstractDialogContext;
40  import org.apache.shale.dialog.basic.model.ActionState;
41  import org.apache.shale.dialog.basic.model.Dialog;
42  import org.apache.shale.dialog.basic.model.EndState;
43  import org.apache.shale.dialog.basic.model.State;
44  import org.apache.shale.dialog.basic.model.SubdialogState;
45  import org.apache.shale.dialog.basic.model.Transition;
46  import org.apache.shale.dialog.basic.model.ViewState;
47  
48  /***
49   * <p>Implementation of {@link DialogContext} for integrating
50   * basic dialog support into the Shale Dialog Manager.</p>
51   *
52   * <p><strong>IMPLEMENTATION NOTE</strong> - Takes on the responsibilities
53   * of the <code>org.apache.shale.dialog.Status</code> implementation in the
54   * original approach.</p>
55   *
56   * @since 1.0.4
57   */
58  final class BasicDialogContext extends AbstractDialogContext
59    implements Serializable {
60  
61  
62      // ------------------------------------------------------------ Constructors
63  
64  
65      /***
66       * Serial version UID.
67       */
68      private static final long serialVersionUID = 4858871161274193403L;
69  
70  
71      /***
72       * <p>Construct a new instance.</p>
73       *
74       * @param manager {@link DialogContextManager} instance that owns us
75       * @param dialog Configured dialog definition we will be following
76       * @param id Dialog identifier assigned to this instance
77       * @param parentDialogId Dialog identifier of the parent DialogContext
78       *  instance associated with this one (if any)
79       */
80      BasicDialogContext(DialogContextManager manager, Dialog dialog,
81                         String id, String parentDialogId) {
82  
83          this.manager = manager;
84          this.dialog = dialog;
85          this.dialogName = dialog.getName();
86          this.id = id;
87          this.parentDialogId = parentDialogId;
88  
89          if (log().isDebugEnabled()) {
90              log().debug("Constructor(id=" + id + ", name="
91                        + dialogName + ")");
92          }
93  
94      }
95  
96  
97      // ------------------------------------------------- DialogContext Variables
98  
99  
100     /***
101      * <p>Flag indicating that this {@link DialogContext} is currently active.</p>
102      */
103     private boolean active = true;
104 
105 
106     /***
107      * <p>The {@link Dialog} that this instance is executing.  This value is
108      * transient and may need to be regenerated.</p>
109      */
110     private transient Dialog dialog = null;
111 
112 
113     /***
114      * <p>The name of the {@link Dialog} that this instance is executing.</p>
115      */
116     private String dialogName = null;
117 
118 
119     /***
120      * <p><code>Map</code> of configured dialogs, keyed by logical name.  This value is
121      * transient and may need to be regenerated.</p>
122      */
123     private transient Map dialogs = null;
124 
125 
126     /***
127      * <p>Dialog identifier for this instance.</p>
128      */
129     private String id = null;
130 
131 
132     /***
133      * <p>{@link DialogContextManager} instance that owns us.</p>
134      */
135     private DialogContextManager manager = null;
136 
137 
138     /***
139      * <p>The <code>Log</code> instance for this dialog context.
140      * This value is lazily created (or recreated) as necessary.</p>
141      */
142     private transient Log log = null;
143 
144 
145     /***
146      * <p>Identifier of the parent {@link DialogContext} associated with
147      * this {@link DialogContext}, if any.  If there is no such parent,
148      * this value is set to <code>null</code>.</p>
149      */
150     private String parentDialogId = null;
151 
152 
153     /***
154      * <p>The stack of currently stored Position instances.  If there
155      * are no entries, we have completed the exit state and should deactivate
156      * ourselves.</p>
157      */
158     private List positions = new ArrayList();
159 
160 
161     /***
162      * <p>Flag indicating that execution has started for this dialog.</p>
163      */
164     private boolean started = false;
165 
166 
167     /***
168      * <p>The strategy identifier for dealing with saving and restoring
169      * state information across requests.  This value is lazily
170      * instantiated, and can be recalculated on demand as needed.</p>
171      */
172     private transient String strategy = null;
173 
174 
175     /***
176      * <p>An empty parameter list to pass to the action method called by
177      * a method binding expression.</p>
178      */
179     private static final Object[] ACTION_STATE_PARAMETERS = new Object[0];
180 
181 
182     /***
183      * <p>Parameter signature we create for method bindings used to execute
184      * expressions specified by an {@link ActionState}.</p>
185      */
186     private static final Class[] ACTION_STATE_SIGNATURE = new Class[0];
187 
188 
189     /***
190      * <p>Flag outcome value that signals a need to transition to the
191      * start state for this dialog.</p>
192      */
193     private static final String START_OUTCOME =
194             "org.apache.shale.dialog.basic.START_OUTCOME";
195 
196 
197     // ------------------------------------------------ DialogContext Properties
198 
199 
200     /*** {@inheritDoc} */
201     public boolean isActive() {
202 
203         return this.active;
204 
205     }
206 
207 
208     /*** {@inheritDoc} */
209     public Object getData() {
210 
211         synchronized (positions) {
212             int index = positions.size() - 1;
213             if (index < 0) {
214                 return null;
215             }
216             Position position = (Position) positions.get(index);
217             return position.getData();
218         }
219 
220     }
221 
222 
223 
224     /*** {@inheritDoc} */
225     public void setData(Object data) {
226 
227         synchronized (positions) {
228             int index = positions.size() - 1;
229             if (index < 0) {
230                 throw new IllegalStateException("Cannot set data when no positions are stacked");
231             }
232             Position position = (Position) positions.get(index);
233             Object old = position.getData();
234             if ((old != null) && (old instanceof DialogContextListener)) {
235                 removeDialogContextListener((DialogContextListener) old);
236             }
237             position.setData(data);
238             if ((data != null) && (data instanceof DialogContextListener)) {
239                 addDialogContextListener((DialogContextListener) data);
240             }
241         }
242 
243     }
244 
245 
246     /*** {@inheritDoc} */
247     public String getId() {
248 
249         return this.id;
250 
251     }
252 
253 
254     /*** {@inheritDoc} */
255     public String getName() {
256 
257         Position position = peek();
258         if (position != null) {
259             return position.getDialog().getName();
260         } else {
261             return null;
262         }
263 
264     }
265 
266 
267     /*** {@inheritDoc} */
268     public Object getOpaqueState() {
269 
270         if ("top".equals(strategy())) {
271             if (log().isTraceEnabled()) {
272                 log().trace("getOpaqueState<top> returns " + new TopState(peek().getState().getName(), positions.size()));
273             }
274             return new TopState(peek().getState().getName(), positions.size());
275         } else if ("stack".equals(strategy())) {
276             if (log().isTraceEnabled()) {
277                 log().trace("getOpaqueStrategy<stack> returns stack of " + positions.size());
278             }
279             return positions;
280         } else {
281             if (log().isTraceEnabled()) {
282                 log().trace("getOpaqueStrategy<none> returns nothing");
283             }
284             return null;
285         }
286 
287     }
288 
289 
290     /*** {@inheritDoc} */
291     public void setOpaqueState(Object opaqueState) {
292 
293         if ("top".equals(strategy())) {
294             TopState topState = (TopState) opaqueState;
295             if (log().isTraceEnabled()) {
296                 log().trace("setOpaqueState<top> restores " + topState);
297             }
298             if (topState.stackDepth != positions.size()) {
299                 throw new IllegalStateException("Restored stack depth expects "
300                         + positions.size() + " but is actually "
301                         + topState.stackDepth);
302             }
303             Position top = peek();
304             String oldStateName = top.getState().getName();
305             if (!oldStateName.equals(topState.stateName)) {
306                 fireOnExit(oldStateName);
307                 top.setState(top.getDialog().findState(topState.stateName));
308                 fireOnEntry(topState.stateName);
309             }
310         } else if ("stack".equals(strategy())) {
311             if (log().isTraceEnabled()) {
312                 log().trace("setOpaqueState<stack> restores stack of " + ((List) opaqueState).size());
313             }
314             List list = (List) opaqueState;
315             String oldStateName = peek().getState().getName();
316             String newStateName = ((Position) list.get(list.size() - 1)).getState().getName();
317             if (!oldStateName.equals(newStateName) || (list.size() != positions.size())) {
318                 fireOnExit(oldStateName);
319                 positions = list;
320                 fireOnEntry(newStateName);
321             }
322         } else {
323             if (log().isTraceEnabled()) {
324                 log().trace("setOpaqueState<none> restores nothing");
325             }
326             ; // Do nothing
327         }
328 
329     }
330 
331 
332     /*** {@inheritDoc} */
333     public DialogContext getParent() {
334 
335         if (this.parentDialogId != null) {
336             DialogContext parent = manager.get(this.parentDialogId);
337             if (parent == null) {
338                 throw new IllegalStateException("Dialog instance '"
339                         + parentDialogId + "' was associated with this instance '"
340                         + getId() + "' but is no longer available");
341             }
342             return parent;
343         } else {
344             return null;
345         }
346 
347     }
348 
349 
350     // --------------------------------------------------- DialogContext Methods
351 
352 
353     /*** {@inheritDoc} */
354     public void advance(FacesContext context, String outcome) {
355 
356         if (!started) {
357             throw new IllegalStateException("Dialog instance '"
358                     + getId() + "' for dialog name '"
359                     + getName() + "' has not yet been started");
360         }
361 
362         if (log().isDebugEnabled()) {
363             log().debug("advance(id=" + getId() + ", name=" + getName()
364                       + ", outcome=" + outcome + ")");
365         }
366 
367         // If the incoming outcome is null, we want to stay in the same
368         // (view) state *without* recreating it, which would destroy
369         // any useful information that components might have stored
370         if (outcome == null) {
371             if (log().isTraceEnabled()) {
372                 log().trace("punt early since outcome is null");
373             }
374             return;
375         }
376 
377         // Perform an initial transition from the current state based on
378         // the logical outcome received from the (current) view state
379         Position position = peek();
380         if (!START_OUTCOME.equals(outcome)) {
381             transition(position, outcome);
382         }
383         State state = position.getState();
384         String viewId = null;
385         boolean redirect = false;
386 
387         // Advance through states until we again encounter the
388         // need to render a view state
389         while (true) {
390 
391             if (state instanceof ActionState) {
392                 ActionState astate = (ActionState) state;
393                 if (log().isTraceEnabled()) {
394                     log().trace("-->ActionState(method=" + astate.getMethod() + ")");
395                 }
396                 try {
397                     MethodBinding mb = context.getApplication().
398                       createMethodBinding(astate.getMethod(), ACTION_STATE_SIGNATURE);
399                     outcome = (String) mb.invoke(context, ACTION_STATE_PARAMETERS);
400                 } catch (Exception e) {
401                     fireOnException(e);
402                 }
403                 transition(position, outcome);
404                 state = position.getState();
405                 continue;
406             } else if (state instanceof EndState) {
407                 if (log().isTraceEnabled()) {
408                     log().trace("-->EndState()");
409                 }
410                 pop();
411                 position = peek();
412                 if (position == null) {
413                     stop(context);
414                 }
415                 viewId = ((EndState) state).getViewId();
416                 redirect = ((EndState) state).isRedirect();
417                 break;
418             } else if (state instanceof SubdialogState) {
419                 SubdialogState sstate = (SubdialogState) state;
420                 if (log().isTraceEnabled()) {
421                     log().trace("-->SubdialogState(dialogName="
422                               + sstate.getDialogName() + ")");
423                 }
424                 Dialog subdialog = (Dialog) dialogs(context).get(sstate.getDialogName());
425                 if (subdialog == null) {
426                     throw new IllegalStateException("Cannot find dialog definition '"
427                       + sstate.getDialogName() + "'");
428                 }
429                 start(subdialog);
430                 position = peek();
431                 state = position.getState();
432                 continue;
433             } else if (state instanceof ViewState) {
434                 viewId = ((ViewState) state).getViewId();
435                 redirect = ((ViewState) state).isRedirect();
436                 if (log().isTraceEnabled()) {
437                     log().trace("-->ViewState(viewId="
438                               + ((ViewState) state).getViewId()
439                               + ",redirect=" + ((ViewState) state).isRedirect()
440                               + ")");
441                 }
442                 break;
443             } else {
444                 throw new IllegalStateException
445                   ("State '" + state.getName()
446                    + "' of dialog '" + position.getDialog().getName()
447                    + "' is of unknown type '" + state.getClass().getName() + "'");
448             }
449         }
450 
451         // Navigate to the requested view identifier (if any)
452         if (viewId == null) {
453             return;
454         }
455         if (log().isTraceEnabled()) {
456             log().trace("-->Navigate(viewId=" + viewId + ")");
457         }
458         ViewHandler vh = context.getApplication().getViewHandler();
459         if (redirect) {
460             String actionURL = vh.getActionURL(context, viewId);
461             if (actionURL.indexOf('?') < 0) {
462                 actionURL += '?';
463             } else {
464                 actionURL += '&';
465             }
466             actionURL += Constants.DIALOG_ID + "=" + this.id;
467             try {
468                 ExternalContext econtext = context.getExternalContext();
469                 econtext.redirect(econtext.encodeActionURL(actionURL));
470                 context.responseComplete();
471             } catch (IOException e) {
472                 throw new FacesException("Cannot redirect to " + actionURL, e);
473             }
474         } else {
475             UIViewRoot view = vh.createView(context, viewId);
476             view.setViewId(viewId);
477             context.setViewRoot(view);
478             context.renderResponse();
479         }
480 
481     }
482 
483 
484     /*** {@inheritDoc} */
485     public void start(FacesContext context) {
486 
487         if (started) {
488             throw new IllegalStateException("Dialog instance '"
489                     + getId() + "' for dialog name '"
490                     + getName() + "' has already been started");
491         }
492         started = true;
493 
494         if (log().isDebugEnabled()) {
495             log().debug("start(id=" + getId() + ", name="
496                       + getName() + ")");
497         }
498 
499         // inform listeners we've been started
500         fireOnStart();
501 
502         // set the dialog in motion
503         start(dialog);
504 
505         // Advance the computation of our dialog until a view state
506         // is encountered, then navigate to it
507         advance(context, START_OUTCOME);
508 
509     }
510 
511 
512     /*** {@inheritDoc} */
513     public void stop(FacesContext context) {
514 
515         if (!started) {
516             throw new IllegalStateException("Dialog instance '"
517                     + getId() + "' for dialog name '"
518                     + getName() + "' has not yet been started");
519         }
520         started = false;
521 
522         if (log().isDebugEnabled()) {
523             log().debug("stop(id=" + getId() + ", name="
524                       + getName() + ")");
525         }
526 
527         deactivate();
528         manager.remove(this);
529 
530         // inform listeners
531         fireOnStop();
532 
533     }
534 
535 
536     // ------------------------------------------------- Package Private Methods
537 
538 
539     /***
540      * <p>Mark this {@link DialogContext} as being deactivated.  This should only
541      * be called by the <code>remove()</code> method on our associated
542      * {@link DialogContextManager}, or the logic of our <code>stop()</code>
543      * method.</p>
544      */
545     void deactivate() {
546 
547         while (positions.size() > 0) {
548             pop();
549         }
550         this.active = false;
551 
552     }
553 
554 
555     // --------------------------------------------------------- Private Methods
556 
557 
558     /***
559      * <p>Return the {@link Dialog} instance for the dialog we are executing,
560      * regenerating it if necessary first.</p>
561      *
562      * @param context FacesContext for the current request
563      * @return The {@link Dialog} instance we are executing
564      */
565     private Dialog dialog(FacesContext context) {
566 
567         if (this.dialog == null) {
568             this.dialog = (Dialog) dialogs(context).get(this.dialogName);
569         }
570         return this.dialog;
571 
572     }
573 
574 
575     /***
576      * <p>Return a <code>Map</code> of the configured {@link Dialog}s, keyed
577      * by logical dialog name.</p>
578      *
579      * @param context FacesContext for the current request
580      * @return The map of available dialogs, keyed by dialog logical name
581      *
582      * @exception IllegalStateException if dialog configuration has not
583      *  been completed
584      */
585     private Map dialogs(FacesContext context) {
586 
587         // Return the cached instance (if any)
588         if (this.dialogs != null) {
589             return this.dialogs;
590         }
591 
592         // Return the previously configured application scope instance (if any)
593         this.dialogs = (Map)
594           context.getExternalContext().getApplicationMap().get(Globals.DIALOGS);
595         if (this.dialogs != null) {
596             return this.dialogs;
597         }
598 
599         // Throw an exception if dialog configuration resources have not
600         // been processed yet
601         throw new IllegalStateException("Dialog configuration resources have not yet been processed");
602 
603     }
604 
605 
606     /***
607      * <p>Return the <code>Log</code> instance for this dialog context,
608      * creating one if necessary.</p>
609      */
610     private Log log() {
611 
612         if (log == null) {
613             log = LogFactory.getLog(BasicDialogContext.class);
614         }
615         return log;
616 
617     }
618 
619 
620     /***
621      * <p>Return a <code>Position</code> representing the currently executing
622      * dialog and state (if any); otherwise, return <code>null</code>.</p>
623      *
624      * @return Position representing the currently executing dialog and
625      *         state, may be null
626      */
627     private Position peek() {
628 
629         synchronized (positions) {
630             int index = positions.size() - 1;
631             if (index < 0) {
632                 return null;
633             }
634             return (Position) positions.get(index);
635         }
636 
637     }
638 
639 
640     /***
641      * <p>Pop the currently executing <code>Position</code> and return the
642      * previously executing <code>Position</code> (if any); otherwise,
643      * return <code>null</code>.</p>
644      *
645      * @return Position representing the previously executing dialog and
646      *         state (may be null), currently executing dialog Position
647      *         is lost.
648      */
649     private Position pop() {
650 
651         synchronized (positions) {
652             int index = positions.size() - 1;
653             if (index < 0) {
654                 throw new IllegalStateException("No position to be popped");
655             }
656             Object data = ((Position) positions.get(index)).getData();
657             if ((data != null) && (data instanceof DialogContextListener)) {
658                 removeDialogContextListener((DialogContextListener) data);
659             }
660             positions.remove(index);
661             if (index > 0) {
662                 return (Position) positions.get(index - 1);
663             } else {
664                 return null;
665             }
666         }
667 
668     }
669 
670 
671     /***
672      * <p>Push the specified <code>Position</code>, making it the currently
673      * executing one.</p>
674      *
675      * @param position The new currently executing <code>Position</code>
676      */
677     private void push(Position position) {
678 
679         if (position == null) {
680             throw new IllegalArgumentException();
681         }
682         Object data = position.getData();
683         if ((data != null) && (data instanceof DialogContextListener)) {
684             addDialogContextListener((DialogContextListener) data);
685         }
686         synchronized (positions) {
687             positions.add(position);
688         }
689 
690     }
691 
692 
693     /***
694      * <p>Push a new {@link Position} instance representing the starting
695      * {@link State} of the specified {@link Dialog}.</p>
696      *
697      * @param dialog {@link Dialog} instance to be started and pushed
698      */
699     private void start(Dialog dialog) {
700 
701         // Look up the initial state for the specified dialog
702         State state = dialog.findState(dialog.getStart());
703         if (state == null) {
704             throw new IllegalStateException
705               ("Cannot find starting state '"
706                + dialog.getStart()
707                + "' for dialog '" + dialog.getName() + "'");
708         }
709 
710         // Construct an appropriate data object for the specified dialog
711         Object data = null;
712         try {
713             data = dialog.getDataClass().newInstance();
714         } catch (Exception e) {
715             fireOnException(e);
716         }
717 
718         // Push a Position and corresponding data object to our stacks
719         push(new Position(dialog, state, data));
720 
721         // inform listeners
722         fireOnEntry(state.getName());
723 
724     }
725 
726 
727     /***
728      * <p>Return the strategy identifier for dealing with saving state
729      * information across requests.</p>
730      */
731     private String strategy() {
732 
733         if (this.strategy == null) {
734             this.strategy = FacesContext.getCurrentInstance().getExternalContext().getInitParameter(Globals.STRATEGY);
735             if (this.strategy == null) {
736                 this.strategy = "top";
737             } else {
738                 this.strategy = this.strategy.toLowerCase();
739             }
740         }
741         return this.strategy;
742 
743     }
744 
745 
746     /***
747      * <p>Transition the specified {@link Position}, based on the specified
748      * logical outcome, to the appropriate next {@link State}.</p>
749      *
750      * @param position {@link Position} to be transitioned
751      * @param outcome Logical outcome to use for transitioning
752      */
753     private void transition(Position position, String outcome) {
754 
755         // If the outcome is null, stay on the same state to be consistent
756         // with JSF semantics on null outcomes
757         if (outcome == null) {
758             return;
759         }
760 
761         State current = position.getState();
762         String fromStateId = current.getName();
763 
764         // Select the next state based on the specified outcome
765         Transition transition = current.findTransition(outcome);
766         if (transition == null) {
767             transition = position.getDialog().findTransition(outcome);
768         }
769         if (transition == null) {
770             throw new IllegalStateException
771               ("Cannot find transition '" + outcome
772                + "' for state '" + fromStateId
773                + "' of dialog '" + position.getDialog().getName() + "'");
774         }
775         State next = position.getDialog().findState(transition.getTarget());
776         if (next == null) {
777             throw new IllegalStateException
778               ("Cannot find state '" + transition.getTarget()
779                + "' for dialog '" + position.getDialog().getName() + "'");
780         }
781         String toStateId = next.getName();
782         position.setState(next);
783 
784         // We inform listeners at the end, the assumption being that a transition
785         // must be an atomic transaction, either all associated callbacks must
786         // occur as the application would expect, or none should be initiated
787 
788         // On a temporal axis, we fire the source state exit handlers first ...
789         fireOnExit(fromStateId);
790 
791         // ... followed by the transition handlers ...
792         fireOnTransition(fromStateId, toStateId);
793 
794         // ... and finally, the target state entry handlers
795         fireOnEntry(toStateId);
796 
797     }
798 
799 
800     // ------------------------------------------------ State Storage Subclasses
801 
802 
803     /***
804      * <p>Class that represents the saved opaque state information when the
805      * <code>top</code> strategy is selected.</p>
806      */
807     public class TopState implements Serializable {
808 
809         /***
810          * Serial version UID.
811          */
812         private static final long serialVersionUID = 1L;
813 
814 
815         /***
816          * <p>Construct an uninitialized instance of this state class.</p>
817          */
818         public TopState() {
819             ;
820         }
821 
822 
823         /***
824          * <p>Construct an initialized instance of this state class.</p>
825          *
826          * @param stateName Name of the current state in the topmost position
827          * @param stackDepth Depth of the position stack
828          */
829         public TopState(String stateName, int stackDepth) {
830             this.stateName = stateName;
831             this.stackDepth = stackDepth;
832         }
833 
834 
835         /***
836          * <p>The name of the current state within the topmost
837          * <code>Position</code> on the stack.</p>
838          */
839         public String stateName;
840 
841 
842         /***
843          * <p>The stack depth of the <code>Position</code> stack.  This is
844          * used to detect scenarios where using the back and forward buttons
845          * navigates across a subdialog boundary, which means that the
846          * saved <code>stateName</code> is likely no longer relevant.</p>
847          */
848         public int stackDepth;
849 
850 
851         /***
852          * <p>Return a string representation of this instance.</p>
853          */
854         public String toString() {
855             return "TopState[stateName=" + this.stateName
856                    + ", stackDepth=" + this.stackDepth + "]";
857         }
858 
859 
860 
861     }
862 
863 
864 
865 }