Changeset 1783


Ignore:
Timestamp:
10/08/14 10:02:37 (10 years ago)
Author:
funger
Message:

Parser for onClick events only. Using MouseClick? to simulate onClick events.

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 
    115package de.ugoe.cs.autoquest.plugin.android; 
    216 
    3 public class AndroidLogParser { 
     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.MouseButtonInteraction; 
     41import de.ugoe.cs.autoquest.eventcore.gui.MouseClick; 
     42import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementTree; 
     43import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel; 
     44import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException; 
     45import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 
     46import de.ugoe.cs.autoquest.plugin.android.guimodel.AndroidGUIElementSpec; 
     47import 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 */ 
     59public 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        } 
    4391 
    5392} 
Note: See TracChangeset for help on using the changeset viewer.