1
2
3
4
5
6
7
8
9
10
11
12 package sk.uniba.euromath.editor.widgets;
13 import java.io.IOException;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.IdentityHashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import org.apache.commons.lang.StringUtils;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.events.DisposeEvent;
24 import org.eclipse.swt.events.DisposeListener;
25 import org.eclipse.swt.events.ModifyEvent;
26 import org.eclipse.swt.events.ModifyListener;
27 import org.eclipse.swt.events.SelectionAdapter;
28 import org.eclipse.swt.events.SelectionEvent;
29 import org.eclipse.swt.layout.GridData;
30 import org.eclipse.swt.layout.GridLayout;
31 import org.eclipse.swt.widgets.Button;
32 import org.eclipse.swt.widgets.Combo;
33 import org.eclipse.swt.widgets.Composite;
34 import org.eclipse.swt.widgets.Label;
35 import sk.baka.ikslibs.ResultEnum;
36 import sk.baka.xml.gene.CoordinatorInfo;
37 import sk.baka.xml.gene.ExportException;
38 import sk.baka.xml.gene.exportgraph.ExportGraph;
39 import sk.baka.xml.gene.exportgraph.ExportGraphBuilder;
40 import sk.baka.xml.gene.exportgraph.GraphNode;
41 import sk.baka.xml.gene.exportgraph.IGraphEdgeSelector;
42 import sk.baka.xml.gene.exportgraph.TransformGraph;
43 import sk.baka.xml.gene.exportgraph.TransformNode;
44 import sk.uniba.euromath.config.EuromathConfig;
45 import sk.uniba.euromath.config.bind.NamespaceType;
46 import sk.uniba.euromath.editor.lang.Messages;
47 /***
48 * <p>
49 * Allows user to select choices in an export graph. Able to convert list of
50 * graph nodes to a list of {@link TransformNode} instances.
51 * </p>
52 * @author Martin Vysny
53 */
54 public class GraphSelectorWidget extends AbstractUserInputWidget {
55 /***
56 * Here all controls will be placed.
57 */
58 protected final Composite composite;
59 /***
60 * Constructor.
61 * @param parent the parent widget
62 * @param editableChoices all choice nodes. May be empty or
63 * <code>null</code>.
64 * @param graph offer this graph to user. Must not be empty.
65 * @throws IllegalArgumentException if graphList or editableChoices is
66 * empty, or editableChoices refers to non-choosable nodes.
67 */
68 public GraphSelectorWidget(final Composite parent,
69 final Map<GraphNode, Set<String>> editableChoices,
70 final ExportGraph graph) {
71 super();
72 if ((graph == null) || graph.isEmpty())
73 throw new IllegalArgumentException("Graph is empty or null");
74
75 composite = new Composite(parent, SWT.NONE);
76 final GridLayout l = new GridLayout(2, false);
77 composite.setLayout(l);
78
79 if ((editableChoices != null) && !editableChoices.isEmpty()) {
80 createChoiceNodeControls(editableChoices);
81 }
82 createNamespaceOptionsControls(graph);
83 this.graph = graph;
84 }
85 /***
86 * Creates controls allowing you to choose one of the choice nodes options.
87 * @param editableChoices all choice nodes. Must not be <code>null</code>.
88 */
89 private void createChoiceNodeControls(
90 final Map<GraphNode, Set<String>> editableChoices) {
91 for (final GraphNode graph : editableChoices.keySet()) {
92 for (final String target : editableChoices.get(graph)) {
93 final List<GraphNode> children = graph.getTargets(target);
94 if (children.size() < 2)
95 throw new IllegalArgumentException(graph + ":" + target
96 + " is not choice node");
97
98
99 new Label(composite, SWT.NONE).setText(Messages
100 .getString("SOURCE_NAMESPACE"));
101 final Label ns = new Label(composite, SWT.NONE);
102 ns.setText(StringUtils.defaultString(target));
103 final NamespaceType nsType = EuromathConfig
104 .getNamespace(target);
105 if ((nsType != null) && (nsType.getDesc() != null)) {
106 ns.setToolTipText(nsType.getDesc());
107 }
108 new Label(composite, SWT.NONE).setText(Messages
109 .getString("EXPORTERS"));
110 final ComboAndValue combo = new ComboAndValue(children);
111
112 Map<String, ComboAndValue> map = graphChoices.get(graph);
113 if (map == null) {
114 map = new HashMap<String, ComboAndValue>();
115 graphChoices.put(graph, map);
116 }
117 map.put(StringUtils.defaultString(target), combo);
118 }
119 }
120 }
121 /***
122 * Creates controls for source document namespace processing tuning.
123 * @param graph offer this graph to user. Must not be empty.
124 */
125 private void createNamespaceOptionsControls(final ExportGraph graph) {
126 for (final GraphNode node : graph.getAllNodes()) {
127 if (node.info != null)
128 throw new IllegalArgumentException("Node " + node
129 + " does not represent source document node");
130 new Label(composite, SWT.NONE).setText(Messages
131 .getString("DOCUMENT_NAMESPACE"));
132 final Label ns = new Label(composite, SWT.NONE);
133 ns.setText(StringUtils.defaultString(node.targetNamespace));
134 final NamespaceType nsType = EuromathConfig
135 .getNamespace(node.targetNamespace);
136 if ((nsType != null) && (nsType.getDesc() != null)) {
137 ns.setToolTipText(nsType.getDesc());
138 }
139 final CheckboxAndValue cb = new CheckboxAndValue(composite, !graph
140 .isRegular(node), node.targetNamespace);
141 wildcardCheckboxes.put(StringUtils
142 .defaultString(node.targetNamespace), cb);
143 }
144 }
145 /***
146 * Computes and returns all choice nodes that the user can edit using this
147 * widget.
148 * @param graph the export graph. Should be able to transform whole
149 * document.
150 * @return never <code>null</code>. A map of choice nodes. If empty then
151 * there are no choice points.
152 */
153 public static Map<GraphNode, Set<String>> getEditableChoices(
154 final ExportGraph graph) {
155 Map<GraphNode, Set<String>> result = new HashMap<GraphNode, Set<String>>();
156 for (final GraphNode node : graph.getRegularNodes()) {
157 scanGraph(node, result);
158 }
159 return result;
160 }
161 /***
162 * Scans the graph for a choice node. If such node is found then it is
163 * registered into the {@link #graphChoices} map and the scan continues.
164 * Descendants of the choice node are not searched for the choice nodes.
165 * @param graph graph to analyze.
166 * @param editableChoices put all choice nodes here.
167 */
168 protected static void scanGraph(final GraphNode graph,
169 final Map<GraphNode, Set<String>> editableChoices) {
170 for (final String target : graph.getResultNamespaces()) {
171 final List<GraphNode> children = graph.getTargets(target);
172 if ((children == null) || (children.isEmpty())) {
173
174 } else if (children.size() > 1) {
175
176 Set<String> targets = editableChoices.get(graph);
177 if (targets == null) {
178 targets = new HashSet<String>();
179 editableChoices.put(graph, targets);
180 }
181 targets.add(StringUtils.defaultString(target));
182
183 } else if (children.size() == 1) {
184
185 final GraphNode child = children.get(0);
186 scanGraph(child, editableChoices);
187 }
188 }
189 }
190 /***
191 * Graph provided to the constructor.
192 */
193 protected final ExportGraph graph;
194 /***
195 * Contains choices in graphs. Maps graph node instance and a namespace
196 * string to a combobox instance. Combo allows to select one of the graph
197 * node children.
198 */
199 protected final IdentityHashMap<GraphNode, Map<String, ComboAndValue>> graphChoices = new IdentityHashMap<GraphNode, Map<String, ComboAndValue>>();
200 /***
201 * Contains all wildcard checkboxes. If this checkbox is checked then the
202 * namespace is transmitted to receiver as a tree representation, it is not
203 * exported normally.
204 */
205 protected final Map<String, CheckboxAndValue> wildcardCheckboxes = new HashMap<String, CheckboxAndValue>();
206 /***
207 * Maintains combobox selection index even when combobox is disposed.
208 * @author Martin Vysny
209 */
210 private final class ComboAndValue {
211 /***
212 * Combobox instance.
213 */
214 public final Combo combo;
215 /***
216 * Selection.
217 */
218 private int selection = 0;
219 /***
220 * Constructor.
221 * @param children we may choose from these child nodes.
222 */
223 public ComboAndValue(List<GraphNode> children) {
224 super();
225 combo = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
226 final GridData data = new GridData(SWT.CENTER, SWT.LEFT, false,
227 true);
228 combo.setLayoutData(data);
229
230 final String[] comboItems = new String[children.size()];
231 for (int i = 0; i < children.size(); i++) {
232 comboItems[i] = children.get(i).info.displayableName;
233 }
234 combo.setItems(comboItems);
235 combo.select(0);
236 combo.addDisposeListener(new DisposeListener() {
237
238
239
240
241 public void widgetDisposed(DisposeEvent e) {
242 selection = combo.getSelectionIndex();
243 }
244 });
245 combo.addModifyListener(new ModifyListener() {
246
247
248
249
250 public void modifyText(ModifyEvent e) {
251 fireDataModified();
252 }
253 });
254 }
255 /***
256 * Returns combobox selection index.
257 * @return the selection index.
258 */
259 public int getSelectionIndex() {
260 if (!combo.isDisposed())
261 return combo.getSelectionIndex();
262 return selection;
263 }
264 }
265 /***
266 * Maintains wildcard selection checkbox and its value even when the
267 * checkbox is disposed.
268 * @author Martin Vysny
269 */
270 private final class CheckboxAndValue {
271 /***
272 * The checkbox.
273 */
274 public final Button checkbox;
275 /***
276 * Checkbox value.
277 */
278 private boolean checked;
279 /***
280 * Constructor.
281 * @param composite where the checkbox is placed.
282 * @param alwaysChecked if true then checkbox is disabled and always
283 * checked.
284 * @param namespace the namespace. Set as data property of the checkbox.
285 */
286 public CheckboxAndValue(final Composite composite,
287 final boolean alwaysChecked, final String namespace) {
288 super();
289 checkbox = new Button(composite, SWT.CHECK);
290 checkbox.setText(Messages.getString("USE_TREE_REPRESENTATION"));
291 checkbox.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER,
292 true, false, 2, 1));
293 checkbox.setData(namespace);
294 if (alwaysChecked) {
295 checkbox.setSelection(true);
296 checkbox.setEnabled(false);
297 }
298 checkbox.addSelectionListener(new SelectionAdapter() {
299
300
301
302
303 @Override
304 public void widgetSelected(SelectionEvent e) {
305 enableChoiceSelectors();
306 fireDataModified();
307 }
308 });
309 checkbox.addDisposeListener(new DisposeListener() {
310
311
312
313
314 public void widgetDisposed(DisposeEvent e) {
315 checked = checkbox.getSelection();
316 }
317 });
318 }
319 /***
320 * Tests if checkbox is checked.
321 * @return <code>true</code> if checkbox is checked.
322 */
323 public boolean isChecked() {
324 if (!checkbox.isDisposed())
325 return checkbox.getSelection();
326 return checked;
327 }
328 /***
329 * Enables or disables comboboxes selecting choice nodes, that would
330 * actually get replaced by a wildcard exporter when this checkbox is
331 * checked.
332 */
333 private void enableChoiceSelectors() {
334 final String namespace = StringUtils
335 .defaultString((String) checkbox.getData());
336 final boolean checked = isChecked();
337
338
339 final GraphNode node = graph.getAllNodesAsMap().get(namespace);
340 assert node != null;
341 for (final GraphNode choiceNode : graphChoices.keySet()) {
342 if (choiceNode.equals(node) || node.isAscendantOf(choiceNode)) {
343
344 for (final ComboAndValue combo : graphChoices.get(
345 choiceNode).values()) {
346 combo.combo.setEnabled(!checked);
347 }
348 }
349 }
350 }
351 }
352 /***
353 * <p>
354 * Processes actual selection and returns exact transformation tree. Each
355 * transformation info is produced from appropriate graph node: i-th info is
356 * produced from i-th graphlist item.
357 * </p>
358 * <p>
359 * Not all choice nodes are made selectable; if a choice node is not
360 * selectable then simply the first exporter is selected.
361 * </p>
362 * @param info the coordinator info object.
363 * @return list of transformation infos.
364 * @throws ExportException if exporter fails to initialize.
365 * @throws IOException if i/o error occurs.
366 */
367 public TransformGraph getTransformationInfo(final CoordinatorInfo info)
368 throws ExportException, IOException {
369 final IGraphEdgeSelector userSelector = new ComboGraphEdgeSelector();
370 final ExportGraphBuilder builder = new ExportGraphBuilder();
371 final List<GraphNode> allNodes = graph.getAllNodes();
372 final TransformGraph result = new TransformGraph();
373
374 for (GraphNode node : allNodes) {
375
376
377
378 final String ns = StringUtils.defaultString(node.targetNamespace);
379 if (graph.isRegular(node) && wildcardCheckboxes.get(ns).isChecked()) {
380 node = builder.newWildcardGraphNode(node.targetNamespace,
381 EnumSet.of(ResultEnum.DOM), info);
382 }
383 final TransformNode ti = builder.toTransformationInfo(node, info,
384 userSelector);
385 if (ti == null)
386 result.addDirectPipe(ns, true);
387 else
388 result.add(ti, true);
389 }
390 return result;
391 }
392 /***
393 * Selects the outgoing edge depending on the value of the combobox, if
394 * registered.
395 * @author Martin Vysny
396 */
397 private final class ComboGraphEdgeSelector implements IGraphEdgeSelector {
398
399
400
401
402
403 public int getChildNode(GraphNode node, String namespace) {
404 final Map<String, ComboAndValue> map = graphChoices.get(node);
405 if (map == null)
406 return 0;
407 final ComboAndValue combo = map.get(namespace);
408 if (combo == null)
409 return 0;
410 return combo.getSelectionIndex();
411 }
412 }
413
414
415
416
417 public Composite getComposite() {
418 return composite;
419 }
420
421
422
423
424 public ValidityMessages getMessages() {
425
426 return null;
427 }
428 /***
429 * The state of the widget.
430 * @author Martin Vysny
431 */
432 public class State {
433 /***
434 * Selects correct nodes when a choice of nodes is available.
435 */
436 public final IGraphEdgeSelector nodeSelector;
437 /***
438 * List of namespaces explicitly selected as wildcards.
439 */
440 public final Set<String> wildcards = new HashSet<String>();
441 private State() {
442 super();
443 nodeSelector = new ComboGraphEdgeSelector();
444 final List<GraphNode> allNodes = graph.getAllNodes();
445 for (final GraphNode node : allNodes) {
446 final String ns = StringUtils
447 .defaultString(node.targetNamespace);
448 if (wildcardCheckboxes.get(ns).isChecked())
449 wildcards.add(ns);
450 }
451 }
452 }
453
454
455
456
457 public Object getState() {
458 return new State();
459 }
460
461
462
463
464 public Class< ? > getStateClass() {
465 return State.class;
466 }
467
468
469
470
471 public void setState(Object state) {
472 throw new UnsupportedOperationException();
473 }
474 }