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

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