source: trunk/autoquest-plugin-mfc/src/main/java/de/ugoe/cs/autoquest/plugin/mfc/MFCLogParser.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
File size: 12.6 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.mfc;
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;
28import java.util.SortedMap;
29import java.util.TreeMap;
30import java.util.logging.Level;
31
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.parsers.SAXParser;
34import javax.xml.parsers.SAXParserFactory;
35
36import org.xml.sax.Attributes;
37import org.xml.sax.InputSource;
38import org.xml.sax.SAXException;
39import org.xml.sax.SAXParseException;
40import org.xml.sax.helpers.DefaultHandler;
41
42import de.ugoe.cs.autoquest.eventcore.Event;
43import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
44import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessage;
45import de.ugoe.cs.autoquest.plugin.mfc.eventcore.WindowsMessageType;
46import de.ugoe.cs.autoquest.plugin.mfc.guimodel.MFCGUIElement;
47import de.ugoe.cs.autoquest.plugin.mfc.guimodel.WindowTree;
48import de.ugoe.cs.util.StringTools;
49import de.ugoe.cs.util.console.Console;
50
51/**
52 * <p>
53 * This class provides functionality to parse XML log files generated by the MFCUsageMonitor of
54 * EventBench. The result of parsing a file is a collection of event sequences. It uses the
55 * {@link SequenceSplitter} and the {@link EventGenerator} as well as custom defined
56 * {@link MessageHandler} for the parsing.
57 * </p>
58 *
59 * @author Steffen Herbold
60 * @version 1.0
61 */
62public class MFCLogParser extends DefaultHandler {
63
64    /**
65     * <p>
66     * If a custom message handler is used, this field contains its handle. Otherwise this field is
67     * {@code null}.
68     * </p>
69     */
70    private MessageHandler currentHandler;
71
72    /**
73     * <p>
74     * internal handle to the current window tree
75     * </p>
76     */
77    private WindowTree currentWindowTree;
78
79    /**
80     * <p>
81     * the type of the currently parsed message
82     * </p>
83     */
84    private WindowsMessageType currentMessageType;
85   
86    /**
87     * <p>
88     * the parameters of the currently parsed message
89     * </p>
90     */
91    private Map<String, Object> currentMessageParameters = new HashMap<String, Object>();
92   
93    /**
94     * <p>
95     * {@link SequenceSplitter} instance used by the {@link MFCLogParser}.
96     * </p>
97     */
98    private SequenceSplitter sequenceSplitter;
99
100    /**
101     * <p>
102     * Collection of message sequences that is contained in the log file, which is parsed.
103     * </p>
104     */
105    private Collection<List<Event>> sequences;
106
107    /**
108     * <p>
109     * Debugging variable that allows the analysis which message type occurs how often in the log
110     * file. Can be used to enhance the message filter.
111     * </p>
112     */
113    private SortedMap<WindowsMessageType, Integer> typeCounter;
114
115    /**
116     * <p>
117     * Debugging variable that enables the counting of the occurrences of each message. Used in
118     * combination with {@link #typeCounter}.
119     * </p>
120     */
121    private boolean countMessageOccurences;
122
123    /**
124     * <p>
125     * Constructor. Creates a new LogParser that does not count message occurrences.
126     * </p>
127     */
128    public MFCLogParser() {
129        this(false);
130    }
131
132    /**
133     * <p>
134     * Constructor. Creates a new LogParser.
135     * </p>
136     *
137     * @param countMessageOccurences
138     *            if true, the occurrences of each message type in the log is counted.
139     */
140    public MFCLogParser(boolean countMessageOccurences) {
141        sequences = new LinkedList<List<Event>>();
142        currentHandler = null;
143        this.countMessageOccurences = countMessageOccurences;
144        if (countMessageOccurences) {
145            typeCounter = new TreeMap<WindowsMessageType, Integer>();
146        }
147    }
148
149    /**
150     * <p>
151     * Parses a log file written by the MFCMonitor and creates a collection of event sequences.
152     * </p>
153     *
154     * @param filename
155     *            name and path of the log file
156     */
157    public void parseFile(String filename) {
158        if (filename == null) {
159            throw new IllegalArgumentException("filename must not be null");
160        }
161
162        parseFile(new File(filename));
163    }
164
165    /**
166     * <p>
167     * Parses a log file written by the MFCMonitor and creates a collection of event sequences.
168     * </p>
169     *
170     * @param file
171     *            name and path of the log file
172     */
173    public void parseFile(File file) {
174        if (file == null) {
175            throw new IllegalArgumentException("file must not be null");
176        }
177
178        SAXParserFactory spf = SAXParserFactory.newInstance();
179        spf.setValidating(true);
180
181        SAXParser saxParser = null;
182        InputSource inputSource = null;
183        try {
184            saxParser = spf.newSAXParser();
185            inputSource = new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
186        }
187        catch (UnsupportedEncodingException e) {
188            Console.printerr("Error parsing file " + file.getName());
189            Console.logException(e);
190        }
191        catch (ParserConfigurationException e) {
192            Console.printerr("Error parsing file " + file.getName());
193            Console.logException(e);
194        }
195        catch (SAXException e) {
196            Console.printerr("Error parsing file " + file.getName());
197            Console.logException(e);
198        }
199        catch (FileNotFoundException e) {
200            Console.printerr("Error parsing file " + file.getName());
201            Console.logException(e);
202        }
203       
204        if (inputSource != null) {
205            inputSource.setSystemId("file://" + file.getAbsolutePath());
206            try {
207                if (saxParser == null) {
208                    throw new RuntimeException("SAXParser creation failed");
209                }
210                saxParser.parse(inputSource, this);
211            }
212            catch (SAXParseException e) {
213                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
214                                   ", column " + e.getColumnNumber() + ".");
215                Console.logException(e);
216            }
217            catch (SAXException e) {
218                Console.printerr("Error parsing file " + file.getName());
219                Console.logException(e);
220            }
221            catch (IOException e) {
222                Console.printerr("Error parsing file " + file.getName());
223                Console.logException(e);
224            }
225        }
226       
227        if (countMessageOccurences) {
228            Console.println("Message statistics:");
229            Console.println
230                (typeCounter.toString().replace(" ", StringTools.ENDLINE).replaceAll("[\\{\\}]", ""));
231        }
232    }
233   
234    /**
235     * <p>
236     * Returns the collection of event sequences that is obtained from parsing log files.
237     * </p>
238     *
239     * @return collection of event sequences
240     */
241    public Collection<List<Event>> getSequences() {
242        return sequences;
243    }
244
245    /**
246     * <p>
247     * Returns the gui model that is obtained from parsing log files.
248     * </p>
249     *
250     * @return collection of event sequences
251     */
252    public GUIModel getGuiModel() {
253        if( currentWindowTree!=null ) {
254            return currentWindowTree.getGUIModel();
255        } else {
256            return null;
257        }
258    }
259
260    /*
261     * (non-Javadoc)
262     *
263     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
264     * java.lang.String, org.xml.sax.Attributes)
265     */
266    @Override
267    public void startElement(String uri, String localName, String qName, Attributes atts)
268        throws SAXException
269    {
270        if (qName.equals("session")) {
271            Console.traceln(Level.FINE, "start of session");
272            // in some logs, the session end may be marked in between the log. This is because
273            // of thread problems. So instead of creating a new GUI model, preserve it.
274            if (currentWindowTree == null) {
275                currentWindowTree = new WindowTree();
276            }
277            sequenceSplitter = new SequenceSplitter(currentWindowTree);
278        }
279        else if (qName.equals("msg")) {
280            currentMessageType = WindowsMessageType.parseMessageType(atts.getValue("type"));
281
282            if (countMessageOccurences) {
283                Integer currentCount = typeCounter.get(currentMessageType);
284                if (currentCount == null) {
285                    typeCounter.put(currentMessageType, 1);
286                }
287                else {
288                    typeCounter.put(currentMessageType, currentCount + 1);
289                }
290            }
291
292            if (currentMessageType == WindowsMessageType.WM_CREATE) {
293                currentHandler = new HandlerCreate(currentWindowTree);
294                currentHandler.onStartElement();
295            }
296            else if (currentMessageType == WindowsMessageType.WM_DESTROY) {
297                currentHandler = new HandlerDestroy(currentWindowTree);
298                currentHandler.onStartElement();
299            }
300            else if (currentMessageType == WindowsMessageType.WM_SETTEXT) {
301                currentHandler = new HandlerSetText(currentWindowTree);
302                currentHandler.onStartElement();
303            }
304        }
305        else if (qName.equals("param")) {
306            if (currentHandler != null) {
307                currentHandler.onParameter(atts.getValue("name"), atts.getValue("value"));
308            }
309            else {
310                // provide the parameters directly in the correct type
311                String paramName = atts.getValue("name");
312                if (("window.hwnd".equals(paramName)) ||
313                    ("source".equals(paramName)) ||
314                    ("LPARAM".equals(paramName)) ||
315                    ("WPARAM".equals(paramName)) ||
316                    ("scrollPos".equals(paramName)) ||
317                    ("scrollBarHandle".equals(paramName)))
318                {
319                    Long paramValue = Long.parseLong(atts.getValue("value"));
320                    currentMessageParameters.put(paramName, paramValue);
321                }
322                else {
323                    currentMessageParameters.put(paramName, atts.getValue("value"));
324                }
325            }
326        }
327    }
328
329    /*
330     * (non-Javadoc)
331     *
332     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
333     * java.lang.String)
334     */
335    @Override
336    public void endElement(String uri, String localName, String qName) throws SAXException {
337        if (qName.equals("msg")) {
338            if (currentHandler != null) {
339                currentHandler.onEndElement();
340                currentHandler = null;
341            }
342            else {
343                try {
344                    long hwnd = (Long) currentMessageParameters.get("window.hwnd");
345                    MFCGUIElement target = currentWindowTree.find(hwnd);
346                   
347                    WindowsMessage message = new WindowsMessage
348                        (currentMessageType, target, currentMessageParameters);
349                   
350                    sequenceSplitter.addMessage(message);
351                }
352                catch (IllegalArgumentException e) {
353                    Console.traceln(Level.WARNING, e.getMessage() + " WindowsMessage " + currentMessageType +
354                                    " ignored.");
355                }
356            }
357        }
358        else if (qName.equals("session")) {
359            sequenceSplitter.endSession();
360            List<Event> seq = sequenceSplitter.getSequence();
361            if (seq != null && !seq.isEmpty()) {
362                sequences.add(seq);
363            }
364            Console.traceln(Level.FINE, "end of session");
365        }
366    }
367
368}
Note: See TracBrowser for help on using the repository browser.