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

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