- Timestamp:
- 10/08/14 10:02:37 (10 years ago)
- Location:
- trunk/autoquest-plugin-android/src/main/java/de/ugoe/cs/autoquest/plugin/android
- Files:
-
- 8 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/autoquest-plugin-android/src/main/java/de/ugoe/cs/autoquest/plugin/android/AndroidLogParser.java
r1782 r1783 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 1 15 package de.ugoe.cs.autoquest.plugin.android; 2 16 3 public class AndroidLogParser { 17 import java.io.File; 18 import java.io.FileInputStream; 19 import java.io.FileNotFoundException; 20 import java.io.IOException; 21 import java.io.InputStreamReader; 22 import java.io.UnsupportedEncodingException; 23 import java.util.Collection; 24 import java.util.HashMap; 25 import java.util.LinkedList; 26 import java.util.List; 27 import java.util.Map; 28 29 import javax.xml.parsers.ParserConfigurationException; 30 import javax.xml.parsers.SAXParser; 31 import javax.xml.parsers.SAXParserFactory; 32 33 import org.xml.sax.Attributes; 34 import org.xml.sax.InputSource; 35 import org.xml.sax.SAXException; 36 import org.xml.sax.SAXParseException; 37 import org.xml.sax.helpers.DefaultHandler; 38 39 import de.ugoe.cs.autoquest.eventcore.Event; 40 import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction; 41 import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; 42 import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementTree; 43 import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel; 44 import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException; 45 import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 46 import de.ugoe.cs.autoquest.plugin.android.guimodel.AndroidGUIElementSpec; 47 import de.ugoe.cs.util.console.Console; 48 49 /** 50 * <p> 51 * This class provides functionality to parse XML log files generated by the 52 * AndroidMonitor of autoquest. The result of parsing a file is a collection of 53 * event sequences. 54 * </p> 55 * 56 * @author Florian Unger 57 * @version 1.0 58 */ 59 public class AndroidLogParser extends DefaultHandler { 60 61 /* 62 * (non-Javadoc) 63 * 64 * int java.lang.Object.hashCode() is used in the Androidmonitor Long is 65 * used to internally handle and compare the number. e.g. 66 * currentGUIElementHash != null 67 */ 68 /** 69 * <p> 70 * Internal handle to the id of the event that is currently being parsed. 71 * </p> 72 */ 73 private String currentEventId; 74 75 /** 76 * <p> 77 * Internal handle to the parameters of the event currently being parsed. 78 * </p> 79 */ 80 private Map<String, String> currentEventParameters; 81 82 /** 83 * <p> 84 * Internal handle to the source of the event that is currently being 85 * parsed. 86 * </p> 87 */ 88 private Long currentEventSource; 89 90 /** 91 * <p> 92 * Internal handle to the timestamp of the event that is currently being 93 * parsed. 94 */ 95 private Long currentEventTimestamp = -1l; 96 97 /** 98 * 99 * <p> 100 * Internal handle to the hashcode of the GUI element, that is currently 101 * parsed. 102 * </p> 103 */ 104 private Long currentGUIElementHash; 105 106 /** 107 * <p> 108 * internal handle to the parsed GUI structure, stored in a GUIElementTree 109 * </p> 110 */ 111 private GUIElementTree<Long> currentGUIElementTree; 112 113 /** 114 * <p> 115 * internal handle to the specification currently parsed for a GUI element 116 * </p> 117 */ 118 private AndroidGUIElementSpec currentGUIElementSpec; 119 120 /** 121 * 122 * <p> 123 * Internal handle to the hashcode of the parent of the GUI element, that is 124 * currently parsed. 125 * </p> 126 */ 127 private Long currentParentHash; 128 129 /** 130 * <p> 131 * Internal handle to the event sequence that is currently being parsed. 132 * </p> 133 */ 134 private List<Event> currentSequence; 135 136 /** 137 * <p> 138 * Internal handle to the event sequence that is currently being parsed. 139 * </p> 140 */ 141 // private List<Event> currentSequence; 142 143 /** 144 * <p> 145 * Map that holds events that had no registered target GUI element during 146 * parsing. Keys are the IDs of the unregistered targets. 147 * </p> 148 */ 149 private Map<Long, List<Event>> eventsWithoutTargets; 150 151 /** 152 * <p> 153 * Collection of event sequences that is contained in the log file, which is 154 * parsed. 155 * </p> 156 */ 157 private Collection<List<Event>> sequences; 158 159 /** 160 * <p> 161 * Constructor. Creates a new AndroidLogParser. 162 * </p> 163 */ 164 public AndroidLogParser() { 165 sequences = new LinkedList<List<Event>>(); 166 // currentSequence = null; 167 } 168 169 // TODO create a constructor which creates a new AndroidLogParser with a 170 // specific event filter. 171 172 /** 173 * <p> 174 * Parses a log file written by the JFCMonitor and creates a collection of 175 * event sequences. 176 * </p> 177 * 178 * @param filename 179 * name and path of the log file 180 */ 181 public void parseFile(String filename) { 182 if (filename == null) { 183 throw new IllegalArgumentException("filename must not be null"); 184 } 185 186 parseFile(new File(filename)); 187 } 188 189 /** 190 * <p> 191 * Parses a log file written by the JFCMonitor and creates a collection of 192 * event sequences. 193 * </p> 194 * 195 * @param file 196 * name and path of the log file 197 */ 198 public void parseFile(File file) { 199 if (file == null) { 200 throw new IllegalArgumentException("file must not be null"); 201 } 202 203 SAXParserFactory spf = SAXParserFactory.newInstance(); 204 // set true to validate that the file is well defined 205 spf.setValidating(true); 206 207 SAXParser saxParser = null; 208 InputSource inputSource = null; 209 try { 210 saxParser = spf.newSAXParser(); 211 inputSource = new InputSource(new InputStreamReader( 212 new FileInputStream(file), "UTF-8")); 213 } catch (UnsupportedEncodingException e) { 214 Console.printerr("Error parsing file + " + file.getName()); 215 Console.logException(e); 216 return; 217 } catch (ParserConfigurationException e) { 218 Console.printerr("Error parsing file + " + file.getName()); 219 Console.logException(e); 220 return; 221 } catch (SAXException e) { 222 Console.printerr("Error parsing file + " + file.getName()); 223 Console.logException(e); 224 return; 225 } catch (FileNotFoundException e) { 226 Console.printerr("Error parsing file + " + file.getName()); 227 Console.logException(e); 228 return; 229 } 230 if (inputSource != null) { 231 inputSource.setSystemId("file://" + file.getAbsolutePath()); 232 try { 233 // called a second time to be sure that no error happens 234 if (saxParser == null) { 235 throw new RuntimeException("SAXParser creation failed"); 236 } 237 saxParser.parse(inputSource, this); 238 } catch (SAXParseException e) { 239 Console.printerrln("Failure parsing file in line " 240 + e.getLineNumber() + ", column " + e.getColumnNumber() 241 + "."); 242 Console.logException(e); 243 return; 244 } catch (SAXException e) { 245 Console.printerr("Error parsing file + " + file.getName()); 246 Console.logException(e); 247 return; 248 } catch (IOException e) { 249 Console.printerr("Error parsing file + " + file.getName()); 250 Console.logException(e); 251 return; 252 } 253 } 254 if (!eventsWithoutTargets.isEmpty()) { 255 Console.printerr("Some events reference GUI elements that are not part of logfile. " 256 + "These events have been parsed without target."); 257 } 258 } 259 260 /** 261 * <p> 262 * Returns the collection of event sequences that is obtained from parsing 263 * log files. 264 * </p> 265 * 266 * @return collection of event sequences 267 */ 268 public Collection<List<Event>> getSequences() { 269 return sequences; 270 } 271 272 /** 273 * <p> 274 * Returns the GUI model that is obtained from parsing log files. 275 * </p> 276 * 277 * @return GUIModel 278 */ 279 public GUIModel getGuiModel() { 280 return currentGUIElementTree.getGUIModel(); 281 } 282 283 /* 284 * (non-Javadoc) 285 * 286 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, 287 * java.lang.String, java.lang.String, org.xml.sax.Attributes) 288 */ 289 public void startElement(String uri, String localName, String qName, 290 Attributes atts) throws SAXException { 291 if (qName.equals("sessions")) { 292 // currentSequence = new LinkedList<Event>(); Up to know it is 293 // necessary to handle different sessions. All components are known 294 // before an event occurs. 295 if (currentGUIElementTree == null) 296 currentGUIElementTree = new GUIElementTree<Long>(); 297 } 298 299 if (qName.equals("component")) { 300 currentGUIElementHash = Long.parseLong(atts.getValue("hash")); 301 currentGUIElementSpec = new AndroidGUIElementSpec(); 302 currentGUIElementSpec.setHashCode((int) currentGUIElementHash 303 .longValue()); 304 } else if (qName.equals("event")) { 305 currentEventId = atts.getValue("id"); 306 currentEventParameters = new HashMap<String, String>(); 307 308 } else if (qName.equals("param")) { 309 if (currentGUIElementHash != null) { 310 if ("class".equals(atts.getValue("name"))) { 311 currentGUIElementSpec.setType(atts.getValue("value")); 312 } else if ("path".equals(atts.getValue("name"))) { 313 currentGUIElementSpec.setPath(atts.getValue("value")); 314 } else if ("id".equals(atts.getValue("name"))) { 315 currentGUIElementSpec.setIndex(Integer.parseInt(atts 316 .getValue("value"))); 317 } else if ("parent".equals(atts.getValue("name"))) { 318 currentParentHash = Long.parseLong(atts.getValue("value")); 319 } 320 } else if (currentEventId != null) { 321 if ("source".equals(atts.getValue("name"))) { 322 currentEventSource = Long.parseLong(atts.getValue("value")); 323 } 324 if ("timestamp".equals(atts.getValue("name"))) { 325 currentEventTimestamp = Long.parseLong(atts 326 .getValue("value")); 327 } 328 currentEventParameters.put(atts.getValue("name"), 329 atts.getValue("value")); 330 } 331 } 332 333 // TODO add hierarchy information if available -> Up to know gathering 334 // this information leads to an out of memory exception in the 335 // androidmonitor @see: AndroidmonitorLogFile#addComponent 336 } 337 338 /* 339 * (non-Javadoc) 340 * 341 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, 342 * java.lang.String, java.lang.String) 343 */ 344 @Override 345 public void endElement(String uri, String localName, String qName) 346 throws SAXException { 347 if (qName.equals("component") && currentGUIElementHash != null) { 348 try { 349 currentGUIElementTree.add(currentGUIElementHash, 350 currentParentHash, currentGUIElementSpec); 351 } catch (GUIModelException e) { 352 throw new SAXException( 353 "could not handle GUI element with hash " 354 + currentGUIElementHash + ": " + e.getMessage(), 355 e); 356 } 357 currentGUIElementHash = null; 358 currentParentHash = null; 359 } else if (currentEventId != null) { 360 if (qName.equals("event")) { 361 IGUIElement currentGUIElement; 362 currentGUIElement = currentGUIElementTree 363 .find(currentEventSource); 364 Event event; 365 366 // up to now only onClick events are implemented and each 367 // onclick event is processed as a mouse click 368 int x = Integer.parseInt(currentEventParameters.get("X")); 369 int y = Integer.parseInt(currentEventParameters.get("Y")); 370 MouseButtonInteraction.Button button = null; 371 button = MouseButtonInteraction.Button.LEFT; 372 373 // maybe it would be necessary in the future to check weather 374 // the GUI element exits. 375 event = new Event(new MouseClick(button, x, y), 376 currentGUIElement); 377 378 event.setTimestamp(currentEventTimestamp); 379 currentSequence.add(event); 380 381 currentEventParameters = null; 382 currentEventId = null; 383 currentEventTimestamp = -1l; 384 } 385 } else if (qName.equals("sessions")) { 386 if (currentSequence != null) { 387 sequences.add(currentSequence); 388 } 389 } 390 } 4 391 5 392 }
Note: See TracChangeset
for help on using the changeset viewer.