source: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UnusedGUIElementsRule.java @ 2042

Last change on this file since 2042 was 2042, checked in by pharms, 9 years ago
  • finalized smell detection for phd thesis
File size: 11.7 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.usability;
16
17import java.util.ArrayList;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24
25import de.ugoe.cs.autoquest.eventcore.Event;
26import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
27import de.ugoe.cs.autoquest.eventcore.guimodel.IButton;
28import de.ugoe.cs.autoquest.eventcore.guimodel.ICheckBox;
29import de.ugoe.cs.autoquest.eventcore.guimodel.IComboBox;
30import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
31import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
32import de.ugoe.cs.autoquest.eventcore.guimodel.IListBox;
33import de.ugoe.cs.autoquest.eventcore.guimodel.IMenuButton;
34import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
35import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
41import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
42import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
43
44/**
45 * TODO comment
46 *
47 * @version $Revision: $ $Date: 16.07.2012$
48 * @author 2012, last modified by $Author: pharms$
49 */
50public class UnusedGUIElementsRule implements UsabilityEvaluationRule {
51
52    /*
53     * (non-Javadoc)
54     *
55     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
56     */
57    @Override
58    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
59        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
60       
61        Map<IGUIView, List<Set<IGUIElement>>> viewDisplays =
62            getViewDisplays(taskModel.getUserSessions());
63       
64        Map<IGUIView, Set<IGUIElement>> allGUIElements = getAllGUIElements(taskModel);
65       
66        for (Map.Entry<IGUIView, List<Set<IGUIElement>>> viewDisplay : viewDisplays.entrySet()) {
67            handleUnusedGUIElements
68                (allGUIElements, viewDisplay.getKey(), viewDisplay.getValue(), results);
69        }
70
71        return results;
72    }
73
74    /**
75     * @param results
76     *
77     */
78    private void handleUnusedGUIElements(Map<IGUIView, Set<IGUIElement>> usedGUIElements,
79                                         IGUIView                        view,
80                                         List<Set<IGUIElement>>          viewUsages,
81                                         UsabilityEvaluationResult       results)
82    {
83        Set<IGUIElement> allElementsInView = usedGUIElements.get(view);
84       
85        if (allElementsInView == null) {
86            return;
87        }
88       
89        Map<Integer, List<IGUIElement>> usageCounters = new HashMap<>();
90       
91        for (IGUIElement relevantElement : allElementsInView) {
92            int usageCounter = 0;
93           
94            for (Set<IGUIElement> viewUsage : viewUsages) {
95                if (viewUsage.contains(relevantElement)) {
96                    usageCounter++;
97                }
98            }
99           
100            List<IGUIElement> elementsWithSameUsage = usageCounters.get(usageCounter);
101           
102            if (elementsWithSameUsage == null) {
103                elementsWithSameUsage = new LinkedList<>();
104                usageCounters.put(usageCounter, elementsWithSameUsage);
105            }
106           
107            elementsWithSameUsage.add(relevantElement);
108        }
109       
110        int cumulativeGuiElementUsage = 0;
111        for (Set<IGUIElement> viewUsage : viewUsages) {
112            cumulativeGuiElementUsage += viewUsage.size();
113        }
114       
115        List<IGUIElement> unusedElements = usageCounters.get(0);
116       
117        if (unusedElements != null) {
118            int ratio = 1000 * unusedElements.size() / allElementsInView.size();
119
120            UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity
121                (ratio, cumulativeGuiElementUsage, -1);
122
123            if (severity != null) {
124                Map<String, Object> parameters = new HashMap<String, Object>();
125
126                parameters.put("ratio", ratio / 10);
127                parameters.put("allDisplays", viewUsages.size());
128                parameters.put("view", view);
129                parameters.put("unusedGuiElements", unusedElements);
130                parameters.put("allGuiElements", allElementsInView.size());
131
132                results.addSmell
133                    (severity, UsabilitySmellDescription.UNUSED_GUI_ELEMENTS, parameters);
134            }
135        }
136    }
137
138    /**
139     *
140     */
141    private Map<IGUIView, Set<IGUIElement>> getAllGUIElements(ITaskModel taskModel) {
142        Map<IGUIView, Set<IGUIElement>> result = new HashMap<>();
143       
144        for (ITask task : taskModel.getTasks()) {
145            if (task instanceof IEventTask) {
146                for (ITaskInstance instance : task.getInstances()) {
147                    Event event = ((IEventTaskInstance) instance).getEvent();
148                   
149                    if ((event.getTarget() instanceof IGUIElement) &&
150                        (isRelevant((IGUIElement) event.getTarget())))
151                    {
152                        IGUIView view = ((IGUIElement) event.getTarget()).getView();
153                       
154                        Set<IGUIElement> elements = result.get(view);
155                       
156                        if (elements == null) {
157                            elements = new HashSet<>();
158                            result.put(view, elements);
159                        }
160                       
161                        elements.add((IGUIElement) event.getTarget());
162                    }
163                }
164            }
165        }
166       
167        // the problem is, that using the GUI model does not allow to find all in a specific view
168        // as the GUI model may return a merged element instead. But anyway, we can add those, which
169        // are in the GUI model and have the same view.
170       
171        GUIModel model = result.values().iterator().next().iterator().next().getGUIModel();
172       
173        GUIModel.Traverser traverser = model.getTraverser();
174
175        IGUIElement currentGUIElement = null;
176        do {
177            if (traverser.hasFirstChild()) {
178                currentGUIElement = traverser.firstChild();
179            }
180            else if (traverser.hasNextSibling()) {
181                currentGUIElement = traverser.nextSibling();
182            }
183            else {
184                while (currentGUIElement != null) {
185                    currentGUIElement = traverser.parent();
186                    if (traverser.hasNextSibling()) {
187                        currentGUIElement = traverser.nextSibling();
188                        break;
189                    }
190                }
191            }
192
193            if (isRelevant(currentGUIElement)) {
194                IGUIView view = currentGUIElement.getView();
195               
196                Set<IGUIElement> elements = result.get(view);
197               
198                if (elements == null) {
199                    elements = new HashSet<>();
200                    result.put(view, elements);
201                }
202               
203                elements.add(currentGUIElement);
204            }
205        }
206        while (currentGUIElement != null);
207       
208        return result;
209    }
210
211    /**
212     *
213     */
214    private Map<IGUIView, List<Set<IGUIElement>>> getViewDisplays(List<IUserSession> sessions) {
215        final IGUIView[] currentView = new IGUIView[1];
216        final List<IEventTaskInstance> actionInstances = new ArrayList<>();
217        final Map<IGUIView, List<Set<IGUIElement>>> result = new HashMap<>();
218       
219        for (IUserSession session : sessions) {
220            currentView[0] = null;
221            actionInstances.clear();
222           
223            for (final ITaskInstance currentRoot : session) {
224                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
225                    @Override
226                    public void visit(IEventTaskInstance eventTaskInstance) {
227                        if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) {
228                            IGUIView view =
229                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
230                           
231                            if ((currentView[0] == null) && (view != null)) {
232                                currentView[0] = view;
233                                actionInstances.clear();
234                            }
235                            else if ((currentView[0] != null) && (!currentView[0].equals(view))) {
236                                addRelevantTargets(currentView[0], actionInstances, result);
237                               
238                                currentView[0] = view;
239                                actionInstances.clear();
240                            }
241                        }
242                       
243                        if (eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) {
244                            actionInstances.add(eventTaskInstance);
245                        }
246                    }
247                });
248            }
249           
250            // add the used GUI elements of the last shown view in the session
251            if (currentView[0] != null) {
252                addRelevantTargets(currentView[0], actionInstances, result);
253            }
254        }
255       
256        return result;
257    }
258
259    /**
260     *
261     */
262    private void addRelevantTargets(IGUIView                              view,
263                                    List<IEventTaskInstance>              actionInstances,
264                                    Map<IGUIView, List<Set<IGUIElement>>> result)
265    {
266        List<Set<IGUIElement>> usedGUIElements = result.get(view);
267       
268        if (usedGUIElements == null) {
269            usedGUIElements = new LinkedList<>();
270            result.put(view, usedGUIElements);
271        }
272       
273        Set<IGUIElement> elementsInViewDisplay = new HashSet<>();
274       
275        for (IEventTaskInstance actionInstance : actionInstances) {
276            IGUIElement element = (IGUIElement) actionInstance.getEvent().getTarget();
277           
278            while (element != null) {
279                if (isRelevant(element)) {
280                    elementsInViewDisplay.add(element);
281                }
282               
283                element = element.getParent();
284            }
285        }
286       
287        usedGUIElements.add(elementsInViewDisplay);
288    }
289
290    /**
291     *
292     */
293    private boolean isRelevant(IGUIElement currentGUIElement) {
294        if (currentGUIElement == null) {
295            return false;
296        }
297       
298        if ((currentGUIElement instanceof IButton) ||
299            (currentGUIElement instanceof ICheckBox) ||
300            (currentGUIElement instanceof IComboBox) ||
301            (currentGUIElement instanceof IListBox) ||
302            (currentGUIElement instanceof IMenuButton) ||
303            (currentGUIElement instanceof ITextArea) ||
304            (currentGUIElement instanceof ITextField))
305        {
306            return true;
307        }
308       
309        return false;
310    }
311}
Note: See TracBrowser for help on using the repository browser.