source: trunk/quest-plugin-jfc/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCReplayIDCalculator.java @ 796

Last change on this file since 796 was 796, checked in by fglaser, 12 years ago
  • Interface for replayID calculation and corresponding test case extended
  • Property svn:mime-type set to text/plain
File size: 7.0 KB
Line 
1package de.ugoe.cs.quest.plugin.jfc;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.LinkedHashMap;
6import java.util.List;
7import java.util.ListIterator;
8import java.util.Map;
9import java.util.regex.Matcher;
10import java.util.regex.Pattern;
11
12import de.ugoe.cs.quest.eventcore.Event;
13import de.ugoe.cs.quest.eventcore.IEventTarget;
14import de.ugoe.cs.quest.plugin.jfc.guimodel.JFCGUIElement;
15import de.ugoe.cs.quest.plugin.jfc.guimodel.JFCGUIElementSpec;
16
17
18/**
19 * <p>
20 * This class provides the functionality to calculate the unique GUITAR replayIDs
21 * for {@link JFCEvent}s. This code is mainly based on guitar source code: edu.umd.cs.guitar.
22 * model.JFCDefaultIDGeneratorSimple
23 *   
24 * </p>
25 * @author fabian.glaser
26 *
27 */
28
29public class JFCReplayIDCalculator {
30       
31        static final int prime = 31;
32       
33        /**
34         * Properties that are used to identify widgets
35         */
36        private static List<String> ID_PROPERTIES = Arrays.asList(
37                        "Class","Title","Icon");
38       
39   /**
40    * Those classes are invisible widgets but cause false-positive when
41    * calculating ID (compare guitar source code: edu.umd.cs.guitar.
42    * model.JFCDefaultIDGeneratorSimple)
43    */
44   private static List<String> IGNORED_CLASSES = Arrays.asList("javax.swing.JPanel",
45         "javax.swing.JTabbedPane", "javax.swing.JScrollPane",
46         "javax.swing.JSplitPane", "javax.swing.Box",
47         "javax.swing.JViewport", "javax.swing.JScrollBar",
48         "javax.swing.JLayeredPane",
49         "javax.swing.JList$AccessibleJList$AccessibleJListChild",
50         "javax.swing.JList$AccessibleJList", "javax.swing.JList",
51         "javax.swing.JScrollPane$ScrollBar",
52         "javax.swing.plaf.metal.MetalScrollButton");
53   
54   /**
55    * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite
56    * @param List of {@link JFCGUIElementSpec}s that represent the component path of a event target
57    * for which the replayID should be calculated.
58    * @return replayID
59    */
60       
61   public String calculateReplayID(List<JFCGUIElementSpec> guiElementPath){
62           String replayID = "";
63           long hashCode = 1;
64         
65           ListIterator<JFCGUIElementSpec> iterator = guiElementPath.listIterator();
66           
67           JFCGUIElementSpec currentSpec = iterator.next();
68           String title = currentSpec.getName();
69           String fuzzyTitle = getFuzzyTitle(title);
70           long windowHashCode = fuzzyTitle.hashCode();
71           windowHashCode = (windowHashCode * 2) & 0xffffffffL;
72
73           long propagatedHashCode = windowHashCode;
74           
75           // construct looks complicated but avoids going back and force through path
76           if (iterator.hasNext())
77                   currentSpec = iterator.next();
78           else
79                   currentSpec = null;
80
81           // walk through component path and calculate hashcode
82           while(currentSpec != null){
83                   long localHashCode = getLocalHashCode(currentSpec);
84                   hashCode = propagatedHashCode * prime + localHashCode;
85                   hashCode = (hashCode * 2) & 0xffffffffL;
86
87                   if (iterator.hasNext()){
88                           currentSpec = iterator.next();
89                           Integer index = currentSpec.getIndex();
90                           propagatedHashCode = prime * propagatedHashCode
91                                           + index.hashCode();
92                   }
93                   else
94                           currentSpec = null;
95                           
96           }
97
98           replayID = "e" + hashCode;
99
100           return replayID;
101   }
102   
103   
104        /**
105         * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite
106         * @param event for which the ID should be calculated
107         * @return replayID
108         */
109        public String calculateReplayID(Event event){
110                List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
111               
112                IEventTarget target = event.getTarget();
113                JFCGUIElement jfcTarget = (JFCGUIElement) target;
114               
115                // extract element path
116                JFCGUIElement currentTarget = jfcTarget;
117                while (currentTarget != null){
118                        JFCGUIElementSpec currentSpec = (JFCGUIElementSpec) currentTarget.getSpecification();
119                        guiElementPath.add(0, currentSpec);
120                        currentTarget = (JFCGUIElement) currentTarget.getParent();
121                }
122               
123                // calculation is delegated to other calculateReplayID method
124                return this.calculateReplayID(guiElementPath);
125        }
126       
127        /**
128         * Calculates the hashcode part of a component.
129         * @param spec The {@link JFCGUIElementSpec} for which the hashcode should be calculated.
130         * @return the local hashcode
131         */
132       
133        private long getLocalHashCode(JFCGUIElementSpec spec){
134                long hashcode = 1;
135                String wClass = spec.getType();
136                if (IGNORED_CLASSES.contains(wClass)) {
137                    hashcode = (prime * hashcode + (wClass.equals("null") ? 0 : (wClass
138                               .hashCode())));
139                    return hashcode;
140                }
141                else{
142                        Map<String, String> idProperties = extractIDProperties(spec);
143                        for (String property: idProperties.keySet()){
144                                String value = idProperties.get(property);
145                                if (!value.equals("null")){
146                                        hashcode = prime * hashcode + property.hashCode();
147                                        hashcode = prime * hashcode + value.hashCode();
148                                }
149                        }
150                }
151               
152                hashcode = (hashcode * 2) & 0xffffffffL;
153               
154                return hashcode;
155        }
156       
157        /**
158         * Extracts the IDProperties from a given {@link JFCGUIElementSpec}.
159         * @param spec
160         * @return LinkedHashMap that contains the IDProperties and its values.
161         */
162       
163        private Map<String, String> extractIDProperties(JFCGUIElementSpec spec){
164                LinkedHashMap<String, String> idProperties = new LinkedHashMap<String, String>();
165                if (ID_PROPERTIES.contains("Class")){
166                        idProperties.put("Class", spec.getType());
167                }
168                if (ID_PROPERTIES.contains("Title")){
169                        String name = spec.getName();
170                        // spec returns extra "" that need to be removed
171                        idProperties.put("Title", name.substring(1, name.length() - 1));
172                }
173                if (ID_PROPERTIES.contains("Icon")){
174                        idProperties.put("Icon", spec.getIcon());
175                }
176                if (ID_PROPERTIES.contains("Index")){
177                        idProperties.put("Index", Integer.toString(spec.getIndex()));
178                }       
179                return idProperties;
180        }
181       
182        /**
183         * Guitar has a special way to deal with window titles when
184         * calculating unique widget IDs. This method mimics Guitar's
185         * behavior (compare guitar source code: edu.umd.cs.guitar.
186         * model.JFCDefaultIDGeneratorSimple).
187         * @param title
188         * @return fuzzyTitle
189         */
190
191        private String getFuzzyTitle(String title){
192                final List<String> PATTERNS =
193                                Arrays.asList("Rachota .*",
194                                                "OmegaT-.*",
195                                                "Buddi.*",
196                                                "Open:.*",
197                                                "JabRef.*",
198                                                "GanttProject.*",
199                                                ".*Pauker.*",
200                                                ".*FreeMind.*",
201                                                ".* - ArgoUML.*",
202                                                "Save Project .*");
203
204
205                for (String sPattern : PATTERNS) {
206                        if (matchRegex(title, sPattern)) {
207                                return sPattern;
208                        }
209                }
210
211                return title;
212        }
213
214        /**
215         * Determine if the input string matches the input regex pattern.
216         * This method mimics Guitars behavior.
217         * Attempt to match the pattern 'sPattern' with the string 'sInputString'.
218
219         * @param sInputString    Input string to match with pattern
220         * @param sPattern        Regex pattern to match with string
221         * @return                True if match, false otherwise
222         */
223       
224        private static boolean
225        matchRegex(String sInputString,
226                        String sPattern)
227        {
228                Pattern pattern;
229                Matcher matcher;
230
231                pattern = Pattern.compile(sPattern);
232                matcher = pattern.matcher(sInputString);
233                if (matcher.matches()) {
234                        return true;
235                }
236
237                return false;
238        }
239       
240};
Note: See TracBrowser for help on using the repository browser.