View Javadoc

1   /*
2    * Created on Mar 11, 2005. Copyright 1999-2006 Faculty of Mathematics, Physics
3    * and Informatics, Comenius University, Bratislava. This file is protected by
4    * the Mozilla Public License version 1.1 (the "License"); you may not use this
5    * file except in compliance with the License. You may obtain a copy of the
6    * License at http://euromath2.sourceforge.net/license.html Unless required by
7    * applicable law or agreed to in writing, software distributed under the
8    * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
9    * OF ANY KIND, either express or implied. See the License for the specific
10   * language governing permissions and limitations under the License.
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."); //$NON-NLS-1$
83  		this.xmlAccess = xmlAccess;
84  		this.currentManager = currentManager;
85  		this.parentName = parentName;
86  		// create the composite
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)); //$NON-NLS-1$
92  		tabFolder = new TabFolder(composite, SWT.TOP);
93  		// create the controls
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  			// all controls in one tab shall be placed onto this panel
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)); //$NON-NLS-1$
104 			// create the controls and place them on 'panel'
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 				// small panel containing controls for one attribute
110 				new Label(panel, SWT.NONE).setText(Messages
111 						.getString("NAME_MARK")); //$NON-NLS-1$
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")); //$NON-NLS-1$
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")); //$NON-NLS-1$
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")); //$NON-NLS-1$
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 		 * (non-Javadoc)
152 		 * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
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 		 * (non-Javadoc)
165 		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
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 		 * (non-Javadoc)
269 		 * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
270 		 */
271 		public void modifyText(ModifyEvent e) {
272 			// identify the controls involved.
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")); //$NON-NLS-1$
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 				// ordinary qname was chosen, process it normally
287 				AttributeRule rule = nc.dnl.getRule(sel);
288 				nc.valueType.setText(rule.getDatatypeName());
289 				fireDataModified();
290 				return;
291 			}
292 			// a namespace was selected for which the schema is not yet loaded.
293 			String ns = nc.dnl.getNamespace(sel);
294 			nc.valueType.setText(Messages.getString("NON_APPLICABLE")); //$NON-NLS-1$
295 			if (!Tools.loadSchema(sender.getShell(), xmlAccess.getSchema()
296 					.getRefs(), ns))
297 				return;
298 			// Recreate all namelists, update all combos
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 		// update all combos
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 					// ok, combobox is scrolled on local name, leave it be
319 				} else {
320 					// combobox points onto item that is likely to be moved,
321 					// reset it
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 	 * (non-Javadoc)
332 	 * @see sk.uniba.euromath.editor.widgets.IEncapsulatesComposite#getComposite()
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 		// get all data. Ignore custom prefix entering for now; they can be
390 		// changed using other wizard page.
391 		for (NameControls nc : controls) {
392 			// get the chosen index
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")); //$NON-NLS-1$
399 				return;
400 			}
401 			assert nc.dnl.hasRule(nameIndex);
402 			// verify if the textual value obeys the rule
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", //$NON-NLS-1$
409 						nc.dnl.getStrings()[nameIndex])
410 						+ (StringUtils.isEmpty(errorMsg) ? "" : " " //$NON-NLS-1$ //$NON-NLS-2$
411 								+ errorMsg);
412 				lastMessages = new ValidityMessages(errorMsg);
413 				return;
414 			}
415 			// everything ok, append attribute information to the lists
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 		// exit succesfully
422 	}
423 	/*
424 	 * (non-Javadoc)
425 	 * @see sk.uniba.euromath.editor.widgets.IUserInputWidget#getLastError()
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 	 * (non-Javadoc)
467 	 * @see sk.uniba.euromath.editor.widgets.IUserInputWidget#getState()
468 	 */
469 	public Object getState() {
470 		return new State();
471 	}
472 	/*
473 	 * (non-Javadoc)
474 	 * @see sk.uniba.euromath.editor.widgets.IUserInputWidget#getStateClass()
475 	 */
476 	public Class< ? > getStateClass() {
477 		return State.class;
478 	}
479 	/*
480 	 * (non-Javadoc)
481 	 * @see sk.uniba.euromath.editor.widgets.IUserInputWidget#setState(java.lang.Object)
482 	 */
483 	public void setState(Object state) {
484 		throw new UnsupportedOperationException();
485 	}
486 }