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

Last change on this file since 1301 was 1301, checked in by pharms, 11 years ago
  • removed some TODOs as one TODO for commenting a class is sufficient for that class
File size: 13.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.usability;
16
17import java.text.DecimalFormat;
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23
24import de.ugoe.cs.autoquest.eventcore.IEventTarget;
25import de.ugoe.cs.autoquest.eventcore.IEventType;
26import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
27import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
28import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
36
37/**
38 * TODO comment
39 *
40 * @version $Revision: $ $Date: 16.07.2012$
41 * @author 2012, last modified by $Author: pharms$
42 */
43public class TextInputStatisticsRule implements de.ugoe.cs.autoquest.usability.UsabilityEvaluationRule {
44
45    /*
46     * (non-Javadoc)
47     *
48     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
49     */
50    @Override
51    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
52        TextInputStatistics statistics = new TextInputStatistics();
53        calculateStatistics(taskModel.getTasks(), statistics);
54
55        UsabilityEvaluationResult results = new UsabilityEvaluationResult();
56        analyzeStatistics(statistics, results);
57
58        return results;
59    }
60
61    /**
62     *
63     */
64    private void analyzeStatistics(TextInputStatistics       statistics,
65                                   UsabilityEvaluationResult results)
66    {
67        checkTextInputRatio(statistics, results);
68        checkTextFieldEntryRepetitions(statistics, results);
69        checkTextFieldNoLetterOrDigitInputs(statistics, results);
70    }
71
72    /**
73     *
74     */
75    private void checkTextInputRatio(TextInputStatistics       statistics,
76                                     UsabilityEvaluationResult results)
77    {
78        float allTextFieldInputs =
79            statistics.getNoOfTextFieldInputs() + statistics.getNoOfTextAreaInputs();
80
81        float ratio = allTextFieldInputs / (float) statistics.getNoOfAllEvents();
82
83        UsabilityDefectSeverity severity = null;
84        if (ratio > 0.9) {
85            severity = UsabilityDefectSeverity.HIGH;
86        }
87        else if (ratio > 0.7) {
88            severity = UsabilityDefectSeverity.MEDIUM;
89        }
90        else if (ratio > 0.5) {
91            severity = UsabilityDefectSeverity.LOW;
92        }
93        else if (ratio > 0.3) {
94            severity = UsabilityDefectSeverity.INFO;
95        }
96
97        if (severity != null) {
98            Map<String, String> parameters = new HashMap<String, String>();
99            parameters.put("textInputRatio", DecimalFormat.getInstance().format(ratio * 100) + "%");
100
101            results.addDefect
102                (new UsabilityDefect(severity, UsabilityDefectDescription.TEXT_FIELD_INPUT_RATIO,
103                                     parameters));
104        }
105    }
106
107    /**
108     *
109     */
110    private void checkTextFieldEntryRepetitions(TextInputStatistics       statistics,
111                                                UsabilityEvaluationResult results)
112    {
113        Map<String, Integer> words = new HashMap<String, Integer>();
114        int numberOfRepeatedWords = 0;
115        int maxRepetitions = 0;
116
117        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
118            String[] fragments = statistics.getTextFieldInputFragments(i);
119            for (String fragment : fragments) {
120                if (!"".equals(fragment.trim())) {
121                    Integer count = words.get(fragment);
122                    if (count == null) {
123                        words.put(fragment, 1);
124                    }
125                    else {
126                        count++;
127                        words.put(fragment, count);
128                        maxRepetitions = Math.max(count, maxRepetitions);
129
130                        if (count == 2) {
131                            // do not calculate repeated words several times
132                            numberOfRepeatedWords++;
133                        }
134                    }
135                }
136            }
137        }
138
139        UsabilityDefectSeverity severity = null;
140        if ((numberOfRepeatedWords > 10) || (maxRepetitions > 10)) {
141            severity = UsabilityDefectSeverity.HIGH;
142        }
143        else if ((numberOfRepeatedWords > 4) || (maxRepetitions > 4)) {
144            severity = UsabilityDefectSeverity.MEDIUM;
145        }
146        else if ((numberOfRepeatedWords > 2) || (maxRepetitions > 2)) {
147            severity = UsabilityDefectSeverity.LOW;
148        }
149        else if ((numberOfRepeatedWords > 1) || (maxRepetitions > 1)) {
150            severity = UsabilityDefectSeverity.INFO;
151        }
152
153        if (severity != null) {
154            Map<String, String> parameters = new HashMap<String, String>();
155            parameters.put("textRepetitionRatio", numberOfRepeatedWords +
156                           " repeated tokens, up to " + maxRepetitions + " repetitions per token");
157
158            results.addDefect
159                (new UsabilityDefect(severity,
160                                     UsabilityDefectDescription.TEXT_FIELD_INPUT_REPETITIONS,
161                                     parameters));
162        }
163    }
164
165    /**
166     *
167     */
168    private void checkTextFieldNoLetterOrDigitInputs(TextInputStatistics       statistics,
169                                                     UsabilityEvaluationResult results)
170    {
171        int allCharactersCount = 0;
172        int noLetterOrDigitCount = 0;
173
174        for (int i = 0; i < statistics.getNoOfTextFieldInputs(); i++) {
175            String[] fragments = statistics.getTextFieldInputFragments(i);
176            for (String fragment : fragments) {
177                String effectiveFragment = fragment.trim();
178                for (int j = 0; j < effectiveFragment.length(); j++) {
179                    if (!Character.isWhitespace(effectiveFragment.charAt(j))) {
180                        if (!Character.isLetterOrDigit(effectiveFragment.charAt(j))) {
181                            noLetterOrDigitCount++;
182                        }
183                        allCharactersCount++;
184                    }
185                }
186            }
187        }
188
189        float ratio = (float) noLetterOrDigitCount / (float) allCharactersCount;
190
191        UsabilityDefectSeverity severity = null;
192        if (ratio > 0.1) // every 10th sign
193        {
194            severity = UsabilityDefectSeverity.HIGH;
195        }
196        else if (ratio > 0.05) // every 20th sign
197        {
198            severity = UsabilityDefectSeverity.MEDIUM;
199        }
200        else if (ratio > 0.02) // every 50th sign
201        {
202            severity = UsabilityDefectSeverity.LOW;
203        }
204        else if (ratio > 0.01) // every 100th sign
205        {
206            severity = UsabilityDefectSeverity.INFO;
207        }
208
209        if (severity != null) {
210            Map<String, String> parameters = new HashMap<String, String>();
211            parameters.put("noLetterOrDigitRatio", allCharactersCount + " entered characters of " +
212                           "which " + noLetterOrDigitCount + " were no letter or digit");
213
214            results.addDefect
215                (new UsabilityDefect(severity,
216                                     UsabilityDefectDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
217                                     parameters));
218        }
219    }
220
221    /**
222     *
223     */
224    private void calculateStatistics(Collection<ITask> tasks, TextInputStatistics statistics) {
225        for (ITask task : tasks) {
226            calculateStatistics(task, statistics);
227        }
228    }
229
230    /**
231     *
232     */
233    private void calculateStatistics(ITask task, TextInputStatistics statistics) {
234       
235        if (isTextInput(task)) {
236            calculateStatistics((IEventTask) task, statistics);
237        }
238        else {
239            if (task instanceof IStructuringTemporalRelationship) {
240                for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
241                    calculateStatistics(child, statistics);
242                }
243            }
244            else if (task instanceof IMarkingTemporalRelationship) {
245                calculateStatistics
246                    (((IMarkingTemporalRelationship) task).getMarkedTask(), statistics);
247            }
248            else {
249                statistics.incrementNoOfOtherEventTasks();
250            }
251        }
252    }
253
254    /**
255     *
256     */
257    private boolean isTextInput(ITask task) {
258        if (task.getInstances().size() > 0) {
259            ITaskInstance instance = task.getInstances().iterator().next();
260            if (instance instanceof IEventTaskInstance) {
261                return ((IEventTaskInstance) instance).getEvent().getType() instanceof TextInput;
262            }
263        }
264
265        return false;
266    }
267
268    /**
269     *
270     */
271    private void calculateStatistics(IEventTask node, TextInputStatistics statistics) {
272       
273        for (ITaskInstance instance : node.getInstances()) {
274            IEventType type = ((IEventTaskInstance) instance).getEvent().getType();
275            IEventTarget target = ((IEventTaskInstance) instance).getEvent().getTarget();
276
277            if (type instanceof TextInput) {
278                String[] fragments =
279                    determineTextFragments(((TextInput) type).getEnteredText());
280               
281                if (target instanceof ITextField) {
282                    statistics.addTextFieldInput(node, fragments);
283                }
284                else if (target instanceof ITextArea) {
285                    statistics.addTextAreaInput(node, fragments);
286                }
287            }
288        }
289    }
290
291    /**
292     *
293     */
294    private String[] determineTextFragments(String enteredText) {
295        List<String> fragments = new ArrayList<String>();
296
297        StringBuffer fragment = new StringBuffer();
298        char lastChar = 0;
299
300        for (int i = 0; i < enteredText.length(); i++) {
301            char currentChar = enteredText.charAt(i);
302
303            if (!isEqualCharacterType(lastChar, currentChar)) {
304                // the previous fragment ended. so finalize it and start a new one
305                if ((fragment != null) && (fragment.length() > 0)) {
306                    fragments.add(fragment.toString());
307                    fragment = new StringBuffer();
308                }
309            }
310
311            fragment.append(currentChar);
312            lastChar = currentChar;
313        }
314
315        if ((fragment != null) && (fragment.length() > 0)) {
316            fragments.add(fragment.toString());
317        }
318
319        return fragments.toArray(new String[fragments.size()]);
320    }
321
322    /**
323     *
324     */
325    private boolean isEqualCharacterType(char char1, char char2) {
326        return
327            ((char1 == char2) ||
328            (Character.isWhitespace(char1) && Character.isWhitespace(char2)) ||
329            (Character.isDigit(char1) && Character.isDigit(char2)) ||
330            (Character.isLetter(char1) && Character.isLetter(char2)) ||
331            (Character.isJavaIdentifierPart(char1) && Character.isJavaIdentifierPart(char2)));
332    }
333
334    /**
335     * TODO comment
336     *
337     * @version $Revision: $ $Date: 16.07.2012$
338     * @author 2012, last modified by $Author: pharms$
339     */
340    public static class TextInputStatistics {
341       
342        /** */
343        private List<Object[]> textFieldInputs = new ArrayList<Object[]>();
344
345        /** */
346        private List<Object[]> textAreaInputs = new ArrayList<Object[]>();
347
348        /** */
349        private int otherEventsCount;
350
351        /**
352         *
353         */
354        public void addTextFieldInput(IEventTask node, String[] fragments) {
355            textFieldInputs.add(new Object[] { node, fragments });
356        }
357
358        /**
359         *
360         */
361        public void addTextAreaInput(IEventTask node, String[] fragments) {
362            textAreaInputs.add(new Object[] { node, fragments });
363        }
364
365        /**
366         *
367         */
368        public int getNoOfAllEvents() {
369            return textFieldInputs.size() + textAreaInputs.size() + otherEventsCount;
370        }
371
372        /**
373         *
374         */
375        public int getNoOfTextFieldInputs() {
376            return textFieldInputs.size();
377        }
378
379        /**
380         *
381         */
382        public String[] getTextFieldInputFragments(int index) {
383            return (String[]) textFieldInputs.get(index)[1];
384        }
385
386        /**
387         *
388         */
389        public int getNoOfTextAreaInputs() {
390            return textAreaInputs.size();
391        }
392
393        /**
394         *
395         */
396        public String[] getTextAreaInputFragments(int index) {
397            return (String[]) textAreaInputs.get(index)[1];
398        }
399
400        /**
401         *
402         */
403        public void incrementNoOfOtherEventTasks() {
404            otherEventsCount++;
405        }
406
407    }
408
409}
Note: See TracBrowser for help on using the repository browser.