source: trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/MFCLogParser.java @ 766

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