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    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.wizard.selection;
11  
12  import com.jcorporate.expresso.core.controller.Transition;
13  import com.jcorporate.expresso.core.db.DBException;
14  import com.sri.emo.dbobj.WizDefinition;
15  import com.sri.emo.dbobj.WizStep;
16  import com.sri.emo.wizard.*;
17  import com.sri.emo.wizard.defaults.EmoWizardMetadata;
18  import com.sri.emo.wizard.defaults.Log4jWizMonitor;
19  import com.sri.emo.wizard.defaults.SequentialWizard;
20  import com.sri.emo.wizard.expressoimpl.ExpressoLink;
21  import com.sri.emo.wizard.expressoimpl.WizardController;
22  
23  import java.io.Serializable;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  
30  
31  /***
32   * Wizard Factory for EMO wizards.
33   * It also happens to implement WizardMementoConverter because it is most
34   * intimate with the inner workings of the wizards, therefore it is best able
35   * to do the dehydration and rehydration grunt work.  That said, the client
36   * shouldn't know that!
37   *
38   * @author Michael Rimov
39   */
40  public class EmoSelectionWizardFactory implements WizardFactory, WizardFactorySPI {
41      /***
42       * Wizard Id.
43       */
44      private int wizardId;
45  
46      /***
47       * Constant for the default controller used for Emo.
48       */
49      public final Class DEFAULT_CONTROLLER = com.sri.emo.wizard.selection.WizardAction.class;
50  
51  
52      /***
53       * Constructor that takes a data context.
54       */
55      public EmoSelectionWizardFactory() {
56      }
57  
58      /***
59       * Sets the wizard id for the given selection factory. In other words,
60       * what is the wizard id of what is going to be constructed.
61       *
62       * @param newId int
63       */
64      public void setWizardId(int newId) {
65          wizardId = newId;
66      }
67  
68  
69      protected Transition commonTransitionConstruction(final int id) {
70          Transition t = new Transition();
71          t.setControllerObject(DEFAULT_CONTROLLER);
72          t.addParam(WizardController.WIZ_PARAMETER_ID, Integer.toString(id));
73          return t;
74      }
75  
76      /***
77       * Builds the link that corresponds to the 'next' location.
78       *
79       * @param id int the wizard id
80       * @return Link
81       */
82      protected ExpressoLink buildNextLink(final int id) {
83          Transition t = commonTransitionConstruction(id);
84          t.setState(WizardController.NEXT_STATE);
85          t.setLabel("Next");
86          t.setName("Next");
87          return new ExpressoLink(t);
88      }
89  
90      /***
91       * Builds the link that corresponds to the 'finish' location.
92       *
93       * @param id int the wizard id
94       * @return Link
95       */
96      protected ExpressoLink buildFinishLink(final int id) {
97          Transition t = commonTransitionConstruction(id);
98          t.setState(WizardController.FINISH_STATE);
99          t.setLabel("Finish");
100         t.setName("Finish");
101 
102         return new ExpressoLink(t);
103     }
104 
105     /***
106      * Builds the link that corresponds to the 'cancel' location.
107      *
108      * @param id int the wizard id
109      * @return Link
110      */
111     protected ExpressoLink buildCancelLink(final int id) {
112         Transition t = commonTransitionConstruction(id);
113         t.setState(WizardController.CANCEL_STATE);
114         t.setLabel("Cancel");
115         t.setName("Cancel");
116 
117         return new ExpressoLink(t);
118     }
119 
120     /***
121      * Builds the link that corresponds to the 'back' location.
122      *
123      * @param id int the wizard id
124      * @return Link
125      */
126     protected ExpressoLink buildBackLink(final int id) {
127         Transition t = commonTransitionConstruction(id);
128         t.setState(WizardController.PREVIOUS_STATE);
129         t.setLabel("Back");
130         t.setName("Back");
131 
132         return new ExpressoLink(t);
133     }
134 
135     public Wizard buildWizard() throws WizardException {
136         try {
137             WizDefinition wizard = getWizDefinition();
138 
139             int id = wizard.getFieldInt(WizDefinition.FLD_ID);
140 
141             ExpressoLink next = buildNextLink(id);
142             ExpressoLink finish = buildFinishLink(id);
143             ExpressoLink cancel = buildCancelLink(id);
144             ExpressoLink back = buildBackLink(id);
145 
146             List steps = constructSteps(wizard, next, finish, cancel, back);
147 
148             return constructWizard(wizard, steps);
149         } catch (DBException ex) {
150             throw new WizardException("Error building wizard: "
151                     + wizardId, ex);
152         }
153     }
154 
155     /***
156      * Retrieve the WizDefinition associated with the defined id in the
157      * constructor.
158      *
159      * @return WizDefinition associated with the id.
160      * @throws DBException if the wizdefinition cannot be retrieved.
161      */
162     protected WizDefinition getWizDefinition() throws DBException {
163         WizDefinition wizard = new WizDefinition();
164         wizard.setId(wizardId);
165         wizard.retrieve();
166         return wizard;
167     }
168 
169     /***
170      * Constructs the steps of the wizard.  Override to customize your
171      * step construction.
172      *
173      * @param wizard The Wizard Definition DBObject
174      * @param next   the Next Link instance
175      * @param finish the Finish Link instance
176      * @param cancel the Cancel Link instance
177      * @param back   the Back Link instance
178      * @return List of Wizard Steps
179      * @throws WizardException upon construction error.
180      * @throws DBException     upon database access error.
181      */
182     private List constructSteps(final WizDefinition wizard,
183                                 final ExpressoLink next, final ExpressoLink finish,
184                                 final Link cancel, ExpressoLink back) throws WizardException, DBException {
185 
186         List steps = wizard.getPageDefinitions();
187         List returnValue = new ArrayList();
188         boolean firstPage = true;
189 
190         for (Iterator i = steps.iterator(); i.hasNext();) {
191             WizStep oneStep = (com.sri.emo.dbobj.WizStep) i.next();
192             EmoWizardMetadata pageMetadata;
193 
194             ExpressoLink nextPageLink = cloneTransition(next, oneStep.getId());
195             ExpressoLink backPageLink = cloneTransition(back, oneStep.getId());
196 
197             if (firstPage) {
198                 firstPage = false;
199                 if (i.hasNext()) {
200                     pageMetadata = new EmoWizardMetadata();
201                     pageMetadata.setCancelLink(cancel);
202                     pageMetadata.setNextLink(nextPageLink);
203                 } else {
204                     // first and only page
205                     //However, node determination includes other pages.
206                     pageMetadata = new EmoWizardMetadata();
207                     pageMetadata.setCancelLink(cancel);
208                     pageMetadata.setNextLink(nextPageLink);
209                 }
210 
211                 //Data entry is not required for initial page
212                 pageMetadata.setEntryRequired(false);
213 //This has changed because finish is now a separetely defined page.
214 //            } else if (!i.hasNext()) {
215 //                //last page
216 //                pageMetadata = new EmoWizardMetadata();
217 //                pageMetadata.setCancelLink(cancel);
218 //                pageMetadata.setPreviousLink(backPageLink);
219 //                pageMetadata.setFinishLink(finishPageLink);
220 //
221 //                //Data entry is required for last page in sequence.
222 //                pageMetadata.setEntryRequired(true);
223             } else {
224                 //in the middle
225                 pageMetadata = new EmoWizardMetadata();
226                 pageMetadata.setCancelLink(cancel);
227                 pageMetadata.setPreviousLink(backPageLink);
228                 pageMetadata.setNextLink(nextPageLink);
229 
230                 //Date entry is required for any page inbetween.
231                 pageMetadata.setEntryRequired(true);
232             }
233 
234             WizardPage page = oneStep.getWizardPage(pageMetadata);
235             returnValue.add(page);
236         } // for
237 
238         //
239         //Build display summary page.
240         //
241         EmoWizardMetadata pageMetadata = new EmoWizardMetadata();
242         ExpressoLink nextPageLink = cloneTransition(next, DisplaySummaryPage.PAGE_ID.toString());
243         ExpressoLink finishPageLink = cloneTransition(finish, DisplaySummaryPage.PAGE_ID.toString());
244         ExpressoLink backPageLink = cloneTransition(back, DisplaySummaryPage.PAGE_ID.toString());
245         pageMetadata.setCancelLink(cancel);
246         pageMetadata.setNextLink(nextPageLink);
247         pageMetadata.setPreviousLink(backPageLink);
248         pageMetadata.setViewId("displayResults");
249         WizardPage page = new DisplaySummaryPage(pageMetadata);
250         returnValue.add(page);
251 
252         //
253         //Build finish page.
254         //
255         pageMetadata = new EmoWizardMetadata();
256 
257         nextPageLink = cloneTransition(next, NameNodePage.PAGE_ID);
258         finishPageLink = cloneTransition(finish, NameNodePage.PAGE_ID);
259         backPageLink = cloneTransition(back, NameNodePage.PAGE_ID);
260 
261         pageMetadata.setCancelLink(cancel);
262         pageMetadata.setPreviousLink(backPageLink);
263         pageMetadata.setFinishLink(finishPageLink);
264         pageMetadata.setViewId("promptTemplateName");
265 
266         //Data entry is required for last page in sequence.
267         pageMetadata.setEntryRequired(true);
268         pageMetadata.setEntry(true);
269         pageMetadata.setTitle("Name Your Template");
270 
271         page = new NameNodePage(new Integer(NameNodePage.PAGE_ID), pageMetadata);
272 
273         returnValue.add(page);
274 
275 
276         return returnValue;
277     }
278 
279     protected ExpressoLink cloneTransition(final ExpressoLink next, String pageId) {
280         ExpressoLink nextPageLink = null;
281         try {
282             nextPageLink = (ExpressoLink) next.clone();
283         } catch (CloneNotSupportedException e) {
284             throw new RuntimeException(e);
285         }
286         nextPageLink.getTransition().addParam(WizardController.WIZ_PAGE_PARAMETER, pageId);
287         return nextPageLink;
288     }
289 
290     protected void addFinalStepToConstructedSteps(final WizDefinition definition, final List predefinedSteps) throws
291             DBException {
292     }
293 
294     private void setWizardForStepsThatNeedIt(final Wizard constructedWizard, final List steps) {
295         for (Iterator i = steps.iterator(); i.hasNext();) {
296             WizardPage oneStep = (WizardPage) i.next();
297 
298             if (oneStep instanceof DisplaySummaryPage) {
299                 ((DisplaySummaryPage) oneStep).setMyOwner(constructedWizard);
300             }
301         }
302 
303     }
304 
305     /***
306      * Constructs the wizard itself.
307      *
308      * @param definition the WizDefinition dbobject for this wizard.
309      * @param steps      the List of steps that were constructed previously in
310      *                   the constructSteps function.
311      * @return fully constructed Wizard instance.
312      * @throws DBException upon database exception error.
313      */
314     protected Wizard constructWizard(final WizDefinition definition, final List steps) throws DBException {
315         String wizardClassString = definition.getWizardClass();
316         Class wizardClass;
317         try {
318             wizardClass = Thread.currentThread().getContextClassLoader()
319                     .loadClass(wizardClassString);
320             if (!SequentialWizard.class.isAssignableFrom(wizardClass)) {
321                 throw new IllegalArgumentException("If you do not"
322                         + " implement a wizard"
323                         + " that derives from SequentialWizard and uses the same "
324                         + "constructor then you need to implement your own"
325                         + " Wizard Factory implementation");
326 
327             }
328 
329             Constructor c = wizardClass.getConstructor(new Class[]{
330                     WizardMonitor.class, WizardPage[].class});
331             AbstractWizard emoWizard = (AbstractWizard) c
332                     .newInstance(
333                             new Object[]{constructMonitor(), (WizardPage[]) steps.toArray(
334                                     new WizardPage[steps.size()])});
335 
336             emoWizard.setId(new Integer(definition.getId()));
337             emoWizard.setTitle(definition.getWizName());
338             emoWizard.setSummary(definition.getSummary());
339             setWizardForStepsThatNeedIt(emoWizard, steps);
340             return emoWizard;
341         } catch (ClassNotFoundException ex) {
342             throw new IllegalArgumentException("Unknown class: "
343                     + wizardClassString
344                     + " edit your wizard definition to solve this problem");
345         } catch (InvocationTargetException ex) {
346             throw new DBException("An Exception was thrown constructing the " +
347                     "wizard", ex);
348         } catch (IllegalAccessException ex) {
349             throw new IllegalArgumentException("The wizard specified: "
350                     + wizardClassString + " does not have a public constructor"
351                     + " like the default.  You may need to implement your own "
352                     + "wizard factory.");
353         } catch (InstantiationException ex) {
354             throw new DBException("There was an error constructing your "
355                     + "wizard.", ex);
356         } catch (NoSuchMethodException ex) {
357             throw new IllegalArgumentException("No appropriate constructor"
358                     + " found:" + ex.getMessage());
359         }
360 
361     }
362 
363     /***
364      * Override to provide your own monitor, possibly for testing purposes.
365      *
366      * @return WizardMonitor instance.
367      */
368     protected WizardMonitor constructMonitor() {
369         return new Log4jWizMonitor();
370     }
371 
372     /***
373      * Retrieve the controller for the wizard.  Override for your own
374      * customization.
375      *
376      * @return java.lang.Class should be instance of
377      *         com.jcorporate.expresso.core.controller.Controller
378      */
379     protected Class getWizardController() {
380         try {
381             String className = getWizDefinition().getController();
382             if (className == null || className.length() == 0) {
383                 return DEFAULT_CONTROLLER;
384             } else {
385                 try {
386                     return Class.forName(className, true,
387                             Thread.currentThread().getContextClassLoader());
388                 } catch (ClassNotFoundException ex1) {
389                     return DEFAULT_CONTROLLER;
390                 }
391             }
392         } catch (DBException ex) {
393             return DEFAULT_CONTROLLER;
394         }
395 
396     }
397 
398     /***
399      * getWizardId
400      *
401      * @return String
402      */
403     protected int getWizardId() {
404         return wizardId;
405     }
406 
407 
408     public Serializable toMemento(Wizard target) throws WizardException {
409         return (Serializable) target;
410     }
411 
412     public Wizard fromMemento(Serializable previouslyExternalized) throws WizardException {
413         return (Wizard) previouslyExternalized;
414     }
415 
416 }