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

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