1 package com.sri.emo.wizard.branch;
2
3 import com.sri.emo.wizard.AbstractWizard;
4 import com.sri.emo.wizard.WizardMonitor;
5 import com.sri.emo.wizard.WizardPage;
6 import com.sri.emo.wizard.WizardException;
7 import java.util.Map;
8 import java.util.HashMap;
9 import java.io.Serializable;
10 import java.util.Stack;
11 import java.util.Arrays;
12 import java.util.List;
13 import com.sri.emo.wizard.defaults.SequentialWizard;
14 import java.util.Iterator;
15 import com.sri.emo.wizard.creation.StringUtil;
16 import java.util.ArrayList;
17
18 /***
19 * <p>Title: </p>
20 *
21 * <p>Description: </p>
22 *
23 * <p>Copyright: Copyright (c) 2003</p>
24 *
25 * <p>Company: </p>
26 *
27 * @author not attributable
28 * @version 1.0
29 */
30 public class BranchingWizard extends AbstractWizard {
31
32 /***
33 * An array of steps. Since it's sequential, wizSteps[0] == first
34 * step, wizSteps[1] == second step and so on.
35 */
36 final private BranchNode[] wizSteps;
37
38
39
40 /***
41 * Internal boolean flag -- if finished then page history's index will
42 * include the last page.
43 */
44 boolean finished = false;
45
46 /***
47 * Constructor that takes a monitor and an array of steps.
48 *
49 * @param wizMonitor the monitor for events occuring in the wizard.
50 * @param steps the WizardPage array ordered by the order
51 * they appear. Index 0 == first page.
52 */
53 public BranchingWizard(final WizardMonitor wizMonitor, final BranchNode[] steps) {
54 super(wizMonitor);
55 wizSteps = steps;
56
57 }
58
59 /***
60 * Retrieve the initial page for the wizard.
61 *
62 * @return WizardPage instance.
63 */
64 protected WizardPage getInitialPage() {
65 return wizSteps[0].getPage();
66 }
67
68 /***
69 * Retrieve the index of the current wizard. First page is page 1,
70 * Second Page is page 2, etc.
71 *
72 * @return int
73 */
74 public int getCurrentPageIndex() {
75 return (findWizardPageIndex(getCurrentPage()) + 1);
76 }
77
78 /***
79 * Retrieve the total number of pages in this sequential wizard.
80 *
81 * @return int the number of pages.
82 */
83
84
85
86
87 /***
88 * {@inheritDoc}
89 * <p>The SequentialWizard implementation allows for
90 * the browser back button
91 * to be pressed and still achieve status quo. What happens is that
92 * if we detect that the browser back button was ealier pressed
93 * (we can tell because the src page parameter is already on
94 * the backtrace stack), then we rollback to the page previous to the
95 * src page and re-execute.</p>
96 *
97 * @param src the wizard page source
98 * @param newData the data for the previous page.
99 * @return the next wizard page
100 * @throws WizardException upon error
101 * @throws ArrayIndexOutOfBoundsException if next was clicked and
102 * there should be no next button (ie last page)
103 */
104 public WizardPage next(WizardPage src, Serializable newData,
105 Serializable allData) throws WizardException {
106 finished = false;
107 System.out.println("BranchingWizard.next("+newData+")");
108
109 getMonitor().onForward(src);
110 src.setData(newData);
111
112 int val = findWizardPageIndex(src);
113 if (isNeedingRollback(src)) {
114 rollbackBacktrace(src);
115 }
116
117
118 checkAgainstIllegalJump(val);
119
120
121
122
123
124 if (src.getPageErrors() != null && src.getPageErrors().size() > 0) {
125 return wizSteps[val].getPage();
126 }
127
128
129 BranchNode nextNode = getTransition(wizSteps[val], allData);
130
131
132 if (!onNextPage(wizSteps[val].getPage(), nextNode.getPage(), newData)) {
133
134 return wizSteps[val].getPage();
135 } else {
136 super.getBackTrace().push(src);
137
138
139
140
141
142 getMonitor().onEnterPage(nextNode.getPage());
143 setCurrentPage(nextNode.getPage());
144
145 return nextNode.getPage();
146
147 }
148 }
149
150
151 /***
152 * {@inheritDoc}
153 * <p>The SequentialWizard implementation allows for
154 * the browser back button
155 * to be pressed and still achieve status quo. What happens is that
156 * if we detect that the browser back button was ealier pressed
157 * (we can tell because the src page parameter is already on
158 * the backtrace stack), then we rollback to the page previous to the
159 * src page and re-execute.</p>
160 *
161 * @param src the wizard page source
162 * @param newData the data for the previous page.
163 * @return the next wizard page
164 * @throws WizardException upon error
165 * @throws ArrayIndexOutOfBoundsException if next was clicked and
166 * there should be no next button (ie last page)
167 */
168 public WizardPage next(WizardPage src, Serializable newData) throws WizardException {
169 return next(src, newData, null);
170 }
171
172 /***
173 * getTransition
174 *
175 * @param branchNode BranchNode
176 * @param newData Serializable
177 * @return BranchNode
178 */
179 private BranchNode getTransition(BranchNode branchNode,
180 Serializable newData) {
181 System.out.println("BranchingWizard.getTransition()");
182 if(newData == null){
183 System.out.println("data is null so returning default page");
184 return branchNode.getDefault();
185 }
186 if(newData instanceof String){
187 System.out.println("data is a string so returning default page");
188 return branchNode.getDefault();
189 }
190 if(newData instanceof List){
191 System.out.println("getTransition() LIST: " + StringUtil.toString((List)newData));
192 return branchNode.getDefault();
193 }
194 System.out.println("newData is of type " + newData.getClass());
195 Map map = (Map) newData;
196 String key = branchNode.getTransitionKey();
197 System.out.println("BranchingWizard.getTransition(), map = " + map);
198 System.out.println("transition key = " + key);
199 System.out.println("transition to = " + (String) map.get(key));
200 System.out.println("next node = " + branchNode.getNext((String) map.get(key)));
201 return branchNode.getNext((String) map.get(key));
202
203 }
204
205 /***
206 * Forgivingly converts page id to an internal integer. Allowed parameters
207 * can be either Strings with integers or java.lang.Integer object.
208 *
209 * @param pageId Serializable the requested page id.
210 * @return int the converted value.
211 */
212 protected Integer convertKeyToPageId(Serializable pageId) {
213 assert pageId != null;
214
215 if (pageId instanceof String) {
216 return new Integer((String) pageId);
217 } else if (pageId instanceof Integer) {
218 return ((Integer) pageId);
219 } else {
220 throw new IllegalArgumentException(
221 "Page id must be some sort of integer, either in java.lang.Integer or java.lang.String form");
222 }
223 }
224
225 /***
226 * Allows programmatic rewinding to a previous page.
227 *
228 * @param pageId Serializable the page key.
229 * @return WizardPage the resulting page.
230 */
231 public WizardPage backupToPage(Serializable pageKey) throws ArrayIndexOutOfBoundsException,
232 IllegalArgumentException {
233 assert pageKey != null;
234 System.out.println("BranchingWizard.backupToPage("+pageKey+")");
235 Integer pageId = convertKeyToPageId(pageKey);
236
237
238 WizardPage requestedPage = null;
239 for (int i = 0; i < wizSteps.length; i++) {
240 if (wizSteps[i].getPage().getId().equals(pageId)) {
241 requestedPage = wizSteps[i].getPage();
242 break;
243 }
244 }
245
246 if (requestedPage == null) {
247 throw new IllegalArgumentException("Could not find page with id: " + pageKey.toString());
248 }
249
250 rollbackBacktrace(requestedPage);
251 setCurrentPage(requestedPage);
252 getMonitor().onBack(requestedPage);
253
254 getMonitor().onEnterPage(requestedPage);
255 return requestedPage;
256 }
257
258
259 /***
260 * Template method that gets called after the next page has been determined
261 * in the sequence.
262 *
263 * @param previousPage The previousPage that was displayted
264 * @param nextPage WizardPage the next page that is about to be displayed.
265 * @param previousPageData Serializable the previously entered data from the
266 * last page where the user clicked 'next'.
267 * @return boolean true if you wish for the user to continue. False if
268 * the page data needs to be vetoed.
269 * @throws WizardException upon error.
270 */
271 protected boolean onNextPage(final WizardPage previousPage,
272 final WizardPage nextPage,
273 final Serializable previousPageData) throws WizardException {
274
275 return (previousPage.getPageErrors() == null);
276 }
277
278 /***
279 * Checks that there were no illegally jumping forward moves
280 * caused by parameter manipulation.
281 *
282 * @param src WizardPage the wizard page we're checking against.
283 * @throws IllegalStateException if the check fails.
284 */
285 protected void checkAgainstIllegalJump(int pageIndex) {
286 Stack backtrace = getBackTrace();
287 if (backtrace.size() == 0) {
288 if (pageIndex == 0) {
289 return;
290 } else {
291
292 }
293 } else {
294 WizardPage previous = (WizardPage) backtrace.peek();
295 int previousIndex = findWizardPageIndex(previous);
296 int current = pageIndex;
297
298
299
300
301
302
303 if (!wizSteps[previousIndex].containsTransitionTo(wizSteps[current])) {
304
305 }
306 }
307 }
308
309 /***
310 * {@inheritDoc}
311 *
312 * @param s Serializable
313 * @return WizardPage
314 */
315 public WizardPage getPageById(Serializable s) {
316 for (int i = 0; i < wizSteps.length; i++) {
317 WizardPage onePage = wizSteps[i].getPage();
318
319 if (onePage.getId().equals(s)) {
320 return onePage;
321 }
322 }
323
324 throw new IllegalArgumentException("Key: " + s.toString()
325 + " does not exist for this wizard");
326 }
327
328 /***
329 * Retrieve all the steps for the wizard.
330 *
331 * @return WizardPage[]
332 */
333 protected BranchNode[] getAllSteps() {
334 return wizSteps;
335 }
336
337 /***
338 * Checks the backtrace for presence of the given wizard page.
339 *
340 * @param src WizardPage the source wizard page.
341 * @return boolean true.
342 */
343 protected boolean isNeedingRollback(WizardPage src) {
344 Stack backtrace = getBackTrace();
345 boolean foundInStack = false;
346 for (int i = 0; i < backtrace.size(); i++) {
347 WizardPage onePage = (WizardPage) backtrace.get(i);
348 if (src.equals(onePage)) {
349 foundInStack = true;
350 break;
351 }
352 }
353
354 return foundInStack;
355 }
356
357
358 /***
359 * Rolls back the backtrace stack if the src page is already
360 * on the stack, indicating that the browser pushed the back button.
361 *
362 * @param src WizardPage the source of the 'next' event, so we rollback
363 * so that it is no longer on the backtrace.
364 */
365 protected void rollbackBacktrace(final WizardPage src) {
366 Stack backtrace = getBackTrace();
367
368 while (!backtrace.empty()) {
369 WizardPage onePage = (WizardPage) backtrace.pop();
370 if (src.equals(onePage)) {
371 break;
372 }
373 }
374 }
375
376
377 /***
378 * Find the wizard page index so we can define what the next page is.
379 *
380 * @param toFind the wizard page to find.
381 * @return integer, the index inside wizSteps
382 * @throws IllegalStateException if we're unable to find the page.
383 */
384 protected int findWizardPageIndex(final WizardPage toFind) {
385
386 for (int i = 0; i < wizSteps.length; i++) {
387 if (wizSteps[i].getPage() == toFind) {
388 return i;
389 }
390 }
391 throw new IllegalStateException("Unable to find Wizard Page");
392 }
393
394
395 /***
396 * Retrieve all the data for the wizard keyed by page id.
397 *
398 * @return Map of WizardPage objects keyed by id.
399 * @throws WizardException upon error
400 */
401 public Map getAllData() throws WizardException {
402 Map returnValue = new HashMap(wizSteps.length);
403
404 for (int i = 0; i < wizSteps.length; i++) {
405 WizardPage oneStep = wizSteps[i].getPage();
406 returnValue.put(oneStep.getId(), oneStep.getData());
407 }
408
409 return returnValue;
410 }
411
412 /***
413 * {@inheritDoc}
414 *
415 * @return List
416 * @throws WizardException upon retrieval order.
417 */
418 public List getStepHistory() throws WizardException {
419
420 int currentIndex = getCurrentPageIndex() - 1;
421
422
423 if (finished) {
424
425
426 currentIndex++;
427 }
428
429
430
431 WizardPage history[] = new WizardPage[currentIndex];
432 for (int i = 0; i < currentIndex; i++) {
433 history[i] = wizSteps[i].getPage();
434 }
435 return Arrays.asList(history);
436 }
437
438 /***
439 * Will return a history of steps without accounting for any of the branching
440 * @return List
441 * @throws WizardException
442 */
443 public List getMainStepHistory() throws WizardException {
444
445 int currentIndex = getCurrentPageIndex() - 1;
446
447
448 if (finished) {
449
450
451 currentIndex++;
452 }
453
454
455
456
457 ArrayList list = new ArrayList(currentIndex);
458 for (int i = 0; i < currentIndex; i++) {
459 if (includeInMainStep(wizSteps[i])) {
460 list.add(wizSteps[i].getPage());
461 }
462 }
463 return list;
464 }
465
466 protected boolean includeInMainStep(BranchNode node){
467 return true;
468 }
469
470
471 /***
472 * Override of Object.equals to check field by field.
473 * {@inheritDoc}
474 *
475 * @param parm1 the object SequentialWizard
476 * @return true if the fields are equal.
477 */
478 public boolean equals(final Object parm1) {
479 if (super.equals(parm1)) {
480 BranchingWizard other = (BranchingWizard) parm1;
481 boolean returnValue = true;
482 returnValue &= (wizSteps.length == other.wizSteps.length);
483 if (returnValue) {
484 for (int i = 0; i < wizSteps.length; i++) {
485 returnValue &= wizSteps[i].equals(other.wizSteps[i]);
486 }
487 }
488 return returnValue;
489 } else {
490 return false;
491 }
492 }
493
494 /***
495 * Returns a hash code value for the object.
496 *
497 * @return a hash code value for this object.
498 */
499 public int hashCode() {
500 return super.hashCode();
501 }
502
503 /***
504 * Processes the final finish and returns some sort of data that can be
505 * whatever is desired.
506 *
507 * @param src WizardPage the source of the event.
508 * @param data Serializable the data given during wht wizard post.
509 * @param additonalParams anything that the underlying wizard needs. The
510 * values are set by the Application Controller.
511 * @return Object: whatever object the wizard returns after processing.
512 * @throws WizardException upon error.
513 */
514 public Object processFinish(WizardPage src, Serializable data, Map additonalParams) throws WizardException {
515 finished = true;
516 return super.processFinish(src, data, additonalParams);
517 }
518
519 /***
520 * {@inheritDoc}
521 *
522 * @return WizardPage instance.
523 * @throws WizardException upon error.
524 */
525 public WizardPage previous() throws WizardException {
526 finished = false;
527 return super.previous();
528 }
529
530 /***
531 * Override of toString().
532 *
533 * @return Wizard Title
534 */
535 public String toString() {
536 return getTitle() + " Branching Wizard";
537 }
538
539 }