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