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

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