1
2
3
4
5
6
7
8
9
10
11
12 package sk.uniba.euromath.editor.wizards.document;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.NoSuchElementException;
16 import javax.xml.namespace.QName;
17 import org.w3c.dom.Element;
18 import org.w3c.dom.Text;
19 import sk.baka.ikslibs.DOMUtils;
20 import sk.baka.ikslibs.modify.DOMMutils;
21 import sk.baka.ikslibs.ptr.DOMPoint;
22 import sk.uniba.euromath.document.NamespaceManager;
23 import sk.uniba.euromath.document.XMLAccess;
24 import sk.uniba.euromath.document.schema.AttributeListRule;
25 import sk.uniba.euromath.document.schema.ElementSequenceRule;
26 import sk.uniba.euromath.document.schema.InsertList;
27 import sk.uniba.euromath.document.schema.NewElementRule;
28 import sk.uniba.euromath.document.schema.plug.IValueRule;
29 import sk.uniba.euromath.editor.lang.Messages;
30 import sk.uniba.euromath.editor.wizards.IMultiWizardProvider;
31 import sk.uniba.euromath.editor.wizards.IWizard;
32 import sk.uniba.euromath.editor.wizards.MultiWizard;
33 /***
34 * <p>
35 * Fills given element with new content. The element is expected to be freshly
36 * created and thus must not contain any children nodes nor attributes.
37 * </p>
38 * @author Martin Vysny
39 */
40 public class FillNewElementWizardProvider implements IMultiWizardProvider {
41 /***
42 * Creates instance of the wizard.
43 * @param element the element to be filled. The element is expected to be
44 * freshly created and thus must not contain any children nodes nor
45 * attributes.
46 * @param rule the rule for contents of the element.
47 * @param xmlAccess the document instance.
48 * @param nsMan actual namespace manager. It won't get modified. If
49 * <code>null</code> then <code>xmlAccess</code> current namespace
50 * manager is taken.
51 * @param name the name of the activity provided by this provider. If
52 * <code>null</code> then default name is used.
53 */
54 public FillNewElementWizardProvider(Element element, NewElementRule rule,
55 XMLAccess xmlAccess, NamespaceManager nsMan, String name) {
56 super();
57 this.xmlAccess = xmlAccess;
58 this.name = name == null ? Messages.getString("ENTER_ELEMENT_CONTENT") : name;
59 startupManager = nsMan == null ? xmlAccess.getNsManager() : nsMan;
60
61 firstFactory = getNextStep(rule, element);
62 if (firstFactory != null) {
63
64 stepFactories.add(firstFactory);
65 visitedSteps.add(firstFactory.newStep(startupManager));
66 }
67 }
68 /***
69 * The provider name.
70 */
71 protected final String name;
72 /***
73 * The document instance.
74 */
75 protected final XMLAccess xmlAccess;
76 /***
77 * Represents one step in wizard.
78 * @author Martin Vysny
79 */
80 protected interface WizardStep {
81 /***
82 * Returns wizard that allows user to enter data required for this step
83 * to be finished succesfully.
84 * @return wizard instance. Each call to this method should return same
85 * instance of wizard.
86 */
87 public IWizard getWizard();
88 /***
89 * Returns element where this step creates contents.
90 * @return the element whose attributes or children are being created by
91 * this step.
92 */
93 public Element getContextElement();
94 /***
95 * Checks whether <code>moveToNextStep()</code> will return non-empty
96 * list. It does not finish this step. May be called multiple times.
97 * <code>getWizard().canFinish()</code> is granted to be
98 * <code>true</code>, and <code>getWizard()</code> has already been
99 * invoked.
100 * @return <code>true</code> if there are still more steps.
101 */
102 public boolean hasNextSteps();
103 /***
104 * Finishes this step and moves to the next step.
105 * <code>getWizard().canFinish()</code> is granted to be
106 * <code>true</code>, and <code>getWizard()</code> has already been
107 * invoked.
108 * @return next step, or empty array if no more steps are required.
109 * Never <code>null</code>.
110 */
111 public List<AbstractStepFactory> moveToNextStep();
112 /***
113 * May be called after call to <code>moveToNextStep()</code> if user
114 * wishes to rollback next step and return to this one.
115 */
116 public void rollbackFromNextStep();
117 }
118 /***
119 * Produces new steps. Allows wizards to be created lazily.
120 * @author Martin Vysny
121 */
122 protected abstract class AbstractStepFactory {
123 /***
124 * Rule to use for produced step.
125 */
126 public final NewElementRule rule;
127 /***
128 * Step shall fill contents of this element.
129 */
130 public final Element e;
131 /***
132 * Constructor.
133 * @param rule Rule to use for produced step.
134 * @param e Step shall fill contents of this element.
135 */
136 protected AbstractStepFactory(NewElementRule rule, Element e) {
137 super();
138 this.rule = rule;
139 this.e = e;
140 }
141 /***
142 * Produces instance of the step.
143 * @param manager the namespace manager to use. It must not be modified
144 * by this step, nor by the following steps.
145 * @return instance of the step. Must not return <code>null</code>.
146 */
147 protected abstract WizardStep newStep(NamespaceManager manager);
148 }
149 /***
150 * Step that allows user to create attributes in newly created element.
151 * @author Martin Vysny
152 */
153 protected class CreateAttributesWS implements WizardStep {
154 /***
155 * rule for this new element.
156 */
157 protected final NewElementRule rule;
158 /***
159 * Rules for possible new attributes, as required by the schema.
160 */
161 protected final List<AttributeListRule> attrRules;
162 /***
163 * the new element. Must not contain any elements nor attributes.
164 */
165 protected final Element e;
166 /***
167 * current namespace manager. won't get modified.
168 */
169 protected final NamespaceManager nsManager;
170 /***
171 * Constructor.
172 * @param rule rule for this new element.
173 * @param e the new element. Must not contain any elements nor
174 * attributes.
175 * @param nsManager current namespace manager. won't get modified.
176 */
177 protected CreateAttributesWS(NewElementRule rule, Element e,
178 NamespaceManager nsManager) {
179 super();
180 attrRules = rule.getLists();
181 this.e = e;
182 this.nsManager = nsManager;
183 this.rule = rule;
184 if (attrRules.isEmpty())
185 throw new IllegalArgumentException();
186 }
187 /***
188 * The wizard instance.
189 */
190 protected CreateAttributeListWizard wizard = null;
191
192
193
194
195 public CreateAttributeListWizard getWizard() {
196 if (wizard != null)
197 return wizard;
198 wizard = new CreateAttributeListWizard(attrRules, DOMUtils
199 .printQName(e),
200 FillNewElementWizardProvider.this.xmlAccess, nsManager);
201 return wizard;
202 }
203
204
205
206
207 public Element getContextElement() {
208 return e;
209 }
210
211
212
213
214 public boolean hasNextSteps() {
215 List< ? > result = moveToNextStep();
216 rollbackFromNextStep();
217 return result.size() != 0;
218 }
219
220
221
222
223 public List<AbstractStepFactory> moveToNextStep() {
224
225 if (wizard == null)
226 throw new IllegalStateException("Page has not yet been created");
227
228 wizard.createAttributes(e, null);
229
230 List<AbstractStepFactory> nextStep = new ArrayList<AbstractStepFactory>(
231 1);
232 AbstractStepFactory nextFactory = getNextStep(rule, e);
233 if (nextFactory != null)
234 nextStep.add(nextFactory);
235 return nextStep;
236 }
237
238
239
240
241 public void rollbackFromNextStep() {
242 DOMMutils.clearAttributes(e);
243 }
244 }
245 /***
246 * Creates new elements or text nodes in new element. This element should
247 * already contain attributes, to select correct rule amongst similar rules.
248 * @author Martin Vysny
249 */
250 protected class CreateElementContentsWS implements WizardStep {
251 /***
252 * rule for this new element.
253 */
254 protected final NewElementRule rule;
255 /***
256 * the new element. Must not contain any elements nor attributes.
257 */
258 protected final Element e;
259 /***
260 * current namespace manager. won't get modified.
261 */
262 protected final NamespaceManager nsManager;
263 /***
264 * Constructor.
265 * @param rule rule for this new element.
266 * @param e the new element. Must not contain any elements nor
267 * attributes.
268 * @param nsManager current namespace manager. won't get modified.
269 */
270 protected CreateElementContentsWS(NewElementRule rule, Element e,
271 NamespaceManager nsManager) {
272 super();
273 this.e = e;
274 this.nsManager = nsManager;
275 this.rule = rule;
276 }
277 /***
278 * The wizard instance.
279 */
280 protected InsertListChooserWizard wizard = null;
281 /***
282 * User may select between these insertlists.
283 */
284 protected List<InsertList> insertLists = null;
285
286
287
288
289 public IWizard getWizard() {
290 if (wizard != null)
291 return wizard;
292
293 ElementSequenceRule seqRule = rule.getNewContent(e);
294 insertLists = (seqRule == null) ? new ArrayList<InsertList>()
295 : seqRule.getSequences(DOMPoint.FIRST);
296 IValueRule textRule = rule.getNewTextRule(e);
297
298 assert (insertLists.size() != 0) || (textRule != null);
299 wizard = new InsertListChooserWizard(
300 FillNewElementWizardProvider.this.xmlAccess, insertLists,
301 nsManager, DOMUtils.printQName(e), textRule, Messages
302 .getString("NEW_ELEMENT_CONTENT"));
303 return wizard;
304 }
305
306
307
308
309 public Element getContextElement() {
310 return e;
311 }
312
313
314
315
316 public boolean hasNextSteps() {
317 List< ? > result = moveToNextStep();
318 rollbackFromNextStep();
319 return result.size() != 0;
320 }
321
322
323
324
325 public List<AbstractStepFactory> moveToNextStep() {
326 int sel = wizard.ilcPage.getWidget().getSelected();
327 if (sel == -2) {
328
329 Text text = xmlAccess.getDocument().createTextNode(
330 wizard.ilcPage.getWidget().getText());
331 e.appendChild(text);
332 return new ArrayList<AbstractStepFactory>();
333 }
334
335 assert sel >= 0;
336 List<QName> elementNames = wizard.getNames();
337 InsertList insertList = insertLists.get(sel);
338
339
340 List<AbstractStepFactory> result = new ArrayList<AbstractStepFactory>(
341 insertList.size());
342 for (int i = 0; i < insertList.size(); i++) {
343
344 QName qname = elementNames.get(i);
345 Element newElement = xmlAccess.getDocument().createElementNS(
346 qname.getNamespaceURI(), DOMUtils.printQName(qname));
347 e.appendChild(newElement);
348 NewElementRule newRule = insertList.get(i).nameList.getRule(
349 qname, true);
350
351 final AbstractStepFactory factory = getNextStep(newRule,
352 newElement);
353 if (factory != null)
354 result.add(factory);
355 }
356 return result;
357 }
358
359
360
361
362 public void rollbackFromNextStep() {
363
364 DOMMutils.clearChildren(e, true);
365 }
366 }
367 /***
368 * Determines which step is to be taken next.
369 * @param rule rule for element e.
370 * @param e the element. It may already have attributes created.
371 * @return new wizard step factory, or <code>null</code> if there are no
372 * required elements and/or attributes in given rule.
373 */
374 protected AbstractStepFactory getNextStep(NewElementRule rule, Element e) {
375
376 if (!e.hasAttributes()) {
377 List<AttributeListRule> attrRules = rule.getLists();
378 if ((attrRules != null) && (attrRules.size() > 0)) {
379
380 return new AbstractStepFactory(rule, e) {
381
382
383
384
385 @Override
386 protected WizardStep newStep(NamespaceManager manager) {
387 return new CreateAttributesWS(rule, e, manager);
388 }
389 };
390 }
391 }
392
393 ElementSequenceRule seqRule = rule.getNewContent(e);
394 List<InsertList> insertLists = (seqRule == null) ? new ArrayList<InsertList>()
395 : seqRule.getSequences(DOMPoint.FIRST);
396 IValueRule textRule = rule.getNewTextRule(e);
397
398 if ((insertLists.size() == 0) && (textRule == null))
399 return null;
400 return new AbstractStepFactory(rule, e) {
401
402
403
404
405 @Override
406 protected WizardStep newStep(NamespaceManager manager) {
407 return new CreateElementContentsWS(rule, e, manager);
408 }
409 };
410 }
411 /***
412 * All wizard step factories. This list may contain more items than already
413 * returned wizards - predictions, which wizard will follow are stored here
414 * aswell.
415 */
416 protected final List<AbstractStepFactory> stepFactories = new ArrayList<AbstractStepFactory>();
417 /***
418 * List of steps that have been already visited. Last item of this array is
419 * a step that was returned by recent call to <code>next()</code> and is
420 * currently interacting with user. This list is consistent with the
421 * <code>stepFactories</code>: i-th factory has produced i-th step.
422 */
423 protected final List<WizardStep> visitedSteps = new ArrayList<WizardStep>();
424 /***
425 * Index of item which was returned by last <code>next()</code> call.
426 */
427 private int activeItem = 0;
428 /***
429 * The first factory. May be <code>null</code> - in this case no element
430 * content needs to be created and this provider provides no wizards. If not
431 * <code>null</code> and <code>stepFactories</code> has at least one
432 * item, then <code>stepFactories.get(0)==firstFactory</code>.
433 */
434 protected final AbstractStepFactory firstFactory;
435
436
437
438
439 public void performCancel() {
440
441 }
442
443
444
445
446 public void performFinish() {
447
448 List< ? > result = visitedSteps.get(visitedSteps.size() - 1)
449 .moveToNextStep();
450 assert result.size() == 0;
451 }
452
453
454
455
456 public IWizard current() {
457 if (firstFactory == null)
458 return null;
459 return visitedSteps.get(activeItem).getWizard();
460 }
461
462
463
464
465 public boolean hasNext() {
466
467 if (activeItem < stepFactories.size() - 1)
468 return true;
469
470 WizardStep last = visitedSteps.get(activeItem);
471 return last.hasNextSteps();
472 }
473
474
475
476
477 public boolean hasPrevious() {
478 return activeItem > 0;
479 }
480 /***
481 * Namespace manager provided at construction time.
482 */
483 protected final NamespaceManager startupManager;
484
485
486
487
488 public IWizard next() {
489 assert activeItem == visitedSteps.size() - 1;
490 assert visitedSteps.size() <= stepFactories.size();
491
492 AbstractStepFactory factory;
493 WizardStep activeStep = visitedSteps.get(activeItem);
494
495
496 List<AbstractStepFactory> factories = activeStep.moveToNextStep();
497 int i = activeItem + 1;
498 for (AbstractStepFactory newFactory : factories) {
499 stepFactories.add(i, newFactory);
500 i++;
501 }
502
503 if (activeItem + 1 >= stepFactories.size())
504 throw new NoSuchElementException();
505 factory = stepFactories.get(activeItem + 1);
506
507 WizardStep step = factory.newStep(newNsManager());
508 visitedSteps.add(step);
509
510
511 activeItem++;
512 return step.getWizard();
513 }
514 /***
515 * Determines and returns active namespace manager.
516 * @return namespace manager of current step's wizard.
517 */
518 public NamespaceManager newNsManager() {
519
520
521
522 final WizardStep activeStep = visitedSteps.get(activeItem);
523 final IWizard activeWizard = activeStep.getWizard();
524 final NamespaceManager result;
525 if (activeWizard instanceof CreateAttributeListWizard) {
526 result = ((CreateAttributeListWizard) activeWizard).newNsManager();
527 } else {
528 result = ((InsertListChooserWizard) activeWizard).newNsManager();
529 }
530 return result;
531 }
532
533
534
535
536 public IWizard previous() {
537 if (!hasPrevious())
538 throw new NoSuchElementException();
539 AbstractStepFactory active = stepFactories.get(activeItem);
540
541
542
543 AbstractStepFactory previous = stepFactories.get(activeItem - 1);
544
545
546
547
548
549
550 WizardStep prevStep = visitedSteps.get(activeItem - 1);
551 boolean delete = (prevStep.getWizard() instanceof CreateAttributeListWizard);
552
553
554 if (!delete) {
555 delete = (active.e.getParentNode() != null)
556 && active.e.getParentNode().isSameNode(previous.e);
557 }
558 if (delete) {
559
560
561 while (activeItem < stepFactories.size()) {
562 AbstractStepFactory fac = stepFactories.get(activeItem);
563 if (active.e.isSameNode(fac.e))
564 stepFactories.remove(activeItem);
565 else
566 break;
567 }
568 }
569
570 IWizard result = visitedSteps.get(activeItem).getWizard();
571 visitedSteps.remove(activeItem);
572 assert visitedSteps.size() == activeItem;
573 activeItem--;
574 result = visitedSteps.get(activeItem).getWizard();
575 return result;
576 }
577
578
579
580
581 public void dispose() {
582 visitedSteps.clear();
583 stepFactories.clear();
584 }
585
586
587
588
589 public void setWizard(MultiWizard wizard) {
590
591 }
592
593
594
595
596 public String getName() {
597 return name;
598 }
599 }