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

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