source: trunk/autoquest-ui-swt/src/main/java/de/ugoe/cs/autoquest/ui/swt/ShowUsabilityEvaluationResultDialog.java @ 2047

Last change on this file since 2047 was 2047, checked in by pharms, 9 years ago
  • changed creation of smell tree
File size: 23.4 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.ui.swt;
16
17import java.awt.GraphicsDevice;
18import java.awt.GraphicsEnvironment;
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.HashMap;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.ListIterator;
25import java.util.Map;
26
27import org.eclipse.swt.SWT;
28import org.eclipse.swt.custom.SashForm;
29import org.eclipse.swt.custom.StyleRange;
30import org.eclipse.swt.custom.StyledText;
31import org.eclipse.swt.events.SelectionAdapter;
32import org.eclipse.swt.events.SelectionEvent;
33import org.eclipse.swt.events.ShellAdapter;
34import org.eclipse.swt.events.ShellEvent;
35import org.eclipse.swt.graphics.Font;
36import org.eclipse.swt.graphics.FontData;
37import org.eclipse.swt.layout.GridData;
38import org.eclipse.swt.layout.GridLayout;
39import org.eclipse.swt.widgets.Dialog;
40import org.eclipse.swt.widgets.Display;
41import org.eclipse.swt.widgets.Event;
42import org.eclipse.swt.widgets.Listener;
43import org.eclipse.swt.widgets.Shell;
44import org.eclipse.swt.widgets.Tree;
45import org.eclipse.swt.widgets.TreeColumn;
46import org.eclipse.swt.widgets.TreeItem;
47
48import de.ugoe.cs.autoquest.eventcore.IEventTarget;
49import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
50import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
51import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
52import de.ugoe.cs.autoquest.usability.UsabilitySmell;
53import de.ugoe.cs.autoquest.usability.UsabilitySmellIntensity;
54import de.ugoe.cs.autoquest.usability.UsabilityEvaluationResult;
55
56/**
57 * <p>
58 * a dialog to inspect the results of a usability evaluation
59 * TODO update comments
60 * </p>
61 *
62 * @author Patrick Harms
63 */
64public class ShowUsabilityEvaluationResultDialog extends Dialog {
65
66    /** the main shell */
67    protected Shell shell;
68
69    /** the table containing all smells */
70    private Tree smellList;
71
72    /** the description label of a selected smell */
73    private StyledText description;
74   
75    /** the tree of involved GUI elements of a specific task on the right bottom */
76    private Tree involvedTargetsTree;
77
78    /** the table containing the parents tasks of a displayed task */
79    private Tree involvedTasks;
80
81    /** the displayed usability evaluation result */
82    private UsabilityEvaluationResult usabilityEvalResult;
83   
84    /** task tree dialog to show task details */
85    private ShowTaskTreeDialog showTaskTreeDialog;
86
87    /**
88     * creates the dialog
89     */
90    public ShowUsabilityEvaluationResultDialog(Shell                     parent,
91                                               int                       style,
92                                               UsabilityEvaluationResult usabilityEvalResult,
93                                               String                    dataSetName)
94    {
95        super(parent, style);
96        setText("Usability Evaluation Result " + dataSetName);
97        this.usabilityEvalResult = usabilityEvalResult;
98    }
99
100    /**
101     * displays the dialog
102     */
103    public void open() {
104        showTaskTreeDialog = new ShowTaskTreeDialog
105            (super.getParent(), SWT.NONE, usabilityEvalResult.getTaskModel(),
106             "task details of usability smells");
107
108        createContents();
109        shell.open();
110        shell.layout();
111       
112        shell.addShellListener(new ShellAdapter() {
113            @Override
114            public void shellClosed(ShellEvent e) {
115                showTaskTreeDialog.dispose();
116            }
117        });
118       
119        Display display = getParent().getDisplay();
120        while (!shell.isDisposed()) {
121            if (!display.readAndDispatch()) {
122                display.sleep();
123            }
124        }
125    }
126
127    /**
128     * creates the two views, one on the task instances on the left, on on the task models on the
129     * right. Also adds a selection adapter to the task instances so that for a selected task
130     * instance always the respective model is presented.
131     */
132    private void createContents() {
133        shell = new Shell(getParent(), SWT.SHELL_TRIM | SWT.BORDER);
134        GraphicsDevice gd =
135            GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
136
137        shell.setSize(gd.getDisplayMode().getWidth(), gd.getDisplayMode().getHeight());
138        shell.setText(getText());
139
140        shell.setLayout(new GridLayout(1, false));
141
142        SashForm mainSashForm = new SashForm(shell, SWT.HORIZONTAL);
143        mainSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
144       
145        smellList = new Tree(mainSashForm, SWT.BORDER | SWT.SINGLE | SWT.VIRTUAL);
146        smellList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
147        smellList.setHeaderVisible(true);
148        smellList.setLinesVisible(true);
149
150        TreeColumn treeColumn = new TreeColumn(smellList, SWT.NONE);
151        treeColumn.setWidth(200);
152        treeColumn.setText("smells");
153
154        buildSmellTree();
155       
156        smellList.addSelectionListener(new SelectionAdapter() {
157            @Override
158            public void widgetSelected(SelectionEvent e) {
159                TreeItem[] selectedItems = smellList.getSelection();
160                if ((selectedItems.length == 1) &&
161                    (selectedItems[0].getData() instanceof UsabilitySmell))
162                {
163                    displaySmellDetails((UsabilitySmell) selectedItems[0].getData());
164                }
165                else {
166                    clearSmellDetails();
167                }
168            }
169        });
170
171        SashForm detailsSashForm = new SashForm(mainSashForm, SWT.VERTICAL);
172        detailsSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
173       
174        description = new StyledText(detailsSashForm, SWT.READ_ONLY | SWT.BORDER | SWT.WRAP);
175        description.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
176
177        SashForm detailsBottomSashForm = new SashForm(detailsSashForm, SWT.HORIZONTAL);
178        detailsBottomSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
179       
180        involvedTasks = VisualizationUtils.createTaskDetailsTree
181            (detailsBottomSashForm, "involved tasks", usabilityEvalResult.getTaskModel());
182       
183        VisualizationUtils.addItemSpecificContextMenu
184            (involvedTasks, ITask.class, "show details", new SelectionAdapter()
185        {
186            @Override
187            public void widgetSelected(SelectionEvent e) {
188                showTaskTreeDialog.open((ITask) involvedTasks.getSelection()[0].getData());
189            }
190        });
191       
192        VisualizationUtils.addExpansionListener(involvedTasks, new Listener() {
193            public void handleEvent(final Event event) {
194                ensureChildren((TreeItem) event.item);
195                ((TreeItem) event.item).setExpanded(true);
196            }
197        });
198       
199        involvedTargetsTree =
200            VisualizationUtils.createTargetsTree(detailsBottomSashForm, "involved GUI elements");
201       
202        VisualizationUtils.addInvolvedTargetsHighlighting(involvedTasks, involvedTargetsTree);
203       
204        detailsBottomSashForm.setWeights(new int[] { 1, 1 });
205        detailsSashForm.setWeights(new int[] { 1, 3 });
206        mainSashForm.setWeights(new int[] { 1, 3 });
207
208        //indexColumn.pack();
209        //severityColumn.pack();
210        //descriptionColumn.pack();
211        //smellList.pack();
212    }
213
214    /**
215     * convenience method for creating the display of the instances
216     */
217    private void buildSmellTree() {
218        int groupCount = 5;
219        int groupSize = 30;
220       
221        List<UsabilitySmell> smells = usabilityEvalResult.getAllSmells();
222
223        final Map<String, List<UsabilitySmell>> sortedSmells = new HashMap<>();
224
225        for (UsabilitySmell smell : smells) {
226            List<UsabilitySmell> smellList = sortedSmells.get(smell.getBriefDescription());
227                   
228            if (smellList == null) {
229                smellList = new ArrayList<UsabilitySmell>();
230                sortedSmells.put(smell.getBriefDescription(), smellList);
231            }
232           
233            ListIterator<UsabilitySmell> it = smellList.listIterator();
234            boolean added = false;
235           
236            while (it.hasNext()) {
237                if (smell.getIntensity().getEventCoverage() >
238                    it.next().getIntensity().getEventCoverage())
239                {
240                    it.previous();
241                    it.add(smell);
242                    added = true;
243                    break;
244                }
245            }
246           
247            if (!added) {
248                smellList.add(smell);
249            }
250        }
251
252        final Map<Integer, Map<String, List<UsabilitySmell>>> allSortedSmells =
253            new HashMap<Integer, Map<String, List<UsabilitySmell>>>();
254       
255        for (Map.Entry<String, List<UsabilitySmell>> entry : sortedSmells.entrySet()) {
256            // we create groupCount groups of size groupSize
257            int overallIndex = 0;
258           
259            for (int i = 0; i < groupCount; i++) {
260                List<UsabilitySmell> smellList = new LinkedList<>();
261                for (int j = 0; overallIndex < entry.getValue().size() &&
262                                ((j < groupSize) || (i == (groupCount - 1))); j++)
263                {
264                    UsabilitySmell smell = entry.getValue().get(overallIndex++);
265                   
266                    int ratio = smell.getIntensity().getRatio();
267                    int eventCoverage = smell.getIntensity().getEventCoverage();
268                    int index = 0;
269                    for (index = 0; index < smellList.size(); index++) {
270                        UsabilitySmellIntensity candidate = smellList.get(index).getIntensity();
271                        if ((ratio == candidate.getRatio()) &&
272                            (eventCoverage > candidate.getEventCoverage()))
273                        {
274                            break;
275                        }
276                        else if (ratio > candidate.getRatio()) {
277                            break;
278                        }
279                    }
280                   
281                    smellList.add(index, smell);
282                }
283               
284                if (smellList.size() > 0) {
285                    Map<String, List<UsabilitySmell>> smellGroups = allSortedSmells.get(i);
286                   
287                    if (smellGroups == null) {
288                        smellGroups = new HashMap<>();
289                        allSortedSmells.put(i, smellGroups);
290                    }
291                   
292                    smellGroups.put(entry.getKey(), smellList);
293                }
294            }
295        }
296       
297        if (smellList.getListeners(SWT.Expand).length == 0) {
298            smellList.addListener(SWT.Expand, new Listener() {
299               public void handleEvent(final Event event) {
300                   ensureChildren((TreeItem) event.item);
301                   ((TreeItem) event.item).setExpanded(true);
302               }
303           });
304        }
305
306        for (int i = 0; i < allSortedSmells.size(); i++) {
307            createRootItem("smells group " + (i + 1), allSortedSmells.get(i));
308        }
309       
310    }
311
312    /**
313     * convenience method for creating the display of the instances
314     */
315    /*private void buildSmellTree() {
316        List<UsabilitySmell> smells = usabilityEvalResult.getAllSmells();
317       
318        int[] eventCoverageQuantileGroups = { 990, 975, 950, 0, -1 };
319        int[] minEventCoverages = new int[eventCoverageQuantileGroups.length];
320        int[] maxEventCoverages = new int[eventCoverageQuantileGroups.length];
321
322        final List<Map<String, List<UsabilitySmell>>> sortedSmells =
323            new LinkedList<Map<String, List<UsabilitySmell>>>();
324       
325        for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
326            sortedSmells.add(new HashMap<String, List<UsabilitySmell>>());
327        }
328       
329        for (UsabilitySmell smell : smells) {
330            int eventCoverageQuantile = smell.getIntensity().getEventCoverageQuantile();
331           
332            for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
333                if (eventCoverageQuantile >= eventCoverageQuantileGroups[i]) {
334                    Map<String, List<UsabilitySmell>> smellMap = sortedSmells.get(i);
335                   
336                    List<UsabilitySmell> smellList = smellMap.get(smell.getBriefDescription());
337                   
338                    if (smellList == null) {
339                        smellList = new ArrayList<UsabilitySmell>();
340                        smellMap.put(smell.getBriefDescription(), smellList);
341                    }
342                   
343                    int ratio = smell.getIntensity().getRatio();
344                    int eventCoverage = smell.getIntensity().getEventCoverage();
345                    int index = 0;
346                    for (index = 0; index < smellList.size(); index++) {
347                        UsabilitySmellIntensity candidate = smellList.get(index).getIntensity();
348                        if ((ratio == candidate.getRatio()) &&
349                            (eventCoverage > candidate.getEventCoverage()))
350                        {
351                            break;
352                        }
353                        else if (ratio > candidate.getRatio()) {
354                            break;
355                        }
356                    }
357                   
358                    smellList.add(index, smell);
359                   
360                    if (minEventCoverages[i] == 0) {
361                        minEventCoverages[i] = smell.getIntensity().getEventCoverage();
362                        maxEventCoverages[i] = smell.getIntensity().getEventCoverage();
363                    }
364                    else {
365                        minEventCoverages[i] = Math.min
366                            (minEventCoverages[i], smell.getIntensity().getEventCoverage());
367                        maxEventCoverages[i] = Math.max
368                            (maxEventCoverages[i], smell.getIntensity().getEventCoverage());
369                    }
370                   
371                    break;
372                }
373            }
374        }
375       
376        if (smellList.getListeners(SWT.Expand).length == 0) {
377            smellList.addListener(SWT.Expand, new Listener() {
378               public void handleEvent(final Event event) {
379                   ensureChildren((TreeItem) event.item);
380                   ((TreeItem) event.item).setExpanded(true);
381               }
382           });
383        }
384
385        double taskPercentages = 0;
386        double taskPercentagesCoveredByPreceedingGroups = 0;
387       
388        for (int i = 0; i < eventCoverageQuantileGroups.length; i++) {
389            taskPercentages = ((1000 - eventCoverageQuantileGroups[i]) / 10.0) -
390                taskPercentagesCoveredByPreceedingGroups;
391           
392            if (eventCoverageQuantileGroups[i] > -1) {
393                createRootItem("smells for " + taskPercentages + "% of tasks covering " +
394                               minEventCoverages[i] + " to " + maxEventCoverages[i] +
395                               " recorded events", sortedSmells.get(i));
396            }
397            else {
398                createRootItem("other smells not related to specific tasks", sortedSmells.get(i));
399
400            }
401           
402            taskPercentagesCoveredByPreceedingGroups += taskPercentages;
403        }
404       
405    }
406
407    /**
408     *
409     */
410    private void createRootItem(String name, Map<String, List<UsabilitySmell>> smells) {
411        TreeItem smellItem = new TreeItem(smellList, SWT.NULL);
412       
413        int count = 0;
414        for (Map.Entry<String, List<UsabilitySmell>> entry : smells.entrySet()) {
415            count += entry.getValue().size();
416        }
417
418        smellItem.setText(name + " (" + count + " smells)");
419        smellItem.setData(smells);
420       
421        if (count > 0) {
422            // simulate a child
423            new TreeItem(smellItem, SWT.NULL);
424        }
425    }
426
427    /**
428     *
429     */
430    private void clearSmellDetails() {
431        description.setText("");
432        involvedTargetsTree.removeAll();
433        involvedTasks.removeAll();
434    }
435
436    /**
437     *
438     */
439    private void displaySmellDetails(UsabilitySmell smell) {
440        clearSmellDetails();
441       
442        FontData data = description.getFont().getFontData()[0];
443        int height = (int) (data.getHeight() * 1.5);
444        Font defaultFont = new Font
445            (description.getDisplay(), data.getName(), height, data.getStyle());
446       
447        Font boldFont = new Font
448            (description.getDisplay(), data.getName(), height, data.getStyle() | SWT.BOLD);
449       
450        for (Object fragment : smell.getDescriptionFragments()) {
451            int color;
452            Font font;
453           
454            if (fragment instanceof String) {
455                color = SWT.COLOR_BLACK;
456                font = defaultFont;
457            }
458            else {
459                color = SWT.COLOR_DARK_GREEN;
460                font = boldFont;
461            }
462           
463            int initialLength = description.getText().length();
464           
465            if (fragment instanceof Collection<?>) {
466                int counter = 1;
467                for (Object elem : ((Collection<?>) fragment)) {
468                    description.append("\n");
469                    description.append(Integer.toString(counter++));
470                    description.append(".: ");
471                    description.append(elem.toString());
472                }
473            }
474            else {
475                description.append(fragment.toString());
476            }
477           
478            StyleRange styleRange = new StyleRange
479                (initialLength, description.getText().length() - initialLength,
480                 description.getDisplay().getSystemColor(color), null);
481
482            styleRange.font = font;
483               
484            description.setStyleRange(styleRange);
485            description.append(" ");
486        }
487        description.setLeftMargin(50);
488        description.setRightMargin(50);
489        description.setTopMargin(50);
490        description.setBottomMargin(50);
491
492       
493        StyleRange styleRange = new StyleRange();
494        styleRange.font = new Font(description.getDisplay(), "Courier", 12, SWT.NORMAL);
495        description.setStyleRange(styleRange);
496       
497        List<ITask> involvedTaskList = getInvolvedTasks(smell);
498        for (ITask involvedTask : involvedTaskList) {
499            VisualizationUtils.createTreeItemFor
500                (involvedTask, involvedTasks, usabilityEvalResult.getTaskModel(), true);
501        }
502       
503        List<IEventTarget> involvedTargets = getInvolvedTargets(smell);
504        if (involvedTargets.size() <= 0) {
505            for (ITask involvedTask : involvedTaskList) {
506                VisualizationUtils.getInvolvedTargets(involvedTask, involvedTargets);
507            }
508        }
509       
510        VisualizationUtils.addInvolvedTargets(involvedTargetsTree, involvedTargets);
511       
512        int weightLeft = involvedTaskList.size() == 0 ? 1 :
513            Math.min(4, Math.max(3, involvedTaskList.size()));
514        int weightRight = involvedTargets.size() == 0 ? 1 :
515            Math.min(3, Math.max(1, involvedTargets.size()));
516        ((SashForm) involvedTasks.getParent()).setWeights(new int[] { weightLeft, weightRight });
517       
518        VisualizationUtils.expandAll(involvedTasks, true);
519        VisualizationUtils.updateColumnWidths(involvedTasks);
520    }
521   
522    /**
523     *
524     */
525    private void ensureChildren(TreeItem parent) {
526        if ((parent.getItemCount() == 0) || (parent.getItems()[0].getData() != null)) {
527            return;
528        }
529       
530        for (int i = 0; i < parent.getItems().length; i++) {
531            parent.getItems()[i].dispose();
532        }
533       
534        if (parent.getData() instanceof Map<?, ?>) {
535            @SuppressWarnings("unchecked")
536            Map<String, List<UsabilitySmell>> map =
537                (Map<String, List<UsabilitySmell>>) parent.getData();
538           
539            for (Map.Entry<String, List<UsabilitySmell>> entry : map.entrySet()) {
540                TreeItem child = new TreeItem(parent, SWT.NULL);       
541                child.setText(entry.getKey() + " (" + entry.getValue().size() + " smells)");
542                child.setData(entry);
543               
544                if (entry.getValue().size() > 0) {
545                    // simulate child
546                    new TreeItem(child, SWT.NULL);
547                }
548            }
549        }
550        else if (parent.getData() instanceof Map.Entry<?, ?>) {
551            @SuppressWarnings("unchecked")
552            Map.Entry<String, List<UsabilitySmell>> entry =
553                (Map.Entry<String, List<UsabilitySmell>>) parent.getData();
554           
555            int count = 0;
556            for (UsabilitySmell smell : entry.getValue()) {
557                TreeItem child = new TreeItem(parent, SWT.NULL);       
558                child.setData(smell);
559                child.setText(++count + ": ratio = " + smell.getIntensity().getRatio() +
560                              ", covered events = " + smell.getIntensity().getEventCoverage());
561            }
562        }
563        else if (parent.getData() instanceof ITask) {
564            ITask task = (ITask) parent.getData();
565
566            if (task instanceof IStructuringTemporalRelationship) {
567                for (ITask subTask : ((IStructuringTemporalRelationship) task).getChildren()) {
568                    VisualizationUtils.createTreeItemFor
569                        (subTask, parent, usabilityEvalResult.getTaskModel(), true);
570                }
571            }
572            else if (task instanceof IMarkingTemporalRelationship) {
573                VisualizationUtils.createTreeItemFor
574                    (((IMarkingTemporalRelationship) task).getMarkedTask(), parent,
575                     usabilityEvalResult.getTaskModel(), true);
576            }
577        }
578    }
579   
580    /**
581     *
582     */
583    private List<ITask> getInvolvedTasks(UsabilitySmell smell) {
584        List<Object> fragments = smell.getDescriptionFragments();
585        List<ITask> involvedTasks = new ArrayList<ITask>();
586       
587        for (Object fragment : fragments) {
588            if (fragment instanceof ITask) {
589                involvedTasks.add((ITask) fragment);
590            }
591        }
592       
593        return involvedTasks;
594    }
595
596    /**
597     *
598     */
599    private List<IEventTarget> getInvolvedTargets(UsabilitySmell smell) {
600        List<Object> fragments = smell.getDescriptionFragments();
601        List<IEventTarget> involvedTargets = new ArrayList<IEventTarget>();
602       
603        for (Object fragment : fragments) {
604            if (fragment instanceof IEventTarget) {
605                involvedTargets.add((IEventTarget) fragment);
606            }
607            else if (fragment instanceof Collection<?>) {
608                for (Object elem : (Collection<?>) fragment) {
609                    if (elem instanceof IEventTarget) {
610                        involvedTargets.add((IEventTarget) elem);
611                    }
612                }
613            }
614        }
615       
616        return involvedTargets;
617    }
618}
Note: See TracBrowser for help on using the repository browser.