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.Collections;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.NoSuchElementException;
19
20 import javax.xml.namespace.QName;
21
22 import org.eclipse.jface.dialogs.MessageDialog;
23 import org.eclipse.jface.window.Window;
24 import org.eclipse.swt.widgets.Shell;
25 import org.w3c.dom.Element;
26
27 import sk.baka.ikslibs.DOMUtils;
28 import sk.baka.ikslibs.ptr.DOMPoint;
29 import sk.baka.ikslibs.ptr.DomPointer;
30 import sk.uniba.euromath.document.NamespaceManager;
31 import sk.uniba.euromath.document.XMLAccess;
32 import sk.uniba.euromath.document.schema.ElementLoc;
33 import sk.uniba.euromath.document.schema.INameList;
34 import sk.uniba.euromath.document.schema.InsertList;
35 import sk.uniba.euromath.document.schema.NameListCustom;
36 import sk.uniba.euromath.document.schema.NewElementRule;
37 import sk.uniba.euromath.document.schema.plug.INewElementRuleP;
38 import sk.uniba.euromath.editor.lang.Messages;
39 import sk.uniba.euromath.editor.wizards.IMultiWizardProvider;
40 import sk.uniba.euromath.editor.wizards.IWizard;
41 import sk.uniba.euromath.editor.wizards.MultiWizard;
42 import sk.uniba.euromath.editor.wizards.WizardDialog;
43 /***
44 * A wizard that queries contents of new element(s) that should be inserted at
45 * given location. When wizard finishes, these elements are returned as an array
46 * of insertpoints and elements. Wizard does not insert those elements itself,
47 * you may use
48 * {@link sk.uniba.euromath.editor.wizards.document.ElementLoc#insert()} method
49 * to do that.
50 * @author Martin Vysny
51 */
52 public class InsertElementWizardProvider implements IMultiWizardProvider {
53 /***
54 * Helper method that executes the insert element wizard.
55 * @param parent parent of the wizard dialog. Can be <code>null</code> but
56 * this is not recommended.
57 * @param place where to insert new elements.
58 * @param xmlAccess the document instance.
59 * @param nsManager the namespace manager instance.
60 * @param preselected this element was requested by user to appear as first
61 * element. May be <code>null</code>.
62 * @return new elements and their locations, or <code>null</code> if user
63 * cancelled the wizard or no elements are insertable.
64 */
65 public static sk.uniba.euromath.editor.wizards.document.ElementLoc execute(
66 Shell parent, XMLAccess xmlAccess, DomPointer place,
67 NamespaceManager nsManager, QName preselected) {
68 if (!place.parent.isSameNode(place.parentElement)) {
69 throw new IllegalArgumentException(
70 "Cannot insert elements into an entity.");
71 }
72
73 final List<InsertList> insertLists = xmlAccess.getSchema()
74 .getElementRule(place.parentElement).getInsertableElements(
75 place.ip);
76 if (insertLists.size() == 0) {
77 MessageDialog.openInformation(parent, Messages
78 .getString("INFORMATION"),
79 Messages.getString("NO_INSERTABLE_ELEMENTS"));
80 return null;
81 }
82
83 filterInsertLists(preselected, insertLists, place.ip);
84
85 final InsertElementWizardProvider provider = new InsertElementWizardProvider(
86 xmlAccess, insertLists, place.parentElement, nsManager);
87 final MultiWizard w = new MultiWizard(provider);
88 final WizardDialog dlg = new WizardDialog(parent, w, Messages
89 .getString("INSERT_NEW_ELEMENTS"));
90 if (dlg.open() != Window.OK)
91 return null;
92
93 final List<Element> roots = provider.getElements();
94 final List<DOMPoint> ips = new ArrayList<DOMPoint>(roots.size());
95 final InsertList selected = provider.getInsertList();
96 for (ElementLoc loc : selected) {
97 ips.add(loc.ip);
98 }
99 final sk.uniba.euromath.editor.wizards.document.ElementLoc result = new sk.uniba.euromath.editor.wizards.document.ElementLoc(
100 xmlAccess, place.parentElement, roots, ips);
101 return result;
102 }
103 /***
104 * Filters out all insertlists whose elementloc does not accept the
105 * 'preselected' qname. If no such insertlist exists then the list is kept
106 * intact.
107 * @param preselected the preselected qname.
108 * @param insertLists list of insertlists to filter.
109 * @param ip insertpoint where the element will be inserted.
110 * @return <code>true</code> if some insertlists were filtered out,
111 * <code>false</code> otherwise.
112 */
113 private static boolean filterInsertLists(final QName preselected,
114 final List<InsertList> insertLists, final DOMPoint ip) {
115 if (preselected != null) {
116
117 boolean isPreselected = false;
118 for (final InsertList list : insertLists) {
119 if (list.canCreate(preselected, ip)) {
120 isPreselected = true;
121 break;
122 }
123 }
124 if (!isPreselected) {
125
126 return false;
127 }
128
129 for (final Iterator<InsertList> i = insertLists.iterator(); i
130 .hasNext();) {
131 final InsertList il = i.next();
132 final ElementLoc loc = il.getFirstAt(ip, preselected);
133 if (loc == null) {
134 i.remove();
135 continue;
136 }
137
138 final NewElementRule rule = loc.nameList.getRule(preselected,
139 true);
140 assert rule != null;
141 final Map<QName, NewElementRule> rules = Collections
142 .singletonMap(preselected, rule);
143 loc.nameList = new NameListCustom<NewElementRule, INewElementRuleP>(
144 loc.nameList.getNamespaceUri(), rules);
145 }
146 return true;
147 }
148 return false;
149 }
150 /***
151 * Constructor.
152 * @param xmlAccess instance of the document.
153 * @param insertLists list of possible insertlists.
154 * @param parent the parent element - here all elements will be created.
155 * @param nsManager the namespace manager instance.
156 */
157 public InsertElementWizardProvider(XMLAccess xmlAccess,
158 List<InsertList> insertLists, Element parent,
159 NamespaceManager nsManager) {
160 super();
161 if (insertLists.size() == 0)
162 throw new IllegalArgumentException("No insertlists");
163 this.xmlAccess = xmlAccess;
164 this.startNsManager = nsManager;
165
166 InsertListChooserWizard first = new InsertListChooserWizard(xmlAccess,
167 insertLists, xmlAccess.getNsManager(), DOMUtils
168 .printQName(parent), null, Messages
169 .getString("INSERT_NEW_ELEMENTS"));
170 wizards.add(first);
171 }
172 /***
173 * The document instance.
174 */
175 protected final XMLAccess xmlAccess;
176 /***
177 * Wizards that has been activated. Warning: may contain <code>null</code>s
178 * in order to be consistent with <code>rules</code> and
179 * <code>roots</code> lists.
180 */
181 protected final List<IWizard> wizards = new ArrayList<IWizard>();
182 /***
183 * This manager was given to the constructor.
184 */
185 protected final NamespaceManager startNsManager;
186 /***
187 * We are being executed in this multiwizard.
188 */
189 protected MultiWizard parent;
190
191
192
193
194 public void performFinish() {
195
196 }
197
198
199
200
201 public void performCancel() {
202
203 }
204
205
206
207
208 public void dispose() {
209 wizards.clear();
210 }
211
212
213
214
215 public void setWizard(MultiWizard wizard) {
216 parent = wizard;
217 }
218
219
220
221
222 public IWizard current() {
223 IWizard result = wizards.get(wizards.size() - 1);
224 assert result != null;
225 return result;
226 }
227 /***
228 * Contains rules for elements. Last rule is the rule being currently filled
229 * in. i-th rule fills contents of <code>roots.get(i)</code> using
230 * <code>wizards.get(i+1)</code> wizard. Note that this wizard may be
231 * <code>null</code> if element does not require any contents.
232 */
233 protected final List<NewElementRule> rules = new ArrayList<NewElementRule>();
234 /***
235 * 'Root' elements - elements that'll be inserted into given parent element.
236 */
237 protected List<Element> roots = null;
238 /***
239 * 'Root' elements - elements that'll be inserted into given parent element.
240 * If wizard shows its first page then <code>null</code> is returned.
241 * @return list of root elements or <code>null</code> if wizard shows its
242 * first page.
243 */
244 public List<Element> getElements() {
245 if (roots == null)
246 return null;
247 return Collections.unmodifiableList(roots);
248 }
249 /***
250 * Selected insert list.
251 * @return selected insert list. <code>null</code> if this provider is
252 * showing first wizard.
253 */
254 public InsertList getInsertList() {
255 return selected;
256 }
257 /***
258 * Previous namespace manager. If this provider is showing first wizard,
259 * then construct-time namespace manager is returned.
260 * @return current namespace manager. Queried from the last shown wizard
261 * (i.e. wizard before the currently active wizard).
262 */
263 public NamespaceManager getPrevNsManager() {
264 if (wizards.size() == 1)
265 return startNsManager;
266 for (int i = wizards.size() - 2; i > 0; i--) {
267 MultiWizard w = (MultiWizard) wizards.get(i);
268 if (w == null)
269 continue;
270 FillNewElementWizardProvider p = (FillNewElementWizardProvider) w
271 .getProvider();
272 return p.newNsManager();
273 }
274 InsertListChooserWizard first = (InsertListChooserWizard) wizards
275 .get(0);
276 return first.newNsManager();
277 }
278 /***
279 * Current namespace manager.
280 * @return current namespace manager. Queried from the currently active
281 * wizard.
282 */
283 public NamespaceManager newNsManager() {
284 if (wizards.size() == 1) {
285 InsertListChooserWizard first = (InsertListChooserWizard) wizards
286 .get(0);
287 return first.newNsManager();
288 }
289 MultiWizard w = (MultiWizard) wizards.get(wizards.size() - 1);
290 FillNewElementWizardProvider p = (FillNewElementWizardProvider) w
291 .getProvider();
292 return p.newNsManager();
293 }
294 /***
295 * Selected insert list. <code>null</code> if this provider is showing
296 * first wizard.
297 */
298 protected InsertList selected;
299
300
301
302
303 public IWizard next() {
304 assert wizards.size() > 0;
305 if (wizards.size() == 1) {
306 InsertListChooserWizard first = (InsertListChooserWizard) wizards
307 .get(0);
308 selected = first.ilcPage.getWidget().getInsertList();
309
310 List<QName> rootsNames = first.getNames();
311 roots = new ArrayList<Element>(rootsNames.size());
312 for (QName rootName : rootsNames) {
313 Element e = xmlAccess.getDocument().createElementNS(
314 rootName.getNamespaceURI(),
315 DOMUtils.printQName(rootName));
316 roots.add(e);
317 }
318 }
319
320 IWizard next = findNextWizard();
321 if (next == null)
322 throw new NoSuchElementException();
323 return next;
324 }
325 /***
326 * Finds next wizard for querying contents of an element. Returns
327 * <code>null</code> if no wizard is needed. All used rules are placed
328 * into the <code>rules</code> array to mark progress done. The
329 * <code>wizards</code> array is filled with <code>null</code>s if
330 * required, to remain consistent with the <code>rules</code> array.
331 * @return a wizard instance, or <code>null</code> if no wizard is needed.
332 */
333 protected IWizard findNextWizard() {
334 assert roots != null;
335 if (rules.size() == roots.size())
336 return null;
337 assert wizards.size() == rules.size() + 1;
338
339
340 for (int i = rules.size(); i < roots.size(); i++) {
341 Element root = roots.get(i);
342 INameList<NewElementRule> nameList = selected.get(i).nameList;
343 NewElementRule rule = nameList.getRule(DOMUtils.getQName(root),
344 true);
345
346 FillNewElementWizardProvider p = new FillNewElementWizardProvider(
347 root, rule, xmlAccess, newNsManager(), null);
348 MultiWizard wizard;
349 if (p.current() == null) {
350
351 wizard = null;
352 } else {
353 wizard = new MultiWizard(p);
354 }
355 wizards.add(wizard);
356 rules.add(rule);
357 if (wizard != null)
358 return wizard;
359 }
360
361 for (int i = wizards.size() - 1; i > 0; i--) {
362 if (wizards.get(i) != null)
363 break;
364 wizards.remove(i);
365 rules.remove(i - 1);
366 }
367 return null;
368 }
369
370
371
372
373 public IWizard previous() {
374 if (!hasPrevious())
375 throw new NoSuchElementException();
376
377 int lastWizard = wizards.size() - 1;
378 wizards.set(lastWizard, null);
379 for (int i = lastWizard; i > 0; i--) {
380 if (wizards.get(i) != null)
381 break;
382 wizards.remove(i);
383 rules.remove(i - 1);
384 }
385
386 assert wizards.size() != 0;
387 if (wizards.size() == 1) {
388 selected = null;
389 roots = null;
390 }
391 return wizards.get(wizards.size() - 1);
392 }
393
394
395
396
397 public boolean hasNext() {
398
399 try {
400 next();
401 previous();
402 return true;
403 } catch (NoSuchElementException ex) {
404 return false;
405 }
406 }
407
408
409
410
411 public boolean hasPrevious() {
412 return wizards.size() > 1;
413 }
414
415
416
417
418 public String getName() {
419 return null;
420 }
421 }