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

Last change on this file since 806 was 806, checked in by fglaser, 12 years ago
  • new helper class JFCReplayIDValidator added, that is able to extract all IDs from

GUITAR Gui file and to check if a given (generated) replayID is contained in this GUI file.

  • JFCReplayIDCalculator modified for test purposes (can be initialized with a Validator

to check if IDs that are generated "on the way" are valid)

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