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

Last change on this file was 2042, checked in by pharms, 9 years ago
  • finalized smell detection for phd thesis
File size: 19.2 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.Collection;
19import java.util.HashMap;
20import java.util.HashSet;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25
26import de.ugoe.cs.autoquest.eventcore.IEventTarget;
27import de.ugoe.cs.autoquest.eventcore.IEventType;
28import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
29import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
30import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
39
40/**
41 * TODO comment
42 *
43 * @version $Revision: $ $Date: 16.07.2012$
44 * @author 2012, last modified by $Author: pharms$
45 */
46public class TextInputStatisticsRule implements UsabilityEvaluationRule {
47
48    /*
49     * (non-Javadoc)
50     *
51     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
52     */
53    @Override
54    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
55        TextInputStatistics statistics = new TextInputStatistics();
56        calculateStatistics(taskModel.getUserSessions(), statistics);
57
58        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
59        analyzeStatistics(statistics, results);
60
61        return results;
62    }
63
64    /**
65     *
66     */
67    private void analyzeStatistics(TextInputStatistics       statistics,
68                                   UsabilityEvaluationResult results)
69    {
70        checkTextInputRatio(statistics, results);
71        checkTextFieldEntryRepetitions(statistics, results);
72        checkTextFieldNoLetterOrDigitInputs(statistics, results);
73    }
74
75    /**
76     *
77     */
78    private void checkTextInputRatio(TextInputStatistics       statistics,
79                                     UsabilityEvaluationResult results)
80    {
81        int allTextFieldInputs =
82            statistics.getNoOfTextFieldInputs() + statistics.getNoOfTextAreaInputs();
83
84        int ratio = 1000 * allTextFieldInputs / statistics.getNoOfAllEvents();
85
86        UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity(ratio);
87       
88        if (severity != null) {
89            Map<String, Object> parameters = new HashMap<String, Object>();
90            parameters.put("textInputRatio", (ratio / 10));
91
92            results.addSmell
93                (severity, UsabilitySmellDescription.TEXT_FIELD_INPUT_RATIO, parameters);
94        }
95    }
96
97    /**
98     *
99     */
100    private void checkTextFieldEntryRepetitions(TextInputStatistics       statistics,
101                                                UsabilityEvaluationResult results)
102    {
103        List<TextFieldCorrelation> textFieldCorrelations =
104            statistics.determineTextFieldCorrelations();
105       
106        for (TextFieldCorrelation entry : textFieldCorrelations) {
107            int noOfUsagesOfTextField1 = statistics.getUsageCount(entry.textField1);
108            int noOfUsagesOfTextField2 = statistics.getUsageCount(entry.textField2);
109            int numberOfEqualEntries = entry.enteredTexts.size();
110           
111            createTextFieldEntryRepetitionSmell(noOfUsagesOfTextField1, numberOfEqualEntries,
112                                                entry.textField1, entry.textField2, results);
113           
114            createTextFieldEntryRepetitionSmell(noOfUsagesOfTextField2, numberOfEqualEntries,
115                                                entry.textField2, entry.textField1, results);
116           
117        }
118    }
119
120    /**
121     *
122     */
123    private void createTextFieldEntryRepetitionSmell(int                       allEntries,
124                                                     int                       numberOfEqualEntries,
125                                                     ITextField                textField1,
126                                                     ITextField                textField2,
127                                                     UsabilityEvaluationResult results)
128    {
129        int ratio = 1000 * numberOfEqualEntries / allEntries;
130       
131        UsabilitySmellIntensity severity =
132            UsabilitySmellIntensity.getIntensity(ratio, allEntries, -1);
133       
134        if (severity != null) {
135            Map<String, Object> parameters = new HashMap<String, Object>();
136            parameters.put("numberOfEqualEntries", numberOfEqualEntries);
137            parameters.put("numberOfAllEntries", allEntries);
138            parameters.put("textRepetitionRatio", (ratio / 10));
139            parameters.put("textField1", textField1);
140            parameters.put("textField2", textField2);
141
142            results.addSmell
143                (severity, UsabilitySmellDescription.TEXT_FIELD_INPUT_REPETITIONS, parameters);
144        }
145    }
146
147    /**
148     *
149     */
150    private void checkTextFieldNoLetterOrDigitInputs(TextInputStatistics       statistics,
151                                                     UsabilityEvaluationResult results)
152    {
153        for (ITextField textField : statistics.getAllTextFields()) {
154            int allCharactersCount = 0;
155            int noLetterOrDigitCount = 0;
156
157            for (String textInput : statistics.getAllInputsInto(textField)) {
158                for (int j = 0; j < textInput.length(); j++) {
159                    if (!Character.isWhitespace(textInput.charAt(j))) {
160                        if (!Character.isLetterOrDigit(textInput.charAt(j))) {
161                            noLetterOrDigitCount++;
162                        }
163                        allCharactersCount++;
164                    }
165                }
166            }
167
168            int ratio = 1000 * noLetterOrDigitCount / allCharactersCount;
169
170            UsabilitySmellIntensity severity = UsabilitySmellIntensity.getIntensity
171                (ratio, statistics.getAllInputsInto(textField).size(), -1);
172
173            if (severity != null) {
174                Map<String, Object> parameters = new HashMap<String, Object>();
175                parameters.put("textField", textField);
176                parameters.put("noLetterOrDigitRatio", (ratio / 10));
177
178                results.addSmell
179                    (severity, UsabilitySmellDescription.TEXT_FIELD_NO_LETTER_OR_DIGIT_RATIO,
180                     parameters);
181            }
182        }
183    }
184
185    /**
186     *
187     */
188    private void calculateStatistics(Collection<IUserSession> sessions,
189                                     TextInputStatistics      statistics)
190    {
191        System.out.print("calculating statistics ... ");
192        for (IUserSession session : sessions) {
193            for (ITaskInstance taskInstance : session) {
194                calculateStatistics(taskInstance, session, statistics);
195            }
196        }
197        System.out.println("done");
198    }
199
200    /**
201     *
202     */
203    private void calculateStatistics(ITaskInstance       taskInstance,
204                                     IUserSession        session,
205                                     TextInputStatistics statistics)
206    {
207        if (isTextInput(taskInstance)) {
208            calculateStatistics((IEventTaskInstance) taskInstance, session, statistics);
209        }
210        else {
211            if (taskInstance instanceof ISequenceInstance) {
212                for (ITaskInstance child : (ISequenceInstance) taskInstance) {
213                    calculateStatistics(child, session, statistics);
214                }
215            }
216            else if (taskInstance instanceof IIterationInstance) {
217                for (ITaskInstance child : (IIterationInstance) taskInstance) {
218                    calculateStatistics(child, session, statistics);
219                }
220            }
221            else if (taskInstance instanceof ISelectionInstance) {
222                calculateStatistics
223                    (((ISelectionInstance) taskInstance).getChild(), session, statistics);
224            }
225            else if (taskInstance instanceof IOptionalInstance) {
226                calculateStatistics
227                    (((IOptionalInstance) taskInstance).getChild(), session, statistics);
228            }
229            else{
230                statistics.incrementNoOfOtherEventTasks();
231            }
232        }
233    }
234
235    /**
236     *
237     */
238    private boolean isTextInput(ITaskInstance taskInstance) {
239        if (taskInstance instanceof IEventTaskInstance) {
240            return ((IEventTaskInstance) taskInstance).getEvent().getType() instanceof TextInput;
241        }
242
243        return false;
244    }
245
246    /**
247     *
248     */
249    private void calculateStatistics(IEventTaskInstance  taskInstance,
250                                     IUserSession        session,
251                                     TextInputStatistics statistics)
252    {
253        IEventType type = taskInstance.getEvent().getType();
254        IEventTarget target = taskInstance.getEvent().getTarget();
255
256        if (type instanceof TextInput) {
257            if (target instanceof ITextField) {
258                statistics.addTextFieldInput(taskInstance, session);
259            }
260            else if (target instanceof ITextArea) {
261                statistics.addTextAreaInput(taskInstance, session);
262            }
263        }
264    }
265
266    /**
267     *
268     */
269/*    private static String[] determineTextFragments(String enteredText) {
270        List<String> fragments = new ArrayList<String>();
271
272        StringBuffer fragment = new StringBuffer();
273        char lastChar = 0;
274
275        for (int i = 0; i < enteredText.length(); i++) {
276            char currentChar = enteredText.charAt(i);
277
278            if (!isEqualCharacterType(lastChar, currentChar)) {
279                // the previous fragment ended. so finalize it and start a new one
280                if ((fragment != null) && (fragment.length() > 0)) {
281                    String fragmentStr = fragment.toString().trim();
282                   
283                    if (!"".equals(fragmentStr)) {
284                        fragments.add(fragmentStr);
285                    }
286                   
287                    fragment = new StringBuffer();
288                }
289            }
290
291            fragment.append(currentChar);
292            lastChar = currentChar;
293        }
294
295        if ((fragment != null) && (fragment.length() > 0)) {
296            String fragmentStr = fragment.toString().trim();
297           
298            if (!"".equals(fragmentStr)) {
299                fragments.add(fragmentStr);
300            }
301        }
302
303        return fragments.toArray(new String[fragments.size()]);
304    }
305
306    /**
307     *
308     */
309/*    private static boolean isEqualCharacterType(char char1, char char2) {
310        return
311            ((char1 == char2) ||
312            (Character.isWhitespace(char1) && Character.isWhitespace(char2)) ||
313            (Character.isDigit(char1) && Character.isDigit(char2)) ||
314            (Character.isLetter(char1) && Character.isLetter(char2)) ||
315            (Character.isJavaIdentifierPart(char1) && Character.isJavaIdentifierPart(char2)));
316    }*/
317
318    /**
319     *
320     */
321    private static class TextInputStatistics {
322       
323        /** */
324        private List<IEventTaskInstance> textFieldInputs = new ArrayList<IEventTaskInstance>();
325       
326        /** */
327        private Map<ITextField, List<String>> textFields = new HashMap<ITextField, List<String>>();
328
329        /** */
330        private List<IEventTaskInstance> textAreaInputs = new ArrayList<IEventTaskInstance>();
331
332        /** */
333        private Map<IUserSession, Map<String, TextEntryData>> textEntries =
334            new HashMap<IUserSession, Map<String, TextEntryData>>();
335
336        /** */
337        private int otherEventsCount;
338
339        /**
340         *
341         */
342        private void addTextFieldInput(IEventTaskInstance instance, IUserSession session) {
343            String enteredText = ((TextInput) instance.getEvent().getType()).getEnteredText();
344           
345            if ((enteredText != null) && (!"".equals(enteredText.trim()))) {
346                enteredText = enteredText.trim();
347               
348                textFieldInputs.add(instance);
349
350                // store text entries into text fields
351                List<String> entries = textFields.get(instance.getEvent().getTarget());
352
353                if (entries == null) {
354                    entries = new LinkedList<String>();
355                    textFields.put((ITextField) instance.getEvent().getTarget(), entries);
356                }
357               
358                entries.add(enteredText);
359
360                // writing down all text entries in text fields to check later for cooccurrences in
361                // same session
362                Map<String, TextEntryData> sessionTextEntries = textEntries.get(session);
363
364                if (sessionTextEntries == null) {
365                    sessionTextEntries = new HashMap<String, TextEntryData>();
366                    textEntries.put(session, sessionTextEntries);
367                }
368
369                TextEntryData data = sessionTextEntries.get(enteredText);
370
371                if (data == null) {
372                    data = new TextEntryData(enteredText);
373                    sessionTextEntries.put(enteredText, data);
374                }
375
376                data.addTaskInstance(instance);
377            }
378        }
379       
380        /**
381         *
382         */
383        public List<String> getAllInputsInto(ITextField textField) {
384            return textFields.get(textField);
385        }
386
387        /**
388         *
389         */
390        private int getUsageCount(ITextField textField) {
391            List<String> entries = textFields.get(textField);
392           
393            if (entries == null) {
394                return 0;
395            }
396            else {
397                return entries.size();
398            }
399        }
400
401        /**
402         *
403         */
404        private List<TextFieldCorrelation> determineTextFieldCorrelations() {
405            System.out.print("determining text field correlations of " + textFields.size() +
406                             " text fields ... ");
407            List<TextFieldCorrelation> correlations = new ArrayList<TextFieldCorrelation>();
408           
409            // we need an ordered list of text fields to be able compare all with each other
410            // through a nested loop
411            List<ITextField> textFieldList = getAllTextFields();
412           
413            List<TextEntryData> relevantTextEntryData = new LinkedList<TextEntryData>();
414           
415            for (Map<String, TextEntryData> sessionSpecEntries : textEntries.values()) {
416                for (TextEntryData data : sessionSpecEntries.values()) {
417                    if (data.textFields.size() > 1) {
418                        relevantTextEntryData.add(data);
419                    }
420                }
421            }
422           
423            for (int i = 0; i < (textFieldList.size() - 1); i++) {
424                for (int j = i + 1; j < textFieldList.size(); j++) {
425                    // count the number of times, in which the same text was entered in both
426                    // text fields within the same session
427                    List<String> sameEnteredTexts = new LinkedList<String>();
428
429                    for (TextEntryData data : relevantTextEntryData) {
430                        if (data.textFields.contains(textFieldList.get(i)) &&
431                            data.textFields.contains(textFieldList.get(j)))
432                        {
433                            sameEnteredTexts.add(data.enteredText);
434                        }
435                    }
436
437                    if (sameEnteredTexts.size() > 0) {
438                        // for the checked combination of text fields, there is at least once
439                        // the same text entered into both text fields during the same session
440                        correlations.add(new TextFieldCorrelation(textFieldList.get(i),
441                                                                  textFieldList.get(j),
442                                                                  sameEnteredTexts));
443                    }
444                }
445            }
446           
447            System.out.println("done");
448           
449            return correlations;
450        }
451
452        /**
453         *
454         */
455        private void addTextAreaInput(IEventTaskInstance instance, IUserSession session) {
456            textAreaInputs.add(instance);
457        }
458
459        /**
460         *
461         */
462        private int getNoOfAllEvents() {
463            return textFieldInputs.size() + textAreaInputs.size() + otherEventsCount;
464        }
465
466        /**
467         *
468         */
469        private int getNoOfTextFieldInputs() {
470            return textFieldInputs.size();
471        }
472
473        /**
474         *
475         */
476        private int getNoOfTextAreaInputs() {
477            return textAreaInputs.size();
478        }
479
480        /**
481         *
482         */
483        private void incrementNoOfOtherEventTasks() {
484            otherEventsCount++;
485        }
486
487        /**
488         *
489         */
490        private List<ITextField> getAllTextFields() {
491            List<ITextField> textFieldList = new ArrayList<ITextField>(textFields.size());
492           
493            for (ITextField textField : textFields.keySet()) {
494                textFieldList.add(textField);
495            }
496           
497            return textFieldList;
498        }
499    }
500   
501    /**
502     *
503     */
504    private static class TextEntryData {
505       
506        /** */
507        private String enteredText;
508       
509        /** */
510        private List<IEventTaskInstance> respectiveTaskInstances =
511            new LinkedList<IEventTaskInstance>();
512
513        /** */
514        private Set<ITextField> textFields = new HashSet<ITextField>();
515       
516        /**
517         *
518         */
519        private TextEntryData(String text) {
520            this.enteredText = text;
521        }
522
523        /**
524         *
525         */
526        private void addTaskInstance(IEventTaskInstance instance) {
527            respectiveTaskInstances.add(instance);
528            textFields.add((ITextField) instance.getEvent().getTarget());
529        }
530       
531    }
532
533    /**
534     *
535     */
536    private static class TextFieldCorrelation {
537       
538        /** */
539        private List<String> enteredTexts = new LinkedList<String>();
540
541        /** */
542        private ITextField textField1;
543       
544        /** */
545        private ITextField textField2;
546       
547        /**
548         *
549         */
550        private TextFieldCorrelation(ITextField   textField1,
551                                     ITextField   textField2,
552                                     List<String> enteredTexts)
553        {
554            this.textField1 = textField1;
555            this.textField2 = textField2;
556            this.enteredTexts = enteredTexts;
557        }
558
559    }
560
561}
Note: See TracBrowser for help on using the repository browser.