package de.ugoe.cs.quest.plugin.mfc; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.logging.Level; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import de.ugoe.cs.quest.eventcore.Event; import de.ugoe.cs.quest.eventcore.guimodel.GUIModel; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; import de.ugoe.cs.util.StringTools; import de.ugoe.cs.util.console.Console; /** *
* This class provides functionality to parse XML log files generated by the MFCUsageMonitor of * EventBench. The result of parsing a file is a collection of event sequences. It uses the * {@link SequenceSplitter} and the {@link EventGenerator} as well as custom defined * {@link MessageHandler} for the parsing. *
* * @author Steffen Herbold * @version 1.0 */ public class MFCLogParser extends DefaultHandler { /** ** If a custom message handler is used, this field contains its handle. Otherwise this field is * {@code null}. *
*/ private MessageHandler currentHandler; /** ** internal handle to the current window tree *
*/ private WindowTree currentWindowTree; /** ** the type of the currently parsed message *
*/ private WindowsMessageType currentMessageType; /** ** the parameters of the currently parsed message *
*/ private Map* {@link SequenceSplitter} instance used by the {@link MFCLogParser}. *
*/ private SequenceSplitter sequenceSplitter; /** ** Collection of message sequences that is contained in the log file, which is parsed. *
*/ private Collection* Debugging variable that allows the analysis which message type occurs how often in the log * file. Can be used to enhance the message filter. *
*/ private SortedMap* Debugging variable that enables the counting of the occurrences of each message. Used in * combination with {@link #typeCounter}. *
*/ private boolean countMessageOccurences; /** ** Constructor. Creates a new LogParser that does not count message occurrences. *
*/ public MFCLogParser() { this(false); } /** ** Constructor. Creates a new LogParser. *
* * @param countMessageOccurences * if true, the occurrences of each message type in the log is counted. */ public MFCLogParser(boolean countMessageOccurences) { sequences = new LinkedList* Parses a log file written by the MFCMonitor and creates a collection of event sequences. *
* * @param filename * name and path of the log file */ public void parseFile(String filename) { if (filename == null) { throw new IllegalArgumentException("filename must not be null"); } parseFile(new File(filename)); } /** ** Parses a log file written by the MFCMonitor and creates a collection of event sequences. *
* * @param file * name and path of the log file */ public void parseFile(File file) { if (file == null) { throw new IllegalArgumentException("file must not be null"); } SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(true); SAXParser saxParser = null; InputSource inputSource = null; try { saxParser = spf.newSAXParser(); inputSource = new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8")); } catch (UnsupportedEncodingException e) { // TODO handle Exception e.printStackTrace(); } catch (ParserConfigurationException e) { // TODO handle Exception e.printStackTrace(); } catch (SAXException e) { // TODO handle Exception e.printStackTrace(); } catch (FileNotFoundException e) { // TODO handle Exception e.printStackTrace(); } if (inputSource != null) { inputSource.setSystemId("file://" + file.getAbsolutePath()); try { if (saxParser == null) { throw new RuntimeException("SAXParser creation failed"); } saxParser.parse(inputSource, this); } catch (SAXParseException e) { Console.printerrln("Failure parsing file in line " + e.getLineNumber() + ", column " + e.getColumnNumber() + "."); e.printStackTrace(); } catch (SAXException e) { // TODO handle Exception e.printStackTrace(); } catch (IOException e) { // TODO handle Exception e.printStackTrace(); } } if (countMessageOccurences) { Console.println("Message statistics:"); Console.println (typeCounter.toString().replace(" ", StringTools.ENDLINE).replaceAll("[\\{\\}]", "")); } } /** ** Returns the collection of event sequences that is obtained from parsing log files. *
* * @return collection of event sequences */ public Collection* Returns the gui model that is obtained from parsing log files. *
* * @return collection of event sequences */ public GUIModel getGuiModel() { return currentWindowTree.getGUIModel(); } /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, * java.lang.String, org.xml.sax.Attributes) */ @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if (qName.equals("session")) { Console.traceln(Level.FINE, "start of session"); // in some logs, the session end may be marked in between the log. This is because // of thread problems. So instead of creating a new GUI model, preserve it. if (currentWindowTree == null) { currentWindowTree = new WindowTree(); } sequenceSplitter = new SequenceSplitter(currentWindowTree); } else if (qName.equals("msg")) { currentMessageType = WindowsMessageType.parseMessageType(atts.getValue("type")); if (countMessageOccurences) { Integer currentCount = typeCounter.get(currentMessageType); if (currentCount == null) { typeCounter.put(currentMessageType, 1); } else { typeCounter.put(currentMessageType, currentCount + 1); } } if (currentMessageType == WindowsMessageType.WM_CREATE) { currentHandler = new HandlerCreate(currentWindowTree); currentHandler.onStartElement(); } else if (currentMessageType == WindowsMessageType.WM_DESTROY) { currentHandler = new HandlerDestroy(currentWindowTree); currentHandler.onStartElement(); } else if (currentMessageType == WindowsMessageType.WM_SETTEXT) { currentHandler = new HandlerSetText(currentWindowTree); currentHandler.onStartElement(); } } else if (qName.equals("param")) { if (currentHandler != null) { currentHandler.onParameter(atts.getValue("name"), atts.getValue("value")); } else { // provide the parameters directly in the correct type String paramName = atts.getValue("name"); if (("window.hwnd".equals(paramName)) || ("source".equals(paramName)) || ("LPARAM".equals(paramName)) || ("WPARAM".equals(paramName)) || ("scrollPos".equals(paramName)) || ("scrollBarHandle".equals(paramName))) { Long paramValue = Long.parseLong(atts.getValue("value")); currentMessageParameters.put(paramName, paramValue); } else { currentMessageParameters.put(paramName, atts.getValue("value")); } } } } /* * (non-Javadoc) * * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, * java.lang.String) */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equals("msg")) { if (currentHandler != null) { currentHandler.onEndElement(); currentHandler = null; } else { try { long hwnd = (Long) currentMessageParameters.get("window.hwnd"); MFCGUIElement target = currentWindowTree.find(hwnd); WindowsMessage message = new WindowsMessage (currentMessageType, target, currentMessageParameters); sequenceSplitter.addMessage(message); } catch (IllegalArgumentException e) { Console.traceln(Level.WARNING, e.getMessage() + " WindowsMessage " + currentMessageType + " ignored."); } } } else if (qName.equals("session")) { sequenceSplitter.endSession(); List