Changeset 2162 for trunk/autoquest-core-usability/src/main/java/de/ugoe
- Timestamp:
- 09/07/17 16:15:00 (7 years ago)
- Location:
- trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability
- Files:
-
- 2 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/CommonTaskRateRule.java
r1959 r2162 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.text.DecimalFormat;18 17 import java.util.ArrayList; 19 18 import java.util.HashMap; … … 21 20 import java.util.List; 22 21 import java.util.Map; 22 import java.util.Set; 23 23 24 24 import org.apache.commons.math3.stat.descriptive.SummaryStatistics; … … 26 26 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 27 27 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; 28 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; 29 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance; 28 30 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; 29 31 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; 30 32 import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; 33 import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils; 31 34 32 35 /** … … 46 49 public UsabilityEvaluationResult evaluate(ITaskModel taskModel) { 47 50 SummaryStatistics statistics = new SummaryStatistics(); 48 int allObserved = calculateStatistics(taskModel.getUserSessions(), statistics); 51 SummaryStatistics mpStatistics = new SummaryStatistics(); 52 int allObserved = calculateStatistics 53 (taskModel.getUserSessions(), TaskTreeUtils.getMostProminentTasks(taskModel), 54 statistics, mpStatistics); 49 55 50 56 UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel); 51 analyzeStatistics(statistics, allObserved, results); 57 analyzeStatistics(statistics, false, allObserved, results); 58 analyzeStatistics(mpStatistics, true, allObserved, results); 52 59 53 60 return results; … … 58 65 */ 59 66 private void analyzeStatistics(SummaryStatistics statistics, 67 boolean mostProminentSequencesOnly, 60 68 int allObserved, 61 69 UsabilityEvaluationResult results) … … 75 83 if (intensity != null) { 76 84 Map<String, Object> parameters = new HashMap<String, Object>(); 77 parameters.put("ratio", new DecimalFormat("#.##").format(mean * 10)); 85 parameters.put("ratio", ((float) Math.round(mean * 100)) / 10); 86 87 if (mostProminentSequencesOnly) { 88 parameters.put("tasksType", "representative tasks"); 89 } 90 else { 91 parameters.put("tasksType", "tasks"); 92 } 78 93 79 94 results.addSmell(intensity, UsabilitySmellDescription.COMMON_TASK_RATE, parameters); … … 87 102 */ 88 103 private int calculateStatistics(List<IUserSession> sessions, 89 final SummaryStatistics statistics) 104 final Set<ISequence> mostProminentTasks, 105 final SummaryStatistics statistics, 106 final SummaryStatistics mpStatistics) 90 107 { 91 108 final LinkedList<ITaskInstance> rootNodes = new LinkedList<>(); 109 final LinkedList<ITaskInstance> mpRootNodes = new LinkedList<>(); 92 110 final List<IEventTaskInstance> leafNodes = new ArrayList<>(); 93 111 … … 96 114 for (IUserSession session : sessions) { 97 115 rootNodes.clear(); 116 mpRootNodes.clear(); 98 117 99 118 for (final ITaskInstance currentRoot : session) { 100 119 currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() { 120 private ITaskInstance currentMpRoot = null; 121 122 @Override 123 public void visit(ISequenceInstance sequenceInstance) { 124 boolean currentInstancesIsMpRoot = false; 125 if (mostProminentTasks.contains(sequenceInstance.getSequence())) { 126 if (currentMpRoot == null) { 127 currentMpRoot = sequenceInstance; 128 currentInstancesIsMpRoot = true; 129 } 130 // else already detected most prominent root task 131 } 132 super.visit(sequenceInstance); 133 134 if (currentInstancesIsMpRoot) { 135 // if the current instance is also the root instance considering only 136 // most prominent sequences, then reset the stored instance to null 137 // after traversing this task 138 currentMpRoot = null; 139 } 140 } 141 101 142 @Override 102 143 public void visit(IEventTaskInstance eventTaskInstance) { 103 144 rootNodes.add(currentRoot); 145 mpRootNodes.add(currentMpRoot != null ? currentMpRoot : currentRoot); 104 146 leafNodes.add(eventTaskInstance); 105 147 … … 109 151 while (rootNodes.size() >= 10) { 110 152 rootNodes.removeFirst(); 153 } 154 } 155 156 if (mpRootNodes.size() >= 10) { 157 mpStatistics.addValue(getTaskCoverageMeasure(mpRootNodes)); 158 159 while (mpRootNodes.size() >= 10) { 160 mpRootNodes.removeFirst(); 111 161 } 112 162 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/RequiredInefficientActionsRule.java
r1949 r2162 17 17 import java.util.Collection; 18 18 import java.util.HashMap; 19 import java.util.LinkedList; 20 import java.util.List; 19 21 import java.util.Map; 20 22 21 23 import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; 22 24 23 import de.ugoe.cs.autoquest.eventcore.Event;24 import de.ugoe.cs.autoquest.eventcore.gui.Scroll;25 25 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 26 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor; 27 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; 26 28 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; 27 29 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; … … 90 92 for (ITask task : tasks) { 91 93 if (task instanceof ISequence) { 92 double[] ratios = getRatiosOfInefficientActionsInInstances((ISequence) task); 93 94 for (int i = 0; i < ratios.length; i++) { 95 if (ratios[i] > 0) { 96 // there is at least on inefficient action 97 inefficientActionRatios.put((ISequence) task, ratios); 98 break; 94 if (countEfficientActions((ISequence) task) > 1) { 95 double[] ratios = getRatiosOfInefficientActionsInInstances((ISequence) task); 96 97 for (int i = 0; i < ratios.length; i++) { 98 if (ratios[i] > 0) { 99 // there is at least on inefficient action 100 inefficientActionRatios.put((ISequence) task, ratios); 101 break; 102 } 99 103 } 100 104 } … … 103 107 104 108 return inefficientActionRatios; 109 } 110 111 /** 112 * 113 */ 114 private int countEfficientActions(ISequence task) { 115 final List<IEventTask> efficientActions = new LinkedList<>(); 116 117 task.accept(new DefaultTaskTraversingVisitor() { 118 @Override 119 public void visit(IEventTask eventTask) { 120 if (!ActionClassifier.isInefficient(eventTask)) { 121 efficientActions.add(eventTask); 122 } 123 } 124 }); 125 126 return efficientActions.size(); 105 127 } 106 128 … … 131 153 @Override 132 154 public void visit(IEventTaskInstance eventTaskInstance) { 133 if ( isInefficientAction(eventTaskInstance.getEvent())) {155 if (ActionClassifier.isInefficient(eventTaskInstance.getEvent())) { 134 156 count[0]++; 135 157 } … … 143 165 } 144 166 145 /**146 *147 */148 private boolean isInefficientAction(Event event) {149 return (event.getType() instanceof Scroll);150 }151 152 167 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/TargetDistanceRule.java
r1918 r2162 19 19 import java.util.List; 20 20 import java.util.Map; 21 import java.util.regex.Matcher; 22 import java.util.regex.Pattern; 21 23 22 import de.ugoe.cs.autoquest.eventcore.IEventTarget; 23 import de.ugoe.cs.autoquest.eventcore.gui.Scroll; 24 import de.ugoe.cs.autoquest.eventcore.Event; 24 25 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 25 26 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; … … 37 38 */ 38 39 public class TargetDistanceRule implements UsabilityEvaluationRule { 40 41 /** pattern for parsing target position parameter values */ 42 private Pattern targetPositionPattern = Pattern.compile 43 ("\\(\\s*(-?\\d*(\\.\\d*)?),\\s*(-?\\d*(\\.\\d*)?),\\s*(-?\\d*(\\.\\d*)?)\\s*\\)"); 39 44 40 45 /* … … 78 83 */ 79 84 private int[] getTargetDistance(ITaskInstance instance) { 80 List< IEventTarget> eventTargets = new LinkedList<IEventTarget>();81 getEvent Targets(instance, eventTargets);82 int noOf GUIElements = eventTargets.size();85 List<Event> events = new LinkedList<Event>(); 86 getEvents(instance, events); 87 int noOfEvents = events.size(); 83 88 int distance = 0; 84 89 85 while (event Targets.size() > 1) {86 distance += getDistance(event Targets.get(0), eventTargets.get(1));87 event Targets.remove(0);90 while (events.size() > 1) { 91 distance += getDistance(events.get(0), events.get(1)); 92 events.remove(0); 88 93 } 89 94 90 return new int[] { noOf GUIElements, distance };95 return new int[] { noOfEvents, distance }; 91 96 } 92 97 … … 94 99 * 95 100 */ 96 private int getDistance(IEventTarget eventTarget1, IEventTarget eventTarget2) { 97 if ((eventTarget1 instanceof IGUIElement) && (eventTarget2 instanceof IGUIElement)) { 98 return (int) 99 (1000 * (((IGUIElement) eventTarget1).getDistanceTo((IGUIElement) eventTarget2))); 101 private int getDistance(Event event1, Event event2) { 102 String location1 = event1.getParameter("targetPosition"); 103 String location2 = event2.getParameter("targetPosition"); 104 105 if ((location1 != null) && (location2 != null)) { 106 Matcher matcher1 = targetPositionPattern.matcher(location1); 107 Matcher matcher2 = targetPositionPattern.matcher(location2); 108 if (matcher1.matches() && matcher2.matches()) { 109 try { 110 double x = 111 Double.parseDouble(matcher2.group(1)) - Float.parseFloat(matcher1.group(1)); 112 113 double y = 114 Double.parseDouble(matcher2.group(3)) - Float.parseFloat(matcher1.group(3)); 115 116 double z = 117 Double.parseDouble(matcher2.group(5)) - Float.parseFloat(matcher1.group(5)); 118 119 return (int) (100 * Math.sqrt(x*x + y*y + z*z)); 120 } 121 catch (NumberFormatException e) { 122 // ignore and just continue with other variants. 123 } 124 } 100 125 } 101 else if (eventTarget1.equals(eventTarget2)) { 126 127 if ((event1.getTarget() instanceof IGUIElement) && 128 (event2.getTarget() instanceof IGUIElement)) 129 { 130 IGUIElement target1 = (IGUIElement) event1.getTarget(); 131 IGUIElement target2 = (IGUIElement) event2.getTarget(); 132 return (int) (1000 * target1.getDistanceTo(target2)); 133 } 134 135 if (event1.getTarget().equals(event2.getTarget())) { 102 136 return 0; 103 137 } 104 else { 105 return 1000; 106 } 138 139 return 1000; 107 140 } 108 141 … … 110 143 * 111 144 */ 112 private void getEvent Targets(ITaskInstance instance, final List<IEventTarget> eventTargets) {145 private void getEvents(ITaskInstance instance, final List<Event> events) { 113 146 instance.accept(new DefaultTaskInstanceTraversingVisitor() { 114 147 @Override 115 148 public void visit(IEventTaskInstance eventTaskInstance) { 116 if (!( eventTaskInstance.getEvent().getType() instanceof Scroll)) {117 event Targets.add(eventTaskInstance.getEvent().getTarget());149 if (!(ActionClassifier.isInefficient(eventTaskInstance.getEvent()))) { 150 events.add(eventTaskInstance.getEvent()); 118 151 } 119 152 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationManager.java
r2042 r2162 17 17 import java.util.ArrayList; 18 18 import java.util.HashMap; 19 import java.util.HashSet; 19 20 import java.util.LinkedList; 20 21 import java.util.List; 21 22 import java.util.ListIterator; 22 23 import java.util.Map; 24 import java.util.Set; 23 25 import java.util.logging.Level; 24 26 27 import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor; 28 import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance; 25 29 import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship; 30 import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; 26 31 import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship; 27 32 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; 33 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; 28 34 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; 29 35 import de.ugoe.cs.util.console.Console; … … 58 64 rules.add(new TargetDistanceRule()); 59 65 rules.add(new MissingFeedbackRule()); 66 rules.add(new TaskRetryRule()); 60 67 rules.add(new DataEntryMethodChangeRule()); 61 68 rules.add(new CommonTaskRateRule()); … … 73 80 * 74 81 */ 75 public UsabilityEvaluationResult evaluateUsability(ITaskModel taskModel, int maxCount) { 82 public UsabilityEvaluationResult evaluateUsability(ITaskModel taskModel, 83 int maxCount, 84 boolean onlyMostRepresentative) 85 { 76 86 Console.traceln(Level.INFO, "evaluating usability of task model " + taskModel); 77 87 78 88 List<UsabilityEvaluationResult> interimResults = new ArrayList<UsabilityEvaluationResult>(); 89 Set<ITask> mostRepresentativeTasks = null; 79 90 80 91 for (UsabilityEvaluationRule rule : rules) { 81 92 Console.traceln(Level.INFO, "\napplying rule " + rule.getClass().getSimpleName()); 82 UsabilityEvaluationResult r esult = rule.evaluate(taskModel);93 UsabilityEvaluationResult ruleResult = rule.evaluate(taskModel); 83 94 84 95 Map<String, List<UsabilitySmell>> smellGroups = new HashMap<>(); 85 96 86 for (UsabilitySmell smell : r esult.getAllSmells()) {97 for (UsabilitySmell smell : ruleResult.getAllSmells()) { 87 98 List<UsabilitySmell> smellGroup = smellGroups.get(smell.getBriefDescription()); 88 99 … … 99 110 " usability smells of type \"" + smellGroup.getKey() + "\""); 100 111 101 result = new UsabilityEvaluationResult(taskModel, smellGroup.getValue()); 102 103 checkDuplicates(result); 104 105 if (maxCount < result.getAllSmells().size()) { 106 Console.traceln(Level.INFO, "filtering for " + maxCount + 107 " smells of same type with highest event coverage."); 108 112 ruleResult = new UsabilityEvaluationResult(taskModel, smellGroup.getValue()); 113 114 checkDuplicates(ruleResult); 115 116 if ((onlyMostRepresentative) || (maxCount < ruleResult.getAllSmells().size())) { 109 117 LinkedList<UsabilitySmell> sortedSmells = new LinkedList<>(); 110 118 111 for (UsabilitySmell smell : result.getAllSmells()) { 119 if (onlyMostRepresentative) { 120 Console.traceln(Level.INFO, "filtering for smells that refer to only most " + 121 "representative tasks."); 122 } 123 124 for (UsabilitySmell smell : ruleResult.getAllSmells()) { 125 if (onlyMostRepresentative && (smell.getSmellingTask() != null)) { 126 if (mostRepresentativeTasks == null) { 127 mostRepresentativeTasks = getMostRepresentativeSequences(taskModel); 128 } 129 130 if (!mostRepresentativeTasks.contains(smell.getSmellingTask())) { 131 continue; 132 } 133 } 134 112 135 ListIterator<UsabilitySmell> iterator = sortedSmells.listIterator(); 113 136 … … 116 139 while (iterator.hasNext()) { 117 140 if (iterator.next().getIntensity().getEventCoverage() < 118 141 smell.getIntensity().getEventCoverage()) 119 142 { 120 143 iterator.previous(); … … 129 152 } 130 153 154 } 155 156 if (maxCount < ruleResult.getAllSmells().size()) { 157 Console.traceln(Level.INFO, "filtering for " + maxCount + 158 " smells of same type with highest event coverage."); 159 131 160 while (sortedSmells.size() > maxCount) { 132 161 sortedSmells.removeLast(); … … 134 163 } 135 164 136 result = new UsabilityEvaluationResult(taskModel, sortedSmells); 137 checkDuplicates(result); 138 } 139 140 interimResults.add(result); 165 Console.traceln(Level.INFO, sortedSmells.size() + " remaining."); 166 ruleResult = new UsabilityEvaluationResult(taskModel, sortedSmells); 167 checkDuplicates(ruleResult); 168 } 169 170 interimResults.add(ruleResult); 141 171 } 142 172 } … … 147 177 148 178 return result; 179 } 180 181 /** 182 * <p> 183 * TODO: comment 184 * </p> 185 * 186 * @param taskModel 187 * @return 188 */ 189 private Set<ITask> getMostRepresentativeSequences(ITaskModel taskModel) { 190 Map<Integer, List<ISequence>> coverageCounts = new HashMap<>(); 191 Map<ISequence, Set<IEventTaskInstance>> coverages = new HashMap<>(); 192 int maxCoverage = 0; 193 194 for (ITask task : taskModel.getTasks()) { 195 if (task instanceof ISequence) { 196 final Set<IEventTaskInstance> coveredEvents = new HashSet<>(); 197 198 for (ITaskInstance instance : task.getInstances()) { 199 instance.accept(new DefaultTaskInstanceTraversingVisitor() { 200 @Override 201 public void visit(IEventTaskInstance eventTaskInstance) { 202 coveredEvents.add(eventTaskInstance); 203 } 204 }); 205 } 206 207 coverages.put((ISequence) task, coveredEvents); 208 209 List<ISequence> tasksWithSameCoverage = coverageCounts.get(coveredEvents.size()); 210 211 if (tasksWithSameCoverage == null) { 212 tasksWithSameCoverage = new LinkedList<>(); 213 coverageCounts.put(coveredEvents.size(), tasksWithSameCoverage); 214 } 215 216 tasksWithSameCoverage.add((ISequence) task); 217 218 maxCoverage = Math.max(maxCoverage, coveredEvents.size()); 219 } 220 } 221 222 Set<ITask> mostRepresentativeSequences = new HashSet<>(); 223 224 for (int i = maxCoverage; i > 0; i--) { 225 List<ISequence> sequencesWithSameCoverage = coverageCounts.get(i); 226 227 if (sequencesWithSameCoverage == null) { 228 continue; 229 } 230 231 for (ISequence sequence : sequencesWithSameCoverage) { 232 mostRepresentativeSequences.add(sequence); 233 } 234 235 if ((100 * mostRepresentativeSequences.size() / coverages.size()) > 20) { 236 break; 237 } 238 } 239 240 return mostRepresentativeSequences; 149 241 } 150 242 -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilityEvaluationResult.java
r2042 r2162 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.io.Serializable; 17 18 import java.util.ArrayList; 18 19 import java.util.Collection; … … 29 30 * @author 2012, last modified by $Author: pharms$ 30 31 */ 31 public class UsabilityEvaluationResult {32 public class UsabilityEvaluationResult implements Serializable { 32 33 34 /** */ 35 private static final long serialVersionUID = 1L; 36 33 37 /** */ 34 38 private ITaskModel taskModel; -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmell.java
r1918 r2162 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.io.Serializable; 18 import java.util.Collections; 19 import java.util.LinkedList; 17 20 import java.util.List; 18 21 import java.util.Map; … … 26 29 * @author 2012, last modified by $Author: pharms$ 27 30 */ 28 public class UsabilitySmell { 31 public class UsabilitySmell implements Serializable { 32 33 /** */ 34 private static final long serialVersionUID = 1L; 29 35 30 36 /** */ … … 39 45 /** */ 40 46 private Map<String, Object> descriptionParameters; 47 48 /** */ 49 private List<String> tags; 50 51 /** */ 52 private ManualLabel manualLabel = ManualLabel.UNCHECKED; 41 53 42 54 /** … … 124 136 125 137 /* 138 * 126 139 */ 127 140 public String getBriefDescription() { 128 141 return description.getBriefDescription(); 142 } 143 144 /** 145 * 146 */ 147 public void addTag(String tag) { 148 if (this.tags == null) { 149 this.tags = new LinkedList<>(); 150 } 151 152 if (!this.tags.contains(tag)) { 153 this.tags.add(tag); 154 } 155 } 156 157 /** 158 * 159 */ 160 public void removeTag(String tag) { 161 if (this.tags != null) { 162 this.tags.remove(tag); 163 } 164 } 165 166 /** 167 * @param manualLabel the manualLabel to set 168 */ 169 public void setManualLabel(ManualLabel manualLabel) { 170 this.manualLabel = manualLabel; 171 } 172 173 /** 174 * @return the tags 175 */ 176 public List<String> getTags() { 177 if (tags != null) { 178 return Collections.unmodifiableList(tags); 179 } 180 else { 181 return Collections.emptyList(); 182 } 183 } 184 185 /** 186 * @return the manualLabel 187 */ 188 public ManualLabel getManualLabel() { 189 return manualLabel; 129 190 } 130 191 … … 170 231 } 171 232 233 /** */ 234 public static enum ManualLabel { 235 UNCHECKED, TRUE_POSITIVE, FALSE_POSITIVE; 236 } 172 237 } -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellDescription.java
r1918 r2162 17 17 import java.io.IOException; 18 18 import java.io.InputStream; 19 import java.io.Serializable; 19 20 import java.util.ArrayList; 20 21 import java.util.Collection; … … 32 33 * @author 2012, last modified by $Author: pharms$ 33 34 */ 34 public enum UsabilitySmellDescription {35 public enum UsabilitySmellDescription implements Serializable { 35 36 36 37 INEFFICIENT_ACTIONS, … … 43 44 HIGH_TARGET_DISTANCE, 44 45 MISSING_FEEDBACK, 46 TASK_RETRIED, 45 47 UNUSED_GUI_ELEMENTS, 46 48 DATA_ENTRY_METHOD_CHANGE, -
trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/UsabilitySmellIntensity.java
r1918 r2162 15 15 package de.ugoe.cs.autoquest.usability; 16 16 17 import java.io.Serializable; 18 17 19 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; 18 20 import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo; … … 26 28 * @author 2012, last modified by $Author: pharms$ 27 29 */ 28 public class UsabilitySmellIntensity {30 public class UsabilitySmellIntensity implements Serializable { 29 31 32 /** */ 33 private static final long serialVersionUID = 1L; 34 30 35 /** */ 31 36 private int ratio;
Note: See TracChangeset
for help on using the changeset viewer.