source: trunk/autoquest-plugin-android/src/main/java/de/ugoe/cs/autoquest/plugin/android/AndroidLogParser.java @ 1797

Last change on this file since 1797 was 1797, checked in by funger, 10 years ago

bug fix: add events

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