1
2
3
4
5
6
7
8
9
10
11
12 package sk.uniba.euromath.editor.widgets;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Set;
16 import javax.xml.namespace.QName;
17 import org.apache.commons.lang.StringUtils;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.events.DisposeEvent;
20 import org.eclipse.swt.events.DisposeListener;
21 import org.eclipse.swt.events.ModifyEvent;
22 import org.eclipse.swt.events.ModifyListener;
23 import org.eclipse.swt.events.SelectionAdapter;
24 import org.eclipse.swt.events.SelectionEvent;
25 import org.eclipse.swt.layout.GridData;
26 import org.eclipse.swt.layout.GridLayout;
27 import org.eclipse.swt.layout.RowLayout;
28 import org.eclipse.swt.widgets.Button;
29 import org.eclipse.swt.widgets.Combo;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Label;
32 import org.eclipse.swt.widgets.Text;
33 import sk.uniba.euromath.document.NamespaceManager;
34 import sk.uniba.euromath.document.XMLAccess;
35 import sk.uniba.euromath.document.schema.ElementLoc;
36 import sk.uniba.euromath.document.schema.InsertList;
37 import sk.uniba.euromath.document.schema.NewElementRule;
38 import sk.uniba.euromath.document.schema.plug.IValueRule;
39 import sk.uniba.euromath.editor.lang.Messages;
40 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameItemTypeEnum;
41 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameListImpl;
42 /***
43 * Allows the user to choose between multiple insertlists, and to choose the
44 * name of each element. These user settings are returned. Alternatively, user
45 * may choose to enter some text if it is permitted by the rule.
46 * @author Martin Vysny
47 */
48 public class InsertListChooser extends AbstractUserInputWidget {
49 /***
50 * Here all controls will be placed.
51 */
52 protected final Composite composite;
53 /***
54 * The XML Access instance.
55 */
56 protected final XMLAccess xmlAccess;
57 /***
58 * Insertlists.
59 */
60 protected final List<InsertList> insertLists;
61 /***
62 * User may choose to enter text contents of an element instead.
63 */
64 protected final IValueRule textRule;
65 /***
66 * Current namespace manager.
67 */
68 protected final NamespaceManager nsManager;
69 /***
70 * Creates an instance of the window.
71 * @param parent where to place controls.
72 * @param xmlAccess the XML Access instance.
73 * @param insertLists the list of choosable insertlists.
74 * @param nsManager the map of namespace>prefix mapping. It will not get
75 * modified. If <code>null</code> then manager from <code>xmlAccess</code>
76 * will be used.
77 * @param parentName the displayable qname of the parent. It is only
78 * displayed in a window as a text - it is not used in other way.
79 * @param textRule if not <code>null</code> then it is possible to choose
80 * a text value also. This value must comply this rule.
81 */
82 public InsertListChooser(Composite parent, XMLAccess xmlAccess,
83 List<InsertList> insertLists, NamespaceManager nsManager,
84 String parentName, IValueRule textRule) {
85 super();
86 this.xmlAccess = xmlAccess;
87 this.insertLists = (insertLists == null) ? new ArrayList<InsertList>()
88 : insertLists;
89 this.textRule = textRule;
90 this.nsManager = (nsManager == null) ? xmlAccess.getNsManager()
91 : nsManager;
92
93 composite = new Composite(parent, SWT.NONE);
94 final RowLayout shellLayout = new RowLayout();
95 shellLayout.type = SWT.VERTICAL;
96 shellLayout.fill = true;
97 composite.setLayout(shellLayout);
98 new Label(composite, SWT.NONE).setText(Messages.getString(
99 "SELECT_ELEMENT_SEQ", parentName));
100
101 final Composite panel = new Composite(composite, SWT.NONE);
102 final GridLayout panelLayout = new GridLayout(2, false);
103 panel.setLayout(panelLayout);
104
105 if (textRule != null) {
106 radioText = new Button(panel, SWT.RADIO);
107 if (insertLists.size() == 0) {
108
109 radioText.setSelection(true);
110 }
111 radioText.addDisposeListener(new DisposeListener() {
112
113
114
115
116 public void widgetDisposed(DisposeEvent e) {
117 if (((Button) e.widget).getSelection())
118 _selected = -2;
119 }
120 });
121 radioText.addSelectionListener(new SelectionAdapter() {
122
123
124
125
126 @Override
127 public void widgetSelected(SelectionEvent e) {
128 fireDataModified();
129 }
130 });
131 final Composite tPanel = new Composite(panel, SWT.NONE);
132 tPanel.setLayout(new GridLayout(2, false));
133 new Label(tPanel, SWT.NONE).setText(Messages
134 .getString("VALUE_MARK"));
135 textText = new Text(tPanel, SWT.SINGLE | SWT.BORDER);
136 textText
137 .setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
138 textText.addModifyListener(new ModifyListener() {
139
140
141
142
143 public void modifyText(ModifyEvent e) {
144 fireDataModified();
145 }
146 });
147 textText.addDisposeListener(new DisposeListener() {
148
149
150
151
152 public void widgetDisposed(DisposeEvent e) {
153
154 _text = ((Text) e.widget).getText();
155 }
156 });
157 new Label(tPanel, SWT.NONE).setText(Messages
158 .getString("VALUE_TYPE_MARK"));
159 new Label(tPanel, SWT.NONE).setText(textRule.getDatatypeName());
160 } else {
161 radioText = null;
162 textText = null;
163 }
164
165 ilControls = new ArrayList<List<IlItemData>>(insertLists.size());
166 radioButtons = new ArrayList<Button>(insertLists.size());
167 for (int i = 0; i < insertLists.size(); i++) {
168 final Button radio = new Button(panel, SWT.RADIO);
169 if (i == 0)
170 radio.setSelection(true);
171 radioButtons.add(radio);
172 radio.setData(i);
173 radio.addSelectionListener(new SelectionAdapter() {
174
175
176
177
178 @Override
179 public void widgetSelected(SelectionEvent e) {
180 fireDataModified();
181 }
182 });
183 radio.addDisposeListener(new DisposeListener() {
184
185
186
187
188 public void widgetDisposed(DisposeEvent e) {
189 if (((Button) e.widget).getSelection())
190 _selected = ((Integer) e.widget.getData()).intValue();
191 }
192 });
193
194 final Composite ilPanel = new Composite(panel, SWT.NONE);
195 ilPanel.setLayout(new RowLayout());
196 final List<IlItemData> combos = new ArrayList<IlItemData>(
197 insertLists.get(i).size());
198 InsertList insertList = insertLists.get(i);
199 ilControls.add(combos);
200 for (final ElementLoc loc : insertList) {
201 final Combo combo = new Combo(ilPanel, SWT.READ_ONLY);
202 final DisplayableNameListImpl<NewElementRule> dnl = new DisplayableNameListImpl<NewElementRule>(
203 loc.nameList, xmlAccess, nsManager, true);
204 combo.setItems(dnl.getStrings());
205 combo.select(0);
206 final IlItemData id = new IlItemData(combo, dnl);
207 combo.setData(id);
208 combo.addSelectionListener(csInstance);
209 combos.add(id);
210 }
211 }
212 }
213 /***
214 * Controls (comboboxes) containing chosen qnames for each insertlist. Each
215 * list is a list of <code>IlItemData</code> instances, one instance
216 * depicts one <code>ElementLoc</code>.
217 */
218 protected final List<List<IlItemData>> ilControls;
219 /***
220 * List of radio buttons. These radio buttons serves for choosing the
221 * insertlist.
222 */
223 protected final List<Button> radioButtons;
224 /***
225 * This control is valid (non- <code>null</code>) when the user can enter
226 * some text as an alternative to the insertlists.
227 */
228 protected final Text textText;
229 /***
230 * This controls is valid (non- <code>null</code>) when the user can
231 * enter some text as an alternative to the insertlists.
232 */
233 protected final Button radioText;
234 /***
235 * Returns the index of the radio button, that is checked (selected). Radio
236 * buttons must not be disposed.
237 * @return the index of selected radio button, -1 if none is selected and -2
238 * if the text is selected.
239 */
240 protected int getSelectedFromControl() {
241 int i = 0;
242 for (final Button radio : radioButtons) {
243 if (radio.getSelection())
244 return i;
245 i++;
246 }
247 if ((textRule != null) && (radioText.getSelection()))
248 return -2;
249 return -1;
250 }
251 /***
252 * Returns the index of the radio button, that is checked (selected). Does
253 * not check for errors.
254 * @return the index of selected radio button, -1 if none is selected and -2
255 * if the text is selected.
256 */
257 protected int getSelectedInternal() {
258 final boolean disposed = (radioText != null) ? radioText.isDisposed()
259 : radioButtons.get(0).isDisposed();
260 if (disposed)
261 return _selected;
262 return getSelectedFromControl();
263 }
264 /***
265 * Returns the index of the insert list, that is checked (selected). Sets
266 * error if -1 is returned.
267 * @return the index of selected radio button, -1 if none is selected and -2
268 * if the text is selected.
269 */
270 public int getSelected() {
271 lastMessages = null;
272 final int result = getSelectedInternal();
273 if (result == -1) {
274 lastMessages = MessageLevelEnum.ERROR.setMessage(lastMessages,
275 Messages.getString("SELECT_INSERTLIST"));
276 }
277 return result;
278 }
279 /***
280 * Returns new textual value of element. Sets error if text value is
281 * invalid.
282 * @return new text value, never <code>null</code>.
283 */
284 public String getText() {
285 if (getSelected() != -2)
286 throw new IllegalStateException("Text must be selected");
287 final String text = textText.isDisposed() ? _text : textText.getText();
288 if (!textRule.acceptsValue(text)) {
289 String errorMsg = textRule.getErrorMessage(text);
290 if (!StringUtils.isEmpty(text))
291 errorMsg = " " + errorMsg;
292 errorMsg = Messages.getString("ERROR_INVALID_VALUE") + errorMsg;
293 lastMessages = MessageLevelEnum.ERROR.setMessage(lastMessages,
294 errorMsg);
295 }
296 return text;
297 }
298 /***
299 * Returns selected insertlist as a list of qnames. Calls
300 * <code>getInsertList()</code>.
301 * @return list of qnames, never <code>null</code>. May contain
302 * <code>null</code>s if appropriate qnames were not selected.
303 * @throws IllegalStateException if insertlist is not selected.
304 */
305 public List<QName> getInsertListNames() {
306 int i = getSelected();
307 final InsertList il = getInsertList();
308 final List<QName> result = new ArrayList<QName>(il.size());
309 for (final IlItemData item : ilControls.get(i)) {
310 int selected = item.getSelectionIndex();
311 if (selected < 0) {
312 result.add(null);
313 lastMessages = MessageLevelEnum.ERROR.setMessage(lastMessages,
314 Messages.getString("ERROR_MISSING_ELEMENT_NAME"));
315 continue;
316 }
317 final String ns = item.comboItems.getDomNamespaceUri(selected);
318 final String prefix = nsManager.getBestPrefix(ns);
319 result.add(item.comboItems.getDomQName(selected, prefix));
320 }
321 return result;
322 }
323 /***
324 * Returns selected insertlist as a list of qnames.
325 * @return list of qnames, never <code>null</code>.
326 * @throws IllegalStateException if insertlist is not selected.
327 */
328 public InsertList getInsertList() {
329 int i = getSelected();
330 if (i < 0)
331 throw new IllegalStateException("Insertlist must be selected");
332 InsertList il = insertLists.get(i);
333 return il;
334 }
335 /***
336 * Stores the index of the insertlist radio button, that is checked
337 * (selected). -1 if none is selected and -2 if the text is selected. Valid
338 * after all radio buttons are disposed.
339 */
340 protected int _selected = -1;
341 /***
342 * Stores the text value that new element may contain. Valid after
343 * <code>textText</code> is disposed.
344 */
345 protected String _text = null;
346 /***
347 * Contains information on insertlist's comboboxes. Caches value when the
348 * combobox gets disposed.
349 * @author Martin Vysny
350 */
351 protected class IlItemData {
352 /***
353 * Combobox reference.
354 */
355 protected final Combo combo;
356 /***
357 * Combobox items.
358 */
359 protected final DisplayableNameListImpl<NewElementRule> comboItems;
360 /***
361 * Constructor.
362 * @param combo reference to combobox
363 * @param comboItems items displayed in the combobox.
364 */
365 protected IlItemData(Combo combo,
366 DisplayableNameListImpl<NewElementRule> comboItems) {
367 super();
368 this.combo = combo;
369 this.comboItems = comboItems;
370 combo.addDisposeListener(new DisposeListener() {
371 public void widgetDisposed(DisposeEvent e) {
372 _selectedIndex = ((Combo) e.widget).getSelectionIndex();
373 }
374 });
375 }
376 /***
377 * Returns index of selected name list item.
378 * @return index to <code>comboItems</code> list.
379 */
380 public int getSelectionIndex() {
381 if (combo.isDisposed())
382 return _selectedIndex;
383 return combo.getSelectionIndex();
384 }
385 /***
386 * Here the index is stored when combobox is disposed.
387 */
388 protected int _selectedIndex;
389 }
390 /***
391 * Event handler for each combobox.
392 * @author Martin Vysny
393 */
394 protected class ComboSelected extends SelectionAdapter {
395
396
397
398
399 @Override
400 public void widgetSelected(SelectionEvent e) {
401 final Combo sender = (Combo) e.widget;
402 final IlItemData id = (IlItemData) sender.getData();
403 final int sel = sender.getSelectionIndex();
404 if (sel < 0) {
405 fireDataModified();
406 return;
407 }
408 final DisplayableNameItemTypeEnum type = id.comboItems.getType(sel);
409 if (type != DisplayableNameItemTypeEnum.ITEM_NAMESPACE) {
410 fireDataModified();
411 return;
412 }
413
414 final String ns = id.comboItems.getNamespace(sel);
415 if (!Tools.loadSchema(composite.getShell(), xmlAccess.getSchema()
416 .getRefs(), ns)) {
417
418 sender.select(-1);
419
420
421 return;
422 }
423
424 update();
425 fireDataModified();
426 }
427 }
428 /***
429 * The event handler instance.
430 */
431 private final ComboSelected csInstance = new ComboSelected();
432 /***
433 * Updates the contents of all combos. Run when new schema is loaded.
434 */
435 private void update() {
436 suppressModifyEvent();
437
438 for (final List<IlItemData> list : ilControls) {
439 for (final IlItemData id : list) {
440 int item = id.combo.getSelectionIndex();
441 if ((item >= 0)
442 && (item < id.comboItems.getLength())
443 && (id.comboItems.getType(item) == DisplayableNameItemTypeEnum.ITEM_LOCAL)) {
444
445 } else {
446
447
448 item = -1;
449 }
450 id.comboItems.refresh();
451 id.combo.setItems(id.comboItems.getStrings());
452 id.combo.select(item);
453 }
454 }
455 allowModifyEvent();
456 }
457
458
459
460
461 public Composite getComposite() {
462 return composite;
463 }
464 /***
465 * Last messages.
466 */
467 protected ValidityMessages lastMessages = null;
468
469
470
471
472 public ValidityMessages getMessages() {
473 return lastMessages;
474 }
475 /***
476 * Updates error messages.
477 */
478 protected void updateMessages() {
479 final int i = getSelected();
480 if (i == -1)
481 return;
482 if (i == -2) {
483 getText();
484 return;
485 }
486 getInsertListNames();
487 }
488 /***
489 * Retrieves all namespaces of currently selected attributes. Function must
490 * not be used when no insertlist is selected.
491 * @return all namespaces of the attributes. If there are errors on the
492 * page, the function may return incomplete set.
493 */
494 public Set<String> getAllNamespaces() {
495 final List<QName> qnames = getInsertListNames();
496 return NewPrefixesQuery.getNamespaces(qnames);
497 }
498 /***
499 * The state of the chooser.
500 * @author Martin Vysny
501 */
502 public class State {
503 /***
504 * Creates new snapshot of chooser state.
505 */
506 public State() {
507 super();
508 final int sel = getSelected();
509 textSelected = (sel == -2);
510 insertListSelected = (sel >= 0);
511 text = textSelected ? getText() : null;
512 insertList = insertListSelected ? getInsertList() : null;
513 insertListNames = insertListSelected ? getInsertListNames() : null;
514 }
515 /***
516 * If <code>true</code> then user wishes to insert a text. Cannot be
517 * <code>true</code> if {@link #insertListSelected} is
518 * <code>true</code>.
519 */
520 public final boolean textSelected;
521 /***
522 * If <code>true</code> then user wishes to insert an insert list.
523 * Cannot be <code>true</code> if {@link #textSelected} is
524 * <code>true</code>.
525 */
526 public final boolean insertListSelected;
527 /***
528 * New textual value of element. Sets error if text value is invalid.
529 * <code>null</code> if {@link #textSelected} is <code>false</code>.
530 */
531 public final String text;
532 /***
533 * Selected insertlist as a list of qnames. <code>null</code> if
534 * {@link #insertListSelected} is <code>false</code>.
535 */
536 public final List<QName> insertListNames;
537 /***
538 * Selected insert list. <code>null</code> if
539 * {@link #insertListSelected} is <code>false</code>.
540 */
541 public final InsertList insertList;
542 }
543
544
545
546
547 public Object getState() {
548 return new State();
549 }
550
551
552
553
554 public Class< ? > getStateClass() {
555 return State.class;
556 }
557
558
559
560
561 public void setState(Object state) {
562 throw new UnsupportedOperationException();
563 }
564 }