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

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