View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    *
6    * This program is distributed in the hope that it will be useful, but
7    * WITHOUT ANY WARRANTY; without even the implied warranty of
8    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9    * See the MOZILLA PUBLIC LICENSE for more details.
10   * =================================================================== */
11  
12  package com.sri.emo.controller;
13  
14  import com.jcorporate.expresso.core.controller.*;
15  import com.jcorporate.expresso.core.db.DBConnection;
16  import com.jcorporate.expresso.core.db.DBConnectionPool;
17  import com.jcorporate.expresso.core.db.DBException;
18  import com.jcorporate.expresso.core.dbobj.ValidValue;
19  import com.jcorporate.expresso.core.registry.RequestRegistry;
20  import com.sri.common.util.ObjectCombinator;
21  import com.sri.emo.dbobj.*;
22  import com.sri.emo.wizard.IWizardManager;
23  import com.sri.emo.wizard.expressoimpl.WizardController;
24  import com.sri.emo.wizard.selection.EmoSelectionWizard;
25  import com.sri.emo.wizard.selection.WizardAction;
26  import com.sri.emo.wizard.selection.management.PromptStepType;
27  import com.sri.emo.wizard.selection.management.WizardStepController;
28  import com.sri.emo.wizard.wizardgateway.ListWizards;
29  import com.sri.emo.wizard.wizardgateway.WizardGatewayController;
30  
31  import java.util.*;
32  
33  
34  /***
35   * Provides CRUD capabilities for Wizard DBObjects.  It is designed as a
36   * series of Transaction Scripts.  See <a href="http://www.martinfowler.com/eaaCatalog/">
37   * Patterns of Enterprise Application Architecture</a> pattern list for
38   * more information.
39   * <p> A controller is a rough model of a finite state machine for a web application.
40   * Each state provides user interaction elements that can be rendered in a web
41   * page or other medium such as a command line app.</p>
42   *
43   * @author Mike Rimov
44   */
45  public class SelectionWizardManager extends EmoAction implements IWizardManager {
46  
47      /***
48  	 * 
49  	 */
50  	private static final long serialVersionUID = 1L;
51  
52  	/***
53       * Constant for access to state: Add Wizard.
54       */
55      public static final String STATE_PROMPT_ADDWIZARD = "promptAddWizard";
56  
57      /***
58       * Finishes the post to state.
59       */
60      public static final String STATE_DO_ADDWIZARD = "doAdd";
61  
62      /***
63       * Finishes the edit to update state.
64       */
65      public static final String STATE_DO_EDIT = "doEdit";
66  
67      /***
68       * Constant for access to state: Edit Wizard.
69       */
70      public static final String STATE_PROMPT_EDIT = "promptEdit";
71  
72      /***
73       * Constant for access to state: Delete Wizard.
74       */
75      public static final String STATE_DO_DELETE = "doDelete";
76  
77  
78      /***
79       * Constant for access to state: Run Wizard.
80       */
81      public static final String STATE_RUNWIZARD = "runWizard";
82  
83      /***
84       * synonym to serve old URLs
85       */
86      public static final String STATE_PUBLIC_LIST = "publicList";
87  
88  
89      /***
90       * Creates an instance of WizardManager.  The Controller constructor also defines
91       * what states and parameters are officially defined by the Controller.
92       */
93      public SelectionWizardManager() {
94          State s = new State(STATE_PROMPT_ADDWIZARD, "Add Wizard");
95          addState(s);
96  
97          s = new State(STATE_DO_ADDWIZARD, "Save New Wizard");
98          addState(s);
99  
100         s = new State(STATE_DO_EDIT, "Save Updated Wizard");
101         s.addRequiredParameter(WizardController.WIZ_PARAMETER_ID);
102         addState(s);
103 
104         s = new State(STATE_PROMPT_EDIT, "Prompt Edit Wizard");
105         s.addRequiredParameter(WizardController.WIZ_PARAMETER_ID);
106         addState(s);
107 
108 
109         s = new State(STATE_RUNWIZARD, "Run Wizard");
110         s.addRequiredParameter(WizardController.WIZ_PARAMETER_ID);
111         addState(s);
112 
113         s = new State(STATE_PUBLIC_LIST, "Public List");
114         addState(s);
115 
116         setInitialState(STATE_PROMPT_ADDWIZARD);
117         setSchema(com.sri.emo.EmoSchema.class);
118     }
119 
120     /***
121      * Override of Controller.getTitle() to provide a meaningful name to this
122      * controller.
123      *
124      * @return java.lang.String
125      */
126     public String getTitle() {
127         return "Wizard Manager";
128     }
129 
130     /***
131      * Runs the Add Wizard Prompt state.
132      *
133      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
134      *                 The ControllerRequest contains all the parameters such as web parameters, user
135      *                 security credentials and user locale
136      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
137      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
138      *                 for ths sytem.
139      * @throws ControllerException    upon error
140      * @throws NonHandleableException upon fatal error
141      */
142     protected void runPromptAddWizardState(final ControllerRequest request,
143                                            final ControllerResponse response) throws ControllerException,
144             NonHandleableException {
145         response.setTitle("Add Wizard");
146 
147         Hashtable cache = response.getFormCache();
148 
149         String value = (String) cache.get("title");
150         if (value == null) {
151             value = "";
152         }
153         Input i = new Input("title", "Name");
154         i.setDefaultValue(value);
155         response.add(i);
156         value = (String) cache.get("wizardClass");
157         if (value == null) {
158             value = EmoSelectionWizard.class.getName();
159         }
160 
161         i = new Input("wizardClass", "Wizard Class");
162         i.setDefaultValue(value);
163 
164         response.add(i);
165         value = (String) cache.get("summary");
166         if (value == null) {
167             value = "";
168         }
169         i = new Input("summary", "Summary");
170         i.setDefaultValue(value);
171         response.add(i);
172 
173         Transition save = new Transition("save", "save", getClass(), STATE_DO_ADDWIZARD);
174         response.add(save);
175     }
176 
177     /***
178      * Runs the complete update state.
179      *
180      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
181      *                 The ControllerRequest contains all the parameters such as web parameters, user
182      *                 security credentials and user locale
183      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
184      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
185      *                 for ths sytem.
186      * @throws ControllerException    upon error
187      * @throws NonHandleableException upon fatal error
188      */
189     protected void runDoEditState(final ControllerRequest request,
190                                   final ControllerResponse response) throws ControllerException,
191             NonHandleableException {
192         try {
193             ErrorCollection ec = request.getErrorCollection();
194 
195             if (ec == null) {
196                 ec = new ErrorCollection();
197             }
198 
199             WizDefinition wizdef = getWizDef(request);
200 
201             //
202             //Validate parameters
203             //
204             verifyWizardParameters(request, ec, wizdef);
205             if (wizdef.getPageDefinitions().size() == 0) {
206                 ec.addError("You need to have one or more steps defined.  " +
207                         "Click 'add step' below to add a step.");
208             }
209             List pertinentSteps = wizdef.getPertinentDecisionSteps();
210             ObjectCombinator combinator = buildObjectCombinator(pertinentSteps);
211 
212             // see if IDs entered have corresponding nodes
213             validateDecisionMatrix(request, ec, combinator);
214 
215             if (ec.getErrorCount() > 0) {
216                 if (getLogger().isDebugEnabled()) {
217                     getLogger().debug("Errors in error collection: " + ec.getErrorCount()
218                             + " errors.");
219                 }
220                 response.saveErrors(ec);
221                 response.setFormCache();
222                 transition(STATE_PROMPT_EDIT, request, response);
223                 return;
224             }
225 
226             DBConnection transaction = DBConnectionPool.getInstance(request.
227                     getDataContext()).getConnection("Wizard Update");
228             try {
229                 transaction.setAutoCommit(false);
230                 wizdef.setConnection(transaction);
231                 wizdef.update(true); // write the summary & other field updates that were collected above
232                 WizDecisionSet decisionSet = new WizDecisionSet(transaction, RequestRegistry.getUser().getUid());
233 
234                 //There is no way to know without extensive reads if we have
235                 //identical sets here.  So for now we delete them all and rewrite
236                 //them.
237 
238                 /***
239                  * @todo Optimize the writing behavior here with some sort
240                  * of unit of work.
241                  */
242 
243                 decisionSet.setWizardId(wizdef.getId());
244                 decisionSet.deleteAll(true);
245                 decisionSet.clear();
246                 decisionSet.setWizardId(wizdef.getId());
247                 //
248                 //Translate each object combinator row to a WizDecisionSet.
249                 //
250                 int counter = 0;
251                 for (Iterator i = combinator.iterator(); i.hasNext();) {
252                     decisionSet.clear();
253                     decisionSet.setWizardId(wizdef.getId());
254                     String param = "exId" + counter;
255                     counter++;
256                     String paramValue = request.getParameter(param);
257                     if (paramValue == null || paramValue.length() == 0) {
258                         i.next();
259                         continue;
260                     }
261 
262                     int parameterValue = Integer.parseInt(paramValue);
263 
264                     decisionSet.setDecisionResult(parameterValue);
265                     decisionSet.setWizardId(wizdef.getId());
266                     decisionSet.add(); // always add because we delete all, above
267 
268                     ValidValue[] vv = (ValidValue[]) i.next();
269                     int[] stepIds = new int[vv.length];
270                     String pickListIds[] = new String[vv.length];
271                     for (int j = 0; j < vv.length; j++) {
272                         WizStep oneStep = (WizStep) pertinentSteps.get(j);
273                         stepIds[j] = oneStep.getFieldInt(WizStep.FLD_ID);
274                         pickListIds[j] = vv[j].getKey();
275                     }
276 
277                     decisionSet.addDecisionEntity(stepIds, pickListIds);
278                 }
279 
280                 transaction.commit();
281             } catch (DBException ex) {
282                 transaction.rollback();
283                 throw new ControllerException("Database Access Error", ex);
284             } finally {
285                 transaction.release();
286             }
287 
288             Transition t = new Transition("nextpage", "nextpage", WizardGatewayController.class,
289                     ListWizards.STATE_NAME);
290 
291             t.addParam(WizardController.WIZ_PARAMETER_ID, wizdef.getId());
292 
293             t.redirectTransition(request, response);
294         } catch (DBException ex) {
295             throw new ControllerException("Error accessing wizard definition", ex);
296         }
297     }
298 
299     /***
300      * Retrieve the wizard definition based upon the parameters in the controller request.
301      *
302      * @param request ControllerRequest the ControllerRequest object.
303      * @return WizDefinition the defined wizard definition.
304      * @throws DBException if we are unable to locate it in the database.
305      */
306     private WizDefinition getWizDef(final ControllerRequest request) throws DBException {
307         WizDefinition wizdef = new WizDefinition();
308         wizdef.setId(request.getParameter(WizardController.WIZ_PARAMETER_ID));
309         wizdef.retrieve();
310         return wizdef;
311     }
312 
313     /***
314      * Provides some validation against the decision matrix values entered
315      * by &quot;DoEditWizard&quot;
316      *
317      * @param request    ControllerRequest The ControllerRequest object
318      * @param ec         ErrorCollection the ErrorCollection
319      * @param combinator ObjectCombinator the ObjectCombinator for the wizard.
320      * @throws DBException upon error.
321      */
322     private void validateDecisionMatrix(final ControllerRequest request,
323                                         final ErrorCollection ec,
324                                         final ObjectCombinator combinator) throws DBException {
325 
326         int counter = 0;
327         for (Iterator i = combinator.iterator(); i.hasNext(); i.next()) {
328             String param = "exId" + counter;
329             String paramValue = request.getParameter(param);
330             if (paramValue == null || paramValue.length() == 0) {
331                 continue;
332             } else {
333                 try {
334                     int paramInteger = Integer.parseInt(paramValue);
335                     Node node = new Node();
336                     node.setNodeId(Integer.toString(paramInteger));
337                     if (!node.find()) {
338                         ec.addError("Value for Decision Matrix Cell #"
339                                 + counter
340                                 + " has no corresponding example id. Please "
341                                 + "double-check the value you entered");
342                     }
343                 } catch (NumberFormatException ex) {
344                     ec.addError("Value for Decision Matrix Cell #"
345                             + counter
346                             + " needs to be a number");
347                 }
348             }
349             counter++;
350         }
351     }
352 
353 
354     /***
355      * Runs the Complete Add Wizard state.
356      *
357      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
358      *                 The ControllerRequest contains all the parameters such as web parameters, user
359      *                 security credentials and user locale
360      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
361      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
362      *                 for ths sytem.
363      * @throws ControllerException    upon error
364      * @throws NonHandleableException upon fatal error
365      */
366     protected void runDoAddState(final ControllerRequest request,
367                                  final ControllerResponse response) throws ControllerException,
368             NonHandleableException {
369         try {
370             ErrorCollection ec = request.getErrorCollection();
371 
372             if (ec == null) {
373                 ec = new ErrorCollection();
374             }
375 
376             WizDefinition wizdef = new WizDefinition();
377             wizdef.setFieldsWithDefaults();
378             verifyWizardParameters(request, ec, wizdef);
379 
380             if (ec.getErrorCount() > 0) {
381                 if (getLogger().isDebugEnabled()) {
382                     getLogger().debug("Errors in error collection: " + ec.getErrorCount()
383                             + " errors.");
384                 }
385                 response.saveErrors(ec);
386                 response.setFormCache();
387                 transition(STATE_PROMPT_ADDWIZARD, request, response);
388                 return;
389             }
390 
391             //
392             //Validate parameters
393             //
394             DBConnection trx = DBConnectionPool.getInstance(request.getDataContext())
395                     .getConnection("EMO Add Wizard");
396             try {
397                 trx.setAutoCommit(false);
398 
399                 wizdef.setConnection(trx);
400                 wizdef.add();
401 
402                 //Create step for welcome screen by default
403                 WizStep welcomeStep = new WizStep(trx);
404                 welcomeStep.setField(WizStep.FLD_TITLE, "Welcome");
405                 welcomeStep.setField(WizStep.FLD_DIRECTIVE, "Click the 'next' button.");
406                 welcomeStep.setWizId(wizdef.getId());
407                 welcomeStep.setField(WizStep.FLD_STEP_TYPE, PromptStepType.INSTRUCTIONS_ONLY);
408                 welcomeStep.setField(WizStep.FLD_HELPTEXT, "A short interview will follow.");
409                 welcomeStep.add();
410 
411                 trx.commit();
412             } catch (Exception e) {
413                 trx.rollback();
414                 getLogger().error("Error writing transaction", e);
415                 throw new ControllerException("Error writing transaction", e);
416             } finally {
417                 trx.release();
418             }
419 
420             Transition t = new Transition("nextpage", "nextpage", getClass(), STATE_PROMPT_EDIT);
421             t.addParam(WizardController.WIZ_PARAMETER_ID, wizdef.getId());
422 
423             t.redirectTransition(request, response);
424         } catch (DBException ex) {
425             throw new ControllerException("Error accessing wizard definition", ex);
426         }
427     }
428 
429     /***
430      * Checks the parameters for add and edit wizard state pertaining to
431      * the wizard definition itself.
432      *
433      * @param request The <code>CntrollerRequest</code> object.
434      * @param ec      The error collection to fill with errors if there are
435      *                issues.
436      * @param wizdef  the Wizard definition object to populate with values for
437      *                title, summary, etc if the items are ok.
438      * @throws DBException upon database setField error.
439      */
440     protected void verifyWizardParameters(final ControllerRequest request,
441                                           final ErrorCollection ec,
442                                           final WizDefinition wizdef) throws
443             DBException {
444         String value = request.getParameter("title");
445         try {
446             if (value == null || value.length() == 0) {
447                 ec.addError("You must have a title for your wizard.");
448             } else {
449                 wizdef.checkField(WizDefinition.FLD_NAME, value);
450                 wizdef.setField(WizDefinition.FLD_NAME, value);
451             }
452         } catch (DBException ex) {
453             getLogger().error("Error validating field: " + WizDefinition.FLD_NAME, ex);
454             ec.addError(ex.getMessage());
455         }
456 
457         value = request.getParameter("wizardClass");
458         try {
459             wizdef.checkField(WizDefinition.FLD_WIZARD, value);
460             wizdef.setField(WizDefinition.FLD_WIZARD, value);
461         } catch (DBException ex) {
462             getLogger().error("Error validating field: " + WizDefinition.FLD_WIZARD, ex);
463             ec.addError(ex.getMessage());
464         }
465 
466         value = request.getParameter("summary");
467         if (value != null) {
468             wizdef.setField(WizDefinition.FLD_SUMMARY, value);
469         }
470     }
471 
472 
473     /***
474      * Runs the Delete Wizard state.
475      *
476      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
477      *                 The ControllerRequest contains all the parameters such as web parameters, user
478      *                 security credentials and user locale
479      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
480      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
481      *                 for ths sytem.
482      * @throws ControllerException    upon error
483      * @throws NonHandleableException upon fatal error
484      */
485     protected void runDoDeleteState(final ControllerRequest request,
486                                     final ControllerResponse response) throws ControllerException,
487             NonHandleableException {
488         try {
489             WizDefinition wizdef = getWizDef(request);
490             wizdef.delete(true);
491             Transition next = new Transition("next", "next", WizardGatewayController.class, ListWizards.STATE_NAME);
492             next.redirectTransition(request, response);
493         } catch (DBException ex) {
494             throw new ControllerException("Database error deleting wizard.", ex);
495         }
496     }
497 
498     /***
499      * Runs the Prompt Delete Wizard state.
500      *
501      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
502      *                 The ControllerRequest contains all the parameters such as web parameters, user
503      *                 security credentials and user locale
504      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
505      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
506      *                 for ths sytem.
507      * @throws ControllerException    upon error
508      * @throws NonHandleableException upon fatal error
509      */
510     protected void runPromptDeleteState(final ControllerRequest request,
511                                         final ControllerResponse response) throws ControllerException,
512             NonHandleableException {
513 
514         try {
515             response.setTitle("Confirm Delete Wizard");
516             WizDefinition wizdef = getWizDef(request);
517             response.add(new Output("prompt", wizdef.getWizName()));
518 
519             Transition yes = new Transition("yes", "yes", getClass(), STATE_DO_DELETE);
520             yes.addParam(WizardController.WIZ_PARAMETER_ID, request.getParameter(WizardController.WIZ_PARAMETER_ID));
521             response.add(yes);
522             Transition no = new Transition("no", "no", WizardGatewayController.class, ListWizards.STATE_NAME);
523             response.add(no);
524         } catch (DBException ex) {
525             throw new ControllerException("Database Error retrieving wizard.", ex);
526         }
527     }
528 
529 
530     /***
531      * Runs the Edit Wizard state.
532      *
533      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
534      *                 The ControllerRequest contains all the parameters such as web parameters, user
535      *                 security credentials and user locale
536      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
537      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
538      *                 for ths sytem.
539      * @throws ControllerException    upon error
540      * @throws NonHandleableException upon fatal error
541      */
542     protected void runPromptEditState(final ControllerRequest request,
543                                       final ControllerResponse response) throws ControllerException,
544             NonHandleableException {
545         try {
546             WizDefinition wizdef = getWizDef(request);
547             response.setTitle("'" + wizdef.getWizName() + "' Wizard");
548             Hashtable cache = response.getFormCache();
549 
550             String value = (String) cache.get("title");
551             if (value == null) {
552                 value = wizdef.getWizName();
553             }
554             Input input = new Input("title", "Name");
555             input.setDefaultValue(value);
556             response.add(input);
557             value = (String) cache.get("wizardClass");
558             if (value == null) {
559                 value = wizdef.getWizardClass();
560             }
561 
562             input = new Input("wizardClass", "Wizard Class");
563             input.setDefaultValue(value);
564             response.add(input);
565             value = (String) cache.get("summary");
566             if (value == null) {
567                 value = wizdef.getSummaryRaw();
568             }
569             input = new Input("summary", "Summary");
570             input.setDefaultValue(value);
571             response.add(input);
572 
573             Block wizSteps = new Block("steps");
574             buildWizardSteps(request, wizSteps, wizdef);
575             response.add(wizSteps);
576 
577             Transition addStep = new Transition("add", "add step", WizardStepController.class,
578                     PromptStepType.STATE_NAME);
579             addStep.addParam(WizStep.FLD_WIZID, wizdef.getId());
580             response.add(addStep);
581 
582             Block decisionMatrix = new Block("decisionMatrix");
583             buildDecisionMatrix(request, response, decisionMatrix, wizdef);
584             response.add(decisionMatrix);
585 
586             Transition save = new Transition("save", "save", getClass(), SelectionWizardManager.STATE_DO_EDIT);
587             save.addParam(WizardController.WIZ_PARAMETER_ID, wizdef.getId());
588             response.add(save);
589 
590             Transition run = getRunTrans(wizdef.getId());
591             response.add(run);
592         } catch (DBException ex) {
593             throw new ControllerException("Error accessing wizard definition", ex);
594         }
595     }
596 
597     /***
598      * Builds the steps for the current wizard.
599      *
600      * @param request       the ControllerRequest object
601      * @param steps         the block to populate with the steps.
602      * @param currentWizard the current wizard to build the steps for.
603      * @throws DBException         upon database access error
604      * @throws ControllerException upon ControllerElement related error
605      */
606     protected void buildWizardSteps(final ControllerRequest request,
607                                     final Block steps,
608                                     final WizDefinition currentWizard) throws DBException, ControllerException {
609         List pages = currentWizard.getPageDefinitions();
610         int counter = 1;
611         for (Iterator iterator = pages.iterator(); iterator.hasNext();) {
612             WizStep onestep = (WizStep) iterator.next();
613             Block oneRow = new Block("oneRow" + counter);
614             steps.add(oneRow);
615             oneRow.add(new Output("index", Integer.toString(counter)));
616 
617             Transition delete = new Transition("delete", "delete", WizardStepController.class,
618                     WizardStepController.STATE_PROMPT_DELETE);
619             delete.addParam(WizStep.FLD_ID, onestep.getId());
620             delete.addParam(WizardController.WIZ_PARAMETER_ID, currentWizard.getId());
621             oneRow.add(delete);
622 
623             Transition edit = new Transition("edit", onestep.getTitle(), WizardStepController.class,
624                     WizardStepController.STATE_PROMPT_EDIT);
625             edit.addParam(WizStep.FLD_ID, onestep.getId());
626             edit.addParam(WizStep.FLD_WIZID, onestep.getWizId());
627             oneRow.add(edit);
628 
629             // add trans for editing menu if possible
630             switch (onestep.getStepType()) {
631                 case PromptStepType.CUSTOM_PICKLIST: {
632 //                        String picklistId = onestep.getStepAttribute(StepAttributes.ATTRIBUTE_PICKLIST_ID);
633                     /***@todo Set up parameters for custom picklist */
634 //                    Transition editMenu = new Transition("Picklist ID: " + picklistId, PicklistAction.class
635 //                        , PicklistAction.PROMPT_LIST);
636 //                    editMenu.addParam(PickList.NODE_TYPE, part.getParentType());
637 //                    editMenu.addParam(PickList.ATTRIBUTE_TYPE, part.getPartType());
638 //                    oneRow.add(editMenu);
639                     break;
640                 }
641 
642                 case PromptStepType.INSTANCE_PICKLIST: {
643                     String nodeId = onestep.getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID);
644                     String attributeId = onestep.getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_FIELD);
645                     Transition editMenu = new Transition(onestep.getMenuAttribLabel() + ":" + nodeId, NodeAction.class,
646                             NodeAction.PROMPT_EDIT_ATTRIB);
647                     editMenu.addParam(Node.NODE_ID, nodeId);
648 
649                     Part part = new Part();
650                     part.setPartId(attributeId);
651                     part.retrieve();
652 
653                     editMenu.addParam(Attribute.ATTRIBUTE_TYPE, part.getPartType());
654                     editMenu.setName("picklist");
655                     oneRow.add(editMenu);
656                     break;
657                 }
658 
659                 case PromptStepType.MODEL_PICKLIST: {
660                     Part part = PartsFactory.getPart(
661                             onestep.getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD));
662                     if (part != null && part.hasPicklist()) {
663                         Transition editMenu = new Transition(part.getPartLabel(), PicklistAction.class,
664                                 PicklistAction.PROMPT_LIST);
665                         editMenu.addParam(PickList.NODE_TYPE, part.getParentType());
666                         editMenu.addParam(PickList.ATTRIBUTE_TYPE, part.getPartType());
667                         editMenu.setName("picklist");
668                         oneRow.add(editMenu);
669                         break;
670                     }
671                 }
672             }
673 
674             if (counter != 1) {
675                 Transition up = new Transition("up", "up",
676                         WizardStepController.class, WizardStepController.STATE_ORDERUP);
677                 up.addParam(WizStep.FLD_ID, onestep.getId());
678                 up.addParam(WizardController.WIZ_PARAMETER_ID, currentWizard.getId());
679                 oneRow.add(up);
680             }
681 
682             if (iterator.hasNext()) {
683                 Transition down = new Transition("down", "down",
684                         WizardStepController.class,
685                         WizardStepController.STATE_ORDERDOWN);
686                 down.addParam(WizStep.FLD_ID, onestep.getId());
687                 down.addParam(WizardController.WIZ_PARAMETER_ID, currentWizard.getId());
688                 oneRow.add(down);
689             }
690             counter++;
691         }
692     }
693 
694     /***
695      * Builds the decision matrix for the current wizard.
696      *
697      * @param request       the ControllerRequest object.
698      * @param response      the ControllerResponse object.
699      * @param matrix        the block to populate with the steps.
700      * @param currentWizard the current wizard to build the steps for.
701      * @throws DBException         upon database access error
702      * @throws ControllerException upon ControllerElement related error
703      */
704     protected void buildDecisionMatrix(final ControllerRequest request,
705                                        final ControllerResponse response,
706                                        final Block matrix,
707                                        final WizDefinition currentWizard) throws DBException, ControllerException {
708         List pertinentSteps = currentWizard.getPertinentDecisionSteps();
709 
710         if (pertinentSteps.size() == 0) {
711             //Nothing with menus defined.
712             return;
713         }
714 
715         Block headerRow = new Block("header");
716         headerRow.add(new Output("Cell #"));
717         for (int i = 0; i < pertinentSteps.size(); i++) {
718             WizStep oneStep = (WizStep) pertinentSteps.get(i);
719             headerRow.add(new Output(oneStep.getMenuAttribLabel()));
720         }
721 
722         headerRow.add(new Output("Prototype ID"));
723         Output titles = new Output("Prototype Title");
724         titles.setAttribute("SubTitle", "(updates after save)");
725         headerRow.add(titles);
726         matrix.add(headerRow);
727 
728         ObjectCombinator combinator = buildObjectCombinator(pertinentSteps);
729         Block items = new Block("items");
730         matrix.add(items);
731         int counter = 0;
732         DecisionMatrix decisionMatrix = new DecisionMatrix(currentWizard.getId());
733 
734         for (Iterator i = combinator.iterator(); i.hasNext();) {
735             Block oneRow = new Block("Row" + counter);
736             items.add(oneRow);
737             ValidValue[] pickList = (ValidValue[]) i.next();
738             Map idToVVMap = new HashMap(pickList.length);
739             oneRow.add(new Output("IterationId", Integer.toString(counter) + ")"));
740             for (int j = 0; j < pickList.length; j++) {
741                 ValidValue onePick = pickList[j];
742                 WizStep oneStep = (WizStep) pertinentSteps.get(j);
743                 idToVVMap.put(new Integer(oneStep.getFieldInt(WizStep.FLD_ID)), new Integer(onePick.getKey()));
744                 Output o = new Output("item" + j, combinator.getRowIndex(i, j) + ") "
745                         + onePick.getDescription());
746 
747                 oneRow.add(o);
748             }
749 
750             Input nodeVal = new Input("exId" + counter, "Example Id");
751             String value = response.getFormCache("exId" + counter);
752             boolean valueFound = false;
753             if (value == null || value.length() == 0) {
754                 WizDecisionSet decisionSet = decisionMatrix.getDecision(idToVVMap);
755                 if (decisionSet != null) {
756                     valueFound = true;
757                     nodeVal.setDefaultValue(decisionSet.getField(WizDecisionSet.FLD_DECISIONRESULT));
758                 } else {
759                     valueFound = true;
760                     nodeVal.setDefaultValue("");
761                 }
762             } else {
763                 nodeVal.setDefaultValue(value);
764                 WizDecisionSet set = new WizDecisionSet();
765                 try {
766                     int valueInt = Integer.parseInt(value);
767                     set.setWizardId(currentWizard.getId());
768                     set.setDecisionResult(valueInt);
769                     if (set.find()) {
770                         if (value.equals(set.getDecisionResult())) {
771                             valueFound = true;
772                         }
773                     } else {
774                         Output o = new Output("nodeLabel", "**[Error: No Such Node]**");
775                         o.setAttribute("error", "y");
776                         nodeVal.addNested(o);
777                     }
778                 } catch (NumberFormatException ex1) {
779                     valueFound = false;
780                 }
781             }
782 
783             oneRow.add(nodeVal);
784 
785             /*
786              * If there is a decision value set, AND it hasn't been edited for
787              * errors, then we load the value.  We can't do it if there is
788              * a value set in the request because we there may have been an
789              * error with the value picked.
790              */
791             if ((nodeVal.getDefaultValue() != null && nodeVal.getDefaultValue().length() > 0)
792                     && valueFound) {
793 
794                 Node node = new Node(request);
795                 node.setNodeId(nodeVal.getDefaultValue());
796                 try {
797                     node.retrieve();
798                     nodeVal.addNested(new Output("nodeLabel", node.getNodeTitle()));
799                 } catch (DBException ex) {
800                     Output o = new Output("nodeLabel", "**[Error: No Such Node]**");
801                     o.setAttribute("error", "y");
802                     nodeVal.addNested(o);
803                 }
804             }
805             counter++;
806         }
807     }
808 
809     /***
810      * Constructs an object combinator given a list of wizard steps that
811      * are pertinent to the current wizard.
812      *
813      * @param pertinentSteps List A list of WizSteps.
814      * @return ObjectCombinator constructed ObjectCombinator
815      */
816     private ObjectCombinator buildObjectCombinator(final List pertinentSteps) {
817         ValidValue[][] allPossibleValidValues = new ValidValue[pertinentSteps.size()][];
818         for (int i = 0; i < pertinentSteps.size(); i++) {
819             WizStep oneStep = (WizStep) pertinentSteps.get(i);
820             allPossibleValidValues[i] = oneStep.getMenuItems();
821         }
822 
823         return new ObjectCombinator(allPossibleValidValues, ValidValue.class);
824     }
825 
826     /***
827      * Runs the Run Wizard state.
828      *
829      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
830      *                 The ControllerRequest contains all the parameters such as web parameters, user
831      *                 security credentials and user locale
832      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
833      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
834      *                 for ths sytem.
835      * @throws ControllerException    upon error
836      * @throws NonHandleableException upon fatal error
837      */
838     protected void runRunWizardState(final ControllerRequest request,
839                                      final ControllerResponse response) throws ControllerException,
840             NonHandleableException {
841 
842         String id = request.getParameter(WizardController.WIZ_PARAMETER_ID);
843         Transition t = getRunTrans(id);
844         t.redirectTransition(request, response);
845     }
846 
847     /***
848      * synonym for wizard gateway 'list wizards'
849      *
850      * @param request  The <code>ControllerRequest</code> object handed to us by the framework.
851      *                 The ControllerRequest contains all the parameters such as web parameters, user
852      *                 security credentials and user locale
853      * @param response The <code>ControllerResponse</code> object handed to us by the framework.
854      *                 The ControllerResponse will be populated with the Inputs/Outputs/Transitions/Blocks
855      *                 for ths sytem.
856      * @throws ControllerException    upon error
857      * @throws NonHandleableException upon fatal error
858      */
859     protected void runPublicListState(final ControllerRequest request,
860                                       final ControllerResponse response) throws ControllerException,
861             NonHandleableException {
862         Transition t = new Transition("", WizardGatewayController.class, ListWizards.STATE_NAME);
863         t.redirectTransition(request, response);
864     }
865 
866     private Transition getRunTrans(final String id) {
867         Transition t = new Transition("run", WizardAction.class, WizardAction.STATE_BEGIN);
868         t.addParam(WizardController.WIZ_PARAMETER_ID, id);
869         return t;
870     }
871 
872 
873     /***
874      * Runs the wizard that the manager is associated with.
875      *
876      * @param wizard   WizDefinition
877      * @param request  ExpressoRequest
878      * @param response ExpressoResponse
879      * @throws ControllerException
880      */
881     public void run(final WizDefinition wizard, final ExpressoRequest request, final ExpressoResponse response) throws
882             ControllerException {
883         try {
884             Transition runTransition = getRunTrans(wizard.getId());
885             runTransition.redirectTransition(request, response);
886         } catch (DBException ex) {
887             throw new ControllerException("Error processing run transition.", ex);
888         }
889     }
890 
891     /***
892      * Begins an 'add' end-user transaction to create a new wizard.
893      *
894      * @param request  ExpressoRequest the request object.
895      * @param response ExpressoResponse the response object.
896      * @throws ControllerException
897      */
898     public void add(final ExpressoRequest request, final ExpressoResponse response) throws ControllerException {
899         Transition t = new Transition(STATE_PROMPT_ADDWIZARD, this);
900         t.redirectTransition(request, response);
901     }
902 
903     /***
904      * Begins an 'edit' end-user transaction to edit an existing wizard.
905      *
906      * @param wizard   The WizardDefinition to edit.
907      * @param request  ExpressoRequest the request object.
908      * @param response ExpressoResponse the response object.
909      * @throws ControllerException upon error.
910      */
911     public void edit(final WizDefinition wizard, final ExpressoRequest request,
912                      final ExpressoResponse response) throws ControllerException {
913 
914         Transition t = new Transition(STATE_PROMPT_EDIT, this);
915         try {
916             t.addParam(WizardController.WIZ_PARAMETER_ID, wizard.getId());
917         } catch (DBException ex) {
918             throw new ControllerException("Error querying object for id.", ex);
919         }
920         t.redirectTransition(request, response);
921     }
922 }