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

Last change on this file since 927 was 927, checked in by sherbold, 12 years ago
  • added copyright under the Apache License, Version 2.0
  • Property svn:mime-type set to text/plain
File size: 9.0 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.plugin.jfc;
16
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.LinkedHashMap;
20import java.util.List;
21import java.util.ListIterator;
22import java.util.Map;
23import java.util.regex.Matcher;
24import java.util.regex.Pattern;
25
26import de.ugoe.cs.autoquest.eventcore.Event;
27import de.ugoe.cs.autoquest.eventcore.IEventTarget;
28import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
29import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElementSpec;
30
31
32/**
33 * <p>
34 * This class provides the functionality to calculate the unique GUITAR replayIDs
35 * for {@link JFCEvent}s. This code is mainly based on guitar source code: edu.umd.cs.guitar.
36 * model.JFCDefaultIDGeneratorSimple
37 *   
38 * </p>
39 * @author fabian.glaser
40 *
41 */
42
43public class JFCReplayIDCalculator {
44       
45        static final int prime = 31;
46        private JFCReplayIDValidator validator;
47       
48        public JFCReplayIDCalculator() {
49                this.validator = null;
50        }
51       
52        public JFCReplayIDCalculator(JFCReplayIDValidator validator){
53                this.validator = validator;
54        }
55       
56        /**
57         * Properties that are used to identify widgets
58         */
59        private static List<String> ID_PROPERTIES = Arrays.asList(
60                        "Class","Title","Icon");
61       
62   /**
63    * Those classes are invisible widgets but cause false-positive when
64    * calculating ID (compare guitar source code: edu.umd.cs.guitar.
65    * model.JFCDefaultIDGeneratorSimple)
66    */
67   private static List<String> IGNORED_CLASSES = Arrays.asList("javax.swing.JPanel",
68         "javax.swing.JTabbedPane", "javax.swing.JScrollPane",
69         "javax.swing.JSplitPane", "javax.swing.Box",
70         "javax.swing.JViewport", "javax.swing.JScrollBar",
71         "javax.swing.JLayeredPane",
72         "javax.swing.JList$AccessibleJList$AccessibleJListChild",
73         "javax.swing.JList$AccessibleJList", "javax.swing.JList",
74         "javax.swing.JScrollPane$ScrollBar",
75         "javax.swing.plaf.metal.MetalScrollButton");
76   
77   /**
78    * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite
79    * @param List of {@link JFCGUIElementSpec}s that represent the component path of a event target
80    * for which the replayID should be calculated.
81    * @return replayID
82    */
83       
84   public String calculateReplayID(List<JFCGUIElementSpec> guiElementPath){
85           String replayID = "";
86           long hashCode = 1;
87         
88           ListIterator<JFCGUIElementSpec> iterator = guiElementPath.listIterator();
89           
90           JFCGUIElementSpec currentSpec = iterator.next();
91           String title = currentSpec.getName();
92           String fuzzyTitle = getFuzzyTitle(title);
93           long windowHashCode = fuzzyTitle.hashCode();
94           windowHashCode = (windowHashCode * 2) & 0xffffffffL;
95
96           long propagatedHashCode = windowHashCode;
97           
98           // added validator to check if generated component ids are known
99           if (validator != null){
100                   if (validator.validateReplayID("w" + windowHashCode)){
101                           System.out.println("ID w" + windowHashCode + " is valid.");
102                   }
103                   else{
104                           System.err.println(currentSpec + " describes an unknown component.");
105                           System.err.println("ID w" + windowHashCode + " is unknown." );
106                           System.err.println();
107                   }
108                           
109           }
110           
111           // construct looks complicated but avoids going back and forth through path
112           if (iterator.hasNext())
113                   currentSpec = iterator.next();
114           else{
115                   currentSpec = null;
116                   // there are no subcomponents, so we use windowHashCode as hashCode
117                   hashCode = windowHashCode;
118           }
119
120           // walk through component path and calculate hashcode
121           while(currentSpec != null){
122                   long localHashCode = getLocalHashCode(currentSpec);
123                   hashCode = propagatedHashCode * prime + localHashCode;
124                   hashCode = (hashCode * 2) & 0xffffffffL;
125                   
126                   // added validator to check if generated component ids are known
127                   if (validator != null){
128                           if (validator.validateReplayID("w" + hashCode)){
129                                   System.out.println("ID w" + hashCode + " is valid.");
130                                   System.out.println("==> " + currentSpec + " describes a known component.");
131                           }
132                           else{
133                                   System.err.println("ID w" + hashCode + " is unknown." );
134                                   System.err.println("==> " + currentSpec + " describes an unknown component.");
135                                   System.err.println();
136                           }
137                   }
138
139                   if (iterator.hasNext()){
140                           currentSpec = iterator.next();
141                           Integer index = currentSpec.getIndex();
142                           propagatedHashCode = prime * propagatedHashCode
143                                           + index.hashCode();
144                   }
145                   else
146                           currentSpec = null;
147                           
148           }
149
150           replayID = "e" + hashCode;
151
152           return replayID;
153   }
154   
155   
156        /**
157         * Calculates the replayID of a JFCEvent needed for compatibility with guitar suite
158         * @param event for which the ID should be calculated
159         * @return replayID
160         */
161        public String calculateReplayID(Event event){
162                List<JFCGUIElementSpec> guiElementPath = new ArrayList<JFCGUIElementSpec>();
163               
164                IEventTarget target = event.getTarget();
165                if (!target.getPlatform().equals("JFC")){
166                        throw new IllegalArgumentException("Event target must be of type JFC.");
167                }
168               
169                JFCGUIElement currentTarget = (JFCGUIElement) target;
170               
171                // extract element path
172                while (currentTarget != null){
173                        JFCGUIElementSpec currentSpec = (JFCGUIElementSpec) currentTarget.getSpecification();
174                       
175                        // new specification must be inserted at the beginning of the list
176                        guiElementPath.add(0, currentSpec);
177                        currentTarget = (JFCGUIElement) currentTarget.getParent();
178                }
179               
180                // calculation is delegated to other calculateReplayID method
181                return this.calculateReplayID(guiElementPath);
182        }
183       
184        /**
185         * Calculates the hashcode part of a component.
186         * @param spec The {@link JFCGUIElementSpec} for which the hashcode should be calculated.
187         * @return the local hashcode
188         */
189       
190        private long getLocalHashCode(JFCGUIElementSpec spec){
191                long hashcode = 1;
192                String wClass = spec.getType();
193                if (IGNORED_CLASSES.contains(wClass)) {
194                    hashcode = (prime * hashcode + (wClass.equals("null") ? 0 : (wClass
195                               .hashCode())));
196                    return hashcode;
197                }
198                else{
199                        Map<String, String> idProperties = extractIDProperties(spec);
200                        for (String property: idProperties.keySet()){
201                                String value = idProperties.get(property);
202                                if (!value.equals("null")){
203                                        hashcode = prime * hashcode + property.hashCode();
204                                        hashcode = prime * hashcode + value.hashCode();
205                                }
206                        }
207                }
208               
209                hashcode = (hashcode * 2) & 0xffffffffL;
210               
211                return hashcode;
212        }
213       
214        /**
215         * Extracts the IDProperties from a given {@link JFCGUIElementSpec}.
216         * @param spec
217         * @return LinkedHashMap that contains the IDProperties and its values.
218         */
219       
220        private Map<String, String> extractIDProperties(JFCGUIElementSpec spec){
221                LinkedHashMap<String, String> idProperties = new LinkedHashMap<String, String>();
222                if (ID_PROPERTIES.contains("Class")){
223                        idProperties.put("Class", spec.getType());
224                }
225                if (ID_PROPERTIES.contains("Title")){
226                        String name = spec.getName();
227                        // spec returns extra "" that need to be removed
228                        idProperties.put("Title", name.substring(1, name.length() - 1));
229                }
230                if (ID_PROPERTIES.contains("Icon")){
231                        idProperties.put("Icon", spec.getIcon());
232                }
233                if (ID_PROPERTIES.contains("Index")){
234                        idProperties.put("Index", Integer.toString(spec.getIndex()));
235                }       
236                return idProperties;
237        }
238       
239        /**
240         * Guitar has a special way to deal with window titles when
241         * calculating unique widget IDs. This method mimics Guitar's
242         * behavior (compare guitar source code: edu.umd.cs.guitar.
243         * model.JFCDefaultIDGeneratorSimple).
244         * @param title
245         * @return fuzzyTitle
246         */
247
248        private String getFuzzyTitle(String title){
249                final List<String> PATTERNS =
250                                Arrays.asList("Rachota .*",
251                                                "OmegaT-.*",
252                                                "Buddi.*",
253                                                "Open:.*",
254                                                "JabRef.*",
255                                                "GanttProject.*",
256                                                ".*Pauker.*",
257                                                ".*FreeMind.*",
258                                                ".* - ArgoUML.*",
259                                                "Save Project .*");
260
261
262                for (String sPattern : PATTERNS) {
263                        if (matchRegex(title, sPattern)) {
264                                return sPattern;
265                        }
266                }
267
268                return title;
269        }
270
271        /**
272         * Determine if the input string matches the input regex pattern.
273         * This method mimics Guitars behavior.
274         * Attempt to match the pattern 'sPattern' with the string 'sInputString'.
275
276         * @param sInputString    Input string to match with pattern
277         * @param sPattern        Regex pattern to match with string
278         * @return                True if match, false otherwise
279         */
280       
281        private static boolean
282        matchRegex(String sInputString,
283                        String sPattern)
284        {
285                Pattern pattern;
286                Matcher matcher;
287
288                pattern = Pattern.compile(sPattern);
289                matcher = pattern.matcher(sInputString);
290                if (matcher.matches()) {
291                        return true;
292                }
293
294                return false;
295        }
296       
297};
Note: See TracBrowser for help on using the repository browser.