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.Collections;
15 import java.util.List;
16 import java.util.Set;
17 import javax.xml.namespace.QName;
18 import org.apache.commons.lang.StringUtils;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.DisposeEvent;
21 import org.eclipse.swt.events.DisposeListener;
22 import org.eclipse.swt.events.ModifyEvent;
23 import org.eclipse.swt.events.ModifyListener;
24 import org.eclipse.swt.events.SelectionAdapter;
25 import org.eclipse.swt.events.SelectionEvent;
26 import org.eclipse.swt.events.SelectionListener;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
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.TabFolder;
33 import org.eclipse.swt.widgets.TabItem;
34 import org.eclipse.swt.widgets.Text;
35 import sk.uniba.euromath.document.NamespaceManager;
36 import sk.uniba.euromath.document.XMLAccess;
37 import sk.uniba.euromath.document.schema.AttributeListRule;
38 import sk.uniba.euromath.document.schema.AttributeRule;
39 import sk.uniba.euromath.document.schema.INameList;
40 import sk.uniba.euromath.editor.lang.Messages;
41 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameItemTypeEnum;
42 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameListImpl;
43 /***
44 * Manages the process of creating new attributes into newly created element.
45 * Attributes themselves are not created, their names and values are returned
46 * instead.
47 * @author Martin Vysny
48 */
49 public class CreateAttributeList extends AbstractUserInputWidget {
50 /***
51 * All controls are placed here.
52 */
53 protected final Composite composite;
54 /***
55 * Document instance.
56 */
57 public final XMLAccess xmlAccess;
58 /***
59 * Namespace manager.
60 */
61 public final NamespaceManager currentManager;
62 /***
63 * Name of parent element; used only to display the name on the shell.
64 */
65 public final String parentName;
66 /***
67 * Constructs the instance of the window.
68 * @param parent the parent composite.
69 * @param listRules the list of lists of attribute. Exactly one list will be
70 * chosen.
71 * @param parentName the name of the parent element. Used only to display
72 * the name on the shell.
73 * @param xmlAccess the xml document instance.
74 * @param currentManager the current manager.
75 */
76 public CreateAttributeList(Composite parent,
77 List<AttributeListRule> listRules, String parentName,
78 XMLAccess xmlAccess, NamespaceManager currentManager) {
79 super();
80 if (listRules.size() == 0)
81 throw new IllegalArgumentException(
82 "The listRules array must contain at least one item.");
83 this.xmlAccess = xmlAccess;
84 this.currentManager = currentManager;
85 this.parentName = parentName;
86
87 composite = new Composite(parent, SWT.NONE);
88 composite.setLayout(new GridLayout(1, false));
89 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
90 new Label(composite, SWT.NONE).setText(Messages.getString(
91 "SELECT_LIST_OF_ATTRIBUTES", parentName));
92 tabFolder = new TabFolder(composite, SWT.TOP);
93
94 nameControls = new ArrayList<List<NameControls>>(listRules.size());
95 int tabOrder = 0;
96 for (final AttributeListRule listRule : listRules) {
97 final TabItem item = new TabItem(tabFolder, SWT.NONE);
98
99 final Composite panel = new Composite(tabFolder, SWT.NONE);
100 panel.setLayout(new GridLayout(2, false));
101 item.setControl(panel);
102 item
103 .setText(Messages.getString("SET") + Integer.toString(++tabOrder));
104
105 final List<INameList<AttributeRule>> nameLists = listRule.getList();
106 final List<NameControls> oneTabControls = new ArrayList<NameControls>(
107 nameLists.size());
108 for (INameList<AttributeRule> nameList : nameLists) {
109
110 new Label(panel, SWT.NONE).setText(Messages
111 .getString("NAME_MARK"));
112 final Combo comboName = new Combo(panel, SWT.READ_ONLY);
113 comboName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,
114 true, false));
115 comboName.addSelectionListener(selectionListener);
116 new Label(panel, SWT.NONE).setText(Messages
117 .getString("VALUE_MARK"));
118 final Text value = new Text(panel, SWT.SINGLE | SWT.BORDER);
119 value.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
120 false));
121 value.setEnabled(false);
122 value.addModifyListener(modifyListener);
123 final Label l = new Label(panel, SWT.NONE);
124 l.setText(Messages.getString("VALUE_TYPE_MARK"));
125 l.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING,
126 false, false));
127 final Label valueType = new Label(panel, SWT.WRAP);
128 final GridData valueTypeGD = new GridData(SWT.FILL, SWT.FILL,
129 true, true);
130 valueTypeGD.widthHint = 300;
131 valueTypeGD.heightHint = 50;
132 valueType.setLayoutData(valueTypeGD);
133 valueType.setText(Messages.getString("NON_APPLICABLE"));
134 final NameControls nc = new NameControls(comboName, value,
135 valueType, nameList);
136 comboName.setItems(nc.dnl.getStrings());
137 oneTabControls.add(nc);
138 comboName.setData(nc);
139 comboName.addModifyListener(cnmInstance);
140 }
141 nameControls.add(oneTabControls);
142 }
143 tabFolder.addSelectionListener(selectionListener);
144 fillData();
145 }
146 /***
147 * Captures component changes events.
148 */
149 private final ModifyListener modifyListener = new ModifyListener() {
150
151
152
153
154 public void modifyText(ModifyEvent e) {
155 fillData();
156 fireDataModified();
157 }
158 };
159 /***
160 * Captures component changes events.
161 */
162 private final SelectionListener selectionListener = new SelectionAdapter() {
163
164
165
166
167 @Override
168 public void widgetSelected(SelectionEvent e) {
169 fillData();
170 fireDataModified();
171 }
172 };
173 /***
174 * Contains links to controls. First index is the index to
175 * <code>listRules</code>, second points to appropriate
176 * <code>AttributeListRule</code>.
177 */
178 private List<List<NameControls>> nameControls;
179 /***
180 * Contains links to controls. Grabs the values from controls when control
181 * is disposed.
182 * @author Martin Vysny
183 */
184 private class NameControls {
185 /***
186 * Attribute name selector.
187 */
188 private final Combo comboName;
189 /***
190 * Attribute value text box.
191 */
192 private final Text value;
193 /***
194 * Prints the datatype of allowed contents of attribute value.
195 */
196 private final Label valueType;
197 /***
198 * Namelist; datasource for the name selector combobox.
199 */
200 final DisplayableNameListImpl<AttributeRule> dnl;
201 /***
202 * Constructs the instance.
203 * @param comboName attribute name selector.
204 * @param value attribute value text box.
205 * @param valueType prints the datatype of allowed contents of attribute
206 * value.
207 * @param nameList namelist; datasource for the name selector combobox.
208 */
209 private NameControls(Combo comboName, Text value, Label valueType,
210 INameList<AttributeRule> nameList) {
211 super();
212 this.comboName = comboName;
213 this.value = value;
214 this.valueType = valueType;
215 this.dnl = new DisplayableNameListImpl<AttributeRule>(nameList,
216 xmlAccess, currentManager, false);
217 comboName.addDisposeListener(new DisposeListener() {
218 public void widgetDisposed(DisposeEvent e) {
219 _attributeNameIndex = NameControls.this.comboName
220 .getSelectionIndex();
221 }
222 });
223 value.addDisposeListener(new DisposeListener() {
224 public void widgetDisposed(DisposeEvent e) {
225 _value = NameControls.this.value.getText();
226 }
227 });
228 }
229 /***
230 * Returns the attribute name index.
231 * @return selected attribute name.
232 */
233 private int getAttributeNameIndex() {
234 if (comboName.isDisposed())
235 return _attributeNameIndex;
236 return comboName.getSelectionIndex();
237 }
238 /***
239 * Valid when combobox is disposed.
240 */
241 private int _attributeNameIndex;
242 /***
243 * Returns the attribute value.
244 * @return the attribute value.
245 */
246 private String getValue() {
247 if (value.isDisposed())
248 return _value;
249 return value.getText();
250 }
251 /***
252 * Valid when the textbox is disposed.
253 */
254 private String _value;
255 }
256 /***
257 * The tab folder, where all attribute-related controls are stored.
258 */
259 protected TabFolder tabFolder;
260 /***
261 * Change listener, listens to the attribute name selector combobox. On
262 * change updates the datatype name of selected attribute, and loads schema
263 * if namespace is selected.
264 * @author Martin Vysny
265 */
266 private class ComboNameModified implements ModifyListener {
267
268
269
270
271 public void modifyText(ModifyEvent e) {
272
273 Combo sender = (Combo) e.widget;
274 NameControls nc = (NameControls) e.widget.getData();
275 int sel = sender.getSelectionIndex();
276 if (sel < 0) {
277 nc.value.setEnabled(false);
278 nc.valueType.setText(Messages.getString("NON_APPLICABLE"));
279 return;
280 }
281 DisplayableNameItemTypeEnum type = nc.dnl.getType(sel);
282 nc.value
283 .setEnabled((type == DisplayableNameItemTypeEnum.ITEM_LOCAL)
284 || (type == DisplayableNameItemTypeEnum.ITEM_FOREIGN));
285 if (type != DisplayableNameItemTypeEnum.ITEM_NAMESPACE) {
286
287 AttributeRule rule = nc.dnl.getRule(sel);
288 nc.valueType.setText(rule.getDatatypeName());
289 fireDataModified();
290 return;
291 }
292
293 String ns = nc.dnl.getNamespace(sel);
294 nc.valueType.setText(Messages.getString("NON_APPLICABLE"));
295 if (!Tools.loadSchema(sender.getShell(), xmlAccess.getSchema()
296 .getRefs(), ns))
297 return;
298
299 update();
300 fireDataModified();
301 }
302 }
303 /***
304 * Single instance of the listener.
305 */
306 private final ComboNameModified cnmInstance = new ComboNameModified();
307 /***
308 * Recreates all namelists and updates all combo boxes.
309 */
310 protected void update() {
311
312 for (final List<NameControls> tabControls : nameControls) {
313 for (final NameControls nc : tabControls) {
314 int item = nc.comboName.getSelectionIndex();
315 if ((item >= 0)
316 && (item < nc.dnl.getLength())
317 && (nc.dnl.getType(item) == DisplayableNameItemTypeEnum.ITEM_LOCAL)) {
318
319 } else {
320
321
322 item = -1;
323 }
324 nc.dnl.refresh();
325 nc.comboName.setItems(nc.dnl.getStrings());
326 nc.comboName.select(item);
327 }
328 }
329 }
330
331
332
333
334 public Composite getComposite() {
335 return composite;
336 }
337 /***
338 * List of chosen attribute names.
339 */
340 protected List<QName> _names = null;
341 /***
342 * List of chosen attribute values.
343 */
344 protected List<String> _values = null;
345 /***
346 * Returns names of actually selected attributes. On error, function sets
347 * error message and returns <code>null</code>.
348 * @return list of names, or <code>null</code> if {@link #fillData()}
349 * fails.
350 */
351 public List<QName> getNames() {
352 if (_names == null)
353 return null;
354 return Collections.unmodifiableList(_names);
355 }
356 /***
357 * Returns text values of attributes. On error, function sets error message
358 * and returns <code>null</code>.
359 * @return list containing text values of attributes. <code>null</code> if
360 * {@link #fillData()} fails.
361 */
362 public List<String> getValues() {
363 if (_values == null)
364 return null;
365 return Collections.unmodifiableList(_values);
366 }
367 /***
368 * Errors that occured during last <code>fillData()</code> call.
369 */
370 protected ValidityMessages lastMessages = null;
371 /***
372 * <p>
373 * Fills the <code>_names</code>, <code>_values</code> lists and the
374 * <code>_namespaces</code> set. The tab control must not yet be disposed.
375 * </p>
376 * <p>
377 * If any combobox does not have name selected then function fails - it sets
378 * both lists to <code>null</code> and exits, returning non-<code>null</code>
379 * error message. If any attribute has illegal value, then function sets
380 * error message and fails.
381 * </p>
382 */
383 protected void fillData() {
384 lastMessages = null;
385 int listRuleIndex = tabFolder.getSelectionIndex();
386 List<NameControls> controls = nameControls.get(listRuleIndex);
387 _names = new ArrayList<QName>(controls.size());
388 _values = new ArrayList<String>(controls.size());
389
390
391 for (NameControls nc : controls) {
392
393 int nameIndex = nc.getAttributeNameIndex();
394 if (nameIndex < 0) {
395 _names = null;
396 _values = null;
397 lastMessages = new ValidityMessages(Messages
398 .getString("SELECT_ALL_NAMES"));
399 return;
400 }
401 assert nc.dnl.hasRule(nameIndex);
402
403 AttributeRule rule = nc.dnl.getRule(nameIndex);
404 if (!rule.acceptsValue(nc.getValue())) {
405 _names = null;
406 _values = null;
407 String errorMsg = rule.getErrorMessage(nc.value.getText());
408 errorMsg = Messages.getString("INVALID_ATTRIBUTE_VALUE_NAME",
409 nc.dnl.getStrings()[nameIndex])
410 + (StringUtils.isEmpty(errorMsg) ? "" : " "
411 + errorMsg);
412 lastMessages = new ValidityMessages(errorMsg);
413 return;
414 }
415
416 _values.add(nc.getValue());
417 String prefix = currentManager.getPrefix(nc.dnl
418 .getDomNamespaceUri(nameIndex));
419 _names.add(nc.dnl.getDomQName(nameIndex, prefix));
420 }
421
422 }
423
424
425
426
427 public ValidityMessages getMessages() {
428 return lastMessages;
429 }
430 /***
431 * Retrieves all namespaces of currently selected attributes.
432 * @return all namespaces of the attributes. If there are errors on the
433 * page, the function may return <code>null</code> or incomplete set.
434 */
435 public Set<String> getAllNamespaces() {
436 List<QName> qnames = getNames();
437 if (qnames == null)
438 return null;
439 return NewPrefixesQuery.getNamespaces(qnames);
440 }
441 /***
442 * Current state of the widget.
443 * @author Martin Vysny
444 */
445 public class State {
446 /***
447 * Text values of attributes. May be <code>null</code> if there are
448 * validity errors on the widget.
449 */
450 public final List<String> values;
451 /***
452 * Names of currently selected attributes. May be <code>null</code> if
453 * there are validity errors on the widget.
454 */
455 public final List<QName> names;
456 /***
457 * Constructs new state.
458 */
459 State() {
460 super();
461 values = getValues();
462 names = getNames();
463 }
464 }
465
466
467
468
469 public Object getState() {
470 return new State();
471 }
472
473
474
475
476 public Class< ? > getStateClass() {
477 return State.class;
478 }
479
480
481
482
483 public void setState(Object state) {
484 throw new UnsupportedOperationException();
485 }
486 }