1 package com.sri.emo.wizard.completion;
2
3 import com.jcorporate.expresso.core.controller.Transition;
4 import com.jcorporate.expresso.core.db.DBException;
5 import com.jcorporate.expresso.core.dbobj.ValidValue;
6 import com.sri.common.dbobj.ObjectNotFoundException;
7 import com.sri.emo.controller.CompletionWizardAction;
8 import com.sri.emo.dbobj.*;
9 import com.sri.emo.wizard.Wizard;
10 import com.sri.emo.wizard.WizardException;
11 import com.sri.emo.wizard.completion.model.CompletionBean;
12 import com.sri.emo.wizard.completion.model.CompletionPartsBean;
13 import com.sri.emo.wizard.completion.model.FieldCompletion;
14 import com.sri.emo.wizard.defaults.EmoWizardMetadata;
15 import com.sri.emo.wizard.defaults.EmoWizardPage;
16 import com.sri.emo.wizard.expressoimpl.WizardController;
17
18 import java.io.Serializable;
19 import java.util.*;
20
21 /***
22 * Constructs Completion Wizards.
23 *
24 * @author Michael Rimov
25 * @version 1.0
26 */
27 public class EmoCompletionFactory extends CompletionBuilder {
28
29
30 /***
31 * A sorted Set of special ids.
32 */
33 private SortedSet preMultiAttributeStepIds = new TreeSet();
34
35 /***
36 * Initial prestep id must be lower than any special steps in the EmoCompletionWizard
37 */
38 private static final int INITIAL_PRESTEP_ID = -3;
39
40 /***
41 * Constructs a given wizard.
42 *
43 * @param completionRepository the CompletionRepository to draw the various
44 * wizards from.
45 */
46 public EmoCompletionFactory(final CompletionRepository completionRepository) {
47 super(completionRepository);
48 }
49
50 /***
51 * Override of base class to change controller
52 *
53 * @param id int
54 * @return Transition
55 */
56 protected Transition commonTransitionConstruction(final int id) {
57 Transition t = new Transition();
58 t.setControllerObject(CompletionWizardAction.class);
59 t.addParam(WizardController.WIZ_PARAMETER_ID, Integer.toString(id));
60 return t;
61 }
62
63
64 /***
65 * Constructs the wizard itself.
66 *
67 * @param definition the WizDefinition dbobject for this wizard.
68 * @param steps the List of steps that were constructed previously in
69 * the constructSteps function.
70 * @return fully constructed Wizard instance.
71 * @throws com.jcorporate.expresso.core.db.DBException
72 * upon database exception error.
73 */
74 protected Wizard constructWizard(final WizDefinition definition, final List steps) throws DBException {
75 EmoCompletionWizard cwiz = (EmoCompletionWizard) super.constructWizard(definition, steps);
76
77 CompletionBean bean = null;
78 try {
79 bean = repository.findById(cwiz.getId());
80 } catch (ObjectNotFoundException e) {
81 throw new DBException("Could not find underlying wizard of id: "
82 + definition.getFieldInt(WizDefinition.FLD_ID), e);
83 }
84
85
86 cwiz.setCompletionBean(bean);
87
88 return cwiz;
89 }
90
91 /***
92 * Constructs a new wizard instance and returns it.
93 *
94 * @return Wizard instance
95 * @throws WizardException upon error building it.
96 */
97 public Wizard buildWizard() throws WizardException {
98 try {
99
100 WizDefinition wizard = this.getWizDefinition();
101
102 List steps = constuctCompletionSteps();
103
104 return constructWizard(wizard, steps);
105 } catch (DBException ex) {
106 throw new WizardException("Error building wizard: " + this.getWizardId(), ex);
107 }
108 }
109
110
111 /***
112 * Constructs the steps of the wizard. Override to customize your
113 * step construction.
114 *
115 * @return List of Wizard Steps
116 * @throws DBException if there is an error querying the underlying data object.
117 */
118 private List constuctCompletionSteps() throws DBException {
119 List pages = new ArrayList(getDefinition().getCompletionParts().size() + 3);
120
121 pages.add(constructTitlePage());
122 pages.addAll(constructPartsPages());
123 pages.add(constructCompletionPage());
124
125 return pages;
126 }
127
128 /***
129 * Probably the most involved portion of the builder -- go through the Parts
130 * and build the various entry pages.
131 *
132 * @return List
133 */
134 private List constructPartsPages() {
135 List results = new ArrayList();
136 for (Iterator i = getDefinition().getCompletionParts().iterator(); i.hasNext();) {
137 CompletionPartsBean partsbean = (CompletionPartsBean) i.next();
138
139 if (FieldCompletion.WIZARD.equals(partsbean.getFieldCompletion())) {
140 try {
141 Part stepPart = partsbean.getPart();
142 String id = stepPart.getId();
143
144 EmoWizardMetadata pageMetadata;
145
146
147 if (stepPart.isSharedNodeAttrib()) {
148 RelationPageMetadata relationMetadata = new RelationPageMetadata();
149 relationMetadata.setMaxEntries(partsbean.getMaxEntries());
150 relationMetadata.setMinEntries(partsbean.getMinEntries());
151 relationMetadata.setNodeId(getDefinition().getTargetId().toString());
152 relationMetadata.setNodeType(stepPart.getPartType());
153 relationMetadata.setPartNodeRelation(stepPart.getNodeRelation());
154 relationMetadata.setEntry(true);
155 relationMetadata.setEntryRequired(false);
156 relationMetadata.setViewId("MultiCheckbox");
157
158
159 pageMetadata = relationMetadata;
160
161 } else if (stepPart.areMultipleAttributesAllowed()) {
162
163 pageMetadata = new EmoWizardMetadata();
164 } else {
165 pageMetadata = new EmoWizardMetadata();
166 }
167
168
169 ValidValue[] selectionMenu = null;
170
171 if (stepPart.hasPicklist()) {
172
173
174
175 ValidValue[] tempSelectionMenu = stepPart.getPickListValidValues();
176 selectionMenu = new ValidValue[tempSelectionMenu.length + 1];
177 selectionMenu[0] = new ValidValue("", "(No Selection Made)");
178 System.arraycopy(tempSelectionMenu, 0, selectionMenu, 1, tempSelectionMenu.length);
179
180 pageMetadata.setEntryRequired(false);
181 pageMetadata.setEntry(true);
182 } else if (stepPart.isHaveCustomHandler()) {
183
184
185
186
187
188 pageMetadata = this.constructCustomHandlerPageMetadata(partsbean, id);
189 } else if (stepPart.areMultipleAttributesAllowed() && !stepPart.isSharedNodeAttrib()) {
190
191 pageMetadata = this.constructMultiAttributePageMetadata(partsbean, id);
192
193
194
195 if (!minEqualsMax(partsbean)) {
196
197
198
199
200
201
202
203
204
205 Integer howmanyPartsId = generatePreMultiAttributePageId();
206 EmoWizardMetadata howManyEntriesMetadata = constructHowManyEntriesPageMetadata(partsbean,
207 howmanyPartsId.toString());
208
209
210 EmoWizardPage preMultiEntryPage = new EmoWizardPage(howmanyPartsId, howManyEntriesMetadata,
211 null);
212
213
214 Attribute[] currentNumberAttributes = getDefinition().getCurrentNode().getAttributes(
215 partsbean.getPart().getPartType());
216 preMultiEntryPage.setData(Integer.toString(currentNumberAttributes.length));
217 results.add(preMultiEntryPage);
218 }
219
220
221 } else {
222
223 pageMetadata.setEntryRequired(false);
224 }
225
226 setCommonPartsParameters(partsbean, id, pageMetadata);
227
228
229 EmoWizardPage wizardPage = constructWizardPage(partsbean, id, pageMetadata, selectionMenu);
230
231 results.add(wizardPage);
232 } catch (Exception e) {
233 e.printStackTrace();
234 throw new RuntimeException(e);
235 }
236 }
237 }
238
239 return results;
240 }
241
242 /***
243 * Constructs the actual wizard page.
244 *
245 * @param partsbean CompletionPartsBean the current part for the page.
246 * @param id finalString the page id.
247 * @param pageMetadata EmoWizardMetadata the wizard metadata that has been
248 * constructed earlier in the process.
249 * @param selectionMenu ValidValue[] the selection menu (may be null)
250 * @return EmoWizardPage or derivative.
251 * @throws DBException upon database query error.
252 * @throws Exception upon custom part handler error.
253 */
254 private EmoWizardPage constructWizardPage(final CompletionPartsBean partsbean,
255 final String id,
256 final EmoWizardMetadata pageMetadata,
257 final ValidValue[] selectionMenu) throws DBException, Exception {
258 EmoWizardPage wizardPage;
259 Part stepPart = partsbean.getPart();
260 Node wizardTarget = getDefinition().getCurrentNode();
261 Attribute[] attributes = wizardTarget.getAttributes(stepPart.getPartType());
262
263
264 if (stepPart.isHaveCustomHandler()) {
265 wizardPage = constructCustomHandlerPage(id, pageMetadata);
266 } else if (stepPart.isSharedNodeAttrib()) {
267 wizardPage = new RelationWizardPage(new Integer(id), pageMetadata);
268
269
270
271
272
273
274
275
276
277 } else if (stepPart.areMultipleAttributesAllowed()) {
278
279 wizardPage = new MultiEntryWizardPage(new Integer(id), pageMetadata);
280 if (minEqualsMax(partsbean)) {
281 ((MultiEntryWizardPage) wizardPage).setNumEntries(partsbean.getMinEntries().intValue());
282 }
283
284 List dataToSet = new ArrayList(attributes.length);
285 for (int i = 0; i < attributes.length; i++) {
286 dataToSet.add(attributes[i].getAttribValueRaw());
287 }
288
289 if (dataToSet.size() > 0) {
290
291 wizardPage.setData((Serializable) dataToSet);
292 }
293 } else {
294 wizardPage = new EmoWizardPage(new Integer(id), pageMetadata, selectionMenu);
295 if (attributes.length == 0) {
296 wizardPage.setData(null);
297 } else {
298 assert attributes.length == 0 || attributes.length == 1:
299 "Expected only zero or one attributes returned for single attribute";
300 wizardPage.setData(attributes[0].getAttribValueRaw());
301 }
302 }
303 return wizardPage;
304 }
305
306
307 /***
308 * Special case. If min equals max values in multi-entry pages.
309 *
310 * @param partsbean CompletionPartsBean the current part.
311 * @return boolean true if min equals max.
312 */
313 private boolean minEqualsMax(final CompletionPartsBean partsbean) {
314 if (partsbean.getMinEntries() != null && partsbean.getMaxEntries() != null) {
315 if (partsbean.getMinEntries().intValue() == partsbean.getMaxEntries().intValue()) {
316 return true;
317 }
318 }
319 return false;
320 }
321
322
323 private void setCommonPartsParameters(final CompletionPartsBean partsbean, final String id,
324 final EmoWizardMetadata pageMetadata) throws DBException {
325
326 pageMetadata.setCancelLink(cloneTransition(cancel, id));
327 pageMetadata.setNextLink(cloneTransition(next, id));
328 pageMetadata.setPreviousLink(cloneTransition(back, id));
329 pageMetadata.setTitle(partsbean.getPart().getPartLabel());
330 pageMetadata.setDirective(partsbean.getDirective());
331 pageMetadata.setHelpText(partsbean.getHelpText());
332
333
334 pageMetadata.setEntry(true);
335
336 }
337
338 /***
339 * Constructs 'how many entries' page metadata. This is coupled with the
340 * multi-attribute pages and is ordered prior to those pages.
341 *
342 * @param partsbean CompletionPartsBean the current part bean.
343 * @param id String page id.
344 * @return EmoWizardMetadata the emo wizard metadata previously constructed
345 * @throws DBException upon error.
346 */
347 private EmoWizardMetadata constructHowManyEntriesPageMetadata(final CompletionPartsBean partsbean,
348 final String id) throws DBException {
349
350 EmoWizardMetadata howManyEntriesMetadata = new EmoWizardMetadata();
351 howManyEntriesMetadata.setEntryRequired(false);
352 howManyEntriesMetadata.setEntry(true);
353 howManyEntriesMetadata.setCancelLink(cloneTransition(cancel, id));
354 howManyEntriesMetadata.setNextLink(cloneTransition(next, id));
355 howManyEntriesMetadata.setPreviousLink(cloneTransition(back, id));
356 howManyEntriesMetadata.setTitle("How many " + partsbean.getPart().getPartLabel());
357 howManyEntriesMetadata.setDirective(buildMultiAttributeIntroDirective(partsbean));
358 howManyEntriesMetadata.setViewId("HowManyAttributes");
359 return howManyEntriesMetadata;
360 }
361
362
363 /***
364 * Custructs metadata based on custom handlers.
365 *
366 * @param partsbean CompletionPartsBean
367 * @param pageId String
368 * @return EmoWizardMetadata
369 * @throws DBException
370 */
371 private EmoWizardMetadata constructCustomHandlerPageMetadata(CompletionPartsBean partsbean, String pageId) throws
372 DBException {
373 CustomPartHandlerMetadata customHandlerMetadata = new CustomPartHandlerMetadata();
374 customHandlerMetadata.setCancelLink(cloneTransition(cancel, pageId));
375 customHandlerMetadata.setNextLink(cloneTransition(next, pageId));
376 customHandlerMetadata.setPreviousLink(cloneTransition(back, pageId));
377 customHandlerMetadata.setTitle(partsbean.getPart().getPartLabel());
378
379 String directive = "Please enter settings. To learn more about the " +
380 "choices, click on the label of a setting, and after reading more," +
381 " navigate back to here with the browser's 'back' button. ";
382
383 customHandlerMetadata.setHelpText(directive);
384 customHandlerMetadata.setDirective(directive);
385 partsbean.setDirective(directive);
386
387
388 String handlerClass = partsbean.getPart().getField(Part.SPECIAL_HANDLER);
389 customHandlerMetadata.setCustomHandlerClassName(handlerClass);
390
391 return customHandlerMetadata;
392 }
393
394 /***
395 * Construct a custom handler page.
396 *
397 * @param id String id of page in wizard
398 * @param pageMetadata EmoWizardMetadata
399 * @return EmoWizardPage
400 * @throws Exception upon error.
401 * @todo Finish Me.
402 */
403 private EmoWizardPage constructCustomHandlerPage(String id,
404 EmoWizardMetadata pageMetadata) throws Exception {
405
406 /***
407 * @todo populate default page from custom handler.
408 */
409
410
411 CustomPartHandlerMetadata customHandlerMetadata = (CustomPartHandlerMetadata) pageMetadata;
412 IPartHandler handler = customHandlerMetadata.getCustomHandler();
413 Integer templateId = getDefinition().getTargetId();
414
415 if (handler instanceof AbstractSettingHandler) {
416 if (customHandlerMetadata.getInputList() == null) {
417 AbstractSettingHandler settingHandler = (AbstractSettingHandler) handler;
418 List list = settingHandler.getInputs(templateId.toString());
419 customHandlerMetadata.setInputList(list);
420 customHandlerMetadata.setSettingPrototype(settingHandler.getSettingPrototype());
421 }
422 } else {
423 throw new Exception("unimplemented custom part: " + handler.getClass().getName());
424 }
425
426 return (EmoWizardPage) new CustomPartHandlerPage(new Integer(id), pageMetadata);
427 }
428
429
430 private EmoWizardMetadata constructMultiAttributePageMetadata(CompletionPartsBean partsbean, String id) throws
431 DBException {
432 MultiEntryMetadata enterMultiple = new MultiEntryMetadata();
433 enterMultiple.setEntryRequired(false);
434 enterMultiple.setEntry(true);
435 enterMultiple.setCancelLink(cloneTransition(cancel, id));
436 enterMultiple.setNextLink(cloneTransition(next, id));
437 enterMultiple.setPreviousLink(cloneTransition(back, id));
438 enterMultiple.setTitle(partsbean.getPart().getPartLabel());
439 enterMultiple.setDirective(buildMultiAttributeIntroDirective(partsbean));
440 enterMultiple.setViewId("HowManyAttributes");
441 enterMultiple.setMinEntries(partsbean.getMinEntries());
442 enterMultiple.setMaxEntries(partsbean.getMaxEntries());
443
444
445 if (partsbean.getHelpText() == null || partsbean.getHelpText().length() == 0) {
446 enterMultiple.setHelpText("Page 2 of 2: Please enter values.");
447 } else {
448 enterMultiple.setHelpText("Page 2 of 2: " + partsbean.getHelpText());
449 }
450
451 enterMultiple.setViewId("MultiAttributeWizardEntry");
452
453 return enterMultiple;
454
455 }
456
457 /***
458 * Generates a unique page id that differentiates it from the parts pages.
459 *
460 * @return Integer
461 */
462 private Integer generatePreMultiAttributePageId() {
463 if (preMultiAttributeStepIds.size() == 0) {
464 Integer returnValue = new Integer(INITIAL_PRESTEP_ID);
465 preMultiAttributeStepIds.add(returnValue);
466 return returnValue;
467 } else {
468 Integer lowestInt = (Integer) preMultiAttributeStepIds.first();
469 Integer nextLowest = new Integer(lowestInt.intValue() - 1);
470 preMultiAttributeStepIds.add(nextLowest);
471 return nextLowest;
472 }
473 }
474
475 /***
476 * Builds the directive for the 'multiple attribute' selection intro screens.
477 * The text creation logic is a little convoluted so since it depends on whether
478 * min or max is defined.
479 *
480 * @param partsbean CompletionPartsBean
481 * @return String the resulting string.
482 * @throws DBException upon Part query error.
483 */
484 private String buildMultiAttributeIntroDirective(CompletionPartsBean partsbean) throws DBException {
485 StringBuffer buffer = new StringBuffer(128);
486
487 buffer.append("Page 1 of 2: How many ");
488 buffer.append(partsbean.getPart().getPartLabel());
489 buffer.append(" would you like to enter?");
490 boolean addedMin = false;
491 if (partsbean.getMinEntries() != null) {
492 addedMin = true;
493 buffer.append(" You must enter a minimum value of ");
494 buffer.append(partsbean.getMinEntries());
495 }
496
497 boolean addedMax = false;
498 if (partsbean.getMaxEntries() != null) {
499 addedMax = true;
500 if (addedMin) {
501 buffer.append(" and a maximum value of ");
502 } else {
503 buffer.append(" You must enter a maximum value of");
504 }
505 buffer.append(partsbean.getMaxEntries());
506 }
507
508 if (addedMin || addedMax) {
509 buffer.append(".");
510 }
511
512 return buffer.toString();
513 }
514
515 /***
516 * Construct the introduction page that is the first thing the user sees
517 * when the wizard is run.
518 *
519 * @return EmoWizardPage
520 */
521 private EmoWizardPage constructTitlePage() {
522 EmoWizardMetadata pageMetadata = new EmoWizardMetadata();
523 pageMetadata.setCancelLink(cloneTransition(cancel, EmoCompletionWizard.TITLE_PAGE_ID));
524 pageMetadata.setNextLink(cloneTransition(next, EmoCompletionWizard.TITLE_PAGE_ID));
525 pageMetadata.setTitle(getDefinition().getWizardTitle());
526 pageMetadata.setDirective(getDefinition().getSummary());
527 pageMetadata.setEntry(false);
528
529 return new EmoWizardPage(new Integer(EmoCompletionWizard.TITLE_PAGE_ID), pageMetadata,
530 null);
531 }
532
533 /***
534 * Construct the final page for the wizard that is displayed before the user
535 * clicks the finish button.
536 *
537 * @return EmoWizardPage
538 * @throws DBException upon error querying the underlying data objects.
539 */
540 private EmoWizardPage constructCompletionPage() throws DBException {
541
542 CompletionBean cb = this.getDefinition();
543 EmoWizardMetadata pageMetadata = new EmoWizardMetadata();
544 pageMetadata.setCancelLink(cloneTransition(cancel, EmoCompletionWizard.FINAL_PAGE_ID));
545 pageMetadata.setFinishLink(cloneTransition(finish, EmoCompletionWizard.FINAL_PAGE_ID));
546 pageMetadata.setPreviousLink(cloneTransition(back, EmoCompletionWizard.FINAL_PAGE_ID));
547
548
549 String nodeTitleId = " <strong>"" + cb.getCurrentNode().getNodeTitleRaw()
550 + ""</strong> Template (" + cb.getCurrentNode().getNodeId() + ") ";
551
552 pageMetadata.setTitle("Finishing completion wizard for " + cb.getCurrentNode().getNodeTitle());
553 pageMetadata.setHelpText(nodeTitleId);
554 pageMetadata.setDirective(
555 "The values you entered are summarized below. Click 'next' to save those values into" + nodeTitleId + "and finish the wizard.");
556 pageMetadata.setEntry(false);
557 pageMetadata.setViewId("Confirm");
558 return new EmoWizardPage(new Integer(EmoCompletionWizard.FINAL_PAGE_ID), pageMetadata,
559 null);
560 }
561
562
563 }